Calling Global Constructors: Difference between revisions

Undo revision 14725 by Shikhin (talk) and fix typo
[unchecked revision][unchecked revision]
(Undo revision 14727 by Sortie (talk))
(Undo revision 14725 by Shikhin (talk) and fix typo)
Line 5:
= GNU Compiler Collection - System V ABI =
 
The System V ABI (as used by <tt>i586-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.
 
This scheme allows the compiler great control in program initialization and makes things easy for you, but you have to cooperate with the compiler or bad things will happen. Your cross-compiler will provide you with <tt>crtbegin.o</tt> and <tt>crtend.o</tt>. These files contain the internals that the compiler wish to hide from you, but wants you to use. To get access to this information, you will need to provide your own implementation of <tt>crti.o</tt> and <tt>crtn.o</tt>. Fortunately, this is easy and described in detail in this tutorial. The fifth file <tt>crt0.o</tt> contains the program entry point (normally <tt>_start</tt>) and calls the special <tt>_init</tt> function that runs the "program initialization tasks" that <tt>crti.o</tt>, <tt>crtbegin.o</tt>, <tt>crtend.o</tt>, and <tt>crtn.o</tt> together form, and your exit function will normally call the <tt>_fini</tt> function made by these objects. However, <tt>crt0</tt>.o is out of scope of this article. (Note that the object file that contains <tt>_start</tt> acts as <tt>crt0.o</tt> in a kernel.)
Line 17:
<source lang="bash">i585-elf-ld crt0.o crti.o crtbegin.o foo.o bar.o crtend.o crtn.o</source>
 
The idea is that the these files together form the <tt>_init</tt> and <tt>_fini</tt> functions during the linking. This is ddone 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 ==
Line 36:
== 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 nnone 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 is 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> is 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>i586-elf-gcc</tt> is your cross-compiler and <tt>$CFLAGS</tt> is the flags you would normally provide to your compiler, then
 
<source lang="bash">i586-elf-gcc $CFLAGS -print-file-name=crtbegin.o</source>
Line 77:
 
== 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 ddone from a <tt>crt0.o</tt> or <tt>my-kernel-boot-object.o</tt>).
 
<source lang="asm">
/* xx86 crti.s */
.section .init
.global _init
Line 99:
 
<source lang="asm">
/* xx86 crtn.s */
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
Line 115:
== x86_64 (64-bit) ==
 
The system ABI on x86_itsx86_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>.
 
<source lang="asm">
/* x86_x86_64 crti.s */
.section .init
.global _init
Line 137:
 
<source lang="asm">
/* x86_x86_64 crtn.s */
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
Anonymous user