.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))
,那么只有一组括号就很难“淘汰”。