Building libgcc for mcmodel=kernel

From OSDev Wiki
Jump to navigation Jump to search
Difficulty level
Difficulty 2.png
Medium

In x86_64, if you link (e.g. through a linker script) your kernel directly to the higher half of the virtual memory, you might want to build the kernel with -mcmodel=kernel or -mcmodel=large.

Also, if you link with libgcc and use crtbegin.o and crtend.o (see Calling Global Constructors), you might wand to build libgcc with -mcmodel=kernel or -mcmodel-large.

Otherwise, you may get errors when linking, either from crtstuff.c or from kernel code, like:

.../crtbegin.o: In function `deregister_tm_clones':
crtstuff.c:(.text+0x2): relocation truncated to fit: R_X86_64_32 against symbol `__TMC_END__' defined in .dtors section in ...
crtstuff.c:(.text+0x1d): relocation truncated to fit: R_X86_64_32 against `.tm_clone_table'
.../crtbegin.o: In function `register_tm_clones':
crtstuff.c:(.text+0x31): relocation truncated to fit: R_X86_64_32 against symbol `__TMC_END__' defined in .dtors section in ...
crtstuff.c:(.text+0x5f): relocation truncated to fit: R_X86_64_32 against `.tm_clone_table'
.../crtbegin.o: In function `__do_global_dtors_aux':
crtstuff.c:(.text+0x88): relocation truncated to fit: R_X86_64_32 against symbol `__DTOR_END__' defined in .dtors section in ...
.../crtend.o
crtstuff.c:(.text+0x8e): relocation truncated to fit: R_X86_64_32 against `.dtors'
crtstuff.c:(.text+0xdb): relocation truncated to fit: R_X86_64_32 against `.eh_frame'
.../crtbegin.o: In function `frame_dummy':
crtstuff.c:(.text+0x10c): relocation truncated to fit: R_X86_64_32 against `.bss'
crtstuff.c:(.text+0x111): relocation truncated to fit: R_X86_64_32 against `.eh_frame'
.../crtend.o: In function `__do_global_ctors_aux':
crtstuff.c:(.text+0x13): relocation truncated to fit: R_X86_64_32 against `.ctors'
collect2: error: ld returned 1 exit status

One solution is to build a specific cross compiler, used only for kernel compilation (different from the one used for the userland), with libgcc (where crtbegin.o and crtend.o are generated, from crtstuff.c) compiled, like the kernel, with -mcmodel=kernel or -mcmodel=large. As far as we can tell, you could even mix the two modes, e.g.: compile the kernel with -mcmodel=kernel and libgcc with -mcmodel=large or the other way around.

In this case we also use -mno-red-zone to compile libgcc because it may be needed for the kernel (see Libgcc without red zone).

Afterwards, you can compile and link your kernel with the generated cross compiler using -mcmodel=kernel or -mcmodel=large, and -mno-red-zone, and link with crtbegin.o and crtend.o.

These recipes were tested and work at least for GCC 6, 7 and 8.

Starting from the GCC Cross-Compiler recipe, and using TARGET=x86_64-elf, we can proceed as in the next sections.

Compiling libgcc with -mcmodel=kernel

This may require a workaround. First compile binutils as usual, then add the binaries to the PATH, and start to compile gcc. When compiling libgcc with -mcmodel=kernel, it will fail. Then patch the Makefile to disable PIC, repeat and continue:

# (...)
mkdir build
cd build
../gcc-*/configure --target=$TARGET --disable-nls --enable-languages=c,c++ --without-headers --prefix=$PREFIX
make all-gcc
make all-target-libgcc CFLAGS_FOR_TARGET='-g -O2 -mcmodel=kernel -mno-red-zone' || true
# will fail with: cc1: error: code model kernel does not support PIC mode
sed -i 's/PICFLAG/DISABLED_PICFLAG/g' $TARGET/libgcc/Makefile
make all-target-libgcc CFLAGS_FOR_TARGET='-g -O2 -mcmodel=kernel -mno-red-zone'
make install-gcc
make install-target-libgcc

Compiling libgcc with -mcmodel=large

First compile binutils as usual, then add the binaries to the PATH, and start to compile gcc. Then compile libgcc with -mcmodel=large:

# (...)
mkdir build
cd build
../gcc-*/configure --target=$TARGET --disable-nls --enable-languages=c,c++ --without-headers --prefix=$PREFIX
make all-gcc
make all-target-libgcc CFLAGS_FOR_TARGET='-g -O2 -mcmodel=large -mno-red-zone'
make install-gcc
make install-target-libgcc

See Also

Articles