Search code examples
cgcclinker

Can the GCC linker emit an error when a specific symbol is included in the link?


I'd like to forbid a set of specifically-named symbols from being linked into my executable, to ensure that my executable isn't carrying around functionality and dependencies that I don't want.

In my specific case, these symbols come from libc_nano.a on an ARM Cortex-M MCU, but I've wanted this functionality for application-level code as well on "bigger" computers as well, so my question is general-purpose.

I don't see a linker flag that would trigger an error if a named symbol is included in the final link, but I think that would do what I want.

Is there a way to achieve this with the GCC linker?


Solution

  • Interesting question, below is an example that might help. The example code comes from Hello World for bare metal ARM

    As mentioned above this approach uses ASSERT ( DEFINED( symbol, "message" ) )

    Problem

    . . . forbid a set of specifically-named symbols from being linked into my executable . . . Is there a way to achieve this with the GCC linker?

    What if the symbols are coming from a static library file *.a ?

    Output

    In this output the symbol print_uart0 is the symbol being excluded from an object file test.o. Also shown is what happens when the symbols are coming from a static library file libtest.a.

    arm-none-eabi-ld -T test-no-print-uart0.ld test.o startup.o -o test.elf
    arm-none-eabi-ld: OOPS, THE SYMBOL print_uart0 IS DEFINED
    
    arm-none-eabi-ld -T test-no-print-uart0.ld libtest.a startup.o -o test.elf
    arm-none-eabi-ld: OOPS, THE SYMBOL print_uart0 IS DEFINED
    
    

    Process

    1. Build inputs into a working ARM example
    2. Modify linker script to prevent link when print_uart0 is included from object file
    3. Modify linker command to use static library created from object file
    
    # step 1
    arm-none-eabi-as -mcpu=arm926ej-s -g startup.s -o startup.o
    arm-none-eabi-gcc -c -mcpu=arm926ej-s -g test.c -o test.o
    arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf
    arm-none-eabi-objcopy -O binary test.elf test.bin
    qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin
    
    Hello World!
    # ctrl-a x
    
    # Step 2
    arm-none-eabi-ld -T test-no-print-uart0.ld test.o startup.o -o test.elf
    arm-none-eabi-ld: OOPS, THE SYMBOL print_uart0 IS DEFINED
    
    # Step 3
    arm-none-eabi-ar rcs libtest.a test.o 
    arm-none-eabi-ld -T test.ld libtest.a startup.o -o test.elf
    arm-none-eabi-ld: OOPS, THE SYMBOL print_uart0 IS DEFINED
    

    Inputs

    startup.s

    .global _Reset
    _Reset:
     LDR sp, =stack_top
     BL c_entry
     B .
    

    test.c

    volatile unsigned int * const UART0DR = (unsigned int *)0x101f1000;
     
    void print_uart0(const char *s) {
     while(*s != '\0') { /* Loop until end of string */
     *UART0DR = (unsigned int)(*s); /* Transmit char */
     s++; /* Next char */
     }
    }
     
    void c_entry() {
     print_uart0("Hello world!\n");
    }
    

    test.ld

    ENTRY(_Reset)
    SECTIONS
    {
     . = 0x10000;
     .startup . : { startup.o(.text) }
     .text : { *(.text) }
     .data : { *(.data) }
     .bss : { *(.bss COMMON) }
     . = ALIGN(8);
     . = . + 0x1000; /* 4kB of stack memory */
     stack_top = .;
    }
    

    test-no-print-uart0.ld

    ENTRY(_Reset)
    SECTIONS
    {
     . = 0x10000;
     .startup . : { startup.o(.text) }
     .text : { *(.text) }
     .data : { *(.data) }
     .bss : { *(.bss COMMON) }
     . = ALIGN(8);
     . = . + 0x1000; /* 4kB of stack memory */
     stack_top = .;
    }
    ASSERT( !DEFINED(print_uart0), "OOPS, THE SYMBOL print_uart0 IS DEFINED" );