.init /。不反对使用Fini。它仍然是ELF标准的一部分,我敢说它将永远是。.init/中的代码。Fini由加载器/运行时链接器在加载/卸载代码时运行。例如,在每次ELF加载(例如共享库)时,将运行.init中的代码。仍然可以使用该机制来实现与__attribute__((构造函数))/((析构函数))相同的功能。这很老派,但也有好处。
.ctors/.dtors mechanism for example require support by system-rtl/loader/linker-script. This is far from certain to be available on all systems, for example deeply embedded systems where code executes on bare metal. I.e. even if __attribute__((constructor))/((destructor)) is supported by GCC, it's not certain it will run as it's up to the linker to organize it and to the loader (or in some cases, boot-code) to run it. To use .init/.fini instead, the easiest way is to use linker flags: -init & -fini (i.e. from GCC command line, syntax would be -Wl -init my_init -fini my_fini).
在支持这两种方法的系统上,一个可能的好处是.init中的代码在.ctors之前运行,而.fini中的代码在.dtors之后运行。如果顺序是相关的,那么至少有一种粗略但简单的方法可以区分init/exit函数。
一个主要的缺点是,你不能轻易地在每个可加载模块中有一个以上的_init和_fini函数,并且可能不得不将代码分割成更多的。so而不是动机。另一种情况是,当使用上面描述的链接器方法时,会替换原来的_init和_fini默认函数(由crti.o提供)。这是通常发生各种初始化的地方(在Linux上,这是初始化全局变量赋值的地方)。这里描述了一种解决方法
注意,在上面的链接中,不需要级联到原来的_init(),因为它仍然存在。然而,内联程序集中的调用是x86助记符的,从程序集调用函数对于许多其他架构(例如ARM)来说完全不同。也就是说,代码不透明。
.init /。Fini和.ctors/.detors机制类似,但不完全相同。.init/中的代码。Fini“按原样”运行。例如,你可以在.init/中有几个函数。但是在纯C语言中完全透明地把它们放在那里,而不把代码分解成许多小的.so文件,这在语法上是困难的。
.ctors /。Dtors的组织方式与.init/.fini. .ctors/不同。Dtors部分都是带有函数指针的表,而“调用者”是系统提供的间接调用每个函数的循环。例如,循环调用者可以是特定于体系结构的,但由于它是系统的一部分(如果它存在的话),这并不重要。
下面的代码段向.ctors函数数组添加了新的函数指针,主要与__attribute__((构造函数))的方式相同(方法可以与__attribute__((构造函数))共存)。
#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
printf("Hello\n");
}
void (*funcptr)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;
也可以将函数指针添加到完全不同的自创部分。一个修改过的链接器脚本和一个模仿加载器.ctors/的附加函数。在这种情况下需要使用Dtors循环。但有了它,可以更好地控制执行顺序,添加参数内和返回代码处理等(例如,在c++项目中,如果需要在全局构造函数之前或之后运行一些东西,它会很有用)。
我更喜欢__attribute__((构造函数))/((析构函数))在可能的情况下,这是一个简单而优雅的解决方案,即使它感觉像作弊。对于像我这样的裸金属编码员来说,这并不总是一个选择。
一些很好的参考在书链接器和加载器。