Question

Missing ELF symbol for extern const float?

I have seen Missing ELF symbol "var" when using GDB? , but this is a different issue.

I am using gdb with RP2040 over openocd. Unfortunately I cannot provide a full code that reproduces the issue, but in a .h file I have:

extern const float kLDC;

... and in a .c file I have:

const float kLDC = (100-0)/(255-0);

When I start a gdb session with the debug build of the program over openocd, I can print pretty much any global variable, but for kLDC, I get:

(gdb) p kLDC
Missing ELF symbol "kLDC".

What is going on? Is it expected for a symbol declared extern const float, for it to not be printable in gdb? I use:

$ gdb-multiarch.exe --version
GNU gdb (GDB) 14.2

Thanks for the commenters who pointed out that there was an error in that the (100-0)/(255-0) expression is integer; I myself ended up realizing that when I first removed the const, so I could inspect the value in gdb. With this statement:

float kLDC = (100.0-0)/(255-0);

... I confirmed the value is about 0.3921; made a note of calculation results obtained with this variable, and then set it back to const. I can confirm the calculation results are the same, however I again get Missing ELF symbol "kLDC".. There is a .map file generated by the build process, it outputs:

$ grep kLDC myprogram.elf.map
 .rodata.kLDC   0x00000000        0x4 CMakeFiles/myprogram.dir/my_function.c.obj

Also I've tried:

$ arm-none-eabi-objdump -g myprogram.elf | grep kLDC
    <23e00>   DW_AT_name        : (indirect string, offset: 0x7ae4): kLDC
  0x00007ae0 70657100 6b4c4443 00475049 4f5f4f56 peq.kLDC.GPIO_OV

(I don't really understand the above, but it surprises me to see kLDC close to anything named GPIO - it is not used directly with GPIO in code at all)

Still cannot wrap my head around this - There are traces of kLDC in the .elf, and I can conceive that the compiler might have "known" about some other initialized read-only memory with same contents as kLDC = 0.39..., and therefore "optimized" by not bothering to allocate new memory specifically for the kLDC var, but reusing the existing memory instead - but now that I bother declaring "extern" (and using a -g/Debug build), shouldn't the compiler at least try to make this constant available in gdb so I can print its value? At least, I'd like to understand the conditions under which the build process decides that it is ok to "miss" the "symbol" from the ELF - or conversely, if I can use any other specification to force this value to be readable in gdb, even if it is const ...

 3  53  3
1 Jan 1970

Solution

 2

The fact that the map file shows the section containing the object you want has the address 0x00000000 means that it has been removed from the final binary by the linker.

It has done this because your build process contains the linker flag --gc-sections. This tells it to remove any section that isn't referenced.

You could remove this flag, but that will make your binary huge with all kinds of unused junk. This is not a good idea in embedded development.

Declaring the variable extern stops the compiler optimizing it away, but that doesn't help because the garbage collection here is being done by the linker. You can obtain the behaviour you expect by adding the flag --gc-keep-exported to the linker (or -Wl,--gc-keep-exported if you are invoking ld via gcc).

Alternatively you just need to make the program use the variable in a way that cannot be optimized out, eg: printf("%08X", (unsigned int)&kLDC);

You can also tell the linker to keep this specific section by addding KEEP() to the linker script, but this can get difficult to maintain.

2024-07-11
Tom V