Search code examples
carmembedded

libc and libgcc in PIC


Working on some firmware code that needs to be completely position independent. Already have my build system set up, and some initial firmware code that fixes the GOT to the right addresses at runtime. I'm able to link in libgcc and libc, but it doesn't work properly.

Some functions work, like memcpy, but utoa doesn't correctly reference the digits string. Root cause of the problem seems to be that the libs are compiled without fPIC (so base address is zero).

Is there a way to add symbols from the shared library to the GOT?

I'm using arm-none-eabi-gcc on Debian, targeting an ARM v7.


Solution

  • You will need to recompile libc.a and libm.a to support PIC. You can query your gcc with '-v' to get the specifics. Often this will lead to a build record. For example, I have gcc-arm-none-eabi-9-2019-q4 which leads to launchpad and you can find the build logs and exact libc repo used. Here is another used by the STM32CubeIDE.

    https://launchpad.net/gcc-arm-embedded/10.0/10.3-2021.10

    You will need '-fPIE -mno-pic-data-is-text-relative' and '--with-pic' and leave the other libraries build options as per the original. The 'CFLAGS_FOR_TARGET' can be used to over-ride the defaults. I use '-mno-pic-data-is-text-relative'; My system had absolute data, but PIC code. It seems gcc does not support this option. So, I had to sacrifice r9 which is always the same.

    The newlib is likely hosted at git://sourceware.org/git/newlib-cygwin.git.

    It would be nice if the compilers were produced with a PIC multi-lib. Alas, they are not currently.

    Is there a way to add symbols from the shared library to the GOT?

    Sure. You need to compile with PIC/PIE options, or the libraries will not have GOT entries. Also, you need to write your own linker script. Just be sure to include the library objects in the GOT section as well. I added mine to the .data section.

    /* Global offset allows relocation of data. */
    _got_address = .;
    *(.got)
    *(.got*)
    _got_end = .;
    

    You can use adr and ldr to perform relocations.

    /* Relocate got table flash address to runtime */
    got_update:
      /* Fixup for runtime address. */
      adr    r0, got_update   /* Run time. */
      ldr    r1, =got_update  /* Link time. */
    

    So, the 'GOT' offset is the difference between r0,r1. If the GOT target was in flash, I added the offset. But what relocations you need will depend on your system.

    I used static libraries, which I guess is your intent as a 'bare metal' application that supports 'over the air' type updates. In this case, the code can run from multiple locations in NOR/QSPI flash. There is Q/A on statically linked shared libraries, which might also use PIC mechanics and is much more complex than what I describe above. For this case, the code might be common, but the data is seperate.