i.amniels
i.amniels

Reputation: 1841

CMake linker "undefined reference" when switching from gcc to g++ for cross compilation

I have a C project which compiles successfully. Now I want to use C++ code in the same project, so I renamed main.c to main.cpp. The project is for an embedded microcontroller, so I'm cross compiling with the arm-none-eabi toolchain.

When I have renamed the main file to .cpp, I get the following error:

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'

This is because some standard libraries are not available for this "bare metal" target. (see https://stackoverflow.com/a/13237079/507369)

This is solved in my linker script:

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

The linker script is added by my CMake toolchain file:

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)

My CMakeLists.txt looks like:

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")

When linking as a C executable, this works. When linking as a C++ executable I get the undefined reference error.

Update

I looked at the exact linker commands composed by CMake and those are:

For GCC (successful):

/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

For G++ (Error):

/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 

So at least the parameters passed to g++ are the ones I expected. I tried removing --gc-sections in combination with adding -nostartfiles, but this didn't help.

Upvotes: 3

Views: 3251

Answers (3)

i.amniels
i.amniels

Reputation: 1841

The missing functions have to provide the interface between newlib and the hardware or the OS, these are called the system calls.

The linking issue can be solved by adding the --specs=nosys.specs command line option as stated by user Cinder Biscuits. This option provides mostly non-functional implementations of the system calls.

But this only solves the linking issue, if functionality from newlib is actually required, an implementation of the system calls needs to be provided.

A guide for developing the system calls can be found here.

For the STM32 microcontrollers, ST provides a file syscalls.c as part of their STM32CubeF4 software package. The file can be found in the package at Projects/STM32F4-Discovery/Examples/BSP/SW4STM32/. By adding this file to the project, implementations for all the syscalls are provided and newlib can be used.

For a small microcontroller like the STM32, newlib nano should be used as it is much smaller. This can be achieved by adding the --specs=nano.specs command line parameter.

Upvotes: 2

ollo
ollo

Reputation: 25380

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

The _exit function is part of newlib (check newlib/_exit.c ). Try adding following flag to the CMAKE_EXE_LINKER_FLAGS: --specs=nano.specs

As you are on a freestanding system it makes also sense to add -ffreestanding to both – C and C++ – compiler flags.

Upvotes: 0

Cinder Biscuits
Cinder Biscuits

Reputation: 5259

I'm not sure if there's a more CMakeish way to do this, but try adding -specs=nosys.specs your CMake toolchain file like so:

SET(CMAKE_EXE_LINKER_FLAGS "-specs=nosys.specs, -Wl,--gc-sections -Wl,-T ${LINKER_SCRIPT}" CACHE STRING "" FORCE)

"nosys" is generic implementation for barebone systems.

https://launchpadlibrarian.net/170926122/readme.txt

Upvotes: 1

Related Questions