Auto-linked Standard Libraries despite -nostdlib


I’m finding that calls to standard functions like memset and memcpy are producing hard faults. The reason is that when the processor, a Cortex M4, Atmel D51, goes into that function, it attempts to branch to an address in memory that contains no code (viewing memory in the debugger shows 0xFFF… FF), so the processor is unsurprisingly indicating hard fault due to an invalid instruction, i.e. 0xFFFF.

My problem is that I cannot figure out why this function (either memset or memcpy; issue is the same as long as the number of bytes to modify is smaller than the compiler will unroll/inline for) is even trying to branch to an invalid address. The function itself disassembles to a few lines consisting primarily of branch instructions whose addresses are beyond the size of the program. This screams ‘linker error’ to me, most likely involved with the standard libraries, which brings me to the actual issue.

I’m finding that the compiler toolchain is automatically linking a standard library even whenever I specify -nostdlib. I’ve included several tags in build_flags ( -nostdlib, -nodefaultlibs, -nostartfiles, -ffreestanding) of the INI file that should break any references to standard functions like memset and memcpy, but it still figures it out. I tried using -nolibc, but that flag is apparently not recognized by the arm compiler. The includes will certainly get me to the header files for those functions, but the compiled library is coming from somewhere, and it’s entirely unclear where. I will not that I tried compiling what is essentially the same project through Atmel Studio, and including -nostdlib throws errors when calling any memset, memcpy, etc

The following are the last few lines from a Verbose Build.

arm-none-eabi-ar rc .pio/build/commonsense/libFrameworkCommonSense.a .pio/build/commonsense/FrameworkCommonSense/commonsense.o .pio/build/commonsense/FrameworkCommonSense/cortex_handlers.o .pio/build/commonsense/FrameworkCommonSense/led.o .pio/build/commonsense/FrameworkCommonSense/pinConfig.o .pio/build/commonsense/FrameworkCommonSense/startup.o
arm-none-eabi-ranlib .pio/build/commonsense/libFrameworkCommonSense.a
arm-none-eabi-gcc -o .pio/build/commonsense/firmware.elf -T commonsense_linker.ld -mfpu=fpv4-sp-d16 -mthumb -Wl,--gc-sections -Wl,--check-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align --specs=nosys.specs --specs=nano.specs -mcpu=cortex-m4 .pio/build/commonsense/src/main.o -L.pio/build/commonsense -L/Users/work-reese/.platformio/packages/toolchain-gccarmnoneeabi/arm-none-eabi/lib -L/Users/work-reese/.platformio/packages/framework-cmsis/CMSIS/Lib/GCC -L/Users/work-reese/.platformio/packages/framework-commonsense/linker -Wl,--start-group .pio/build/commonsense/libFrameworkCommonSense.a -larm_cortexM4lf_math -lm -Wl,--end-group
arm-none-eabi-objcopy -O binary .pio/build/commonsense/firmware.elf .pio/build/commonsense/firmware.bin

However, I do notice that some warnings are thrown w.r.t. memcpy from the previous line (small pointer conversion stuff; not cause for concern), whenever the compiler is attempting to generate a .A archive for the framework, none of which references memcpy. I believe this is because the startup file (configures clocks and such) includes an external reference to main that makes it try to compile many functions (even some in separate files?) into that framework library.

I’m not entirely sure where this ties back to, whether it’s platformIO or the arm compiler itself, but I must imagine the issue is a linker issue, unless someone has a better idea. I borrowed a linker script with virtually no changes from a similar Adafruit project.

Please let me know if anyone has an idea why I’m seeing such issues with invalid branch targets in the compiled code or knows of a potential solution. I’ll likely be updating this post as I learn more, and may cross-post to stack overflow. I’m happy to provide more information.

Edit 1: It appears the linking phase does not respect some of the compiler flags like -nostdlib. When I manually run some of the compiler commands (copied from verbose build output), I find that including -nostdlib while linking causes errors for undefined references to the standard library calls, which I had expected it to do all along when I included that option. Still unclear where stdlib is coming from, but my guess is somewhere in the arm-none-eabi-gcc toolchain. Issue remains that standard library calls branch to addresses that don’t contain code.

Edit 2: I may be having issues because I was missing a call to __libc_init_array() in the reset handler. I removed it because it always generates a fault. I see it tries to branch into the exception table unconditionally. Unsure why. This could be the issue, or it could be entirely unrelated. I also tried using --print-multi-lib, but that fails to finish compilation because it thinks a object file is missing, even though ealrier messages clearly compiled that file.

I believe I found what the issue was. I was using -mfloat-abi=softfp through all of this, and when I changed to -mfloat-abi=hard, the issue went away.

I am not sure why this is the case, although I understand the multi-lib has different versions for hard and softfp. That suggests to me that there’s something wrong with the softfp variant of these library functions.

__libc_init_array still hardfaults.

One more thing I’ll add:

I think that some of my problems have occurred because the compiler flags and the linker flags were not quite the same. I believe that some flags like -mfloat-abi=hard, -mthumb and -mcpu=cortex-m4 were present for the compiler, but not the linker. I believe this would have caused the linker to grab the wrong standard library from among the multi-lib options.

I believe -mthumb was the main offender here, as other discussions on this topic suggest that the dissembly above looks strange because it’s actually 32-bit ARM instructions, rather 16-bit thumb instructions (which the processor was interpreting the ARM instr. as).

Since I am using a custom platform, I am simply making sure that the flags in compilation and linking match in the corresponding builder PY scripts.