From ebe02afcaef386c1657f1b7ecaa1bb3793d235c9 Mon Sep 17 00:00:00 2001 From: seteiro Date: Sat, 10 Oct 2026 20:34:34 +0800 Subject: [PATCH] [components][libc] run .init_array constructors in cplusplus_system_init Newer GCC emits C++ global constructors into .init_array, which many linker scripts keep outside [__ctors_start__, __ctors_end__), so they never ran. Walk __init_array_start/__init_array_end (weak) as well, skipping entries already covered by the ctors range. Add RT_USING_CPP_CRT_INIT for ports whose startup runtime already runs .init_array, default on for the simulator. With it set, only the .init_array walk is skipped; the ctors range is still walked. Renesas RA/RA_WIRELESS/RZ families select it when C++ is enabled: FSP SystemInit() already walks .init_array, so boards whose linker script only provides __init_array_* would run constructors twice. Boards that fold .init_array into the ctors range (ra6m3-hmi-board, ra8d1, rz) still run them once via RT-Thread. Also source env.sh and restore RTT_EXEC_PATH in ToolsCI project generate and dist steps so scons is on PATH (exit 127) and the ARM toolchain path is not overwritten by env.sh. Fixes #11778 --- .github/workflows/action_tools.yml | 5 +++ bsp/renesas/libraries/Kconfig | 5 +++ components/libc/cplusplus/Kconfig | 13 +++++++ components/libc/cplusplus/cxx_crt_init.c | 43 ++++++++++++++++++++---- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/.github/workflows/action_tools.yml b/.github/workflows/action_tools.yml index b3e1b954d4e5..c9111de9d0fb 100644 --- a/.github/workflows/action_tools.yml +++ b/.github/workflows/action_tools.yml @@ -67,6 +67,7 @@ jobs: - name: Build Tools run: | source ~/.env/env.sh + export RTT_EXEC_PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin scons --pyconfig-silent -C $TEST_BSP_ROOT 2>menuconfig.log cat menuconfig.log if grep -q "warning:" menuconfig.log; then @@ -79,6 +80,8 @@ jobs: - name: Project generate Tools if: ${{ success() }} run: | + source ~/.env/env.sh + export RTT_EXEC_PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin echo "Test to generate eclipse project" scons --target=eclipse -s -C $TEST_BSP_ROOT echo "Test to generate cmake project" @@ -89,6 +92,8 @@ jobs: - name: Project dist Tools if: ${{ success() }} run: | + source ~/.env/env.sh + export RTT_EXEC_PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin echo "Test to dist project" scons --dist -C $TEST_BSP_ROOT scons --dist-ide -C $TEST_BSP_ROOT diff --git a/bsp/renesas/libraries/Kconfig b/bsp/renesas/libraries/Kconfig index 1d0aff98aa8d..acc50d0734d5 100644 --- a/bsp/renesas/libraries/Kconfig +++ b/bsp/renesas/libraries/Kconfig @@ -1,14 +1,19 @@ +# The FSP startup runtime (SystemInit()/startup) already walks .init_array, +# so RT-Thread must skip that range to keep constructors running exactly once. config SOC_FAMILY_RENESAS_RA bool + select RT_USING_CPP_CRT_INIT if RT_USING_CPLUSPLUS default n config SOC_FAMILY_RENESAS_RA_WIRELESS bool + select RT_USING_CPP_CRT_INIT if RT_USING_CPLUSPLUS default n config SOC_FAMILY_RENESAS_RZ bool + select RT_USING_CPP_CRT_INIT if RT_USING_CPLUSPLUS default n config SOC_SERIES_R7FA6M3 diff --git a/components/libc/cplusplus/Kconfig b/components/libc/cplusplus/Kconfig index 1dcc6f389ce7..749a3ab0a5c8 100644 --- a/components/libc/cplusplus/Kconfig +++ b/components/libc/cplusplus/Kconfig @@ -4,6 +4,19 @@ menuconfig RT_USING_CPLUSPLUS if RT_USING_CPLUSPLUS + config RT_USING_CPP_CRT_INIT + bool "Startup runtime already runs .init_array constructors" + default y if SOC_SIMULATOR + default n + help + Enable this for GCC ports whose startup runtime already walks the + .init_array section, so that RT-Thread skips it and does not run + the constructors twice. RT-Thread still walks the ctors range. + This option is usually set by the BSP; do not change it manually. + Leave this disabled when the startup code relies on the empty + __libc_init_array provided by RT-Thread's newlib port, which + defers constructors to RT-Thread. + config RT_USING_CPLUSPLUS11 bool "Enable C++11 standard multi-threading feature support" default n diff --git a/components/libc/cplusplus/cxx_crt_init.c b/components/libc/cplusplus/cxx_crt_init.c index c5d1e434b5e1..be8619051f32 100644 --- a/components/libc/cplusplus/cxx_crt_init.c +++ b/components/libc/cplusplus/cxx_crt_init.c @@ -40,8 +40,8 @@ rt_weak void *__dso_handle = 0; * @note If there is no SHT$$INIT_ARRAY section, calling $Super$$__cpp_initialize__aeabi_() will cause an error * in ARMCC compiler. Therefore, this function manually iterates through the base addresses of the * SHT$$INIT_ARRAY section to call the constructor functions of each object. In GCC compiler, this function - * uses the __ctors_start__ and __ctors_end__ global variables to determine the range of constructor function - * pointers and calls each constructor function of every object in that range. + * always walks the ctors range; RT_USING_CPP_CRT_INIT only indicates that the startup runtime already + * walked the init_array range. * * @return Returns 0 if the initialization of the C++ runtime environment is successful. Otherwise, it returns * an error code indicating the failure of the operation. @@ -67,12 +67,43 @@ rt_weak int cplusplus_system_init(void) } #elif defined(__GNUC__) typedef void(*pfunc)(); - extern pfunc __ctors_start__[]; - extern pfunc __ctors_end__[]; + /* Linker scripts may provide the ctors range, the init_array range, or both. */ + extern pfunc __ctors_start__[] __attribute__((weak)); + extern pfunc __ctors_end__[] __attribute__((weak)); + pfunc *ctors_start = __ctors_start__; + pfunc *ctors_end = __ctors_end__; +#ifndef RT_USING_CPP_CRT_INIT + extern pfunc __init_array_start[] __attribute__((weak)); + extern pfunc __init_array_end[] __attribute__((weak)); + pfunc *init_start = __init_array_start; + pfunc *init_end = __init_array_end; +#endif pfunc *p; - for (p = __ctors_start__; p < __ctors_end__; p++) - (*p)(); + if (ctors_start && ctors_end) + { + for (p = ctors_start; (uintptr_t)p < (uintptr_t)ctors_end; p++) + { + (*p)(); + } + } + +#ifndef RT_USING_CPP_CRT_INIT + if (init_start && init_end) + { + for (p = init_start; (uintptr_t)p < (uintptr_t)init_end; p++) + { + /* Skip entries already called through the ctors range. */ + if (ctors_start && ctors_end && + (uintptr_t)p >= (uintptr_t)ctors_start && + (uintptr_t)p < (uintptr_t)ctors_end) + { + continue; + } + (*p)(); + } + } +#endif #endif return 0;