How exactly does __attribute__((constructor)) work?

155,254

Solution 1

  1. It runs when a shared library is loaded, typically during program startup.
  2. That's how all GCC attributes are; presumably to distinguish them from function calls.
  3. GCC-specific syntax.
  4. Yes, this works in C and C++.
  5. No, the function does not need to be static.
  6. 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.

Solution 2

.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.

Solution 3

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.

Solution 4

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..

  1. 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.

  2. run my tests.... in this case, with less inane verbosity in the logs (implementation upon request)

  3. 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.

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)

enter image description here

WITH the -ldemure.a linker flag... (Comprehensible results, gasp... "thanks constructor/destructor"... Crowd cheers) enter image description here

Solution 5

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 MUST be started at init time.

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

The library was written in C.

Share:
155,254
Casebash
Author by

Casebash

Bachelor of Science (Adv Maths) with Honors in Computer Science from University of Sydney Programming C/C++/Java/Python/Objective C/C#/Javascript/PHP

Updated on July 08, 2022

Comments

  • Casebash
    Casebash almost 2 years

    It seems pretty clear that it is supposed to set things up.

    1. When exactly does it run?
    2. Why are there two parentheses?
    3. Is __attribute__ a function? A macro? Syntax?
    4. Does this work in C? C++?
    5. Does the function it works with need to be static?
    6. When does __attribute__((destructor)) run?

    Example in Objective-C:

    __attribute__((constructor))
    static void initialize_navigationBarImages() {
      navigationBarImages = [[NSMutableDictionary alloc] init];
    }
    
    __attribute__((destructor))
    static void destroy_navigationBarImages() {
      [navigationBarImages release];
    }
    
  • C. K. Young
    C. K. Young over 14 years
    The double brackets make them easy to "macro out" (#define __attribute__(x)). If you have multiple attributes, e.g., __attribute__((noreturn, weak)), it'd be hard to "macro out" if there were only one set of brackets.
  • ephemient
    ephemient over 14 years
    It isn't done with .init/.fini. (You can validly have multiple constructors and destructors in a single translation unit, nevermind multiple in a single library -- how would that work?) Instead, on platforms using ELF binary format (Linux, etc.), the constructors and destructors are referenced in the .ctors and .dtors sections of the header. True, in the old days, functions named init and fini would be run on dynamic library load and unload if they existed, but that's deprecated now, replaced by this better mechanism.
  • janneb
    janneb over 14 years
    @ephemient: Thanks, I had forgot about the new and improved way of doing things. Answer updated accordingly.
  • cheshirekow
    cheshirekow almost 14 years
    FYI: In the gcc online documents (gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html) it says the following under "constructor, destructor": "These attributes are not currently implemented for Objective-C."
  • jcayzac
    jcayzac over 11 years
    @ChrisJester-Young: #define __attribute__(x, ...) would work well in that case, with no added complexity.
  • C. K. Young
    C. K. Young over 11 years
    @jcayzac No, because variadic macros is a gcc extension, and the main reason for macroing out __attribute__ is if you're not using gcc, since that, too, is a gcc extension.
  • jcayzac
    jcayzac over 11 years
    @ChrisJester-Young variadic macros are a standard C99 feature, not a GNU extension.
  • C. K. Young
    C. K. Young over 11 years
    @jcayzac Before C99, back when __attribute__ was first invented, guess which C compilers had to be supported.
  • jcayzac
    jcayzac over 11 years
    @ChrisJester-Young of course back then the double parentheses were needed, I wasn't questioning that. I just wanted to point out your use of present tense ("make" instead of "made, "it'd be" instead of "it'd have been"…). C99 is 13 years old.
  • C. K. Young
    C. K. Young over 11 years
    @jcayzac C99 is 13 years old, but C++11 is not. ;-) Many header files are written for C++ compatibility too, and there are still many, many programs that are written for C++98. So I'd still say that the use of the present tense remains justified, for a great number of programs still in maintenance.
  • Jim Balter
    Jim Balter almost 10 years
    "your use of present tense ("make" instead of "made" -- the double parens still make them easy to macro out. You barked up the wrong pedantic tree.
  • Justin
    Justin over 9 years
    Where did you find that "the priority values you give must be greater than 100"? That information isn't present on the GCC function attributes documentation.
  • David C. Rankin
    David C. Rankin over 9 years
    IIRC, there were a couple of references, PATCH: Support priority argument for constructor/destructor arguments(MAX_RESERVED_INIT_PRIORITY), and that they were the same as C++ (init_priority) 7.7 C++-Specific Variable, Function, and Type Attributes. Then I tried it with 99: warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));.
  • Justin
    Justin over 9 years
    Ah. I tried priorities < 100 with clang and it seemed to be working, but my simple test case (a single compilation unit) was too simple.
  • dashesy
    dashesy almost 8 years
    What is the priority of static global variables (static ctors)?
  • David C. Rankin
    David C. Rankin almost 8 years
    The effect and visibility of a static global will depend on how your program is structured (e.g single file, multiple files (translation units)) and in which the global is declared See: Static (keyword), specifically the Static global variable description.
  • izac89
    izac89 over 5 years
    how can the loader call those functions? those functions can use globals and other functions in the process address space, but loader is a process with its own address space, isnt it?
  • izac89
    izac89 over 5 years
    how can the loader call those functions? those functions can use globals and other functions in the process address space, but loader is a process with its own address space, isnt it?
  • Paul Stelian
    Paul Stelian about 5 years
    @user2162550 No, ld-linux.so.2 (the usual "interpreter", the loader for dynamic libraries that runs on all dynamically linked executables) runs in the very address space of the executable itself. In general the dynamic library loader itself is something specific to the userspace, running in the context of the thread that tries to access a library resource.
  • Scott Franco
    Scott Franco about 5 years
    So this is the best overview I have found so far. One question: How is the .init section order determined/which .init routine runs first? I got hosed by another package calling my routines before the constructors ran, so I believe it was an .init section routine that did it (and of course it crashed with a segfault). Sounds like I need to be at the top of the .init list.
  • David C. Rankin
    David C. Rankin about 5 years
    @ScottFranco if you use the section attribute or inline assembly to place a function reference in the .init section of the ELF executable, then that function will be called before any function placed in an object file by either method or with constructor attribute. If you write more than one call in the .init section, then the functions would be called in the order placed. A second call simply adds another call somefunction entry in the ELF .init section.
  • David C. Rankin
    David C. Rankin about 5 years
    For example if you add a second int elf_init2 (void) function below the original int elf_init(void) it will be called after the original elf_init but before any of the functions declared with the constructor attribute.
  • B Abali
    B Abali almost 5 years
    When I call execv() from the code that has __attribute__((constructor))/((destructor)) the destructor does not run. I tried few things such as adding an entry to .dtor as shown above. But no success. The problem is easy to duplicate by running the code with numactl. For example, assume test_code contains the destructor (add a printf to the constructor and desctructor functions to debug the problem). Then run LD_PRELOAD=./test_code numactl -N 0 sleep 1. You will see that the constructor is called twice but destructor only once.
  • Conrad Meyer
    Conrad Meyer over 4 years
    @user2162550 the runtime linker/loader runs in the same process and address space as the dynamically linked binaries and libraries it loads.
  • fwyzard
    fwyzard almost 4 years
    According to gcc.gnu.org/bugzilla/show_bug.cgi?id=46770, starting from GCC 4.7.0 __attribute__((constructor)) uses the .init_array section instead of the .ctors section, and similarly for .fini_array instead of .dtors.