The GNU Compiler Collection uses a special library called libgcc during code generation, which contains shared code that would be inefficient to duplicate every time as well as auxiliary helper routines and runtime support. Its exact contents depends on the particular target, configuration and even command line options. GCC unconditionally assumes it can safely emit calls to libgcc symbols as it sees fit, thus all code compiled by GCC must be linked with libgcc. The library is automatically included by default when you link with GCC and you need to do nothing further to use it. However, kernels usually don't link with the standard user-space libc for obvious reasons and are linked with the -nodefaultlibs (implied by -nostdlib) which disables the automatic linking with libc and libgcc. This is a problem as gcc still thinks it can use libgcc and you need to link with it.
How to build libgcc
- Main article: GCC Cross-Compiler
You need to invoke the all-target-libgcc and install-target-libgcc make targets while building your GCC Cross-Compiler to build and install libgcc along with your cross-compiler. A static library called libgcc.a is installed into your compiler prefix along with the other compiler-specific files. Note that some architectures, such as ARM, have multiple kinds of ABIs and instruction sets: these targets will thus need multiple versions of libgcc depending on which particular compile options you use and they all go into compiler-specific subdirectories.
- Main article: Bare Bones
You can link with libgcc by passing -lgcc when linking your kernel with your compiler. You don't need to do this unless you pass the -nodefaultlibs option (implied by -nostdlib). For instance:
i686-elf-gcc -T linker.ld -o myos.kernel -ffreestanding boot.o kernel.o -nostdlib -lgcc
Note how libgcc is installed in a compiler-specific directory known to your compiler, but not to your linker. Thus you must use the compiler as a linker, rather than invoking ld directly, or you'll need to tell the linker where to find libgcc. Note also how you must be certain to give the machine compile options you compile with when you link (the -mfoo and -fbar options, among others) or you may get the wrong libgcc. If you want to know the full path of libgcc (if you insist on linking with ld rather than your compiler), you can do so:
i686-elf-gcc $CFLAGS -print-libgcc-file-name
You naturally also need to pass the machine compile options when using the -print-libgcc-file-name option. Make your build scripts or Makefile locate libgcc rather than hard-coding the path somewhere or other people will have a harder time building your operating system.
Frequently Asked Questions
All code compiled with gcc must be linked with libgcc.
The compiler is not required to emit calls to libgcc and on some platforms libgcc is a small library and you may be able to write complex programs that never need libgcc. The compiler will only link in the specific parts of libgcc that are needed, since it is a static library. However, the compiler is free to change its mind and suddenly emit calls to libgcc if it is a Tuesday and the stock prices have gone up (or you upgraded your compiler to a newer version).
See the above question. The compiler doesn't care if you actually link with libgcc, it will emit calls just the same. You will never get libgcc-related linking errors if you link with libgcc, which is simple and makes your kernel compilation process reliable.
Think of it this way: You went into the compiler source code and randomly commented out a few randomly chosen functions. The compiler still happens to work for your kernel, but will it always work for all the possible kernel source codes you can write? The libgcc library is part of the compiler and it would be foolish to unsafely disable parts of the compiler.
Can I use the Linux libgcc?
You must use the correct libgcc that came with your cross-compiler. Whatever else libgcc you found likely has a different target, was built with different machine compile options, has dependencies on the standard library, is part of a different compiler revision (your distribution may have patched its gcc, even). It is possible that using a different libgcc will work, but perhaps not reliably.
Can I copy the libgcc functions I need into my OS?
You really shouldn't. These functions are compiler revision specific and the set of needed functions can potentially change when you upgrade your compiler. You'll be playing catch-up with the compiler developers and you might have even more trouble if your other contributors use different compiler versions. There is also the issue of whether you have copied enough of libgcc, if you don't have all of it, things will not work reliably.
Can I just re-implement libgcc?
You really shouldn't. See the above question. It's a lot of effort and very tricky to get right, you will likely get an inefficient 64-bit division function that doesn't work properly for all values. It's much simpler to just link with libgcc and get it over with.
Does libgcc have dependencies?
It usually doesn't, the -elf target used for tutorials here certainly doesn't. If you have a hosted user-space, it may enable additional parts of itself and have special symbols that rely on particular user-space functions it found in your system headers. These are common functions that you'd want to implement in your kernel standard library regardless. Some dependencies are only relevant if you use the relevant libgcc parts that trigger them.
What is the libgcc license?
The libgcc library is licensed under the GNU GPL plus the GCC Runtime Library Exception (see COPYING.RUNTIME in your gcc source tree). This roughly means that you are allowed to link in libgcc into your software even if it would normally violate the GNU GPL, as long as you used a non-proprietary version of GCC. This is nothing unusual, such licensed code is linked into everything GCC creates, especially user-space programs and you can legally compile and distribute proprietary programs with GCC because of the runtime library exception.
It's too big!
It isn't. The static archive contains a lot of object files that each contain debugging symbols. You'll find the archive gets considerably smaller if you attempt to strip it. Furthermore, it's not even linked yet and a lot of that information is simply overhead from the file format and linking information. Finally, it's a static archive which means that only the relevant parts actually gets linked into your binary.
What if I am careful?
On some platforms the compiler doesn't generate calls to libgcc for most common code except in particular circumstances. You can avoid particular features and be mostly safe. But I'll bet you that you haven't actually read into the GCC source code and found the exact rules for when calls to libgcc are emitted. The rules may change when you update your compiler. Indeed, tomorrow may be a Tuesday. The library provides useful features and it's simple to link with it and enjoy the features. If the rules change and you cannot build your kernel with the new compiler, but you fix your kernel, then you still cannot build older versions of your operating system (which could have been useful for debugging or historical purposes).
I don't want it because I want control!
You didn't write the compiler you are using. Ponder that for a few moments and let the Not-Invented-Here kick in.
I really don't want libgcc!
The above rule is:
- All code compiled with gcc must be linked with libgcc.
But that's okay, you can just change what gcc is and get rid of the rule. Modify the gcc source code such that it never emits calls to libgcc (perhaps if -ffreestanding) but generates code directly instead. You don't have to link with it then.
I'm using clang
This is an article about gcc, you need to do different things if you are using another compiler.
Report them to the local compiler crime police.