diff --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp --- a/libc/loader/linux/x86_64/start.cpp +++ b/libc/loader/linux/x86_64/start.cpp @@ -90,6 +90,35 @@ : true; } +using InitCallback = void(int, char **, char **); +using FiniCallback = void(void); + +extern "C" { +// These arrays are present in the .init_array and .fini_array sections. +// The symbols are inserted by linker when it sees references to them. +extern uintptr_t __preinit_array_start[]; +extern uintptr_t __preinit_array_end[]; +extern uintptr_t __init_array_start[]; +extern uintptr_t __init_array_end[]; +extern uintptr_t __fini_array_start[]; +extern uintptr_t __fini_array_end[]; +} + +static void call_init_array_callbacks(int argc, char **argv, char **env) { + size_t preinit_array_size = __preinit_array_end - __preinit_array_start; + for (size_t i = 0; i < preinit_array_size; ++i) + reinterpret_cast(__preinit_array_start[i])(argc, argv, env); + size_t init_array_size = __init_array_end - __init_array_start; + for (size_t i = 0; i < init_array_size; ++i) + reinterpret_cast(__init_array_start[i])(argc, argv, env); +} + +static void call_fini_array_callbacks() { + size_t fini_array_size = __fini_array_end - __fini_array_start; + for (size_t i = 0; i < fini_array_size; ++i) + reinterpret_cast(__fini_array_start[i])(); +} + } // namespace __llvm_libc using __llvm_libc::app; @@ -175,8 +204,15 @@ __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib; + __llvm_libc::call_init_array_callbacks( + app.args->argc, reinterpret_cast(app.args->argv), + reinterpret_cast(env_ptr)); + int retval = main(app.args->argc, reinterpret_cast(app.args->argv), reinterpret_cast(env_ptr)); + + __llvm_libc::call_fini_array_callbacks(); + __llvm_libc::cleanup_tls(tls.addr, tls.size); __llvm_libc::syscall(SYS_exit, retval); } diff --git a/libc/test/integration/loader/linux/CMakeLists.txt b/libc/test/integration/loader/linux/CMakeLists.txt --- a/libc/test/integration/loader/linux/CMakeLists.txt +++ b/libc/test/integration/loader/linux/CMakeLists.txt @@ -51,3 +51,12 @@ libc.src.errno.errno libc.src.sys.mman.mmap ) + +add_integration_test( + init_fini_array_test + SUITE libc-loader-tests + LOADER + libc.loader.linux.crt1 + SRCS + init_fini_array_test.cpp +) diff --git a/libc/test/integration/loader/linux/init_fini_array_test.cpp b/libc/test/integration/loader/linux/init_fini_array_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/loader/linux/init_fini_array_test.cpp @@ -0,0 +1,54 @@ +//===-- Loader test to test init and fini array iteration -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "utils/IntegrationTest/test.h" + +class A { +private: + int val[1024]; + +public: + A(int i, int a) { + for (int k = 0; k < 1024; ++k) + val[k] = 0; + val[i] = a; + } + + // TODO: When we have implementation for __cxa_atexit, an explicit definition + // of the destructor should be provided to test that path of registering the + // destructor callback for a global. + ~A() = default; + + int get(int i) const { return val[i]; } +}; + +int GLOBAL_INDEX = 512; +int INITVAL_INITIALIZER = 0x600D; + +A global(GLOBAL_INDEX, INITVAL_INITIALIZER); + +int initval = 0; +__attribute__((constructor)) void set_initval() { + initval = INITVAL_INITIALIZER; +} +__attribute__((destructor)) void reset_initval() { initval = 0; } + +int preinitval = 0; +void set_preinitval() { preinitval = INITVAL_INITIALIZER; } +__attribute__((destructor)) void reset_preinitval() { preinitval = 0; } + +using PreInitFunc = void(); +__attribute__((section(".preinit_array"))) PreInitFunc *preinit_func_ptr = + &set_preinitval; + +TEST_MAIN() { + ASSERT_EQ(global.get(GLOBAL_INDEX), INITVAL_INITIALIZER); + ASSERT_EQ(initval, INITVAL_INITIALIZER); + ASSERT_EQ(preinitval, INITVAL_INITIALIZER); + return 0; +}