与静态库链接不等同于与其对象链接

时间:2015-05-20 16:09:37

标签: gcc linker embedded static-libraries static-linking

问题:

与静态库链接时生成的固件映像与链接直接从静态库中提取的对象时生成的固件映像不同。

两个固件映像都没有错误地链接并成功加载到微控制器上。

后一个二进制文件(与对象链接)成功执行并按预期执行,而前者(链接到静态库)则没有。

编译期间唯一的警告是制造商提供的HAL中的unused-but-set-variable,由于编译实现不需要各种宏定义;和unused-parameter各种弱功能,也在制造商提供的HAL中。

说明

我正在为STM32F407开发一个嵌入式应用程序。到目前为止,我一直在使用一个代码库,包括微处理器的HAL&设置代码,特定外设的驱动程序和使用前两者的应用程序。

因为我希望使用相同的驱动程序开发多个应用程序。 HAL(两者都是完整的和经过测试的,因此不会经常更改),我希望编译和将HAL和驱动程序分发为静态库,然后可以将其与应用程序源链接。

问题在于,在链接应用程序和静态库时,固件映像无法在微处理器上正确执行。链接应用程序和直接从静态库中提取的目标文件时,固件映像将按预期执行。

具体地:

使用以下方法链接静态库时,创建的二进制文件不起作用:

$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(APPOBJECTS) Library/libtest.a

使用以下方法链接从静态库中提取的对象时,创建的二进制文件工作:

@cd Library && $(AR) x libtest.a && cd ..
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(APPOBJECTS) Library/*.o

在这两种情况下:

CFLAGS = $(INCLUDES) $(DEFS) -ggdb3 -O0 -std=c99 -Wall -specs=nano.specs -nodefaultlibs
CFLAGS+= -fdata-sections -ffunction-sections -mcpu=cortex-m4 -march=armv7e-m -mthumb
CFLAGS+= -mfloat-abi=hard -mfpu=fpv4-sp-d16 -MD -MP -MF $@.d

LDFLAGS = -T$(LDSCRIPT) -Wl,-static -Wl,-Map=$(@:.elf=.map),--cref -Wl,--gc-sections

我已经比较了-Wl,--print-gc-sections以及app.map文件的输出,但是在两个版本之间有足够的不同,没有任何东西跳出来是错误的。我也试过没有-Wl,--gc-sections,但没有用。

两个固件映像的arm-none-eabi-size输出为:

 text      data     bss     dec     hex filename
43464        76    8568   52108    cb8c workingapp.elf

 text      data     bss     dec     hex filename
17716        44    8568   26328    66d8 brokenapp.elf

在没有-Wl,--gc-sections

的情况下进行编译时,可以看到类似的大小差异

使用arm-none-eabi-gdb调试微控制器的执行,当WWDG中断发生时,故障固件映像进入无限循环。固件中未启用此中断,因此中断处理程序默认为Default_Handler(无限循环)。运行固件映像时,不会发生此中断。

发生的WWDG中断实际上是一个红色鲱鱼,如接受的答案所述

- 麦克

3 个答案:

答案 0 :(得分:6)

<强>要点:

问题是并非静态库中的所有对象都包含在固件映像中。这可以通过使用--whole-archive--no-whole-archive链接器标志包围静态库来解决:

 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(APPOBJECTS) -Wl,--whole-archive Library/libtest.a -Wl,--no-whole-archive

出现问题的原因是,如果链接器包含具有弱符号定义的库对象,则会认为这些符号已定义,并且不再搜索其(强)定义。因此,具有强定义的对象可能包括也可能不包括在内,具体取决于搜索顺序以及它定义的其他符号。

解决方案路径:

使用arm-none-eabi-gdb进行调试,似乎发生已禁用 WWDG中断并调用Default_Handler。事实证明这是一个红色的鲱鱼......它经常发生,它通过"STM32 WWDG interrupt firing when not configured" stackoverflow帖子引导我回答。

在阅读本文并了解到gdb函数名称报告对于共享相同内存地址的函数通常不准确时,我检查了生成的.map文件以查找有故障的固件映像并确认{{1} }}与大多数IRQHandlers 位于相同的内存地址,包括 IRQHandlers,用于系统定义和使用的中断(例如,某些定时器中断)。

此外,WWDG_IRQHandler对象中定义的所有中断(它定义了系统使用的中断的IRQHandler,并且包含在静态库中)指向了内存地址。 stm32f4xx_it.o以及相应的IRQHandler符号被列为由Default_Handler提供。

然后我检查了哪些对象文件实际链接到固件映像(startup_stm32f407xx.o),发现只有一部分对象被链接。

perl -n -e '/libtest\.a\((.*?)\)/ && print "$1\n"' app.map | sort -u的进一步检查表明它定义了许多弱符号,例如:

startup_stm32f407xx.s

在链接静态库的过程中,链接器在库中搜索未定义的符号,并包含它找到的第一个定义这些符号的对象。然后它从未定义的列表中删除符号,以及由包含的对象定义的任何其他未定义的符号。

我猜测发生的事情是链接器在.weak TIM2_IRQHandler 中找到了一个未定义的符号并包含了该对象。它认为所有IRQHandler符号都由其中的弱定义来定义。从未包含对象startup_stm32f407xx.o,因为它没有定义任何未定义的符号。这发生了很多次,有许多不同的目标文件;有时会包含强符号,有时会包含弱符号,具体取决于首先搜索的对象。有趣(但并不令人惊讶)是,如果删除弱定义,则包含包含强定义的对象,并且该文件中的所有强定义(正确)将覆盖已包含的弱定义。

解决了这个问题后,我不确定从哪里开始。这是链接器错误吗?

答案 1 :(得分:1)

如果你能解释什么&#34;二进制文件没有工作&#34;你会得到一个更好的答案。真的意思是。

您是否获得了编程工具无法加载到芯片中的二进制文件?

如果是这样,请仔细查看命令行上的链接器输出。

您是否正在制作可加载到芯片中而未看到预期行为的内容?

如果是这样,请使用硬件调试器。逐步执行代码,直到某些内容中断或让它运行,然后停止它并查看最终结果。

有可能,您只是通过重新排列内存中的所有内容来发现代码中始终存在的错误。数组溢出,错误的指针解引用和未初始化的变量是典型的罪魁祸首。启用-Wextra-Wall可以帮助发现这些内容。

另一个想法:确保您的LDSCRIPT具有正确的闪存和放大器。实际部件号的RAM大小(即不适用于系列中的不同部分)。

答案 2 :(得分:1)

我目前也在使用该MCU。但是,我有充分理由避免使用ST“标准”库。

看起来好像看门狗在启动期间已经启用并且很快就会过期(中断是一个早期警告。这可能是由于运行时行为的变化。这可能因为创建了蹦床而导致的联系变化很大通过链接器和/或tink-time优化(LTO)以及编译器和其他优化的内联。

使用相同的编译/链接选项,给出的大小似乎是正常变化的超出范围。但它们很可能用于-Os与-O3和LTO /无LTO(而对于后者,所得到的代码大小可能非常大或小,取决于-O)。另外,我注意到一些gcc / ld版本存在LTO问题,所有代码都必须使用相同的选项进行编译和链接(!)。还要检查所使用的ABI,它是否匹配(使用的C-和gcc-lib。

一个良好的开端将是在WWDG-> CR的观察点从重置粗略地逐步启动。还要检查EWI位;这实际上允许中断。

相关问题