summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseteiro <[email protected]>2026-10-10 20:34:34 +0800
committerRbb666 <[email protected]>2026-10-11 16:11:07 +0800
commit3577e7a6ed0f183534e207179de5251e155fd49e (patch)
tree424c4f10acd38b0807e7cfe25db1af1e92a44136
parent33b1dd58bafbd850e982191b5f138cf8d7578e61 (diff)
[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
-rw-r--r--.github/workflows/action_tools.yml5
-rw-r--r--bsp/renesas/libraries/Kconfig5
-rw-r--r--components/libc/cplusplus/Kconfig13
-rw-r--r--components/libc/cplusplus/cxx_crt_init.c43
4 files changed, 60 insertions, 6 deletions
diff --git a/.github/workflows/action_tools.yml b/.github/workflows/action_tools.yml
index b3e1b954d4..c9111de9d0 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 1d0aff98aa..acc50d0734 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 1dcc6f389c..749a3ab0a5 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 c5d1e434b5..be8619051f 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;