mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-03 10:30:58 +02:00
feat(esp_system): switch to standard __libc_init_array initialization
Initially, ESP-IDF used the do_global_ctors() function to run global constructors. This was done to accommodate Xtensa targets that emit .ctors.* sections, which are ordered in descending order. For RISC-V, compilation used .init_array.* sections, which are designed to have ascending order. Priority constructors in .init_array.* sections were correctly processed in ascending order. However, non-priority .init_array section was processed in descending order, as it was done for Xtensa .ctors. Starting with ESP-IDF v6.0, the implementation switched to the standard LibC behavior (__libc_init_array()), which processes both priority and non-priority constructors in ascending order. To achieve this, a breaking changes were introduced: - Xtensa .ctors.* priority entries converted to .init_array.* format (ascending), to be passed to __libc_init_array(). - Processing order of non-priority .init_array and .ctors sections was changed from descending to ascending. Also, this change introduces .preinit_array for linking. This may be needed for some C++ or sanitizer features. Related to https://github.com/espressif/esp-idf/issues/15529
This commit is contained in:
@@ -359,8 +359,12 @@ SECTIONS
|
|||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*/
|
*/
|
||||||
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors SORT(.ctors.*)))
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors.*)))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
|
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
|
||||||
|
@@ -197,20 +197,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -319,20 +319,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -355,20 +355,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -364,20 +364,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -213,20 +213,13 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -366,20 +366,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -355,20 +355,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -200,20 +200,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -377,20 +377,12 @@ SECTIONS
|
|||||||
* C++ constructor tables.
|
* C++ constructor tables.
|
||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*
|
|
||||||
* RISC-V gcc is configured with --enable-initfini-array so it emits
|
|
||||||
* .init_array section instead. But the init_priority sections will be
|
|
||||||
* sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending
|
|
||||||
* order during startup, however. Hence a different section is generated for
|
|
||||||
* the init_priority functions which is iterated in ascending order during
|
|
||||||
* startup. The corresponding code can be found in startup.c.
|
|
||||||
*/
|
*/
|
||||||
ALIGNED_SYMBOL(4, __init_priority_array_start)
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*))
|
KEEP (*(.preinit_array))
|
||||||
__init_priority_array_end = ABSOLUTE(.);
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
@@ -362,8 +362,12 @@ SECTIONS
|
|||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*/
|
*/
|
||||||
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors SORT(.ctors.*)))
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors.*)))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
|
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
|
||||||
|
@@ -373,8 +373,12 @@ SECTIONS
|
|||||||
*
|
*
|
||||||
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
|
||||||
*/
|
*/
|
||||||
|
ALIGNED_SYMBOL(4, __preinit_array_start)
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
__preinit_array_end = ABSOLUTE(.);
|
||||||
ALIGNED_SYMBOL(4, __init_array_start)
|
ALIGNED_SYMBOL(4, __init_array_start)
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors SORT(.ctors.*)))
|
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors.*)))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .ctors))
|
||||||
__init_array_end = ABSOLUTE(.);
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
|
||||||
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
|
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -58,54 +58,26 @@ const sys_startup_fn_t g_startup_fn[1] = { start_cpu0 };
|
|||||||
|
|
||||||
static const char* TAG = "cpu_start";
|
static const char* TAG = "cpu_start";
|
||||||
|
|
||||||
/**
|
|
||||||
* Xtensa gcc is configured to emit a .ctors section, RISC-V gcc is configured with --enable-initfini-array
|
|
||||||
* so it emits an .init_array section instead.
|
|
||||||
* But the init_priority sections will be sorted for iteration in ascending order during startup.
|
|
||||||
* The rest of the init_array sections is sorted for iteration in descending order during startup, however.
|
|
||||||
* Hence a different section is generated for the init_priority functions which is looped
|
|
||||||
* over in ascending direction instead of descending direction.
|
|
||||||
* The RISC-V-specific behavior is dependent on the linker script ld/esp32c3/sections.ld.in.
|
|
||||||
*/
|
|
||||||
__attribute__((no_sanitize_undefined)) /* TODO: IDF-8133 */
|
|
||||||
static void do_global_ctors(void)
|
|
||||||
{
|
|
||||||
#if __riscv
|
|
||||||
extern void (*__init_priority_array_start)(void);
|
|
||||||
extern void (*__init_priority_array_end)(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern void (*__init_array_start)(void);
|
|
||||||
extern void (*__init_array_end)(void);
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
|
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||||
|
/**
|
||||||
|
* @brief A helper function for __do_global_ctors, which is in crtend.o.
|
||||||
|
* It has been adapted from GCC source code. In GCC, it resides under
|
||||||
|
* the USE_EH_FRAME_REGISTRY macro, which is not enabled in Espressif
|
||||||
|
* toolchains to save small memory amount. Nevertheless, when C++ exceptions
|
||||||
|
* are enabled this initialization becomes necessary.
|
||||||
|
*/
|
||||||
|
static void __do_global_ctors_1(void)
|
||||||
|
{
|
||||||
struct object {
|
struct object {
|
||||||
long placeholder[ 10 ];
|
long placeholder[10];
|
||||||
};
|
};
|
||||||
void __register_frame_info(const void *begin, struct object * ob);
|
void __register_frame_info(const void *begin, struct object * ob);
|
||||||
extern char __eh_frame[];
|
extern char __eh_frame[];
|
||||||
|
|
||||||
static struct object ob;
|
static struct object ob;
|
||||||
__register_frame_info(__eh_frame, &ob);
|
__register_frame_info(__eh_frame, &ob);
|
||||||
#endif // CONFIG_COMPILER_CXX_EXCEPTIONS
|
|
||||||
|
|
||||||
void (**p)(void);
|
|
||||||
|
|
||||||
#if __riscv
|
|
||||||
for (p = &__init_priority_array_start; p < &__init_priority_array_end; ++p) {
|
|
||||||
ESP_LOGD(TAG, "calling init function: %p", *p);
|
|
||||||
(*p)();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-out-of-bounds")
|
|
||||||
for (p = &__init_array_end - 1; p >= &__init_array_start; --p) {
|
|
||||||
ESP_LOGD(TAG, "calling init function: %p", *p);
|
|
||||||
(*p)();
|
|
||||||
}
|
|
||||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-out-of-bounds")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif // CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Call component init functions defined using ESP_SYSTEM_INIT_Fn macros.
|
* @brief Call component init functions defined using ESP_SYSTEM_INIT_Fn macros.
|
||||||
@@ -199,12 +171,16 @@ static void do_secondary_init(void)
|
|||||||
|
|
||||||
static void start_cpu0_default(void)
|
static void start_cpu0_default(void)
|
||||||
{
|
{
|
||||||
|
extern void __libc_init_array(void);
|
||||||
// Initialize core components and services.
|
// Initialize core components and services.
|
||||||
// Operations that needs the cache to be disabled have to be done here.
|
// Operations that needs the cache to be disabled have to be done here.
|
||||||
do_core_init();
|
do_core_init();
|
||||||
|
|
||||||
// Execute constructors.
|
// Execute constructors.
|
||||||
do_global_ctors();
|
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||||
|
__do_global_ctors_1();
|
||||||
|
#endif
|
||||||
|
__libc_init_array();
|
||||||
|
|
||||||
/* ----------------------------------Separator-----------------------------
|
/* ----------------------------------Separator-----------------------------
|
||||||
* After this stage, other CPU start running with the cache, however the scheduler (and ipc service) is not available.
|
* After this stage, other CPU start running with the cache, however the scheduler (and ipc service) is not available.
|
||||||
|
@@ -58,18 +58,4 @@ menu "Unity unit testing library"
|
|||||||
jumping back to the test menu. The jumping is usually occurs in assert
|
jumping back to the test menu. The jumping is usually occurs in assert
|
||||||
functions such as TEST_ASSERT, TEST_FAIL etc.
|
functions such as TEST_ASSERT, TEST_FAIL etc.
|
||||||
|
|
||||||
config UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE
|
|
||||||
bool "Order unit tests by file path and line number"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If enabled, the Unity test framework will automatically insert test cases
|
|
||||||
in a sorted order at registration time (during constructor execution),
|
|
||||||
based on their source file path and line number.
|
|
||||||
|
|
||||||
This ensures consistent execution order across platforms (e.g., Linux vs. on-chip),
|
|
||||||
preserving the logical order in which tests are written in the source files.
|
|
||||||
|
|
||||||
Note, the file path used for sorting follows the full absolute path format.
|
|
||||||
/IDF/examples/system/unit_test/components/testable/test/test_mean.c
|
|
||||||
|
|
||||||
endmenu # "Unity unit testing library"
|
endmenu # "Unity unit testing library"
|
||||||
|
@@ -1,2 +1 @@
|
|||||||
CONFIG_ESP_TASK_WDT_EN=n
|
CONFIG_ESP_TASK_WDT_EN=n
|
||||||
CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE=y
|
|
||||||
|
@@ -27,35 +27,9 @@ void unity_testcase_register(test_desc_t *desc)
|
|||||||
s_unity_tests_last = desc;
|
s_unity_tests_last = desc;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE
|
|
||||||
test_desc_t *prev = NULL;
|
|
||||||
test_desc_t *current = s_unity_tests_first;
|
|
||||||
|
|
||||||
while (current) {
|
|
||||||
int file_cmp = strcmp(desc->file, current->file);
|
|
||||||
if (file_cmp < 0 || (file_cmp == 0 && desc->line < current->line)) {
|
|
||||||
// Insert before current
|
|
||||||
if (prev) {
|
|
||||||
prev->next = desc;
|
|
||||||
} else {
|
|
||||||
// Inserting at the head
|
|
||||||
s_unity_tests_first = desc;
|
|
||||||
}
|
|
||||||
desc->next = current;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
prev = current;
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert at the end
|
// Insert at the end
|
||||||
prev->next = desc;
|
s_unity_tests_last->next = desc;
|
||||||
s_unity_tests_last = desc;
|
s_unity_tests_last = desc;
|
||||||
#else
|
|
||||||
// Insert at head (original behavior)
|
|
||||||
desc->next = s_unity_tests_first;
|
|
||||||
s_unity_tests_first = desc;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print the multiple function case name and its sub-menu
|
/* print the multiple function case name and its sub-menu
|
||||||
|
@@ -24,3 +24,53 @@ If you encounter an orphan section error during linking, you can resolve it usin
|
|||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
The option 3 is **not recommended**, as orphan sections may indicate misconfigured memory mapping or unintentional behavior in your application.
|
The option 3 is **not recommended**, as orphan sections may indicate misconfigured memory mapping or unintentional behavior in your application.
|
||||||
|
|
||||||
|
Change in Global Constructor Order
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Initially, global constructors were executed using the internal `do_global_ctors()` function. This approach was used to support Xtensa targets, which emit `.ctors.*` sections ordered in **descending** order.
|
||||||
|
|
||||||
|
On RISC-V targets, the toolchain emits `.init_array.*` sections, which follow a standard **ascending** order. While priority constructors in `.init_array.*` were correctly processed, the non-priority `.init_array` section was previously handled in **descending** order and matched the Xtensa `.ctors` behavior.
|
||||||
|
|
||||||
|
Starting from ESP-IDF v6.0, the startup code uses `__libc_init_array()`, consistent with standard toolchain behavior. This function processes both priority and non-priority constructors in **ascending** order.
|
||||||
|
|
||||||
|
To support this behavior, the following breaking changes were introduced:
|
||||||
|
|
||||||
|
- On Xtensa targets `.ctors.*` entries are now converted to ascending order to ensure compatibility with `__libc_init_array()`.
|
||||||
|
- The processing order of non-priority `.init_array` and legacy `.ctors` sections was changed from **descending** to **ascending**.
|
||||||
|
|
||||||
|
These changes align ESP-IDF with toolchain expectations and improve consistency across supported architectures.
|
||||||
|
|
||||||
|
If the application relies on the previous (descending) constructor order and is affected by this change, consider the following approaches.
|
||||||
|
|
||||||
|
Update Constructor Registration Logic
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
In some cases, data structures were built assuming that constructors run in reverse order. To preserve the original behavior, update the registration logic to insert new items at the tail instead of the head.
|
||||||
|
|
||||||
|
Example (from `components/unity/unity_runner.c`):
|
||||||
|
|
||||||
|
.. code-block:: diff
|
||||||
|
|
||||||
|
- // Insert at the head
|
||||||
|
- desc->next = s_unity_tests_first;
|
||||||
|
- s_unity_tests_first = desc;
|
||||||
|
+ // Insert at the end
|
||||||
|
+ _unity_tests_last->next = desc;
|
||||||
|
+ s_unity_tests_last = desc;
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This approach is suitable only for very specific cases.
|
||||||
|
|
||||||
|
Use Constructor Priorities
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To explicitly control constructor order, use the `constructor()` function attribute with a numeric priority:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
__attribute__((constructor(PRIO)))
|
||||||
|
void foo(void);
|
||||||
|
|
||||||
|
Replace `PRIO` with an integer value. Lower values are executed earlier. This is the preferred method when specific ordering is required.
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
@@ -34,12 +34,13 @@ void example_plugin_register(const example_plugin_desc_t* plugin_desc)
|
|||||||
}
|
}
|
||||||
memcpy(&record->plugin_desc, plugin_desc, sizeof(*plugin_desc));
|
memcpy(&record->plugin_desc, plugin_desc, sizeof(*plugin_desc));
|
||||||
|
|
||||||
struct plugin_record *head = LIST_FIRST(&s_plugins_list);
|
static struct plugin_record *tail = NULL;
|
||||||
if (head == NULL) {
|
if (tail == NULL) {
|
||||||
LIST_INSERT_HEAD(&s_plugins_list, record, list_entry);
|
LIST_INSERT_HEAD(&s_plugins_list, record, list_entry);
|
||||||
} else {
|
} else {
|
||||||
LIST_INSERT_BEFORE(head, record, list_entry);
|
LIST_INSERT_AFTER(tail, record, list_entry);
|
||||||
}
|
}
|
||||||
|
tail = record;
|
||||||
printf("Successfully registered plugin '%s'\n", plugin_desc->name);
|
printf("Successfully registered plugin '%s'\n", plugin_desc->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,10 +11,10 @@ from pytest_embedded_idf.utils import idf_parametrize
|
|||||||
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
|
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
|
||||||
def test_plugins(dut: Dut) -> None:
|
def test_plugins(dut: Dut) -> None:
|
||||||
log_text = textwrap.dedent(r"""
|
log_text = textwrap.dedent(r"""
|
||||||
Nihao plugin performing self-registration...
|
|
||||||
Successfully registered plugin 'Nihao'
|
|
||||||
Hello plugin performing self-registration...
|
Hello plugin performing self-registration...
|
||||||
Successfully registered plugin 'Hello'
|
Successfully registered plugin 'Hello'
|
||||||
|
Nihao plugin performing self-registration...
|
||||||
|
Successfully registered plugin 'Nihao'
|
||||||
main_task: Calling app_main()
|
main_task: Calling app_main()
|
||||||
List of plugins:
|
List of plugins:
|
||||||
- Plugin 'Hello'
|
- Plugin 'Hello'
|
||||||
|
@@ -61,6 +61,14 @@ tools/test_apps/system/gdb_loadable_elf:
|
|||||||
temporary: true
|
temporary: true
|
||||||
reason: target esp32c6, esp32h2 is not supported yet
|
reason: target esp32c6, esp32h2 is not supported yet
|
||||||
|
|
||||||
|
tools/test_apps/system/init_array:
|
||||||
|
enable:
|
||||||
|
- if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux"
|
||||||
|
depends_components:
|
||||||
|
- esp_system
|
||||||
|
depends_filepatterns:
|
||||||
|
- tools/tools.json
|
||||||
|
|
||||||
tools/test_apps/system/log:
|
tools/test_apps/system/log:
|
||||||
disable_test:
|
disable_test:
|
||||||
- if: IDF_TARGET not in ["esp32", "esp32c3"]
|
- if: IDF_TARGET not in ["esp32", "esp32c3"]
|
||||||
|
6
tools/test_apps/system/init_array/CMakeLists.txt
Normal file
6
tools/test_apps/system/init_array/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(test_init_array)
|
20
tools/test_apps/system/init_array/README.md
Normal file
20
tools/test_apps/system/init_array/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | Linux |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | ----- |
|
||||||
|
|
||||||
|
# Historical note
|
||||||
|
|
||||||
|
Initially, ESP-IDF used the `do_global_ctors()` function to run global constructors.
|
||||||
|
This was done to accommodate Xtensa targets that emit `.ctors.*` sections, which are ordered in descending order.
|
||||||
|
|
||||||
|
For RISC-V, compilation used `.init_array.*` sections, which are designed to have ascending order.
|
||||||
|
Priority constructors in `.init_array.*` sections were correctly processed in ascending order.
|
||||||
|
However, non-priority `.init_array` section was processed in descending order (as it was done for Xtensa `.ctors`).
|
||||||
|
|
||||||
|
Starting with ESP-IDF v6.0, the implementation switched to the standard LibC behavior (`__libc_init_array()`),
|
||||||
|
which processes both priority and non-priority constructors in ascending order.
|
||||||
|
|
||||||
|
To achieve this, a breaking changes were introduced:
|
||||||
|
- Xtensa `.ctors.*` entries converted to `.init_array.*` format (ascending), to be passed to `__libc_init_array()`.
|
||||||
|
- Processing order of non-priority `.init_array` and `.ctors` sections was changed from descending to ascending.
|
||||||
|
|
||||||
|
This test ensures that the initialization order is correct and consistent between ESP-IDF and Linux targets.
|
2
tools/test_apps/system/init_array/main/CMakeLists.txt
Normal file
2
tools/test_apps/system/init_array/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "test_app_main.c"
|
||||||
|
REQUIRES esp_system)
|
49
tools/test_apps/system/init_array/main/test_app_main.c
Normal file
49
tools/test_apps/system/init_array/main/test_app_main.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
__attribute__((constructor))
|
||||||
|
void foo(void)
|
||||||
|
{
|
||||||
|
printf("%s\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor(103)))
|
||||||
|
void init_prio_103(void)
|
||||||
|
{
|
||||||
|
printf("%s\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor(101)))
|
||||||
|
void init_prio_101(void)
|
||||||
|
{
|
||||||
|
printf("%s\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor(102)))
|
||||||
|
void init_prio_102(void)
|
||||||
|
{
|
||||||
|
printf("%s\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor))
|
||||||
|
void bar(void)
|
||||||
|
{
|
||||||
|
printf("%s\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void preinit_func(void)
|
||||||
|
{
|
||||||
|
printf("%s\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((section(".preinit_array"), used))
|
||||||
|
uintptr_t test_preinit = (uintptr_t) preinit_func;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
printf("app_main running\n");
|
||||||
|
}
|
17
tools/test_apps/system/init_array/pytest_init_array.py
Normal file
17
tools/test_apps/system/init_array/pytest_init_array.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('target', ['supported_targets', 'preview_targets', 'linux'], indirect=['target'])
|
||||||
|
def test_init_array(dut: Dut) -> None:
|
||||||
|
dut.expect_exact('preinit_func')
|
||||||
|
dut.expect_exact('init_prio_101')
|
||||||
|
dut.expect_exact('init_prio_102')
|
||||||
|
dut.expect_exact('init_prio_103')
|
||||||
|
dut.expect_exact('foo')
|
||||||
|
dut.expect_exact('bar')
|
||||||
|
dut.expect_exact('app_main running')
|
Reference in New Issue
Block a user