Calling Global Constructors: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(Minor fixes)
m (Reformat)
Line 3: Line 3:
On most platforms, the global constructors/destructors are stored in a stored array of function pointers and invoking these is as simple as traversing the array and running each element. However, the compiler does not always give access to this list, and some compilers consider this implementation details. In this case you will have to cooperate with the compiler - fighting the compiler will only cause trouble.
On most platforms, the global constructors/destructors are stored in a stored array of function pointers and invoking these is as simple as traversing the array and running each element. However, the compiler does not always give access to this list, and some compilers consider this implementation details. In this case you will have to cooperate with the compiler - fighting the compiler will only cause trouble.


= GNU Compiler Collection - System V ABI =
== GNU Compiler Collection - System V ABI ==


The System V ABI (as used by <tt>i686-elf-gcc</tt>, <tt>x86_64-elf-gcc</tt>, and other ELF platforms) specifies use of five different object files that together handle program initialization. These are traditionally called <tt>crt0.o</tt>, <tt>crti.o</tt>, <tt>crtbegin.o</tt>, <tt>crtend.o</tt>, and <tt>crtn.o</tt>. Together these object files implement two special functions: <tt>_init</tt> which runs the global constructors and other initialization tasks, and <tt>_fini</tt> that runs the global destructors and other termination tasks.
The System V ABI (as used by <tt>i686-elf-gcc</tt>, <tt>x86_64-elf-gcc</tt>, and other ELF platforms) specifies use of five different object files that together handle program initialization. These are traditionally called <tt>crt0.o</tt>, <tt>crti.o</tt>, <tt>crtbegin.o</tt>, <tt>crtend.o</tt>, and <tt>crtn.o</tt>. Together these object files implement two special functions: <tt>_init</tt> which runs the global constructors and other initialization tasks, and <tt>_fini</tt> that runs the global destructors and other termination tasks.
Line 18: Line 18:
The idea is that the these files together form the <tt>_init</tt> and <tt>_fini</tt> functions during the linking. This is done by storing the <tt>_init</tt> function in the <tt>.init</tt> section, and the <tt>_fini</tt> function in the <tt>.fini section</tt>. Each file then contributes a bit to these sections and the linker makes glues together the fragments in the code specified on the command line. <tt>crti.o</tt> provides the function header, <tt>crtbegin.o</tt> and <tt>crtend.o</tt> provide the body, and <tt>crtn.o</tt> provide the footer (return statement). It is important to understand that the link order matters and strange things may happen if the objects is not exactly linked in this order.
The idea is that the these files together form the <tt>_init</tt> and <tt>_fini</tt> functions during the linking. This is done by storing the <tt>_init</tt> function in the <tt>.init</tt> section, and the <tt>_fini</tt> function in the <tt>.fini section</tt>. Each file then contributes a bit to these sections and the linker makes glues together the fragments in the code specified on the command line. <tt>crti.o</tt> provides the function header, <tt>crtbegin.o</tt> and <tt>crtend.o</tt> provide the body, and <tt>crtn.o</tt> provide the footer (return statement). It is important to understand that the link order matters and strange things may happen if the objects is not exactly linked in this order.


== Using global constructors from C ==
=== Using global constructors from C ===
As a special extension, GCC allows C programs to run functions as global constructors. For more information, consult the compiler documentation. This is normally used as:
As a special extension, GCC allows C programs to run functions as global constructors. For more information, consult the compiler documentation. This is normally used as:


Line 33: Line 33:
</source>
</source>


== Using crti.o, crtbegin.o, crtend.o, and crtn.o in a Kernel ==
=== Using crti.o, crtbegin.o, crtend.o, and crtn.o in a Kernel ===


In a kernel, you are not using a user-space C library. You may be using a special kernel "C library", or none at all. The compiler always supplies <tt>crtbegin.o</tt> and <tt>crtend.o</tt>, but normally the C library supplies <tt>crti.o</tt> and <tt>crtn.o</tt>, but not in this case. The kernel should supply its own <tt>crti.o</tt> and <tt>crtn.o</tt> implementation (even if it would be otherwise identical to the user-space libc version). A kernel is linked with <tt>-nostdlib</tt> (which is the same as passing <tt>-nodefaultlibs</tt> and <tt>-nostartfiles</tt>) which disables the "start files" <tt>crt*.o</tt> that are normally automatically added to the link command line. By passing <tt>-nostartfiles</tt>, we promise to the compiler that we take on the responsibility ourselves to call the "program initialization tasks" in the <tt>crtbegin.o</tt> and <tt>crtend.o</tt> files. This means as we need to manually add <tt>crti.o</tt>, <tt>crtbegin.o</tt>, <tt>crtend.o</tt>, and <tt>crtn.o</tt> to the command line. Since we provide <tt>crti.o</tt> and <tt>crtn.o</tt> ourselves, that is trivial to add to the kernel command line. However, <tt>crtbegin.o</tt> and <tt>crtend.o</tt> are installed inside a compiler-specific directory we'll need to figure out the path. Luckily, gcc offers an option just to do this. If <tt>i686-elf-gcc</tt> is your cross-compiler and <tt>$CFLAGS</tt> is the flags you would normally provide to your compiler, then
In a kernel, you are not using a user-space C library. You may be using a special kernel "C library", or none at all. The compiler always supplies <tt>crtbegin.o</tt> and <tt>crtend.o</tt>, but normally the C library supplies <tt>crti.o</tt> and <tt>crtn.o</tt>, but not in this case. The kernel should supply its own <tt>crti.o</tt> and <tt>crtn.o</tt> implementation (even if it would be otherwise identical to the user-space libc version). A kernel is linked with <tt>-nostdlib</tt> (which is the same as passing <tt>-nodefaultlibs</tt> and <tt>-nostartfiles</tt>) which disables the "start files" <tt>crt*.o</tt> that are normally automatically added to the link command line. By passing <tt>-nostartfiles</tt>, we promise to the compiler that we take on the responsibility ourselves to call the "program initialization tasks" in the <tt>crtbegin.o</tt> and <tt>crtend.o</tt> files. This means as we need to manually add <tt>crti.o</tt>, <tt>crtbegin.o</tt>, <tt>crtend.o</tt>, and <tt>crtn.o</tt> to the command line. Since we provide <tt>crti.o</tt> and <tt>crtn.o</tt> ourselves, that is trivial to add to the kernel command line. However, <tt>crtbegin.o</tt> and <tt>crtend.o</tt> are installed inside a compiler-specific directory we'll need to figure out the path. Luckily, gcc offers an option just to do this. If <tt>i686-elf-gcc</tt> is your cross-compiler and <tt>$CFLAGS</tt> is the flags you would normally provide to your compiler, then
Line 70: Line 70:
Your kernel will then have an <tt>_init</tt> and a <tt>_fini</tt> function linked in, which can be called from your <tt>boot.o</tt> (or what your kernel entry point object is called) before passing control to <tt>kernel_main</tt> (or what your kernel main routine is called). Please note that the kernel may not be initialized, at all, at this point in time and you can only do trivial things from your global constructors. In addition, <tt>_fini</tt> may not ever get called because your operating system will remain running, and when it is time to shut down, there is little worth doing that a processor reset won't do. It may be worth setting up a <tt>kernel_early_main</tt> function that initializes the heap, the log, and other core kernel features. Then your <tt>boot.o</tt> can call <tt>kernel_early_main</tt>, then call <tt>_init</tt>, and then finally pass control to the real <tt>kernel_main</tt>. This is analogous to how things work in user-space, where <tt>crt0.o</tt> calls <tt>_initialize_c_library</tt> (or what you call it), then <tt>_init</tt>, and finally <tt>exit(main(argc, argv))</tt>.
Your kernel will then have an <tt>_init</tt> and a <tt>_fini</tt> function linked in, which can be called from your <tt>boot.o</tt> (or what your kernel entry point object is called) before passing control to <tt>kernel_main</tt> (or what your kernel main routine is called). Please note that the kernel may not be initialized, at all, at this point in time and you can only do trivial things from your global constructors. In addition, <tt>_fini</tt> may not ever get called because your operating system will remain running, and when it is time to shut down, there is little worth doing that a processor reset won't do. It may be worth setting up a <tt>kernel_early_main</tt> function that initializes the heap, the log, and other core kernel features. Then your <tt>boot.o</tt> can call <tt>kernel_early_main</tt>, then call <tt>_init</tt>, and then finally pass control to the real <tt>kernel_main</tt>. This is analogous to how things work in user-space, where <tt>crt0.o</tt> calls <tt>_initialize_c_library</tt> (or what you call it), then <tt>_init</tt>, and finally <tt>exit(main(argc, argv))</tt>.


== Using crti.o, crtbegin.o, crtend.o, and crtn.o in User-Space ==
=== Using crti.o, crtbegin.o, crtend.o, and crtn.o in User-Space ===
{{Main|Creating a C Library}}
{{Main|Creating a C Library}}


It is very easy to use these object files in user-space, as the cross-compiler automatically will link them in the right order into the final program. The compiler will, as always, provide <tt>crtbegin.o</tt> and <tt>crtend.o</tt>. Your C library will then provide <tt>crt0.o</tt> (program entry point file), <tt>crti.o</tt>, and <tt>crtn.o</tt>. If you have an [[OS Specific Toolchain]], you can change the name of the program entry point (normally _start), the path where the compiler searches for the <tt>crt{0,i,n}.o</tt> files, and what files is even used (possibly with other names) and what order, by modifying <tt>STARTFILE_SPEC</tt> and <tt>ENDFILE_SPEC</tt>. When you start creating a user-space, it may well be worth creating an OS Specific Toolchain because it allows you great control over exactly how all this works.
It is very easy to use these object files in user-space, as the cross-compiler automatically will link them in the right order into the final program. The compiler will, as always, provide <tt>crtbegin.o</tt> and <tt>crtend.o</tt>. Your C library will then provide <tt>crt0.o</tt> (program entry point file), <tt>crti.o</tt>, and <tt>crtn.o</tt>. If you have an [[OS Specific Toolchain]], you can change the name of the program entry point (normally _start), the path where the compiler searches for the <tt>crt{0,i,n}.o</tt> files, and what files is even used (possibly with other names) and what order, by modifying <tt>STARTFILE_SPEC</tt> and <tt>ENDFILE_SPEC</tt>. When you start creating a user-space, it may well be worth creating an OS Specific Toolchain because it allows you great control over exactly how all this works.


== x86 (32-bit) ==
=== x86 (32-bit) ===
It is very simple to implement this under x86. You simply have to define the header of two functions in <tt>crti.o</tt> and the footer in <tt>crtn.o</tt> and use these objects in your C library or kernel. You can then simply call <tt>_init</tt> to perform the initialization tasks and call <tt>_fini</tt> to perform the termination tasks (normally done from a <tt>crt0.o</tt> or <tt>my-kernel-boot-object.o</tt>).
It is very simple to implement this under x86. You simply have to define the header of two functions in <tt>crti.o</tt> and the footer in <tt>crtn.o</tt> and use these objects in your C library or kernel. You can then simply call <tt>_init</tt> to perform the initialization tasks and call <tt>_fini</tt> to perform the termination tasks (normally done from a <tt>crt0.o</tt> or <tt>my-kernel-boot-object.o</tt>).


Line 110: Line 110:
</pre>
</pre>


== x86_64 (64-bit) ==
=== x86_64 (64-bit) ===


The system ABI on x86_64 is similar to its 32-bit counterpart and we also just need to provide function headers and function footers and the compiler will insert the rest of the <tt>_init</tt> and <tt>_fini</tt> functions through <tt>crtbegin.o</tt> and <tt>crtend.o</tt>.
The system ABI on x86_64 is similar to its 32-bit counterpart and we also just need to provide function headers and function footers and the compiler will insert the rest of the <tt>_init</tt> and <tt>_fini</tt> functions through <tt>crtbegin.o</tt> and <tt>crtend.o</tt>.
Line 146: Line 146:
</pre>
</pre>


== ARM (BPABI) ==
=== ARM (BPABI) ===


In this case things are slightly different. The system ABI mandates the use of special sections called <tt>.init_array</tt> and <tt>.fini_array</tt>, rather than the common <tt>.init</tt> and <tt>.fini</tt> sections. This means that <tt>crtbegin.o</tt> and <tt>crtend.o</tt>, as provided by your cross-compiler, does not insert instructions into the <tt>.init</tt> and <tt>.fini</tt> sections. The result is that if you follow the method from Intel/AMD systems, your <tt>_init</tt> and <tt>_fini</tt> functions will do nothing. Your cross-compiler may actually come with default <tt>crti.o</tt> and <tt>crtn.o</tt> objects, however they also suffer from this ABI decision, and their <tt>_init</tt> and <tt>_fini</tt> functions will also do nothing.
In this case things are slightly different. The system ABI mandates the use of special sections called <tt>.init_array</tt> and <tt>.fini_array</tt>, rather than the common <tt>.init</tt> and <tt>.fini</tt> sections. This means that <tt>crtbegin.o</tt> and <tt>crtend.o</tt>, as provided by your cross-compiler, does not insert instructions into the <tt>.init</tt> and <tt>.fini</tt> sections. The result is that if you follow the method from Intel/AMD systems, your <tt>_init</tt> and <tt>_fini</tt> functions will do nothing. Your cross-compiler may actually come with default <tt>crti.o</tt> and <tt>crtn.o</tt> objects, however they also suffer from this ABI decision, and their <tt>_init</tt> and <tt>_fini</tt> functions will also do nothing.
Line 205: Line 205:
</pre>
</pre>


= Clang =
== Clang ==
Since Clang attemps to be largely compatible with GCC, the information listed there can possibly be adapted easily. If you give it a try, please document the findings here.
Since Clang attemps to be largely compatible with GCC, the information listed there can possibly be adapted easily. If you give it a try, please document the findings here.


= Other Compilers / Platforms =
== Other Compilers / Platforms ==
If your compiler or system ABI is not listed here, you will need to consult the appropriate documentation manually and hopefully document the information here if it is relevant.
If your compiler or system ABI is not listed here, you will need to consult the appropriate documentation manually and hopefully document the information here if it is relevant.