How exactly does __attribute__((constructor)) work?
Here is a "concrete" (and possibly useful) example of how, why, and when to use these handy, yet unsightly constructs...
Xcode uses a "global" "user default" to decide which XCTestObserver
class spews it's heart out to the beleaguered console.
In this example... when I implicitly load this psuedo-library, let's call it... libdemure.a
, via a flag in my test target á 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.
So...
#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
Without the linker flag... (Fashion-police swarm Cupertino demanding retribution, yet Apple's default prevails, as is desired, here)
WITH the -ldemure.a
linker flag... (Comprehensible results, gasp... "thanks constructor
/destructor
"... Crowd cheers)
.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.
.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
).
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.
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
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
/.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.
.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.
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)))
.
#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).
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.
Some good reference in the book Linkers & loaders.
- 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.
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.
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.
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.
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).
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.
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).
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.