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

2023-12-21

Problem:

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

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

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

编译期间唯一的警告是unused-but-set-variable在制造商提供的 HAL 中,由于各种宏定义,这些对于编译实现来说并不是必需的;和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 [email protected] /cdn-cgi/l/email-protection

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

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

正如已接受的答案中所述,发生的 WWDG 中断实际上是一个转移注意力的事件

--Mike


Summary:

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

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

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

解决路径:

Using arm-none-eabi-gdb进行调试,看来disabledWWDG 中断发生并调用Default_Handler。事实证明这是一个转移注意力的事情……这种情况发生得足够频繁,以至于我通过“未配置时 STM32 WWDG 中断触发” https://stackoverflow.com/questions/27623885/stm32-wwdg-interrupt-firing-when-not-configured堆栈溢出帖子。

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

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

然后我检查了哪些目标文件实际上链接到固件映像中(perl -n -e '/libtest\.a\((.*?)\)/ && print "$1\n"' app.map | sort -u)并发现只有一部分对象被链接。

进一步检查startup_stm32f407xx.s表明它定义了许多弱符号,例如:

.weak TIM2_IRQHandler

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

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

解决了问题后,我不知道该去哪里。这是链接器错误吗?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与静态库的链接不等于与其对象的链接 的相关文章

随机推荐