kernel: Unify .ctors and .init_array handling

Handle both of these sections in a single chunk of code instead of
separately. We don't need to use the legacy .ctors ABI as both
the constructors array and startup logic are managed within a single
link result.

This can now also be used with ARC MWDT which had been using the .ctors
sections but with .init_array semantics. For ARC MWDT, we now always
discard .dtors and .fini sections as Zephyr will never cause global
destructors to execute. Stop discarding .eh_frame sections so that
exception handling works as expected.

When building a NATIVE_APPLICATION, we ask the native C library to run all
of the constructors to ensure any non-Zephyr constructors are run before
main is invoked. It might be "nice" to split the constructors so that the
Zephyr constructors were executed by the Zephyr code while the non-Zephyr
ones were executed by the native C library. I think that could be done if
we knew the pathnames of either the Zephyr or non-Zephyr files. That might
make a good future enhancement.

Signed-off-by: Keith Packard <keithp@keithp.com>
This commit is contained in:
Keith Packard 2024-06-21 10:14:20 -07:00 committed by Benjamin Cabé
commit 9398174340
6 changed files with 81 additions and 169 deletions

View file

@ -142,13 +142,6 @@ SECTIONS {
#include <snippets-rodata.ld> #include <snippets-rodata.ld>
#include <zephyr/linker/kobject-rom.ld> #include <zephyr/linker/kobject-rom.ld>
#if defined(CONFIG_CPP) && !defined(CONFIG_STATIC_INIT_GNU) && defined(__MWDT_LINKER_CMD__)
. = ALIGN(4);
_fctors = .;
KEEP(*(.ctors*))
_ectors = .;
#endif /* CONFIG_CPP && !CONFIG_STATIC_INIT_GNU && __MWDT_LINKER_CMD__ */
/* This extra MPU alignment of RAMABLE_REGION is only required if we put ROMABLE_REGION and /* This extra MPU alignment of RAMABLE_REGION is only required if we put ROMABLE_REGION and
* RAMABLE_REGION into the same (continuous) memory - otherwise we can get beginning of the * RAMABLE_REGION into the same (continuous) memory - otherwise we can get beginning of the
* RAMABLE_REGION in the end of ROMABLE_REGION MPU aperture. * RAMABLE_REGION in the end of ROMABLE_REGION MPU aperture.
@ -317,11 +310,9 @@ SECTIONS {
#endif #endif
/DISCARD/ : { /DISCARD/ : {
#if defined(CONFIG_CPP) && !defined(CONFIG_STATIC_INIT_GNU) && defined(__MWDT_LINKER_CMD__) /* Discard all destructors */
*(.dtors*) *(.dtors*)
*(.fini*) *(.fini*)
*(.eh_frame*)
#endif /* CONFIG_CPP && !CONFIG_STATIC_INIT_GNU && __MWDT_LINKER_CMD__ */
*(.note.GNU-stack) *(.note.GNU-stack)
*(.got.plt) *(.got.plt)
*(.igot.plt) *(.igot.plt)

View file

@ -1,75 +1,65 @@
/* SPDX-License-Identifier: Apache-2.0 */ /* SPDX-License-Identifier: Apache-2.0 */
#ifdef CONFIG_STATIC_INIT_GNU #if defined(CONFIG_TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU) || defined(CONFIG_NATIVE_APPLICATION)
SECTION_PROLOGUE(_CTOR_SECTION_NAME,,) SECTION_PROLOGUE(init_array,,)
{ {
/* /*
* The compiler fills the constructor pointers table below, * Add all of the GNU-style constructors in priority order. Note
* hence symbol __CTOR_LIST__ must be aligned on word * that this doesn't build the ctors in the "usual" fashion with
* boundary. To align with the C++ standard, the first element * a length value first and NULL terminator, but we're creating
* of the array contains the number of actual constructors. The * an init_array style list and leaving the ctors list empty
* last element is NULL. */
* #ifdef CONFIG_NATIVE_APPLICATION
* The __CTOR_LIST__ and __CTOR_END__ symbols are always defined /* Use the native LIBC constructor code so that any native
* to result in an empty list. * constructors get run before main is invoked
* Instead, Zephyr's start-up code uses the __ZEPHYR_CTOR_LIST__ and */
* __ZEHPYR_CTOR_END__ symbols. __init_array_start = .;
#else
__zephyr_init_array_start = .;
#endif
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array .ctors))
#ifdef CONFIG_NATIVE_APPLICATION
__init_array_end = .;
#else
__zephyr_init_array_end = .;
#endif
#ifdef CONFIG_NATIVE_LIBC
/*
* The __CTOR_LIST__ and __CTOR_END__ symbols are always defined
* to result in an empty list.
* Instead, Zephyr's start-up code uses the __zephyr_init_array_start__ and
* __zephyr_init_array_end__ symbols.
* In this way, in native_simulator based targets, the host glibc process * In this way, in native_simulator based targets, the host glibc process
* initialization code will not call the constructors before Zephyr loads. * initialization code will not call the constructors before Zephyr loads.
*/ */
__CTOR_LIST__ = .;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
. = ALIGN(8);
__ZEPHYR_CTOR_LIST__ = .;
QUAD((__ZEPHYR_CTOR_END__ - __ZEPHYR_CTOR_LIST__) / 8 - 2)
KEEP(*(SORT_BY_NAME(".ctors*")))
__CTOR_LIST__ = .;
QUAD(0) QUAD(0)
__ZEPHYR_CTOR_END__ = .;
QUAD(0) QUAD(0)
__CTOR_END__ = .;
#else #else
. = ALIGN(4);
__ZEPHYR_CTOR_LIST__ = .;
LONG((__ZEPHYR_CTOR_END__ - __ZEPHYR_CTOR_LIST__) / 4 - 2)
KEEP(*(SORT_BY_NAME(".ctors*")))
__CTOR_LIST__ = .;
LONG(0) LONG(0)
__ZEPHYR_CTOR_END__ = .;
LONG(0) LONG(0)
__CTOR_END__ = .;
#endif #endif
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) __CTOR_END__ = .;
#ifndef CONFIG_NATIVE_APPLICATION
SECTION_PROLOGUE(init_array,,)
{
/* /*
* Similar to the schenanigans required for the __CTOR_LIST__ and * Similar to the schenanigans required for the __CTOR_LIST__ and
* __CTOR_END__ symbols we define __init_array_start and __init_array_end * __CTOR_END__ symbols we define __init_array_start and __init_array_end
* to the same address to define an empty list. This prevents the glibc * to the same address to define an empty list. This prevents the glibc
* startup code from calling any global constructors before Zephyr loads. * startup code from calling any global constructors before Zephyr loads.
* */
* Zephyr's start-up code uses the __zephyr_init_array_start and
* __zephyr_init_array_end symbols, so these need to be set correctly.
*/
. = ALIGN(4);
__init_array_start = .; __init_array_start = .;
__init_array_end = .; __init_array_end = .;
__zephyr_init_array_start = .; #endif
KEEP(*(SORT_BY_NAME(".init_array*"))) #endif
__zephyr_init_array_end = .; } GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#if !defined(CONFIG_STATIC_INIT_GNU) && !defined(CONFIG_NATIVE_APPLICATION)
#elif defined(CONFIG_TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU) && !defined(CONFIG_NATIVE_APPLICATION) ASSERT(__zephyr_init_array_start == __zephyr_init_array_end,
/* "GNU-style constructors required but STATIC_INIT_GNU not enabled")
* If the code to invoke constructors is not enabled, #endif
* make sure there aren't any in the application
*/
SECTION_PROLOGUE(init_array,,)
{
KEEP(*(SORT_BY_NAME(".ctors*")))
KEEP(*(SORT_BY_NAME(".init_array*")))
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
ASSERT (SIZEOF(init_array) == 0,
"GNU-style constructors required but STATIC_INIT_GNU not enabled")
#endif #endif

View file

@ -55,7 +55,6 @@ list(APPEND kernel_files
errno.c errno.c
fatal.c fatal.c
init.c init.c
init_static.c
kheap.c kheap.c
mem_slab.c mem_slab.c
float.c float.c

View file

@ -1046,10 +1046,10 @@ config KERNEL_WHOLE_ARCHIVE
to be included, rather than searching the archive for required object files. to be included, rather than searching the archive for required object files.
config TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU config TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU
# As of today only ARC MWDT toolchain doesn't support GNU-compatible # As of today, we don't know of any toolchains that don't create
# initialization of static objects, new toolchains can be added # either .ctors or .init_array sections containing initializer
# here if required. # addresses in a fashion compatible with how Zephyr uses them.
def_bool "$(ZEPHYR_TOOLCHAIN_VARIANT)" != "arcmwdt" def_bool y
config STATIC_INIT_GNU config STATIC_INIT_GNU
bool "Support GNU-compatible initializers and constructors" bool "Support GNU-compatible initializers and constructors"
@ -1059,14 +1059,11 @@ config STATIC_INIT_GNU
help help
GNU-compatible initialization of static objects. This is required for GNU-compatible initialization of static objects. This is required for
C++ constructor support as well as for initializer functions as C++ constructor support as well as for initializer functions as
defined by GNU-compatible toolchains. This increases the size defined by GNU-compatible toolchains. This increases the size of
of Zephyr binaries by around 100 bytes. If you know your Zephyr binaries by around 24 bytes. If you know your application
application doesn't need any initializers, you can disable this doesn't need any initializers, you can disable this option. The linker
option. will emit an error if constructors are needed and this option has been
The ARC MWDT toolchain, does not support or use this setting, disabled.
and has instead separate C++ constructor initialization code.
Note the option CMAKE_LINKER_GENERATOR does not yet support this feature
or CPP.
config BOOTARGS config BOOTARGS
bool "Support bootargs" bool "Support bootargs"

View file

@ -483,6 +483,27 @@ static char **prepare_main_args(int *argc)
(*argc)++; (*argc)++;
} }
} }
#endif
#ifdef CONFIG_STATIC_INIT_GNU
extern void (*__zephyr_init_array_start[])();
extern void (*__zephyr_init_array_end[])();
static void z_static_init_gnu(void)
{
void (**fn)();
for (fn = __zephyr_init_array_start; fn != __zephyr_init_array_end; fn++) {
/* MWDT toolchain sticks a NULL at the end of the array */
if (*fn == NULL) {
break;
}
(**fn)();
}
}
#endif #endif
/** /**
@ -524,8 +545,9 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3)
#endif /* CONFIG_STACK_POINTER_RANDOM */ #endif /* CONFIG_STACK_POINTER_RANDOM */
boot_banner(); boot_banner();
void z_init_static(void); #ifdef CONFIG_STATIC_INIT_GNU
z_init_static(); z_static_init_gnu();
#endif /* CONFIG_STATIC_INIT_GNU */
/* Final init level before app starts */ /* Final init level before app starts */
z_sys_init_run_level(INIT_LEVEL_APPLICATION); z_sys_init_run_level(INIT_LEVEL_APPLICATION);

View file

@ -1,87 +0,0 @@
/*
* Copyright (c) 2012-2015 Wind River Systems, Inc.
* Copyright (c) 2021 Synopsys, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
void __do_global_ctors_aux(void);
void __do_init_array_aux(void);
void z_init_static(void)
{
#if defined(CONFIG_STATIC_INIT_GNU)
__do_global_ctors_aux();
__do_init_array_aux();
#elif defined(__CCAC__) /* ARC MWDT */
__do_global_ctors_aux();
#endif
}
/**
* @section - Constructor module
* @brief
* The ctors section contains a list of function pointers that execute both the C++ constructors of
* static global objects, as well as either C or C++ initializer functions (declared with the
* attribute constructor). These must be executed before the application's main() routine.
*
* NOTE: Not all compilers put those function pointers into the ctors section;
* some put them into the init_array section instead.
*/
#ifdef CONFIG_STATIC_INIT_GNU
/* What a constructor function pointer looks like */
typedef void (*CtorFuncPtr)(void);
/* Constructor function pointer list is generated by the linker script. */
extern CtorFuncPtr __ZEPHYR_CTOR_LIST__[];
extern CtorFuncPtr __ZEPHYR_CTOR_END__[];
/**
*
* @brief Invoke all C++ style global object constructors
*
* This routine is invoked by the kernel prior to the execution of the
* application's main().
*/
void __do_global_ctors_aux(void)
{
unsigned int nCtors;
nCtors = (unsigned long)__ZEPHYR_CTOR_LIST__[0];
while (nCtors >= 1U) {
__ZEPHYR_CTOR_LIST__[nCtors--]();
}
}
#endif
/*
* @section
* @brief Execute initialization routines referenced in .init_array section
*/
#ifdef CONFIG_STATIC_INIT_GNU
typedef void (*func_ptr)(void);
extern func_ptr __zephyr_init_array_start[];
extern func_ptr __zephyr_init_array_end[];
/**
* @brief Execute initialization routines referenced in .init_array section
*/
void __do_init_array_aux(void)
{
for (func_ptr *func = __zephyr_init_array_start;
func < __zephyr_init_array_end;
func++) {
(*func)();
}
}
#endif