Skip to main content
peterdonchev
Senior
January 8, 2026
Solved

STM32CubeMX CMake generator newlib-nano bug

  • January 8, 2026
  • 2 replies
  • 952 views

Hello,

I have observed this issue in version 6.16.1 across several MCU series (F3, F4, H5, H7), though it appears likely that all earlier versions are affected.

The generated .cmake toolchain files incorrectly treat the --specs=nano.specs option as linker-only. This option should be applied globally, as it also affects the system include paths used during compilation. Specifically, it causes the compiler to include arm-none-eabi/include/newlib-nano/newlib.h instead of the standard arm-none-eabi/include/newlib.h.

While this mismatch probably does not break ABI compatibility—since the structures provided from the standard headers are larger than those used in the compiled newlib-nano library—it does negate the RAM usage benefits of newlib-nano. This is particularly problematic when using the USE_NEWLIB_REENTRANT FreeRTOS option.

Regards,
Peter

 

Best answer by Erlkoenig

Ah, I think this is caused by some funny logic in CMake's own compiler/ABI detection process (not necessarily a bug). Normally this compilation step should always be "skipped", but it is skipped because another compilation step before runs successfully. But because of the misconfigured flags this first step fails.

Anyhow, the issue with the flags is that the toolchain file is supposed to set CMAKE_<LANG>_FLAGS_INIT , not CMAKE_C_FLAGS. Similarly the toolchain file should also use CMAKE_C_FLAGS_RELEASE_INIT instead of CMAKE_C_FLAGS_RELEASE, but that doesn't work, because if we don't override the latter, CMake will initialize it with "-O3 -DNDEBUG", and we don't want "-O3".

So try it with:

# MCU specific flags
set(TARGET_FLAGS "-mcpu=cortex-m0plus --specs=nano.specs")

set(CMAKE_C_FLAGS_INIT "${TARGET_FLAGS}")
set(CMAKE_ASM_FLAGS_INIT "${CMAKE_C_FLAGS_INIT} -x assembler-with-cpp -MMD -MP")
string(APPEND CMAKE_C_FLAGS_INIT " -Wall -fdata-sections -ffunction-sections")

set(CMAKE_ASM_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_ASM_FLAGS_RELEASE "-Os -g3")
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_C_FLAGS_RELEASE "-Os -g3")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_CXX_FLAGS_RELEASE "-Os -g3")

set(CMAKE_CXX_FLAGS_INIT "${CMAKE_C_FLAGS_INIT} -fno-rtti -fno-exceptions -fno-threadsafe-statics")

set(CMAKE_EXE_LINKER_FLAGS_INIT "-T \"${CMAKE_SOURCE_DIR}/STM32U083xx_FLASH.ld\"")
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " -Wl,--print-memory-usage")
set(TOOLCHAIN_LINK_LIBRARIES "-lm")

 

2 replies

Technical Moderator
January 8, 2026

Hello @peterdonchev 

Thank you for highlighting this issue.!

I will check this internally with the dedicated team and get back to you ASAP.

KR, Souhaib

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Senior
January 9, 2026

There's another little issue related to this:

The gcc-arm-none-eabi.cmake file passes TARGET_FLAGS to CMAKE_EXE_LINKER_FLAGS . But this is pointless, as TARGET_FLAGS is already included in CMAKE_C_FLAGS (and thereby also CMAKE_CXX_FLAGS) which gets passed to the linker automatically anyways by CMake. This results in the TARGET_FLAGS being passed to the linker twice. Apparently in the case of the --specs=nano.specs flag, this can cause linker errors.

So I think the --specs=nano.specs flag should be moved to TARGET_FLAGS (instead of CMAKE_EXE_LINKER_FLAGS), and TARGET_FLAGS should not be passed to CMAKE_EXE_LINKER_FLAGS.

For example (including my previous suggestions:(

# MCU specific flags
set(TARGET_FLAGS "-mcpu=cortex-m0plus --specs=nano.specs")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}")
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections")

set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_C_FLAGS_RELEASE "-Os -g3")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_CXX_FLAGS_RELEASE "-Os -g3")

set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")

set(CMAKE_EXE_LINKER_FLAGS "-T \"${CMAKE_SOURCE_DIR}/STM32U083xx_FLASH.ld\"")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--print-memory-usage")
set(TOOLCHAIN_LINK_LIBRARIES "-lm")

 

peterdonchev
Senior
January 9, 2026

Hi @Erlkoenig ,
I ran into the same issue with parameters being passed twice. I'm not sure which extension or tool is causing it, but I found a workaround.


1. Split the toolchain file:
First, I copied the original gcc-arm-none-eabi.cmake to a new file named gcc-arm-none-eabi_include.cmake.

Then, I include this file in the main CMakeLists.txt before the project() definition (this part is crucial):

# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

include("cmake/gcc-arm-none-eabi_include.cmake")

# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})


2. Strip all flags from the original toolchain file
From the original gcc-arm-none-eabi.cmake, I removed (or commented out) all compiler and linker flags. The file now contains only the toolchain definitions:

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_C_COMPILER_ID GNU)
set(CMAKE_CXX_COMPILER_ID GNU)

# Some default GCC settings
# arm-none-eabi- must be part of path environment
set(TOOLCHAIN_PREFIX arm-none-eabi-)

set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)

set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

 

 3. Put all actual flags into gcc-arm-none-eabi_include.cmake
This file contains the original contents plus the modifications for --specs=nano.specs:

# MCU specific flags
set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard --specs=nano.specs ")
#set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard ")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS} ")
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections ")

set(CMAKE_C_FLAGS_DEBUG "-Og -g3")
set(CMAKE_C_FLAGS_RELEASE "-O2 -g3")
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g3")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -g3")

set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")

#set(CMAKE_EXE_LINKER_FLAGS "${TARGET_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T \"${CMAKE_SOURCE_DIR}/MY_PROJECT.ld\"")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --specs=nano.specs")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--print-memory-usage")
set(TOOLCHAIN_LINK_LIBRARIES "m")


With this setup, I'm able to configure and compile the project successfully.
Regards,
Peter

Senior
January 9, 2026

Hmm, but when you're manually modifying the gcc-arm-none-eabi.cmake file anyways, you can just put the fixed flags in there, no need to use an additional gcc-arm-none-eabi_include.cmake file.

The issue is that the gcc-arm-none-eabi.cmake file will be overwritten when re-running STM32CubeMX (not always though...?). A hack to solve this is to have a "fixer" script (e.g. in python) that is run as a "After Code Generation" step from STM32CubeMX, which automatically repairs the flags in gcc-arm-none-eabi.cmake as needed...