.init/ .fini不推荐使用。它仍然是ELF标准的一部分,我敢说它将永远存在。加载/卸载代码时,.init/中的代码.fini由加载器/运行时链接程序运行。即,将在每个ELF加载(例如共享库)中.init运行代码。仍然可以使用该机制来实现与相同的功能 __attribute__((constructor))/((destructor))。这是老式的,但有一些好处。
.ctors/ .dtors机制,例如需要system-rtl / loader / linker-script支持。在所有系统上(例如代码在裸机上执行的深度嵌入式系统),都无法肯定这是可行的。即,即使__attribute__((constructor))/((destructor))GCC支持,也不确定它将运行,因为它取决于链接器的组织和加载器(或在某些情况下是引导代码)的运行。要使用.init/ .fini,最简单的方法是使用链接器标志:-init和-fini(即从GCC命令行,语法为-Wl -init my_init -fini my_fini)。
在同时支持这两种方法的系统上,一个可能的好处是in .init之前运行.ctors代码,in .fini之后运行代码.dtors。如果顺序是相关的,那至少是一种区分init / exit函数的简单但简便的方法。
一个主要的缺点是,每个可加载模块不能轻易拥有_init一个以上的_fini功能,并且可能需要将代码片段化得更多.so。另一个是使用上述链接器方法时,它会替换原始的_init和_fini默认函数(由提供crti.o)。这是通常发生各种初始化的地方(在Linux上,这是初始化全局变量分配的地方)。解决方法在这里描述
请注意,在上面的链接中,_init()不需要层叠到原始文件,因为它仍然存在。但是call,内联汇编中的in是x86助记符,对于许多其他体系结构(例如ARM),从汇编中调用函数看起来会完全不同。即代码不透明。
.init/ .fini和.ctors/ .detors机制相似,但不完全相同。.init/中的代码按.fini“原样”运行。也就是说,您可以在.init/中拥有多个功能.fini,但是AFAIK在语法上很难在不破坏许多小.so文件中代码的情况下,以纯C语言完全透明地将其放置在其中。
.ctors/ .dtors与.init/的组织方式不同.fini。.ctors/ .dtors节都是带有函数指针的表,“调用程序”是系统提供的循环,可间接调用每个函数。也就是说,循环调用器可以是特定于体系结构的,但是由于它是系统的一部分(如果存在的话)就没有关系。
以下代码段向.ctors函数数组添加了新的函数指针,基本上与方法相同__attribute__((constructor))(方法可以与__attribute__((constructor)))。
#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循环的附加功能。但是有了它,就可以更好地控制执行顺序,添加参数并返回代码处理eta(例如,在C ++项目中,如果需要在全局构造函数之前或之后运行的东西很有用)。
我希望__attribute__((constructor))/((destructor))在可能的情况下,即使感觉像作弊,这也是一种简单而优雅的解决方案。对于像我这样的裸机编码员,这并不总是一种选择。
链接器和装载器一书中的一些很好的参考。
#define __attribute__(x))。如果您有多个属性(例如)__attribute__((noreturn, weak)),那么只有一组括号就很难“淘汰”。