0

In my arm cortex-m project, I use cmake to generate the makefile and run make to compile the project. If I peruse the outputted map file from LD, it has linked in an object file that’s not even used anywhere in the project yet, and the name of its one unused function in the .text section. I am trying to figure out if there is a command switch for LD to garbage collect any unused object files. I use the gc-sections flag already but that doesn’t do it. Is there not a way to discard this outputted object file from getting placed somewhere in the binary?

In my cmakelists.txt, I do specify to include all C source files so obviously in there I could prune some object files but that would seem tedious.

  • 2
    Why create a mess and then try to clean it up? If it were me, I'd specify an explicit list of sources to use (e.g. `a.c b.c` instead of `*.c`). Or, put the current sources in (e.g.) `src` and future/unused sources in (e.g.) `future`. Then, just specify all sources in the `src` directory. Otherwise, don't worry. You're in early development mode. Eventually, the "empty" sources will get real functions and be hooked in with calls to them. So, now, your program is slightly larger (with some small unused dummy functions), but so what? – Craig Estey Feb 25 '23 at 17:16
  • Try building a .a library? Only needed objects from static libraries will be linked. Or for a completely different approach: in a Makefile rule it is often useful to use `$(filter` or `$(filter-out`. – o11c Feb 25 '23 at 17:36
  • This is in the category of problems that inspire me to recommend always specifying all source and / or object files explicitly, rather than by using wildcards. It's not hard to do or to maintain, and it makes your build system authoritative about what is built and how (as opposed to it delegating that authority to the filesystem). – John Bollinger Feb 26 '23 at 15:04

1 Answers1

0

See the good advice about avoiding link time manipulations of owned code, if you don't need it then consider removing it at compilation.

CAUTION This approach can lead to removal of some needed sections. Read how this author used --print-gc-sections to review and discover removed sections.

REVIEW This presentation on link time dead code and data elimination using GNU toolchain

SIMILAR here is how to separate the functions and data and use gc-sections to remove symbols.

Maybe that can help, here is how I experiemented with that answer...

Problem

How to discard unused symbols in the resulting elf file

Output

Shows the unused function foo was included in the before elf and removed in the after elf file.

# BEFORE SHOWS THAT SYMBOL FOO EXIST
arm-none-eabi-readelf -a test-before.elf
. . .
    25: 00010068    24 FUNC    GLOBAL DEFAULT    2 foo

# AFTER SHOWS THAT SYMBOL FOO ABSENT
arm-none-eabi-readelf -a test-after.elf
. . .
    25: 00010068    28 FUNC    GLOBAL DEFAULT    3 c_entry
    26: 00010000     0 NOTYPE  GLOBAL DEFAULT    1 _Reset

Process

  1. Create arm elf w/ symbols that aren't used (before)
  2. Modify arm elf to remove symbols that aren't used (after)

Input

Here are the commands, the code is based on Hello World for bare metal ARM

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-before.o
arm-none-eabi-gcc -fdata-sections -ffunction-sections -c -mcpu=arm926ej-s -g test.c -o test-after.o
arm-none-eabi-ld --gc-sections -T test.ld test-before.o startup.o -o test-before.elf
arm-none-eabi-ld --gc-sections -T test.ld test-after.o startup.o -o test-after.elf

arm-none-eabi-readelf -a test-before.elf 
arm-none-eabi-readelf -a test-after.elf 

# SEE OUTPUT FOR RESULTS OF READELF SHOWING UNUSED SYMBOL foo WAS REMOVED

startup.s

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

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.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 foo()
{
}
 
void c_entry() {
 print_uart0("Hello world!\n");
}

Linking .a Instead of .o

The comment about using a library looked interesting so I wrote this experiment. Based on the specific linker and commands below, foo was included in the resulting elf. Still maybe a solution for different linker or better commands. Cool idea though.

arm-none-eabi-ar rcs libtest-before.a test-before.o
arm-none-eabi-ld --static -T test.ld libtest-before.a startup.o -o test-before-lib.elf
arm-none-eabi-readelf -a test-before-lib.elf 
. . .
    25: 00010068    24 FUNC    GLOBAL DEFAULT    2 foo
    26: 00010000     0 NOTYPE  GLOBAL DEFAULT    1 _Reset
atl
  • 575
  • 3
  • 6