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( + cxx_globals + SUITE libc-loader-tests + LOADER + libc.loader.linux.crt1 + SRCS + cxx_globals_test.cpp +) diff --git a/libc/test/integration/loader/linux/cxx_globals_test.cpp b/libc/test/integration/loader/linux/cxx_globals_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/loader/linux/cxx_globals_test.cpp @@ -0,0 +1,29 @@ +//===-- 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; + +public: + A(int a) : val(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() const { return val; } +}; + +A global(0x1234); + +TEST_MAIN() { + ASSERT_EQ(global.get(), 0x1234); + return 0; +}