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

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

在Objective-C中的例子:

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

__attribute__((destructor))
static void destroy_navigationBarImages() {
  [navigationBarImages release];
}

它在加载共享库时运行,通常是在程序启动期间。 这就是所有GCC属性的方式;大概是为了区别于函数调用。 GCC-specific语法。 是的,这可以在C和c++中工作。 不,函数不需要是静态的。 析构函数在卸载共享库时运行,通常在程序退出时运行。

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

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


.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__((构造函数))/((析构函数))在可能的情况下,这是一个简单而优雅的解决方案,即使它感觉像作弊。对于像我这样的裸金属编码员来说,这并不总是一个选择。

一些很好的参考在书链接器和加载器。


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

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

The use of constructor and destructor attributes allow you to additionally assign a priority to the constructor/destructor to control its order of execution before main() is called or after it returns. The lower the priority value given, the higher the execution priority (lower priorities execute before higher priorities before main() -- and subsequent to higher priorities after main() ). The priority values you give must be greater than100 as the compiler reserves priority values between 0-100 for implementation. Aconstructor or destructor specified with priority executes before a constructor or destructor specified without priority.

使用'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

我想…

At load (ie. when XCTest loads my test bundle), override the "default" XCTest "observer" class... (via the constructor function) PS: As far as I can tell.. anything done here could be done with equivalent effect inside my class' + (void) load { ... } method. run my tests.... in this case, with less inane verbosity in the logs (implementation upon request) Return the "global" XCTestObserver class to it's pristine state.. so as not to foul up other XCTest runs which haven't gotten on the bandwagon (aka. linked to libdemure.a). I guess this historically was done in dealloc.. but I'm not about to start messing with that old hag.

所以…

#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

没有链接标志…(时尚警察蜂拥至库比蒂诺要求报复,但苹果的默认盛行,正如大家所希望的那样)

与-ldemure。一个链接标志…(可理解的结果,喘气……构造/析构”“谢谢…人群欢呼)


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

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

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