我最近一直在尝试用Clang和CMake为C++编译固件,使用工具链文件。我可以让它正常工作,没有任何异常。当我使用异常时,出现了一个问题。
LLVM版本: 13.0.0
CMake版本: 3.21.3
处理器: STM32L432KC,ARM Cortex M4
为了成功地编译固件,我使用了预编译的libc
、libm
、libgcc
和libstdc++
,并捆绑了ARM GNU GCC工具链,版本10.3.2021-10。
我不会把整个工具链文件放在这里。相信我,到CMAKE_C_COMPILER
、CMAKE_CXX_COMPILER
、CMAKE_ASM_COMPILER
和CMAKE_LINKER
的路径都很好。
CMAKE_CXX_FLAGS_INIT
定义了C语言的初始编译标志,其定义如下:
-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
-nodefaultlibs
--sysroot=${ARM_GNU_TOOLCHAIN_PATH}/arm-none-eabi
-flto
-fdata-sections -ffunction-sections
# For <iostream>, <string>, ...
-isystem "${ARM_GNU_TOOLCHAIN_PATH}/arm-none-eabi/include/c++/${ARM_GNU_TOOLCHAIN_GCC_VERSION}/"
# For <bits/*>, ...
-isystem "${ARM_GNU_TOOLCHAIN_PATH}/arm-none-eabi/include/c++/${ARM_GNU_TOOLCHAIN_GCC_VERSION}/arm-none-eabi/thumb/v7e-m+fp/hard/"
-fexceptions
ARM_GNU_TOOLCHAIN_PATH
是所提到的ARM GNU GCC工具链的根路径。ARM_GNU_TOOLCHAIN_GCC_VERSION
等于10.3.1
。
链接器标志,使用CMAKE_EXE_LINKER_FLAGS_INIT
定义
-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
-nodefaultlibs
--sysroot=${ARM_GNU_TOOLCHAIN_PATH}/arm-none-eabi
-flto
-fdata-sections -ffunction-sections
-Wl,--gc-sections
-flto
-fexceptions
# Path to standard libraries: libc, libm, ...
-L"${ARM_GNU_TOOLCHAIN_PATH}/arm-none-eabi/lib/thumb/v7e-m+fp/hard/"
# Path to libgcc
-L"${ARM_GNU_TOOLCHAIN_PATH}/lib/gcc/arm-none-eabi/${ARM_GNU_TOOLCHAIN_GCC_VERSION}/thumb/v7e-m+fp/hard/"
-lc -lm -lnosys -lstdc++ -lgcc")
如果在二进制文件中没有,则返回try ... catch
块。一切都编译得很好,但如果至少有一个代码块:
try
{
throw std::runtime_error{"Some error!"};
} catch (const std::exception&e)
{
printf("Error: %s\r\n", e.what());
}
链接器在.data
段之前输入.got
段,而不需要在链接器脚本中进行指示。RAM起始地址为0x20000000。objdump
输出:
...
Contents of section .got:
20000000 848f0108 ....
Contents of section .data:
20000004 00000000 00000000 08000020 08000020 ........... ...
20000014 10000020 10000020 18000020 18000020 ... ... ... ...
20000024 20000020 20000020 28000020 28000020 .. .. (.. (..
20000034 30000020 30000020 38000020 38000020 0.. 0.. 8.. 8..
...
我的链接器脚本,由CubeMX生成,有LMA .data
部分,它应该是内存中的第一部分。:
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
正如您在注释中看到的,启动代码将使用_sdata
来初始化内存中的data
。问题是_sdata
将被设置为0x20000000,而不是第一个全局变量所在的0x20000008。这意味着所有的全局变量都将具有错误的值。
作为一种解决方法,我添加了一个使用所有got*
输入节的.got
节:
...
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
.got :
{
. = ALIGN(8);
*(.got)
*(.got*)
. = ALIGN(8);
} >RAM AT> FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
...
因为.got
部分与动态符号解析相关,所以我对它不是很熟悉。我只使用静态库来编译固件,因为我编写的裸机程序针对每个项目一个二进制文件。
主要的问题是异常不能正常工作。在上面的try ... catch ...
块中没有捕获到异常。固件在Default_Handler
内结束。
我猜这与clang生成的.got
部分有关。Clang无法正确链接libgcc
中的编译器内建来处理异常。
你能帮我调试和修复它吗?
发布于 2021-11-29 09:46:40
编译时使用-Wl,--target2=rel
标志。
我已经在llvm-dev邮件列表上创建了一个帖子,可以在here上找到。Peter Smith帮助解决了这个问题,并指出了解决方案。
https://stackoverflow.com/questions/70001611
复制相似问题