简体   繁体   中英

exceptions not working cross compiling with clang and cmake for arm cortex m4, and "got" section is generated

I have been trying lately to compile firmware with Clang and CMake, using Toolchain files, for C++. I can get it normally working without exceptions. A problem arises when I use exceptions.

LLVM version: 13.0.0
CMake version: 3.21.3
CPU: STM32L432KC, ARM Cortex M4

To successfully compile the firmware I use precompiled libc , libm , libgcc and libstdc++ bundled with ARM GNU GCC Toolchain, version 10.3.2021-10.

I won't put the entire toolchain file here. Trust me that the paths to the CMAKE_C_COMPILER , CMAKE_CXX_COMPILER , CMAKE_ASM_COMPILER and CMAKE_LINKER are good.

CMAKE_CXX_FLAGS_INIT , which define initial compile flags for C language, are defined like so:

-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 is the root path to the mentioned ARM GNU GCC Toolchain. ARM_GNU_TOOLCHAIN_GCC_VERSION is equal to 10.3.1 .

The linker flags, defined with 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")

If in the binary there is no, try... catch block. Everything compiles just fine, but if there is at least one block:

try
{
    throw std::runtime_error{"Some error!"};
} catch (const std::exception&e)
{
    printf("Error: %s\r\n", e.what());
}

The linker inputs the .got section, before .data section without being instructed within the linker script. The RAM start address is 0x20000000. objdump output:

...
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.. 
...

My linker script, generated by CubeMX, has LMA .data section, which shall be the first in RAM.:

  .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

As you can see in the comment, _sdata will be used by the startup code to initialize data in RAM. The problem is that _sdata will be set to 0x20000000, not 0x20000008, where the first global variable lies. This means that all the global variables will have wrong values.

As a workaround, I have added a .got section which uses all the got* input sections:

...
  .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 : 
  {
...

Since .got section is related to dynamic symbol resolution, I am not really fluent with it. I use only static libraries to compile the firmware, since I write bare-metal programs targeted one binary per project.

The main issue is that the exceptions don't work correctly. No exceptions are caught within the above try... catch... block. The firmware ends within Default_Handler .

I guess it's related to the .got section generated by clang. Clang is not able to properly link the compiler builtins from libgcc to handle the exceptions.

Could you help me in debugging and fixing that?

Use -Wl,--target2=rel flag when compiling.


I have created a post on the llvm-dev mailing list, which can be found here . Peter Smith has helped to resolve the problem and pointed to the solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM