__attribute__((构造函数))究竟是如何工作的?

很明显,它是用来建立关系的。

  1. 具体什么时候开始?
  2. 为什么有两个括号?
  3. __attribute__是一个函数吗?一个宏?语法呢?
  4. 这在C语言中可行吗?c++吗?
  5. 它使用的函数需要是静态的吗?
  6. __attribute__((destructor))何时运行?

Objective-C中的例子:

__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}


__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
168200 次浏览
  1. 它在加载共享库时运行,通常是在程序启动期间。
  2. 这就是所有GCC属性的方式;大概是为了区别于函数调用。
  3. GCC-specific语法。
  4. 是的,这可以在C和c++中工作。
  5. 不,函数不需要是静态的。
  6. 析构函数在卸载共享库时运行,通常在程序退出时运行。

因此,构造函数和析构函数的工作方式是共享对象文件包含特殊的部分(。它们分别包含对用构造函数和析构函数属性标记的函数的引用。当库被加载/卸载时,动态加载器程序(ld.so或某某)会检查这些部分是否存在,如果存在,则调用其中引用的函数。

仔细想想,在普通的静态链接器中可能存在一些类似的魔法,以便在启动/关机时运行相同的代码,无论用户选择静态或动态链接。

.init/.fini未弃用。它仍然是ELF标准的一部分,我敢说它将永远是。.init/.fini中的代码在加载/卸载代码时由加载器/运行时链接器运行。也就是说,在每次ELF加载(例如共享库)时,将运行.init中的代码。仍然可以使用该机制来实现与__attribute__((constructor))/((destructor))相同的功能。这很老派,但也有好处。

例如,.ctors/.dtors机制需要system-rtl/loader/link -script的支持。这还远远不能确定在所有系统上都可用,例如代码在裸金属上执行的深度嵌入式系统。也就是说,即使GCC支持__attribute__((constructor))/((destructor)),也不确定它是否会运行,因为它取决于链接器来组织它,并由加载器(或在某些情况下,引导代码)来运行它。要使用.init/.fini,最简单的方法是使用链接器标志:-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中有几个函数,但在纯C中完全透明地将它们放在那里,而不将代码分解到许多小的.so文件中,这在语法上是困难的。

.ctors/.dtors.init/.fini的组织方式不同。.ctors/.dtors部分都是带有指向函数的指针的表,而“;caller"是系统提供的循环,间接调用每个函数。例如,循环调用者可以是特定于体系结构的,但由于它是系统的一部分(如果它存在的话),这并不重要。

下面的代码段向.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循环的附加函数。但有了它,可以更好地控制执行顺序,添加参数内和返回代码处理等(例如,在c++项目中,如果需要在全局构造函数之前或之后运行一些东西,它会很有用)。

我更喜欢__attribute__((constructor))/((destructor))在可能的情况下,这是一个简单而优雅的解决方案,即使它感觉像作弊。对于像我这样的裸金属编码员来说,这并不总是一个选择。

一些很好的参考在书< em >连接器,加载器< / em >

本页提供了关于constructordestructor属性实现以及ELF中允许它们工作的部分的深入理解。在消化了这里提供的信息后,我编译了一些额外的信息,并(借用了上面Michael Ambrus的部分示例)创建了一个示例来说明概念并帮助我的学习。下面提供了这些结果和示例源代码。

正如本线程中所解释的,constructordestructor属性在目标文件的.ctors.dtors部分中创建条目。可以通过以下三种方式之一在任意部分中放置对函数的引用。(1)使用section属性;(2) constructordestructor属性或(3)使用内联汇编调用(参考Ambrus回答中的链接)。

constructordestructor属性的使用允许你额外地为构造函数/析构函数分配优先级,以控制它在main()被调用之前或返回之后的执行顺序。给出的优先级值越低,执行优先级就越高(低优先级在main()之前执行,在main()之后执行,在高优先级之后执行)。你给必须大于__abc3的优先级值是编译器为实现预留的0-100之间的优先级值。具有优先级的constructordestructor在未指定优先级的constructordestructor之前执行。

使用'section'属性或内联汇编,您还可以在.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("\n\ttest() utilizing -- (.section .ctors/.dtors) w/o priority\n");
}


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 .text\n");


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 101\n");
}


void construct2 () {
printf ("\n      construct2() constructor -- (.section .ctors) priority 102\n");
}


void destruct1 () {
printf ("\n      destruct1() destructor -- (.section .dtors) priority 101\n\n");
}


void destruct2 () {
printf ("\n      destruct2() destructor -- (.section .dtors) priority 102\n");
}


/* main makes no function call to any of the functions declared above
*/
int
main (int argc, char *argv[]) {


printf ("\n\t  [ 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

这个例子有助于巩固构造函数/析构函数行为,希望它对其他人也有用。

下面是一个怎样,为什么,什么时候的“具体”(和可能有用的)示例,用于使用这些方便但难看的结构…

Xcode使用一个“全局”“用户默认值”来决定哪个XCTestObserver把心都吐出来了陷入困境的控制台。

在这个例子中……当我隐式加载这个pseudo -library时,让我们称它为…libdemure.a,通过一个标志在我的测试目标á la..

OTHER_LDFLAGS = -ldemure

我想…

  1. 装载时;当XCTest加载我的测试包),覆盖“默认”XCTest“观察者”类…PS:据我所知..在这里所做的任何事情都可以在类的+ (void) load { ... }方法中以相同的效果完成。

  2. 运行我的测试....在这种情况下,在日志中减少无聊的冗长内容(根据请求实现)

  3. 返回"global" XCTestObserver类到原始状态。这样就不会搞砸其他还没有赶上潮流的XCTest运行(也就是。链接到libdemure.a)。我想这在历史上是在dealloc..但我可不想惹那个老巫婆。

所以…

#define USER_DEFS NSUserDefaults.standardUserDefaults


@interface      DemureTestObserver : XCTestObserver @end
@implementation DemureTestObserver


__attribute__((constructor)) static void hijack_observer() {


/*! here I totally hijack the default logging, but you CAN
use multiple observers, just CSV them,
i.e. "@"DemureTestObserverm,XCTestLog"
*/
[USER_DEFS setObject:@"DemureTestObserver"
forKey:@"XCTestObserverClass"];
[USER_DEFS synchronize];
}


__attribute__((destructor)) static void reset_observer()  {


// Clean up, and it's as if we had never been here.
[USER_DEFS setObject:@"XCTestLog"
forKey:@"XCTestObserverClass"];
[USER_DEFS synchronize];
}


...
@end

没有链接标志…(时尚警察云集库比蒂诺要求报复,但苹果的默认盛行,如你所愿,在这里)

enter image description here

WITH -ldemure.a链接器标志…(可理解的结果,喘息…“谢谢constructor / destructor”……人群欢呼) enter image description here

这是另一个具体的例子。它是一个共享库。共享库的主要功能是与智能卡读卡器通信,但它也可以在运行时通过UDP接收“配置信息”。UDP由一个线程处理,该线程在初始化时启动必须

__attribute__((constructor))  static void startUdpReceiveThread (void) {
pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL );
return;
    

}

这个库是用C语言编写的。