Calling Global Constructors: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
No edit summary
m (Bot: Replace deprecated source tag with syntaxhighlight)
Line 10: Line 10:


To understand this apparent complexity, consider a program consisting of <tt>foo.o</tt> and <tt>bar.o</tt> that is being linked:
To understand this apparent complexity, consider a program consisting of <tt>foo.o</tt> and <tt>bar.o</tt> that is being linked:
<source lang="bash">i686-elf-gcc foo.o bar.o -o program</source>
<syntaxhighlight lang="bash">i686-elf-gcc foo.o bar.o -o program</syntaxhighlight>


The compiler will rewrite the command line and pass it to the linker as:
The compiler will rewrite the command line and pass it to the linker as:


<source lang="bash">i686-elf-ld crt0.o crti.o crtbegin.o foo.o bar.o crtend.o crtn.o</source>
<syntaxhighlight lang="bash">i686-elf-ld crt0.o crti.o crtbegin.o foo.o bar.o crtend.o crtn.o</syntaxhighlight>


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.
Line 21: Line 21:
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:


<source lang="c">
<syntaxhighlight lang="c">
__attribute__ ((constructor)) void foo(void)
__attribute__ ((constructor)) void foo(void)
{
{
Line 31: Line 31:
printf("%s: main is running with argc=%i\n", argv[0], argc);
printf("%s: main is running with argc=%i\n", argv[0], argc);
}
}
</syntaxhighlight>
</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 ===
Line 37: Line 37:
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, since <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, since <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


<source lang="bash">i686-elf-gcc $CFLAGS -print-file-name=crtbegin.o</source>
<syntaxhighlight lang="bash">i686-elf-gcc $CFLAGS -print-file-name=crtbegin.o</syntaxhighlight>


will make the compiler print the path to the correct <tt>crtbegin.o</tt> file (that is ABI compatible with the $CFLAGS options) to the standard output. The same works with <tt>crtend.o</tt>. If you are using GNU Make, you can do it easily in your makefile assuming <tt>$(CC)</tt> is your cross-compiler and <tt>$(CFLAGS)</tt> is the flags you would normally pass it:
will make the compiler print the path to the correct <tt>crtbegin.o</tt> file (that is ABI compatible with the $CFLAGS options) to the standard output. The same works with <tt>crtend.o</tt>. If you are using GNU Make, you can do it easily in your makefile assuming <tt>$(CC)</tt> is your cross-compiler and <tt>$(CFLAGS)</tt> is the flags you would normally pass it:


<source lang="make">
<syntaxhighlight lang="make">
CRTBEGIN_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
CRTBEGIN_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
CRTEND_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
CRTEND_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
</syntaxhighlight>
</source>


You can then use them as such (adapted to your real build system):
You can then use them as such (adapted to your real build system):


<source lang="make">
<syntaxhighlight lang="make">
OBJS:=foo.o bar.o
OBJS:=foo.o bar.o


Line 64: Line 64:
clean:
clean:
rm -f myos.kernel $(INTERNAL_OBJS)
rm -f myos.kernel $(INTERNAL_OBJS)
</syntaxhighlight>
</source>


It is important to remember that the objects must be linked in this exact order, or you will experience strange bugs.
It is important to remember that the objects must be linked in this exact order, or you will experience strange bugs.
Line 152: Line 152:
The solution is to provide your own <tt>crti.o</tt> object that inserts a symbol at the start of <tt>.init_array</tt> and <tt>.fini_array</tt> sections, as well as your own <tt>crtn.o</tt> that inserts a symbol at the end of the sections. In this case it is actually possible to write <tt>crti.o</tt> and <tt>crtn.o</tt> in C, because we are not writing incomplete functions. These files should be compiled like the rest of your kernel and otherwise used normally as <tt>crti.o</tt> and <tt>crtn.o</tt>.
The solution is to provide your own <tt>crti.o</tt> object that inserts a symbol at the start of <tt>.init_array</tt> and <tt>.fini_array</tt> sections, as well as your own <tt>crtn.o</tt> that inserts a symbol at the end of the sections. In this case it is actually possible to write <tt>crti.o</tt> and <tt>crtn.o</tt> in C, because we are not writing incomplete functions. These files should be compiled like the rest of your kernel and otherwise used normally as <tt>crti.o</tt> and <tt>crtn.o</tt>.


<source lang="c">
<syntaxhighlight lang="c">
/* crti.c for ARM - BPABI - use -std=c99 */
/* crti.c for ARM - BPABI - use -std=c99 */
typedef void (*func_ptr)(void);
typedef void (*func_ptr)(void);
Line 173: Line 173:
func_ptr _init_array_start[0] __attribute__ ((used, section(".init_array"), aligned(sizeof(func_ptr)))) = { };
func_ptr _init_array_start[0] __attribute__ ((used, section(".init_array"), aligned(sizeof(func_ptr)))) = { };
func_ptr _fini_array_start[0] __attribute__ ((used, section(".fini_array"), aligned(sizeof(func_ptr)))) = { };
func_ptr _fini_array_start[0] __attribute__ ((used, section(".fini_array"), aligned(sizeof(func_ptr)))) = { };
</syntaxhighlight>
</source>


<source lang="c">
<syntaxhighlight lang="c">
/* crtn.c for ARM - BPABI - use -std=c99 */
/* crtn.c for ARM - BPABI - use -std=c99 */
typedef void (*func_ptr)(void);
typedef void (*func_ptr)(void);
Line 181: Line 181:
func_ptr _init_array_end[0] __attribute__ ((used, section(".init_array"), aligned(sizeof(func_ptr)))) = { };
func_ptr _init_array_end[0] __attribute__ ((used, section(".init_array"), aligned(sizeof(func_ptr)))) = { };
func_ptr _fini_array_end[0] __attribute__ ((used, section(".fini_array"), aligned(sizeof(func_ptr)))) = { };
func_ptr _fini_array_end[0] __attribute__ ((used, section(".fini_array"), aligned(sizeof(func_ptr)))) = { };
</syntaxhighlight>
</source>


Additionally, if you use constructor/desctructor priorities, the compiler will append these priorities to the section name. The linker script is expected to expected to sort these, so you will have to add the following to your linker script. Note that we have to treat the <tt>crti.o</tt> and <tt>crtn.o</tt> objects specially because we need to put the symbols in the right order. Alternatively, you can emit the <tt>_init_array_start</tt>, <tt>_init_array_end</tt>, <tt>_fini_array_start</tt>, <tt>_fini_array_end</tt> symbols yourself from the linker script.
Additionally, if you use constructor/desctructor priorities, the compiler will append these priorities to the section name. The linker script is expected to expected to sort these, so you will have to add the following to your linker script. Note that we have to treat the <tt>crti.o</tt> and <tt>crtn.o</tt> objects specially because we need to put the symbols in the right order. Alternatively, you can emit the <tt>_init_array_start</tt>, <tt>_init_array_end</tt>, <tt>_fini_array_start</tt>, <tt>_fini_array_end</tt> symbols yourself from the linker script.