C++ __attribute__((constructor)) 究竟是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2053029/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How exactly does __attribute__((constructor)) work?
提问by Casebash
It seems pretty clear that it is supposed to set things up.
很明显,它应该设置一些东西。
- When exactly does it run?
- Why are there two parentheses?
- Is
__attribute__
a function? A macro? Syntax? - Does this work in C? C++?
- Does the function it works with need to be static?
- When does
__attribute__((destructor))
run?
- 具体什么时候运行?
- 为什么有两个括号?
- 是
__attribute__
函数吗?一个宏?句法? - 这在 C 中有效吗?C++?
- 它使用的功能是否需要是静态的?
- 什么时候
__attribute__((destructor))
跑?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
采纳答案by janneb
- It runs when a shared library is loaded, typically during program startup.
- That's how all GCC attributes are; presumably to distinguish them from function calls.
- GCC-specific syntax.
- Yes, this works in C and C++.
- No, the function does not need to be static.
- The destructor runs when the shared library is unloaded, typically at program exit.
- 它在加载共享库时运行,通常是在程序启动期间。
- 这就是所有 GCC 属性的方式;大概是为了将它们与函数调用区分开来。
- GCC 特定的语法。
- 是的,这适用于 C 和 C++。
- 不,该函数不需要是静态的。
- 析构函数在共享库卸载时运行,通常是在程序退出时。
So, the way the constructors and destructors work is that the shared object file contains special sections (.ctors and .dtors on ELF) which contain references to the functions marked with the constructor and destructor attributes, respectively. When the library is loaded/unloaded the dynamic loader program (ld.so or somesuch) checks whether such sections exist, and if so, calls the functions referenced therein.
因此,构造函数和析构函数的工作方式是共享对象文件包含特殊部分(ELF 上的 .ctors 和 .dtors),其中包含对分别标有构造函数和析构函数属性的函数的引用。当库被加载/卸载时,动态加载程序(ld.so 或 somesuch)检查是否存在这样的部分,如果存在,则调用其中引用的函数。
Come to think of it, there is probably some similar magic in the normal static linker so that the same code is run on startup/shutdown regardless if the user chooses static or dynamic linking.
想想看,普通的静态链接器中可能有一些类似的魔法,因此无论用户选择静态链接还是动态链接,都会在启动/关闭时运行相同的代码。
回答by Michael Ambrus
.init
/.fini
isn't deprecated. It's still part of the the ELF standard and I'd dare say it will be forever. Code in .init
/.fini
is run by the loader/runtime-linker when code is loaded/unloaded. I.e. on each ELF load (for example a shared library) code in .init
will be run. It's still possible to use that mechanism to achieve about the same thing as with __attribute__((constructor))/((destructor))
. It's old-school but it has some benefits.
.init
/.fini
没有被弃用。它仍然是 ELF 标准的一部分,我敢说它将永远存在。代码.init
/.fini
由加载/运行时-接头运行加载代码时/卸载。即在每个 ELF 加载(例如共享库)中的代码.init
将被运行。仍然可以使用该机制来实现与 __attribute__((constructor))/((destructor))
. 这是老派,但它有一些好处。
.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
).
.ctors
/.dtors
机制例如需要 system-rtl/loader/linker-script 的支持。这远非在所有系统上都可用,例如代码在裸机上执行的深度嵌入式系统。即即使__attribute__((constructor))/((destructor))
GCC 支持,也不确定它会运行,因为它取决于链接器来组织它和加载器(或在某些情况下,引导代码)来运行它。要使用.init
/.fini
代替,最简单的方法是使用链接器标志:-init & -fini(即来自 GCC 命令行,语法为-Wl -init my_init -fini my_fini
)。
On system supporting both methods, one possible benefit is that code in .init
is run before .ctors
and code in .fini
after .dtors
. If order is relevant that's at least one crude but easy way to distinguish between init/exit functions.
在支持这两种方法的系统上,一个可能的好处是代码输入在.init
之前运行.ctors
,代码在.fini
之后运行.dtors
。如果顺序是相关的,那至少是一种区分 init/exit 函数的粗略但简单的方法。
A major drawback is that you can't easily have more than one _init
and one _fini
function per each loadable module and would probably have to fragment code in more .so
than motivated. Another is that when using the linker method described above, one replaces the original _init and _fini
default functions (provided by crti.o
). This is where all sorts of initialization usually occur (on Linux this is where global variable assignment is initialized). A way around that is described here
一个主要的缺点是,每个可加载模块不能轻易地拥有一个_init
和一个以上的_fini
功能,并且可能不得不将代码分割成更多的.so
动机。另一个是,在使用上述链接器方法时,会替换原来的 _init 和_fini
默认函数(由 提供crti.o
)。这是通常发生各种初始化的地方(在 Linux 上,这是初始化全局变量赋值的地方)。这里描述了一种解决方法
Notice in the link above that a cascading to the original _init()
is not needed as it's still in place. The call
in the inline assembly however is x86-mnemonic and calling a function from assembly would look completely different for many other architectures (like ARM for example). I.e. code is not transparent.
请注意,在上面的链接中,_init()
不需要级联到原始文件,因为它仍然存在。的call
内联组件然而是x86的助记符和调用函数从组件将看起来完全不同的许多其它体系结构(如ARM例如)。即代码不透明。
.init
/.fini
and .ctors
/.detors
mechanisms are similar, but not quite. Code in .init
/.fini
runs "as is". I.e. you can have several functions in .init
/.fini
, but it is AFAIK syntactically difficult to put them there fully transparently in pure C without breaking up code in many small .so
files.
.init
/.fini
和.ctors
/.detors
机制类似,但不完全相同。.init
/ 中的代码.fini
“按原样”运行。即您可以在.init
/ 中有多个函数.fini
,但是在语法上很难将它们完全透明地放在纯 C 中而不将代码分解为许多小.so
文件。
.ctors
/.dtors
are differently organized than .init
/.fini
. .ctors
/.dtors
sections are both just tables with pointers to functions, and the "caller" is a system-provided loop that calls each function indirectly. I.e. the loop-caller can be architecture specific, but as it's part of the system (if it exists at all i.e.) it doesn't matter.
.ctors
/.dtors
的组织方式与.init
/不同.fini
。.ctors
/.dtors
部分都只是带有指向函数的指针的表,“调用者”是系统提供的循环,它间接调用每个函数。即循环调用者可以是特定于体系结构的,但因为它是系统的一部分(如果它存在,即)它并不重要。
The following snippet adds new function pointers to the .ctors
function array, principally the same way as __attribute__((constructor))
does (method can coexist with __attribute__((constructor)))
.
下面的代码片段向.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;
One can also add the function pointers to a completely different self-invented section. A modified linker script and an additional function mimicking the loader .ctors
/.dtors
loop is needed in such case. But with it one can achieve better control over execution order, add in-argument and return code handling e.t.a. (In a C++ project for example, it would be useful if in need of something running before or after global constructors).
还可以将函数指针添加到完全不同的自创部分。在这种情况下,需要修改的链接器脚本和模拟加载器.ctors
/.dtors
循环的附加函数。但是有了它,可以更好地控制执行顺序,添加参数和返回代码处理 eta(例如,在 C++ 项目中,如果需要在全局构造函数之前或之后运行某些东西,它会很有用)。
I'd prefer __attribute__((constructor))/((destructor))
where possible, it's a simple and elegant solution even it feels like cheating. For bare-metal coders like myself, this is just not always an option.
我更喜欢__attribute__((constructor))/((destructor))
在可能的情况下,这是一个简单而优雅的解决方案,即使感觉像是作弊。对于像我这样的裸机编码员来说,这并不总是一种选择。
Some good reference in the book Linkers & loaders.
Linkers & loaders一书中的一些很好的参考。
回答by David C. Rankin
This page provides great understanding about the constructor
and destructor
attribute implementation and the sections within within ELF that allow them to work. After digesting the information provided here, I compiled a bit of additional information and (borrowing the section example from Michael Ambrus above) created an example to illustrate the concepts and help my learning. Those results are provided below along with the example source.
此页面提供了对constructor
和destructor
属性实现以及 ELF 中允许它们工作的部分的深入了解。在消化了此处提供的信息后,我编译了一些附加信息,并(借用上面 Michael Ambrus 的部分示例)创建了一个示例来说明概念并帮助我的学习。下面提供了这些结果以及示例源。
As explained in this thread, the constructor
and destructor
attributes create entries in the .ctors
and .dtors
section of the object file. You can place references to functions in either section in one of three ways. (1) using either the section
attribute; (2) constructor
and destructor
attributes or (3) with an inline-assembly call (as referenced the link in Ambrus' answer).
如该线程中所述,constructor
anddestructor
属性在对象文件的.ctors
and.dtors
部分创建条目。您可以通过以下三种方式之一在任一部分中放置对函数的引用。(1) 使用任一section
属性;(2)constructor
和destructor
属性或 (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.
constructor
和destructor
属性的使用允许您额外为构造函数/析构函数分配优先级,以控制其在main()
调用之前或返回之后的执行顺序。给定的优先级值越低,执行优先级就越高(在 main() 之前,在更高的优先级之前执行更低的优先级 - 在 main() 之后执行更高的优先级)。您提供的优先级值必须大于100
编译器保留 0-100 之间的优先级值以供实现。A constructor
ordestructor
指定的优先级在 a constructor
ordestructor
指定之前执行,没有优先级。
With the 'section' attribute or with inline-assembly, you can also place function references in the .init
and .fini
ELF code section that will execute before any constructor and after any destructor, respectively. Any functions called by the function reference placed in the .init
section, will execute before the function reference itself (as usual).
使用 'section' 属性或使用内联汇编,您还可以将函数引用放在将分别在任何构造函数之前和任何析构函数之后执行的.init
和.fini
ELF 代码部分。由放置在该.init
部分中的函数引用调用的任何函数都将在函数引用本身之前执行(像往常一样)。
I have tried to illustrate each of those in the example below:
我试图在下面的例子中说明每一个:
#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;
}
output:
输出:
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
The example helped cement the constructor/destructor behavior, hopefully it will be useful to others as well.
该示例有助于巩固构造函数/析构函数的行为,希望它对其他人也有用。
回答by Alex Gray
Here is a "concrete" (and possibly useful) example of how, why, and whento use these handy, yet unsightlyconstructs...
这是一个“具体的”(并且可能有用)示例,说明如何、为什么以及何时使用这些方便但难看的结构......
Xcode uses a "global" "user default" to decide which XCTestObserver
class spews it's heart outto the beleagueredconsole.
Xcode 使用“全局”“用户默认值”来决定哪个XCTestObserver
类将它的核心输出到陷入困境的控制台。
In this example... when I implicitly load this psuedo-library, let's call it... libdemure.a
, via a flag in my test target á la..
在这个例子中...当我隐式加载这个伪库时,让我们称之为... libdemure.a
,通过我的测试目标中的一个标志 á la..
OTHER_LDFLAGS = -ldemure
I want to..
我想要..
At load (ie. when
XCTest
loads my test bundle), override the "default"XCTest
"observer" class... (via theconstructor
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 otherXCTest
runs which haven't gotten on the bandwagon (aka. linked tolibdemure.a
). I guess this historically was done indealloc
.. but I'm not about to start messing with that old hag.
在加载时(即
XCTest
加载我的测试包时),覆盖“默认”XCTest
“观察者”类......(通过constructor
函数) PS:据我所知......这里所做的任何事情都可以在我的内部实现等效效果类”的+ (void) load { ... }
方法。运行我的测试......在这种情况下,日志中的冗长更少(根据要求实施)
将“全局”
XCTestObserver
类返回到它的原始状态.. 以免扰乱其他XCTest
没有加入潮流的运行(也就是链接到libdemure.a
)。我想这在历史上是在dealloc
..完成的,但我不会开始与那个老巫婆混为一谈。
So...
所以...
#define USER_DEFS NSUserDefaults.standardUserDefaults
@interface DemureTestObserver : XCTestObserver @end
@implementation DemureTestObserver
__attribute__((constructor)) static void hiHyman_observer() {
/*! here I totally hiHyman 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
Without the linker flag... (Fashion-police swarm Cupertino demanding retribution, yet Apple's default prevails, as is desired, here)
没有链接器标志......(时尚警察蜂拥而至库比蒂诺要求报复,但 Apple 的默认设置占上风,正如所希望的,这里)
WITH the -ldemure.a
linker flag... (Comprehensible results, gasp... "thanks constructor
/destructor
"... Crowd cheers)
带有-ldemure.a
链接器标志......(可理解的结果,喘气......“谢谢constructor
/ destructor
”......人群欢呼)
回答by drlolly
Here is another concrete example.It is for a shared library. The shared library's main function is to communicate with a smart card reader. But it can also receive 'configuration information' at runtime over udp. The udp is handled by a thread which MUSTbe started at init time.
这是另一个具体的例子。它是一个共享库。共享库的主要功能是与智能卡读卡器通信。但它也可以在运行时通过 udp 接收“配置信息”。udp 由必须在初始化时启动的线程处理。
__attribute__((constructor)) static void startUdpReceiveThread (void) {
pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL );
return;
}
The library was written in c.
该库是用 c 编写的。