Hi all,
We are evaluating if Keil MDK (specially ARM Compiler for Embedded FuSa) is suitable and compatible with our project which current uses GNU's gcc.
At the linking part of the project (and following the migration guide) I've changed the '--gc-sections' flag for '--remove', while having as compiler flags in both cases '-ffunction-sections' and '-fdata-sections'.
My build using gcc shows no issues, while building with Keil MDK gives us some undefined symbols during linkage, these symbols I would expect them to be removed by --remove as they are not used in our project.
Here is a minimal reproducible example:
A.c
#include <stdio.h> void b_func(void); int main(void) { b_func(); // only this one is used return 0; }
B.c
#include <stdio.h> void c_func(void); // declared, but never called from A void b_func(void) { printf("b_func()\n"); // not calling c_func(), but still referring to it below: } void unused_b_func(void) { c_func(); // this one depends on C.c }
C.c
#include <stdio.h> void c_func(void) { printf("c_func()\n"); }
When building this minimal example with GNU toolchain I get no errors and everything works as expected. Commands used are:
arm-none-eabi-gcc -mcpu=cortex-m33 -O2 -ffunction-sections -fdata-sections -c A.c -o A.o arm-none-eabi-gcc -mcpu=cortex-m33 -O2 -ffunction-sections -fdata-sections -c B.c -o B.o arm-none-eabi-gcc -mcpu=cortex-m33 A.o B.o -Wl,--gc-sections -specs=nosys.specs -o test_gcc.elf
But when using Keil toolchain (armclang + armlink), with the following commands I get the resulting error:
armclang --target=arm-arm-none-eabi -mcpu=cortex-m33 -O2 -ffunction-sections -fdata-sections -c A.c -o A.o armclang --target=arm-arm-none-eabi -mcpu=cortex-m33 -O2 -ffunction-sections -fdata-sections -c B.c -o B.o armlink A.o B.o --remove -o test_keil.axf
Error: L6218E: Undefined symbol c_func (referred from B.o).
In my project that happens for a few files. Am I missing some flag to the linker or compiler in order to obtain the same behavior I had with GNU toolchain? Could someone please guide me with this, it is important for us to have a reproducible build of our project before purchasing the commercial license.
Yeah, that’s what we’ve been suspecting as well — it seems to come down to the order of operations inside armlink. It first resolves symbols and only performs the removal of unreferenced sections afterward.
armlink
I find it a bit surprising, since GNU ld behaves differently: it removes unused sections before resolving symbols, which avoids these kinds of issues.
ld
Is there any way to instruct armlink to change this order or to postpone symbol resolution until after the removal step?
We’ve also confirmed that adding C.o to the linker command line makes the build complete successfully, but in our actual project that would mean adding hundreds of files that aren’t really used. Even if the resulting binary ends up the same, it’s not very practical.
C.o
On top of that, one of the biggest limitations we’ve run into is with external libraries that define functions inside #ifdef blocks based on configuration headers. Using the same example, if c_func were guarded by #ifdef C_FUNC_ENABLED, and that flag was never defined, the linker would still complain about the unresolved symbol, even if C.o is included.
#ifdef
c_func
#ifdef C_FUNC_ENABLED