How Exactly Does _Attribute_((Constructor)) Work

How exactly does __attribute__((constructor)) work?

  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.

GCC __attribute__((constructor)) is called before object constructor

__attribute__((constructor)) is a compiler extension, and so you left the realms of standard C++. It looks like GCC's constructor functions are run before global initialization.

The way to fix it is to use another vanilla C++ construct, for example a global object whose initialization is properly sequenced by defining it in the same TU as your other global:

Ala a;

static void initialize_shared_library() {
printf("initializing shared library\n");
printf("a.i=%d\n", a.getI());
printf("end of initialization of the shared library\n");
}

static int const do_init = (initialize_shared_library(), 0);

__attribute__((constructor)) how it change entry point?

Functions marked as "constructor" are placed in a special section in the executable file. The "start" function will then invoke those functions one by one, before it calls main.

The same with "destructor" functions, they are again placed in a special section and called by the code executing after exit is called or main returns.

C++ static initialization vs __attribute__((constructor))

foo will be printed first, as the objects are initialized in the order of their declarations. Run and see:

  • Ideone online demo

By the way, __attribute__((constructor)) is not Standard C++. It is GCC's extension. So the behavior of your program depends on how GCC has defined it. In short, it is implementation-defined, according to it foo is printed first.

The doc says,

The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () has completed or exit () has been called. Functions with these attributes are useful for initializing data that will be used implicitly during the execution of the program.

You may provide an optional integer priority to control the order in which constructor and destructor functions are run. A constructor with a smaller priority number runs before a constructor with a larger priority number; the opposite relationship holds for destructors. So, if you have a constructor that allocates a resource and a destructor that deallocates the same resource, both functions typically have the same priority. The priorities for constructor and destructor functions are the same as those specified for namespace-scope C++ objects (see C++ Attributes).

I think the text in bold implies, the objects are initialized in the order of their declarations, as I said before, which is pretty much confirmed by online demo also.

I guess you would also like to read this:

  • 7.7 C++-Specific Variable, Function, and Type Attributes

If you want to control/alter the initialization order, you can use init_priority attribute, providing priority. Taken from the page:

Some_Class  A  __attribute__ ((init_priority (2000)));
Some_Class B __attribute__ ((init_priority (543)));

Here, B is initialized before A.

Does __attribute__((optimize(0))) apply recursively?

I just figured I could do an experiment (why didn't I think of that sooner), here's what I did:

// toggle this
// __attribute__((optimize(0)))
void waste_time() { for (unsigned i = 100000; i--; ); }

// always leave this on
__attribute__((optimize(0)))
void test() {
for (unsigned i = 1000; i--; ) {
waste_time();
}
}

int main() {
std::cout << Timer::measure(test).count() << "ms\n";
}

What I found with -O3:

waste_time no optimizations: 196.972ms  (obviously the loop is not optimized)
waste_time w/ optimizations: 0.001995ms (loop is optimized)

For me this makes it very clear, that __attribute__ does not "propagate" to nested function calls, so waste_time() still will be optimized even though test() has the attribute specified.

C++ __attribute__ aligned meaning

CPU registers often point to memory.

When you increment a register, it points 64 bits further on a 64 bit machine.
However, if you want to get to a byte inside that section, the compiler has to do more work.

If you align memory on the "edges" of memory, the registers can access them much faster. It also means structure members are padded so some space is wasted. If you do a memory dump of the structure, you may be surprised at the padding.

__attribute__((format(printf, 1, 2))) for MSVC?

While GCC checks format specifiers when -Wformat is enabled, VC++ has no such checking, even for standard functions so there is no equivalent to this __attribute__ because there is no equivalent to -Wformat.

I think Microsoft's emphasis on C++ (evidenced by maintaining ISO compliance for C++ while only supporting C89) may be in part the reason why VC++ does not have format specifier checking; in C++ using <iostream> format specifiers are unnecessary.

__attribute__((section(name))) usage?

There are many possible uses. [Edit to add note: this is just a sample of uses I've seen myself or considered, not a complete list.]

The Linux kernel, for instance, marks some code and data sections as used only during kernel bootstrap. These can be jettisoned after the kernel is running, reclaiming the space for other uses.

You can use this to mark code or data values that need patching on a particular processor variant, e.g., with or without a coprocessor.

You can use it to make things live in "special" address spaces that will be burned to PROM or saved on an EEPROM, rather than in ordinary memory.

You can use it to collect together code or data areas for purposes like initialization and cleanup, as with C++ constructors and destructors that run before the program starts and when it ends, or for using shorter addressing modes (I don't know how much that would apply on ARM as I have not written any ARM code myself).

The actual use depends on the linker script(s).



Related Topics



Leave a reply



Submit