Mik*_*mer 3 embedded linker gcc static-libraries static-linking
问题:
链接静态库时生成的固件映像与链接直接从静态库中提取的对象时生成的固件映像不同.
两个固件映像都没有错误地链接并成功加载到微控制器上.
后一个二进制文件(与对象链接)成功执行并按预期执行,而前者(链接到静态库)则不然.
编译期间唯一的警告是unused-but-set-variable
在制造商提供的HAL中,由于编译实现不需要各种宏定义; 并且unused-parameter
在各种弱功能中,也在制造商提供的HAL内.
描述:
我正在为STM32F407开发一个嵌入式应用程序.到目前为止,我一直在使用一个代码库,包括微处理器的HAL和设置代码,特定外设的驱动程序以及利用前两者的应用程序.
由于我希望使用相同的驱动程序和HAL开发多个应用程序(两者都是完整的和经过测试的,因此不会经常更改),我希望编译和分发HAL和驱动程序作为静态库,然后可以将其与申请来源.
问题是当链接应用程序和静态库时,固件映像无法在微处理器上正确执行.链接应用程序和直接从静态库中提取的目标文件时,固件映像按预期执行.
特别:
使用以下方法链接静态库时,创建的二进制文件不起作用:
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(APPOBJECTS) Library/libtest.a
Run Code Online (Sandbox Code Playgroud)
使用以下方法链接从静态库中提取的对象时创建的二进制文件
@cd Library && $(AR) x libtest.a && cd ..
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(APPOBJECTS) Library/*.o
Run Code Online (Sandbox Code Playgroud)
在这两种情况下:
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
Run Code Online (Sandbox Code Playgroud)
我已经比较了输出-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
Run Code Online (Sandbox Code Playgroud)
在没有编译时可以看到类似的大小差异 -Wl,--gc-sections
使用arm-none-eabi-gdb
调试单片机的执行,当WWDG中断发生故障固件映像进入一个无限循环.固件中未启用此中断,因此中断处理程序默认为Default_Handler
(无限循环).运行工作固件映像时不会发生此中断.
发生的WWDG中断实际上是一个红色鲱鱼,如公认的答案所述
- 麦克风
摘要:
问题是并非静态库中的所有对象都包含在固件映像中.这可以通过使用--whole-archive
和--no-whole-archive
链接器标志包围静态库来解决:
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(APPOBJECTS) -Wl,--whole-archive Library/libtest.a -Wl,--no-whole-archive
Run Code Online (Sandbox Code Playgroud)
出现问题的原因是,如果链接器包含具有弱符号定义的库对象,则会认为这些符号已定义,并且不再搜索其(强)定义.因此,具有强定义的对象可能包括也可能不包括,具体取决于搜索顺序以及它定义的其他符号.
解决途径:
使用arm-none-eabi-gdb
调试,似乎发生了禁用的 WWDG中断并调用了Default_Handler
.事实证明这是一个红色的鲱鱼......它经常发生,它通过"未配置时STM32 WWDG中断触发" stackoverflow post 导致我得到答案.
在阅读本文并了解到gdb函数名称报告对于共享相同内存地址的函数通常不准确时,我检查了生成的.map
文件以查找有故障的固件映像,并确认它WWDG_IRQHandler
与大多数IRQHandler位于相同的内存地址包括用于系统定义和使用的中断的IRQHandler(例如,一些定时器中断).
此外,对象中定义的所有中断stm32f4xx_it.o
(定义IRQHandler用于系统使用的中断,并包含在静态库中)指向的内存地址Default_Handler
,并且相应的IRQHandler符号被列为由提供者提供startup_stm32f407xx.o
.
然后,我检查了哪些目标文件实际链接到固件图像(perl -n -e '/libtest\.a\((.*?)\)/ && print "$1\n"' app.map | sort -u
),发现只有一部分对象被链接.
进一步检查startup_stm32f407xx.s
表明它定义了许多弱符号,例如:
.weak TIM2_IRQHandler
Run Code Online (Sandbox Code Playgroud)
在链接静态库的过程中,链接器在库中搜索未定义的符号,并包含它找到的第一个定义这些符号的对象.然后,它从未定义的列表中删除符号,以及由包含的对象定义的任何其他未定义的符号.
我猜测发生的事情是链接器在其中发现了一个未定义的符号startup_stm32f407xx.o
并包含了该对象.它认为所有IRQHandler符号都由其中的弱定义来定义.stm32f4xx_it.o
从未包含该对象,因为它没有定义任何未定义的符号.这发生了很多次,有许多不同的目标文件; 有时会包含强符号,有时会包含弱符号,具体取决于首先搜索的对象.有趣(但并不令人惊讶)是,如果删除弱定义,则包含包含强定义的对象,并且该文件中的所有强定义(正确)都会覆盖已包含的弱定义.
解决了这个问题后,我不知道从哪里开始.这是链接器错误吗?