Browse Source

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>
pull/88729/head
Keith Packard 1 year ago committed by Benjamin Cabé
parent
commit
9398174340
  1. 11
      include/zephyr/arch/arc/v2/linker.ld
  2. 84
      include/zephyr/linker/common-rom/common-rom-init.ld
  3. 1
      kernel/CMakeLists.txt
  4. 21
      kernel/Kconfig
  5. 26
      kernel/init.c
  6. 87
      kernel/init_static.c

11
include/zephyr/arch/arc/v2/linker.ld

@ -142,13 +142,6 @@ SECTIONS { @@ -142,13 +142,6 @@ SECTIONS {
#include <snippets-rodata.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
* RAMABLE_REGION into the same (continuous) memory - otherwise we can get beginning of the
* RAMABLE_REGION in the end of ROMABLE_REGION MPU aperture.
@ -317,11 +310,9 @@ SECTIONS { @@ -317,11 +310,9 @@ SECTIONS {
#endif
/DISCARD/ : {
#if defined(CONFIG_CPP) && !defined(CONFIG_STATIC_INIT_GNU) && defined(__MWDT_LINKER_CMD__)
/* Discard all destructors */
*(.dtors*)
*(.fini*)
*(.eh_frame*)
#endif /* CONFIG_CPP && !CONFIG_STATIC_INIT_GNU && __MWDT_LINKER_CMD__ */
*(.note.GNU-stack)
*(.got.plt)
*(.igot.plt)

84
include/zephyr/linker/common-rom/common-rom-init.ld

@ -1,75 +1,65 @@ @@ -1,75 +1,65 @@
/* SPDX-License-Identifier: Apache-2.0 */
#ifdef CONFIG_STATIC_INIT_GNU
SECTION_PROLOGUE(_CTOR_SECTION_NAME,,)
#if defined(CONFIG_TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU) || defined(CONFIG_NATIVE_APPLICATION)
SECTION_PROLOGUE(init_array,,)
{
/*
* The compiler fills the constructor pointers table below,
* hence symbol __CTOR_LIST__ must be aligned on word
* boundary. To align with the C++ standard, the first element
* of the array contains the number of actual constructors. The
* last element is NULL.
*
* Add all of the GNU-style constructors in priority order. Note
* that this doesn't build the ctors in the "usual" fashion with
* a length value first and NULL terminator, but we're creating
* an init_array style list and leaving the ctors list empty
*/
#ifdef CONFIG_NATIVE_APPLICATION
/* Use the native LIBC constructor code so that any native
* constructors get run before main is invoked
*/
__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_CTOR_LIST__ and
* __ZEHPYR_CTOR_END__ symbols.
* 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
* initialization code will not call the constructors before Zephyr loads.
*/
#ifdef CONFIG_64BIT
. = ALIGN(8);
__ZEPHYR_CTOR_LIST__ = .;
QUAD((__ZEPHYR_CTOR_END__ - __ZEPHYR_CTOR_LIST__) / 8 - 2)
KEEP(*(SORT_BY_NAME(".ctors*")))
__CTOR_LIST__ = .;
#ifdef CONFIG_64BIT
QUAD(0)
__ZEPHYR_CTOR_END__ = .;
QUAD(0)
__CTOR_END__ = .;
#else
. = ALIGN(4);
__ZEPHYR_CTOR_LIST__ = .;
LONG((__ZEPHYR_CTOR_END__ - __ZEPHYR_CTOR_LIST__) / 4 - 2)
KEEP(*(SORT_BY_NAME(".ctors*")))
__CTOR_LIST__ = .;
LONG(0)
__ZEPHYR_CTOR_END__ = .;
LONG(0)
__CTOR_END__ = .;
#endif
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
SECTION_PROLOGUE(init_array,,)
{
__CTOR_END__ = .;
#ifndef CONFIG_NATIVE_APPLICATION
/*
* Similar to the schenanigans required for the __CTOR_LIST__ and
* __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
* 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_end = .;
__zephyr_init_array_start = .;
KEEP(*(SORT_BY_NAME(".init_array*")))
__zephyr_init_array_end = .;
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#elif defined(CONFIG_TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU) && !defined(CONFIG_NATIVE_APPLICATION)
/*
* If the code to invoke constructors is not enabled,
* make sure there aren't any in the application
*/
SECTION_PROLOGUE(init_array,,)
{
KEEP(*(SORT_BY_NAME(".ctors*")))
KEEP(*(SORT_BY_NAME(".init_array*")))
#endif
#endif
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
ASSERT (SIZEOF(init_array) == 0,
#if !defined(CONFIG_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")
#endif
#endif

1
kernel/CMakeLists.txt

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

21
kernel/Kconfig

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

26
kernel/init.c

@ -483,6 +483,27 @@ static char **prepare_main_args(int *argc) @@ -483,6 +483,27 @@ static char **prepare_main_args(int *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
/**
@ -524,8 +545,9 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3) @@ -524,8 +545,9 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3)
#endif /* CONFIG_STACK_POINTER_RANDOM */
boot_banner();
void z_init_static(void);
z_init_static();
#ifdef CONFIG_STATIC_INIT_GNU
z_static_init_gnu();
#endif /* CONFIG_STATIC_INIT_GNU */
/* Final init level before app starts */
z_sys_init_run_level(INIT_LEVEL_APPLICATION);

87
kernel/init_static.c

@ -1,87 +0,0 @@ @@ -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
Loading…
Cancel
Save