我正在尝试编写一个简单的"你好,世界"Cortex-M0 CPU的固件。目标是正确初始化和关闭C++运行时,以便在main((之前调用全局构造函数,在main((之后调用全局析构函数。到目前为止,我成功地使它的一半工作——全局构造函数正确运行,但全局析构函数却没有。我在Windows上使用gcc和Newlib,它应该初始化运行时。
struct Test {
Test() // This is invoked correctly.
{}
~Test() // This is not invoked at all.
{}
};
Test test;
int main()
{
return 0;
}
extern void __libc_init_array();
extern void __libc_fini_array();
void reset_handler() // This is the entry point.
{
__libc_init_array(); // Test::Test().
asm volatile( "bl main" ); // Invoke 'main'.
__libc_fini_array(); // Expect Test::~Test() but nothing happens.
}
我已经做了很多研究,对于ARM编译器来说,似乎应该为全局构造函数生成一个名为.init_array的部分,为全局析构函数生成一个称为.fini_array的部分。然后,链接器将把所有单元的部分合并在一起,__libc_init_array将"步行;并调用相应的函数。
这是链接器脚本的相关部分:
/* Initialization functions which run before main(),
such as global constructors. */
.init_array : ALIGN( 4 ) {
/* preinit data */
PROVIDE_HIDDEN ( __preinit_array_start = . );
KEEP( *( .preinit_array ) )
PROVIDE_HIDDEN( __preinit_array_end = . );
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN ( __init_array_start = . );
KEEP( *( SORT( .init_array.* ) ) )
KEEP(*(.init_array))
PROVIDE_HIDDEN ( __init_array_end = . );
} > flash
/* Finalization functions which run after main(),
such as global destructors. */
.fini_array : ALIGN( 4 ) {
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP ( *( .fini_array.* ) )
KEEP ( *( .fini_array ) )
PROVIDE_HIDDEN (__fini_array_end = .);
} > flash
然而,让我担心的是,当我转储对象文件,main.o时,即使不是可执行文件,我也只能看到.init_array部分,而不能看到.fini_array:
Sections:
Idx Name Size VMA LMA File off Algn
0 .group 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
1 .group 00000008 00000000 00000000 0000003c 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
2 .text 0000022c 00000000 00000000 00000044 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 .data 00000000 00000000 00000000 00000270 2**0
CONTENTS, ALLOC, LOAD, DATA
4 .bss 00000001 00000000 00000000 00000270 2**2
ALLOC
5 .text._ZN4TestC2Ev 00000012 00000000 00000000 00000270 2**1 << Test::Test()
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .text._ZN4TestD2Ev 00000012 00000000 00000000 00000282 2**1 << Test::~Test()
CONTENTS, ALLOC, LOAD, READONLY, CODE
7 .init_array 00000004 00000000 00000000 00000294 2**2 << WHERE IS .fini_array???
CONTENTS, ALLOC, LOAD, RELOC, DATA
8 .debug_info 000012d9 00000000 00000000 00000298 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
9 .debug_abbrev 000003a1 00000000 00000000 00001571 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
10 .debug_aranges 00000030 00000000 00000000 00001912 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
11 .debug_ranges 00000020 00000000 00000000 00001942 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
12 .debug_line 000002d2 00000000 00000000 00001962 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
13 .debug_str 000009e4 00000000 00000000 00001c34 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
14 .comment 0000004d 00000000 00000000 00002618 2**0
CONTENTS, READONLY
15 .debug_frame 0000054c 00000000 00000000 00002668 2**2
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
16 .ARM.attributes 0000002c 00000000 00000000 00002bb4 2**0
CONTENTS, READONLY
这就是我调用编译器的方式:
arm-none-eabi-c++.exe -o main.elf --verbose -mcpu=cortex-m0 -mthumb --specs=nano.specs --entry reset_handler -T./../src/firmware.ld -ggdb -O0 -Wall -Wextra -Wpedantic -Werror ../src/main.cpp
这是日志:
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 7a1ab17ae8404f635d46188cccacd8be
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/as.exe -v -march=armv6s-m -mfloat-abi=soft -meabi=5
GNU assembler version 2.34.0 (arm-none-eabi) using BFD version (GNU Arm Embedded Toolchain 9-2020-q2-update) 2.34.0.20200428
COMPILER_PATH=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/;c:/id/gcc/bin/../lib/gcc/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/
LIBRARY_PATH=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp/;c:/id/gcc/bin/../arm-none-eabi/lib/thumb/v6-m/nofp/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/;c:/id/gcc/bin/../lib/gcc/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/;c:/id/gcc/bin/../arm-none-eabi/lib/
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/collect2.exe -plugin c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/liblto_plugin-0.dll -plugin-opt=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/lto-wrapper.exe -plugin-opt=-fresolution=C:UsersMAXID~1AppDataLocalTempccpOTkFN.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lg_nano -plugin-opt=-pass-through=-lc_nano -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc_nano --sysroot=c:idgccbin../arm-none-eabi -X -o main.elf -e reset_handler c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crti.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtbegin.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp/crt0.o -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp -Lc:/id/gcc/bin/../arm-none-eabi/lib/thumb/v6-m/nofp -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1 -Lc:/id/gcc/bin/../lib/gcc -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib -Lc:/id/gcc/bin/../arm-none-eabi/lib C:UsersMAXID~1AppDataLocalTempcc1lU5Gg.o -lstdc++_nano -lm --start-group -lgcc -lg_nano -lc_nano --end-group --start-group -lgcc -lc_nano --end-group c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtend.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtn.o -T ./../src/firmware.ld
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
公平地说,我不知道可能出了什么问题——我尝试了默认的链接器脚本,.fini_array没有出现。我想到的唯一想法是,我的编译器是用某种标志构建的,该标志禁用了.fini_array部分???
任何想法都将不胜感激!
更新:经过进一步调查,析构函数似乎更多地与__cxa_atexit有关。。。将进一步调查。
更新2:感谢这里的精彩讨论https://forum.osdev.org/viewtopic.php?f=13&t=36728我添加了-fno使用cxa atexit,然后出现.fini_array部分!
好的,所以对于那些感兴趣的人来说,gcc有两种方法可以生成对全局析构函数的调用。一种方法是通过__cxa_atexit。编译器将向全局构造函数注入一段代码,该构造函数使用__cxa_atexit来注册该对象的析构函数这样,当调用exit时,运行时";知道";调用哪些析构函数。这种复杂性是处理共享库的加载/卸载所必需的。
但是,也可以传递-fno cxa atexit标志,编译器将把对全局析构函数的调用放入.fini_array中。Newlib然后使用此部分来调用析构函数。更多信息请点击此处:https://forum.osdev.org/viewtopic.php?f=13&t=36728