Libgcc without red zone

From OSDev Wiki
Jump to navigation Jump to search
Main article: Libgcc

This article discuses how to build libgcc without the need to support a red-zone in your kernel. If you're not targeting x86-64 you don't need this as i*86 has no such requirements.

What is the 'red-zone'?

The red-zone is a feature described in the x86-64 ABI.

It is a 128 byte long region located directly below the stack pointer. This region is free-for-use for the compiler without the requirement to notify the application / the os or any running interrupt handler.

For user applications there is no issue as interrupts and other kernel related code won't interfere with the user stack. In your kernel however things can get ugly, especially so if you have nested interrupts and no red-zone support. Imagine running inside your interrupt handler, gcc puts some data inside the red zone, a nested interrupt occurs and clobbers the red-zone or vice versa.

To get around this the red-zone can be disabled by passing -mno-red-zone to GCC.

x86_64-elf-gcc $CFLAGS -mno-red-zone ...

Why modify libgcc?

If you link against libgcc (as you should) there is one problem: libgcc is build with red-zone enabled.

So while your kernel works just fine the methods in libgcc may mess things up by accident. The solution is simple - rebuild libgcc with -mno-red-zone. Fortunately GCC supports this in a straight forward way by providing multilib support inside it's source tree.

Preparations

Main article: GCC Cross-Compiler

Extract and prepare the GCC sources as described when building your GCC Cross-Compiler but don't run configure just yet.

Create the following file and save it as t-x86_64-elf inside gcc/config/i386/ under your GCC sources.

# Add libgcc multilib variant without red-zone requirement

MULTILIB_OPTIONS += mno-red-zone
MULTILIB_DIRNAMES += no-red-zone

By default this new configuration will not be used by GCC unless it's explicitly told to. Open gcc/config.gcc in your favorite editor and search for case block like this:

 x86_64-*-elf*)
 	tm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h"
 	;;

This is the target configuration used when creating a GCC Cross-Compiler for x86_64-elf. Modify it to include the new multilib configuration:

 x86_64-*-elf*)
	tmake_file="${tmake_file} i386/t-x86_64-elf" # include the new multilib configuration
	tm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h"
	;;

Building libgcc

Run configure and then invoke the all-target-libgcc and install-target-libgcc as usual and GCC will build libgcc in two versions - one with red-zone enabled and one without. You can check the successful build by checking the installed libgcc.a archives:

find $TOOLCHAIN_PREFIX/lib -name 'libgcc.a'

If all went well you should see an additional libgcc installed in the no-red-zone multilib directory:

./gcc/x86_64-pc-elf/4.9.1/libgcc.a
./gcc/x86_64-pc-elf/4.9.1/no-red-zone/libgcc.a

Linking against the correct multilib version

Assuming you're using GCC to link your kernel you're probably fine. All that's needed is to make sure -mno-red-zone is in your LDFLAGS when doing the final linker call.

x86_64-elf-gcc $LDFLAGS -mno-red-zone -o kernel $SOURCES

If you're unsure which libgcc version is going to be used you can check by passing -mno-red-zone and -print-libgcc-file-name to GCC:

x86_64-elf-gcc -mno-red-zone -print-libgcc-file-name
lib/gcc/x86_64-pc-elf/4.9.1/no-red-zone/libgcc.a

See Also

Articles