从GCC切换到g++进行交叉编译时,C使链接器未定义引用(&Q)

2022-04-10 00:00:00 gcc cross-compiling cmake linker c++

我有一个编译成功的C项目。现在我想在同一个项目中使用C++代码,所以我将main.c重命名为main.cpp。该项目是针对嵌入式微控制器的,因此我正在使用arm-none-eabi工具链进行交叉编译。

将主文件重命名为.cpp后,出现以下错误:

Linking CXX executable discovery_simple_test.elf
/usr/lib/gcc/arm-none-eabi/<long_path>/fpu/libg.a(lib_a-abort.o): In function `abort':
/build/<long_path>/newlib/libc/stdlib/abort.c:63: undefined reference to `_exit'

这是因为某些标准库不适用于该"裸机"目标。(参见https://stackoverflow.com/a/13237079/507369)

这是在我的链接器脚本中解决的:

/* Remove information from the standard libraries */
/DISCARD/ :
{
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
}

链接器脚本由我的CMake工具链文件添加:

INCLUDE(CMakeForceCompiler)

SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_VERSION 1)

# specify the cross compiler
CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU)
CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU)

SET(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/STM32F407VGTx_FLASH.ld)
SET(COMMON_FLAGS "-mcpu=cortex-m4 -mthumb -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -g -fno-common -fmessage-length=0")
UNSET(CMAKE_CXX_FLAGS CACHE)
UNSET(CMAKE_C_FLAGS CACHE)
UNSET(CMAKE_EXE_LINKER_FLAGS CACHE)
SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS} -std=c++11" CACHE STRING "" FORCE)
SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -std=gnu99" CACHE STRING "" FORCE)
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections -Wl,-T ${LINKER_SCRIPT}" CACHE STRING "" FORCE)

我的CMakeLists.txt看起来像:

project(discovery_simple_test CXX C ASM)
add_definitions(-DSTM32F407xx)

file(GLOB_RECURSE USER_SOURCES "Src/*.c" "Src/*.cpp")
include_directories(Inc)
add_executable(${PROJECT_NAME}.elf ${USER_SOURCES}  ${LINKER_SCRIPT})

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.map")

当作为C可执行文件链接时,这是可行的。作为C++可执行文件链接时,出现undefined reference错误。

更新

我查看了CMake编写的链接器命令,它们是:

GCC(成功):

/usr/bin/arm-none-eabi-gcc  -mcpu=cortex-m4 -mthumb -mthumb-interwork 
-mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections 
-g -fno-common -fmessage-length=0 -std=gnu99  -Wl,-gc-sections 
-T /path/STM32F407VGTx_FLASH.ld -Wl,
-Map=/path/build/discovery_simple_test.map 
CMakeFiles/discovery_simple_test.elf.dir/Src/main.c.obj 
<list of obj files>  
-o discovery_simple_test.elf libCMSIS.a

对于G++(错误):

/usr/bin/arm-none-eabi-g++   -mcpu=cortex-m4 -mthumb -mthumb-interwork 
-mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections 
-g -fno-common -fmessage-length=0 -std=c++11  -Wl,-gc-sections 
-T /home/niels/Dev/stm32/discovery_simple_test/STM32F407VGTx_FLASH.ld 
-Wl
-Map=/path/discovery_simple_test/build/discovery_simple_test.map 
CMakeFiles/discovery_simple_test.elf.dir/Src/stm32f4xx_hal_msp.c.obj 
CMakeFiles/discovery_simple_test.elf.dir/Src/stm32f4xx_it.c.obj   
CMakeFiles/discovery_simple_test.elf.dir/Src/main.cpp.obj 
<List of obj files>
-o discovery_simple_test.elf libCMSIS.a 

因此,至少传递给g++的参数是我所期望的。我试着结合添加-nostartfile来删除--gc-sections,但无济于事。


解决方案

缺少的函数必须提供newlib与硬件或操作系统之间的接口,这称为system calls

链接问题可以通过添加--specs=nosys.specs命令行选项来解决,如用户Cinder饼干所述。此选项主要提供系统调用的非函数实现。

但这只解决了链接问题,如果实际上需要来自newlib的功能,则需要提供系统调用的实现。

here可以找到开发系统调用的指南。

对于STM32微控制器,ST提供了一个syscalls.c文件作为其STM32CubeF4软件包的一部分。该文件可以在Projects/STM32F4-Discovery/Examples/BSP/SW4STM32/的包中找到。通过将此文件添加到项目中,将提供所有syscall的实现并可以使用newlib。

对于像STM32这样的小型微控制器,应该使用newlib Nano,因为它要小得多。这可以通过添加--specs=nano.specs命令行参数来实现。

相关文章