embeddedglibcnewlib

Does glibc work on bare metal or RTOS platforms?


Embedded experts, is this possible without major modifications?

I'm building firmware that has both a Linux kernel and a minimal RTOS that runs when resuming from sleep. The same toolchain, aarch64-linux-gnu, is used for both Linux and RTOS code. The reason why it doesn't need a separate bare metal toolchain for RTOS is because the vendor has their own stripped down C runtime which they use instead of glibc.

But that poorly made C runtime is missing a lot of functions, so we want to use a more full featured one. We could use newlib but that would require a 2nd toolchain, which want to avoid.

But I can't find any bare metal or RTOS project using glibc. Currently, it can build with glibc, but crashes real soon, almost certainly because we didn't call the glibc initialization code:

_start
   __libc_start_main  
     __libc_csu_init   (call C++ constructors for global variables)
     main

https://github.molgen.mpg.de/git-mirror/glibc/blob/master/sysdeps/aarch64/start.S https://github.molgen.mpg.de/git-mirror/glibc/blob/master/csu/libc-start.c

But looking at __libc_start_main, it's a lot more complicated than newlib. Seems it depends on a lot of Linux facilities that don't exist:

  1. dynamic linking? _dl_aux_init
  2. pthreads __pthread_initialize_minimal

On the positive side, glibc does let you override sbrk and write, which are weakly defined, so that they can call the device driver code directly instead of making syscalls to the non-existent kernel

Update: working for us means

1. malloc
2. printf,write to serial port, but not to actual files
3. C++ globals initialized correctly
4. no threading

Solution

  • In glibc, malloc depends on a low-level allocator such as sbrk (or mmap). You can probably fake something like sbrk on a bare-metal target, but you would have to stub out all the indirect malloc dependencies (including multi-threading support).

    The glibc printf implementation is coupled to the fopen implementation, which depends on dlopen for loading character set conversion code (the gconv modules). This even applies to the statically linked case. There is really no way to escape the dynamic linker without some far-reaching changes.

    C++ initialization should be fairly easy to get right, even for bare-metal targets. I do not think you need any libc for that, just a little bit of startup code that matches what your target needs. (On some targets, it is sufficient to call the magic _init function before your application.) glibc has some elaborate facilities for that as well, but they are for supporting dynamic linking and dlopen/dlclose. For statically linked binaries, they are not needed.

    A high-quality port of glibc to a non-Linux. non-GNU operating system is definitely a lot of work. If the target is bare-metal or a non-POSIX RTOS, you probably have to start with a POSIX shim layer. Even then, the result will be a not-quite-glibc target, and you still need a separate toolchain.

    It may be better to go with newlib (or the existing RTOS libc) and obtain the missing libc functionality via gnulib and similar projects.