与静态库链接不等同于与其对象链接
Posted
技术标签:
【中文标题】与静态库链接不等同于与其对象链接【英文标题】:Linking with static library not equivalent to linking with its objects 【发布时间】:2015-08-02 00:22:54 【问题描述】:问题:
与静态库链接时生成的固件映像与与直接从静态库中提取的对象链接时生成的固件映像不同。
两个固件映像链接无误并成功加载到微控制器上。
后一个二进制文件(与对象链接)按预期成功执行,而前一个二进制文件(链接到静态库)没有。
编译期间的唯一警告是制造商提供的 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 中断实际上是一个红鲱鱼,如接受的答案中所述
--麦克
【问题讨论】:
【参考方案1】:总结:
问题在于并非静态库中的所有对象都包含在固件映像中。这可以通过使用 --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"*** 帖子找到了答案。
在阅读这篇文章并了解到 gdb 函数名称报告对于共享相同内存地址的函数通常不准确时,我检查了生成的 .map
文件中的错误固件映像,并确认 WWDG_IRQHandler
位于与大多数 IRQHandler 相同的内存地址包括系统定义和使用的中断的 IRQHandler(例如,一些定时器中断)。
此外,在stm32f4xx_it.o
对象中定义的所有中断(它定义了系统使用的中断的IRQHandlers,并且包含在静态库中)指向@的内存地址987654330@,并且相应的 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
从未包含在内,因为它没有定义任何未定义的符号。这发生了很多次,有许多不同的目标文件;有时包含强符号,有时包含弱符号,具体取决于首先搜索的对象。有趣(但不足为奇)是,如果弱定义被删除,则包含强定义的对象会被包含在内,并且该文件中的 所有 个强定义(正确地)会覆盖已经包含的弱定义。 p>
解决了问题后,我不知道从哪里开始。这是链接器错误吗?
【讨论】:
有趣。我从来没有掉进那个洞,但是使用 LTO 的弱符号也有类似的问题(这就是我现在不使用它的原因)。但是,它增强了我不使用这些库的态度。至少我知道所有涉及我自己的代码。然而,对于中断处理程序,我做了很多相同的事情:启动时无限循环的弱别名和实际处理程序是强符号。这个问题是否特定于使用存档(包括库)而不是普通的目标文件?实际上,无论如何我都质疑档案的优点。 关于 gdb 给出的错误名称:当执行停止时,调试器只有当前指令的地址(PC 寄存器)。因此它必须查找与该地址匹配的符号(或者在函数的情况下最接近该地址)。如果多个符号匹配,它可能会显示所有符号,但这在大多数情况下毫无价值,因此 gdb 只是在第一个匹配时停止。哪个符号取决于查找算法(线性、哈希表、..)。这是调试中很常见的问题。【参考方案2】:如果你能解释“二进制文件不起作用”的真正含义,你会得到更好的答案。
您是否获得了一个您的编程工具根本不会加载到芯片中的二进制文件?
如果是这样,请仔细查看命令行上的链接器输出。
您是否正在生产可以加载到芯片中但没有看到预期行为的东西?
如果是这样,请使用硬件调试器。单步执行代码,直到出现问题,或者让它运行,然后停止它,看看你在哪里结束。
很有可能,您只是通过重新排列内存中所有内容的位置来发现代码中一直存在的错误。数组溢出、错误的指针取消引用和未初始化的变量是典型的罪魁祸首。打开-Wextra
和-Wall
可以帮助发现这些东西。
另一个想法:确保您的 LDSCRIPT 具有正确的闪存和 RAM 大小以用于实际部件号(即不用于系列中的其他部件)。
【讨论】:
谢谢你的建议,布赖恩;我已经用更多信息更新了这个问题。我还仔细检查了链接器脚本的目标,不幸的是,这不是这里的问题。【参考方案3】:我目前也在使用那个 MCU。但是,我有充分的理由避免使用 ST“标准”库。
看起来好像看门狗在启动期间已启用并且很快就会到期(中断是一个早期警告。这可能是由于运行时行为的变化。这很可能会因创建蹦床而产生的链接而有所不同由链接器和/或 tink-time 优化 (LTO) 以及由编译器和其他优化进行内联。
对于具有相同编译/链接选项的正常变化,给出的大小似乎超出了范围。但是它们很可能适用于 -Os 与 -O3 和 LTO/无 LTO(而对于后者,最终的代码大小可能会更大或更小,具体取决于 -O)。此外,我注意到一些 gcc/ld 版本的 LTO 存在问题,并且所有代码都必须使用相同的选项进行编译和链接(!)。还要检查使用的 ABI 是否与(使用的 C- 和 gcc-libs 匹配。
一个好的开始是通过 WWDG->CR 的观察点从复位粗略启动启动。还要检查 EWI 位;这实际上会允许中断。
【讨论】:
感谢您的回复,奥拉夫。它帮助指导我找到解决方案。如果我可能会问,您使用什么 HAL 进行 STM32 开发? @MikeHamer:根本没有。我自己编写驱动程序并使用自己的框架。并不比配置和使用“HAL”的东西难多少。更重要的是,这不是一个“硬件抽象层”,而不是一个普通的驱动程序系统。这更快,更适合项目的需求。以上是关于与静态库链接不等同于与其对象链接的主要内容,如果未能解决你的问题,请参考以下文章