到底如何
这似乎很清楚,它应该设置的东西。
__attribute__
是一个函数吗? 宏? 句法? __attribute__((destructor))
运行? 目标C中的示例:
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
因此,构造函数和析构函数的工作方式是共享目标文件包含特殊部分(ELF上的.ctors和.dtors),它们分别包含对构造函数和析构函数属性标记的函数的引用。 当库被加载/卸载时,动态加载程序(ld.so或somesuch)会检查这些段是否存在,如果存在,则调用其中引用的函数。
想想看,在普通静态链接器中可能存在一些相似的魔术,因此无论用户选择静态链接还是动态链接,都可以在启动/关闭时运行相同的代码。
.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
)。
在系统支持这两种方法,一个可能的好处是在代码.init
之前运行.ctors
和代码在.fini
后.dtors
。 如果订单是相关的,那么至少有一个原始但简单的方法可以区分init / exit函数。
其主要缺点是,你不能轻易地拥有多个_init
和一个_fini
每每个可加载模块功能,并可能会在更多的分段代码.so
不是动机。 另一个原因是,当使用上述链接器方法时,将替换原始的_init和_fini
默认函数(由crti.o
提供)。 这是通常发生各种初始化的地方(在Linux上,这是全局变量赋值初始化的地方)。 这里介绍一种解决方法
注意在上面的链接中,不需要级联到原始_init()
,因为它仍然存在。 然而,内联程序集中的call
是x86助记符,并且从程序集调用函数对于许多其他体系结构(例如ARM)看起来完全不同。 即代码不透明。
.init
/ .fini
和.ctors
/ .detors
机制类似,但并不完全。 .init
/ .fini
代码按“原样”运行。 也就是说,你可以在.init
/ .fini
有几个函数,但是AFAIK在语法上很难将它们完全透明地放在纯C中,而不会破坏很多小的.so
文件中的代码。
.ctors
/ .dtors
与.init
/ .fini
。 .ctors
/ .dtors
部分都是指向函数指针的表格,“调用者”是系统提供的循环,间接调用每个函数。 也就是说,循环调用者可以是架构特定的,但由于它是系统的一部分(如果它全部存在),它并不重要。
下面的片段为.ctors
函数数组添加了新的函数指针,主要与__attribute__((constructor))
(方法可以和__attribute__((constructor)))
共存__attribute__((constructor)))
。
#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
printf("Hellon");
}
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))
,这是一个简单而优雅的解决方案,即使它看起来像是作弊。 对于像我这样的裸机编码器来说,这并不总是一种选择。
在书籍链接器和装载机书中的一些很好的参考。
本页提供了对constructor
和destructor
属性实现以及ELF内允许它们工作的部分的深入理解。 在消化了这里提供的信息之后,我汇编了一些额外的信息(借用上面的Michael Ambrus的部分示例)创建了一个示例来说明概念并帮助我学习。 下面结合示例源提供这些结果。
如此线程中所述, constructor
和destructor
属性在对象文件的.ctors
和.dtors
部分中创建条目。 您可以通过以下三种方法之一在任一部分中放置对函数的引用。 (1)使用section
属性; (2) constructor
和destructor
属性,或(3)内联程序集调用(参考Ambrus'答案中的链接)。
constructor
和destructor
的使用允许您在调用main()
之前或返回之后,为构造函数/析构函数额外分配一个优先级来控制其执行顺序。 给定的优先级值越低,执行优先级越高(较低优先级在main()之前的较高优先级之前执行 - 并且在main()之后的较高优先级之后执行)。 您给出的优先级值必须大于100
因为编译器会保留0-100之间的优先级值以供实施。 一个constructor
或destructor
优先指定之前执行constructor
或destructor
没有优先指定。
通过'section'属性或inline-assembly,您还可以将函数引用放置在.init
和.fini
ELF代码部分中,分别在任何构造函数之前和任何析构函数之后执行。 放置在.init
节中的函数引用调用的任何函数将在函数引用自身之前执行(像往常一样)。
我试图说明下面的例子中的每一个:
#include <stdio.h>
#include <stdlib.h>
/* test function utilizing attribute 'section' ".ctors"/".dtors"
to create constuctors/destructors without assigned priority.
(provided by Michael Ambrus in earlier answer)
*/
#define SECTION( S ) __attribute__ ((section ( S )))
void test (void) {
printf("nttest() utilizing -- (.section .ctors/.dtors) w/o priorityn");
}
void (*funcptr1)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;
/* functions constructX, destructX use attributes 'constructor' and
'destructor' to create prioritized entries in the .ctors, .dtors
ELF sections, respectively.
NOTE: priorities 0-100 are reserved
*/
void construct1 () __attribute__ ((constructor (101)));
void construct2 () __attribute__ ((constructor (102)));
void destruct1 () __attribute__ ((destructor (101)));
void destruct2 () __attribute__ ((destructor (102)));
/* init_some_function() - called by elf_init()
*/
int init_some_function () {
printf ("n init_some_function() called by elf_init()n");
return 1;
}
/* elf_init uses inline-assembly to place itself in the ELF .init section.
*/
int elf_init (void)
{
__asm__ (".section .init n call elf_init n .section .textn");
if(!init_some_function ())
{
exit (1);
}
printf ("n elf_init() -- (.section .init)n");
return 1;
}
/*
function definitions for constructX and destructX
*/
void construct1 () {
printf ("n construct1() constructor -- (.section .ctors) priority 101n");
}
void construct2 () {
printf ("n construct2() constructor -- (.section .ctors) priority 102n");
}
void destruct1 () {
printf ("n destruct1() destructor -- (.section .dtors) priority 101nn");
}
void destruct2 () {
printf ("n destruct2() destructor -- (.section .dtors) priority 102n");
}
/* main makes no function call to any of the functions declared above
*/
int
main (int argc, char *argv[]) {
printf ("nt [ main body of program ]n");
return 0;
}
输出:
init_some_function() called by elf_init()
elf_init() -- (.section .init)
construct1() constructor -- (.section .ctors) priority 101
construct2() constructor -- (.section .ctors) priority 102
test() utilizing -- (.section .ctors/.dtors) w/o priority
test() utilizing -- (.section .ctors/.dtors) w/o priority
[ main body of program ]
test() utilizing -- (.section .ctors/.dtors) w/o priority
destruct2() destructor -- (.section .dtors) priority 102
destruct1() destructor -- (.section .dtors) priority 101
这个例子帮助巩固了构造器/析构器的行为,希望对其他人也有用。
链接地址: http://www.djcxy.com/p/15221.html上一篇: How exactly does
下一篇: How do you use gcc to generate assembly code in Intel syntax?