From 9a07780687ab5afd9e1bafe8a7c99f512eb98ce8 Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Wed, 14 Dec 2022 20:38:45 +0000 Subject: [libc][NFC] Rename "loader" to "startup". Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D140049 --- libc/CMakeLists.txt | 6 +- libc/cmake/modules/LLVMLibCTestRules.cmake | 46 ++-- libc/docs/source_tree_layout.rst | 8 +- libc/loader/CMakeLists.txt | 3 - libc/loader/linux/CMakeLists.txt | 96 --------- libc/loader/linux/aarch64/CMakeLists.txt | 16 -- libc/loader/linux/aarch64/start.cpp | 230 -------------------- libc/loader/linux/crti.cpp | 0 libc/loader/linux/crtn.cpp | 0 libc/loader/linux/x86_64/CMakeLists.txt | 20 -- libc/loader/linux/x86_64/start.cpp | 234 --------------------- libc/startup/CMakeLists.txt | 3 + libc/startup/linux/CMakeLists.txt | 96 +++++++++ libc/startup/linux/aarch64/CMakeLists.txt | 16 ++ libc/startup/linux/aarch64/start.cpp | 230 ++++++++++++++++++++ libc/startup/linux/crti.cpp | 0 libc/startup/linux/crtn.cpp | 0 libc/startup/linux/x86_64/CMakeLists.txt | 20 ++ libc/startup/linux/x86_64/start.cpp | 234 +++++++++++++++++++++ libc/test/integration/CMakeLists.txt | 2 +- libc/test/integration/loader/CMakeLists.txt | 62 ------ libc/test/integration/loader/linux/CMakeLists.txt | 62 ------ libc/test/integration/loader/linux/args_test.cpp | 38 ---- .../loader/linux/init_fini_array_test.cpp | 64 ------ .../integration/loader/linux/main_without_args.cpp | 11 - .../integration/loader/linux/main_without_envp.cpp | 11 - libc/test/integration/loader/linux/tls_test.cpp | 38 ---- libc/test/integration/scudo/CMakeLists.txt | 6 +- .../src/__support/threads/CMakeLists.txt | 8 +- libc/test/integration/src/pthread/CMakeLists.txt | 28 +-- libc/test/integration/src/spawn/CMakeLists.txt | 4 +- libc/test/integration/src/stdio/CMakeLists.txt | 8 +- libc/test/integration/src/stdlib/CMakeLists.txt | 4 +- libc/test/integration/src/threads/CMakeLists.txt | 28 +-- libc/test/integration/src/unistd/CMakeLists.txt | 16 +- libc/test/integration/startup/CMakeLists.txt | 62 ++++++ libc/test/integration/startup/linux/CMakeLists.txt | 62 ++++++ libc/test/integration/startup/linux/args_test.cpp | 38 ++++ .../startup/linux/init_fini_array_test.cpp | 64 ++++++ .../startup/linux/main_without_args.cpp | 11 + .../startup/linux/main_without_envp.cpp | 11 + libc/test/integration/startup/linux/tls_test.cpp | 38 ++++ libc/test/src/CMakeLists.txt | 4 +- 43 files changed, 969 insertions(+), 969 deletions(-) delete mode 100644 libc/loader/CMakeLists.txt delete mode 100644 libc/loader/linux/CMakeLists.txt delete mode 100644 libc/loader/linux/aarch64/CMakeLists.txt delete mode 100644 libc/loader/linux/aarch64/start.cpp delete mode 100644 libc/loader/linux/crti.cpp delete mode 100644 libc/loader/linux/crtn.cpp delete mode 100644 libc/loader/linux/x86_64/CMakeLists.txt delete mode 100644 libc/loader/linux/x86_64/start.cpp create mode 100644 libc/startup/CMakeLists.txt create mode 100644 libc/startup/linux/CMakeLists.txt create mode 100644 libc/startup/linux/aarch64/CMakeLists.txt create mode 100644 libc/startup/linux/aarch64/start.cpp create mode 100644 libc/startup/linux/crti.cpp create mode 100644 libc/startup/linux/crtn.cpp create mode 100644 libc/startup/linux/x86_64/CMakeLists.txt create mode 100644 libc/startup/linux/x86_64/start.cpp delete mode 100644 libc/test/integration/loader/CMakeLists.txt delete mode 100644 libc/test/integration/loader/linux/CMakeLists.txt delete mode 100644 libc/test/integration/loader/linux/args_test.cpp delete mode 100644 libc/test/integration/loader/linux/init_fini_array_test.cpp delete mode 100644 libc/test/integration/loader/linux/main_without_args.cpp delete mode 100644 libc/test/integration/loader/linux/main_without_envp.cpp delete mode 100644 libc/test/integration/loader/linux/tls_test.cpp create mode 100644 libc/test/integration/startup/CMakeLists.txt create mode 100644 libc/test/integration/startup/linux/CMakeLists.txt create mode 100644 libc/test/integration/startup/linux/args_test.cpp create mode 100644 libc/test/integration/startup/linux/init_fini_array_test.cpp create mode 100644 libc/test/integration/startup/linux/main_without_args.cpp create mode 100644 libc/test/integration/startup/linux/main_without_envp.cpp create mode 100644 libc/test/integration/startup/linux/tls_test.cpp (limited to 'libc') diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index e4ccf0f..909f5bf 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -197,9 +197,9 @@ add_subdirectory(src) add_subdirectory(utils) if(LLVM_LIBC_FULL_BUILD) - # The loader can potentially depend on the library components so add it - # after the library implementation directories. - add_subdirectory(loader) + # The startup system can potentially depend on the library components so add + # it after the library implementation directories. + add_subdirectory(startup) endif() # The lib and test directories are added at the very end as tests diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake index bc2f85e..00974c8 100644 --- a/libc/cmake/modules/LLVMLibCTestRules.cmake +++ b/libc/cmake/modules/LLVMLibCTestRules.cmake @@ -106,7 +106,7 @@ function(create_libc_unittest fq_target_name) # machine specific object library. Such a test would be testing internals of # the libc and it is assumed that they will be rare in practice. So, they # can be skipped in the corresponding CMake files using platform specific - # logic. This pattern is followed in the loader tests for example. + # logic. This pattern is followed in the startup tests for example. # # Another pattern that is present currently is to detect machine # capabilities and add entrypoints and tests accordingly. That approach is @@ -377,10 +377,10 @@ function(add_libc_fuzzer target_name) endfunction(add_libc_fuzzer) # Rule to add an integration test. An integration test is like a unit test -# but does not use the system libc. Not even the loader from the system libc -# is linked to the final executable. The final exe is fully statically linked. -# The libc that the final exe links to consists of only the object files of -# the DEPENDS targets. +# but does not use the system libc. Not even the startup objects from the +# system libc are linked in to the final executable. The final exe is fully +# statically linked. The libc that the final exe links to consists of only +# the object files of the DEPENDS targets. # # Usage: # add_integration_test( @@ -388,15 +388,15 @@ endfunction(add_libc_fuzzer) # SUITE # SRCS [src2.cpp ...] # HDRS [hdr1.cpp ...] -# LOADER +# STARTUP # DEPENDS # ARGS # ENV # COMPILE_OPTIONS # ) # -# The loader target should provide a property named LOADER_OBJECT which is -# the full path to the object file produces when the loader is built. +# The startup target should provide a property named STARTUP_OBJECT which is +# the full path to the object file produced when the startup system is built. # # The DEPENDS list can be empty. If not empty, it should be a list of # targets added with add_entrypoint_object or add_object_library. @@ -409,7 +409,7 @@ function(add_integration_test test_name) cmake_parse_arguments( "INTEGRATION_TEST" "" # No optional arguments - "SUITE;LOADER" # Single value arguments + "SUITE;STARTUP" # Single value arguments "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS" # Multi-value arguments ${ARGN} ) @@ -417,8 +417,8 @@ function(add_integration_test test_name) if(NOT INTEGRATION_TEST_SUITE) message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") endif() - if(NOT INTEGRATION_TEST_LOADER) - message(FATAL_ERROR "The LOADER to link to the integration test is missing.") + if(NOT INTEGRATION_TEST_STARTUP) + message(FATAL_ERROR "The STARTUP to link to the integration test is missing.") endif() if(NOT INTEGRATION_TEST_SRCS) message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") @@ -456,27 +456,27 @@ function(add_integration_test test_name) file(MAKE_DIRECTORY ${sysroot}/include) set(sysroot_lib ${sysroot}/lib) file(MAKE_DIRECTORY ${sysroot_lib}) - get_target_property(loader_object_file ${INTEGRATION_TEST_LOADER} LOADER_OBJECT) - get_target_property(crti_object_file libc.loader.linux.crti LOADER_OBJECT) - get_target_property(crtn_object_file libc.loader.linux.crtn LOADER_OBJECT) + get_target_property(startup_object_file ${INTEGRATION_TEST_STARTUP} STARTUP_OBJECT) + get_target_property(crti_object_file libc.startup.linux.crti STARTUP_OBJECT) + get_target_property(crtn_object_file libc.startup.linux.crtn STARTUP_OBJECT) set(dummy_archive $/lib$.a) - if(NOT loader_object_file) - message(FATAL_ERROR "Missing LOADER_OBJECT property of ${INTEGRATION_TEST_LOADER}.") + if(NOT startup_object_file) + message(FATAL_ERROR "Missing STARTUP_OBJECT property of ${INTEGRATION_TEST_STARTUP}.") endif() - set(loader_dst ${sysroot_lib}/${LIBC_TARGET_ARCHITECTURE}-linux-gnu/crt1.o) + set(startup_dst ${sysroot_lib}/${LIBC_TARGET_ARCHITECTURE}-linux-gnu/crt1.o) add_custom_command( - OUTPUT ${loader_dst} ${sysroot}/lib/crti.o ${sysroot}/lib/crtn.o ${sysroot}/lib/libm.a ${sysroot}/lib/libc++.a - COMMAND cmake -E copy ${loader_object_file} ${loader_dst} + OUTPUT ${startup_dst} ${sysroot}/lib/crti.o ${sysroot}/lib/crtn.o ${sysroot}/lib/libm.a ${sysroot}/lib/libc++.a + COMMAND cmake -E copy ${startup_object_file} ${startup_dst} COMMAND cmake -E copy ${crti_object_file} ${sysroot}/lib COMMAND cmake -E copy ${crtn_object_file} ${sysroot}/lib # We copy the dummy archive as libm.a and libc++.a as the compiler drivers expect them. COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libm.a COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libc++.a - DEPENDS ${INTEGRATION_TEST_LOADER} libc.loader.linux.crti libc.loader.linux.crtn libc_integration_test_dummy + DEPENDS ${INTEGRATION_TEST_STARTUP} libc.startup.linux.crti libc.startup.linux.crtn libc_integration_test_dummy ) add_custom_target( - ${fq_target_name}.__copy_loader__ - DEPENDS ${loader_dst} + ${fq_target_name}.__copy_startup__ + DEPENDS ${startup_dst} ) add_library( @@ -520,7 +520,7 @@ function(add_integration_test test_name) # as is (and not as paths like /usr/lib/.../crtbegin.o). target_link_options(${fq_target_name} PRIVATE --sysroot=${sysroot} -static -stdlib=libc++ --rtlib=compiler-rt) add_dependencies(${fq_target_name} - ${fq_target_name}.__copy_loader__ + ${fq_target_name}.__copy_startup__ ${fq_libc_target_name} libc.utils.IntegrationTest.test ${INTEGRATION_TEST_DEPENDS}) diff --git a/libc/docs/source_tree_layout.rst b/libc/docs/source_tree_layout.rst index a0565cb..cd328e7 100644 --- a/libc/docs/source_tree_layout.rst +++ b/libc/docs/source_tree_layout.rst @@ -13,7 +13,7 @@ directories:: - fuzzing - include - lib - - loader + - startup - src - test - utils @@ -58,11 +58,11 @@ The ``lib`` directory This directory contains a ``CMakeLists.txt`` file listing the targets for the public libraries ``libc.a``, ``libm.a`` etc. -The ``loader`` directory +The ``startup`` directory ------------------------ -This directory contains the implementations of the application loaders like -``crt1.o`` etc. +This directory contains the implementations of the application startup objects +like ``crt1.o`` etc. The ``src`` directory --------------------- diff --git a/libc/loader/CMakeLists.txt b/libc/loader/CMakeLists.txt deleted file mode 100644 index b4bbe81c..0000000 --- a/libc/loader/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) - add_subdirectory(${LIBC_TARGET_OS}) -endif() diff --git a/libc/loader/linux/CMakeLists.txt b/libc/loader/linux/CMakeLists.txt deleted file mode 100644 index 92ff896..0000000 --- a/libc/loader/linux/CMakeLists.txt +++ /dev/null @@ -1,96 +0,0 @@ -function(add_loader_object name) - cmake_parse_arguments( - "ADD_LOADER_OBJECT" - "ALIAS" # Option argument - "SRC" # Single value arguments - "DEPENDS;COMPILE_OPTIONS" # Multi value arguments - ${ARGN} - ) - - get_fq_target_name(${name} fq_target_name) - get_fq_deps_list(fq_deps_list ${ADD_LOADER_OBJECT_DEPENDS}) - if(ADD_LOADER_OBJECT_ALIAS) - list(LENGTH ADD_LOADER_OBJECT_DEPENDS deps_size) - if(NOT (${deps_size} EQUAL "1")) - message(FATAL_ERROR "A loader object alias should have exactly one dependency.") - endif() - list(GET ADD_LOADER_OBJECT_DEPENDS 0 dep) - get_fq_dep_name(fq_dep_name ${dep}) - - add_custom_target(${fq_target_name}) - add_dependencies(${fq_target_name} ${fq_dep_name}) - get_target_property(loader_object ${fq_dep_name} LOADER_OBJECT) - set_target_properties( - ${fq_target_name} - PROPERTIES - "TARGET_TYPE" "${OBJECT_LIBRARY_TARGET_TYPE}" - "LOADER_OBJECT" "${loader_object}" - "OBJECT_FILES" "" - "DEPS" "${fq_dep_name}" - ) - return() - endif() - - add_object_library( - ${name}.__objects__ - SRCS ${ADD_LOADER_OBJECT_SRC} - DEPENDS ${ADD_LOADER_OBJECT_DEPENDS} - COMPILE_OPTIONS ${ADD_LOADER_OBJECT_COMPILE_OPTIONS} - ) - - set(objfile ${LIBC_BUILD_DIR}/lib/${name}.o) - add_custom_command( - OUTPUT ${objfile} - COMMAND cp $ ${objfile} - DEPENDS $ - ) - add_custom_target( - ${fq_target_name} - DEPENDS ${objfile} - ) - set_target_properties( - ${fq_target_name} - PROPERTIES - "TARGET_TYPE" "${OBJECT_LIBRARY_TARGET_TYPE}" - "LOADER_OBJECT" "${objfile}" - "OBJECT_FILES" "" - "DEPS" "${fq_target_name}.__objects__" - ) -endfunction() - -if(NOT (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})) - message(STATUS "Skipping loader for target architecture ${LIBC_TARGET_ARCHITECTURE}") - return() -endif() - -add_subdirectory(${LIBC_TARGET_ARCHITECTURE}) - -add_loader_object( - crt1 - ALIAS - DEPENDS - .${LIBC_TARGET_ARCHITECTURE}.crt1 -) - -add_loader_object( - crti - SRC - crti.cpp -) - -add_loader_object( - crtn - SRC - crtn.cpp -) - -add_custom_target(libc-startup) -set(startup_components crt1 crti crtn) -foreach(target IN LISTS startup_components) - set(fq_target_name libc.loader.linux.${target}) - add_dependencies(libc-startup ${fq_target_name}) - get_target_property(loader_object ${fq_target_name} LOADER_OBJECT) - install(FILES ${loader_object} - DESTINATION ${CMAKE_INSTALL_LIBDIR} - COMPONENT ${LIBC_COMPONENT}) -endforeach() diff --git a/libc/loader/linux/aarch64/CMakeLists.txt b/libc/loader/linux/aarch64/CMakeLists.txt deleted file mode 100644 index 054df0b..0000000 --- a/libc/loader/linux/aarch64/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_loader_object( - crt1 - SRC - start.cpp - DEPENDS - libc.config.linux.app_h - libc.include.sys_mman - libc.include.sys_syscall - libc.src.__support.OSUtil.osutil - libc.src.stdlib.exit - libc.src.stdlib.atexit - libc.src.string.memory_utils.memcpy_implementation - COMPILE_OPTIONS - -fno-omit-frame-pointer - -ffreestanding # To avoid compiler warnings about calling the main function. -) diff --git a/libc/loader/linux/aarch64/start.cpp b/libc/loader/linux/aarch64/start.cpp deleted file mode 100644 index b02ea7d..0000000 --- a/libc/loader/linux/aarch64/start.cpp +++ /dev/null @@ -1,230 +0,0 @@ -//===-- Implementation of crt for aarch64 ---------------------------------===// -// -// 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 "config/linux/app.h" -#include "src/__support/OSUtil/syscall.h" -#include "src/__support/threads/thread.h" -#include "src/stdlib/atexit.h" -#include "src/stdlib/exit.h" -#include "src/string/memory_utils/memcpy_implementations.h" - -#include - -#include -#include -#include -#include -#include -#include - -extern "C" int main(int, char **, char **); - -// Source documentation: -// https://github.com/ARM-software/abi-aa/tree/main/sysvabi64 - -namespace __llvm_libc { - -#ifdef SYS_mmap2 -static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2; -#elif SYS_mmap -static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap; -#else -#error "Target platform does not have SYS_mmap or SYS_mmap2 defined" -#endif - -AppProperties app; - -static ThreadAttributes main_thread_attrib; - -void init_tls(TLSDescriptor &tls_descriptor) { - if (app.tls.size == 0) { - tls_descriptor.size = 0; - tls_descriptor.tp = 0; - return; - } - - // aarch64 follows the variant 1 TLS layout: - // - // 1. First entry is the dynamic thread vector pointer - // 2. Second entry is a 8-byte reserved word. - // 3. Padding for alignment. - // 4. The TLS data from the ELF image. - // - // The thread pointer points to the first entry. - - const uintptr_t size_of_pointers = 2 * sizeof(uintptr_t); - uintptr_t padding = 0; - const uintptr_t ALIGNMENT_MASK = app.tls.align - 1; - uintptr_t diff = size_of_pointers & ALIGNMENT_MASK; - if (diff != 0) - padding += (ALIGNMENT_MASK - diff) + 1; - - uintptr_t alloc_size = size_of_pointers + padding + app.tls.size; - - // We cannot call the mmap function here as the functions set errno on - // failure. Since errno is implemented via a thread local variable, we cannot - // use errno before TLS is setup. - long mmap_ret_val = __llvm_libc::syscall_impl( - MMAP_SYSCALL_NUMBER, nullptr, alloc_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - // We cannot check the return value with MAP_FAILED as that is the return - // of the mmap function and not the mmap syscall. - if (mmap_ret_val < 0 && static_cast(mmap_ret_val) > -app.pageSize) - __llvm_libc::syscall_impl(SYS_exit, 1); - uintptr_t thread_ptr = uintptr_t(reinterpret_cast(mmap_ret_val)); - uintptr_t tls_addr = thread_ptr + size_of_pointers + padding; - __llvm_libc::inline_memcpy(reinterpret_cast(tls_addr), - reinterpret_cast(app.tls.address), - app.tls.init_size); - tls_descriptor.size = alloc_size; - tls_descriptor.addr = thread_ptr; - tls_descriptor.tp = thread_ptr; -} - -void cleanup_tls(uintptr_t addr, uintptr_t size) { - if (size == 0) - return; - __llvm_libc::syscall_impl(SYS_munmap, addr, size); -} - -static void set_thread_ptr(uintptr_t val) { __arm_wsr64("tpidr_el0", val); } - -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 = fini_array_size; i > 0; --i) - reinterpret_cast(__fini_array_start[i - 1])(); -} - -} // namespace __llvm_libc - -using __llvm_libc::app; - -// TODO: Would be nice to use the aux entry structure from elf.h when available. -struct AuxEntry { - uint64_t type; - uint64_t value; -}; - -__attribute__((noinline)) static void do_start() { - auto tid = __llvm_libc::syscall_impl(SYS_gettid); - if (tid <= 0) - __llvm_libc::syscall_impl(SYS_exit, 1); - __llvm_libc::main_thread_attrib.tid = tid; - - // After the argv array, is a 8-byte long NULL value before the array of env - // values. The end of the env values is marked by another 8-byte long NULL - // value. We step over it (the "+ 1" below) to get to the env values. - uint64_t *env_ptr = app.args->argv + app.args->argc + 1; - uint64_t *env_end_marker = env_ptr; - app.envPtr = env_ptr; - while (*env_end_marker) - ++env_end_marker; - - // Initialize the POSIX global declared in unistd.h - environ = reinterpret_cast(env_ptr); - - // After the env array, is the aux-vector. The end of the aux-vector is - // denoted by an AT_NULL entry. - Elf64_Phdr *programHdrTable = nullptr; - uintptr_t programHdrCount; - for (AuxEntry *aux_entry = reinterpret_cast(env_end_marker + 1); - aux_entry->type != AT_NULL; ++aux_entry) { - switch (aux_entry->type) { - case AT_PHDR: - programHdrTable = reinterpret_cast(aux_entry->value); - break; - case AT_PHNUM: - programHdrCount = aux_entry->value; - break; - case AT_PAGESZ: - app.pageSize = aux_entry->value; - break; - default: - break; // TODO: Read other useful entries from the aux vector. - } - } - - app.tls.size = 0; - for (uintptr_t i = 0; i < programHdrCount; ++i) { - Elf64_Phdr *phdr = programHdrTable + i; - if (phdr->p_type != PT_TLS) - continue; - // TODO: p_vaddr value has to be adjusted for static-pie executables. - app.tls.address = phdr->p_vaddr; - app.tls.size = phdr->p_memsz; - app.tls.init_size = phdr->p_filesz; - app.tls.align = phdr->p_align; - } - - __llvm_libc::TLSDescriptor tls; - __llvm_libc::init_tls(tls); - if (tls.size != 0) - __llvm_libc::set_thread_ptr(tls.tp); - - __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib; - __llvm_libc::main_thread_attrib.atexit_callback_mgr = - __llvm_libc::internal::get_thread_atexit_callback_mgr(); - - // We want the fini array callbacks to be run after other atexit - // callbacks are run. So, we register them before running the init - // array callbacks as they can potentially register their own atexit - // callbacks. - __llvm_libc::atexit(&__llvm_libc::call_fini_array_callbacks); - - __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)); - - // TODO: TLS cleanup should be done after all other atexit callbacks - // are run. So, register a cleanup callback for it with atexit before - // everything else. - __llvm_libc::cleanup_tls(tls.addr, tls.size); - __llvm_libc::exit(retval); -} - -extern "C" void _start() { - // Skip the Frame Pointer and the Link Register - // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst - // Section 6.2.3. Note that this only works if the current function - // is not using any callee-saved registers (x19 to x28). If the - // function uses such registers, then their value is pushed on to the - // stack before the frame pointer an link register values. That breaks - // the assumption that stepping over the frame pointer and link register - // will take us to the previous stack pointer. That is the reason why the - // actual business logic of the startup code is pushed into a non-inline - // function do_start so that this function is free of any stack usage. - app.args = reinterpret_cast<__llvm_libc::Args *>( - reinterpret_cast(__builtin_frame_address(0)) + 2); - do_start(); -} diff --git a/libc/loader/linux/crti.cpp b/libc/loader/linux/crti.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/libc/loader/linux/crtn.cpp b/libc/loader/linux/crtn.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/libc/loader/linux/x86_64/CMakeLists.txt b/libc/loader/linux/x86_64/CMakeLists.txt deleted file mode 100644 index f62ad0a..0000000 --- a/libc/loader/linux/x86_64/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -add_loader_object( - crt1 - SRC - start.cpp - DEPENDS - libc.config.linux.app_h - libc.include.sys_mman - libc.include.sys_syscall - libc.include.unistd - libc.src.__support.threads.thread - libc.src.__support.OSUtil.osutil - libc.src.stdlib.exit - libc.src.stdlib.atexit - libc.src.string.memory_utils.memcpy_implementation - libc.src.unistd.environ - COMPILE_OPTIONS - -fno-omit-frame-pointer - -ffreestanding # To avoid compiler warnings about calling the main function. - -fno-builtin -) diff --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp deleted file mode 100644 index 6ddb344..0000000 --- a/libc/loader/linux/x86_64/start.cpp +++ /dev/null @@ -1,234 +0,0 @@ -//===-- Implementation of crt for x86_64 ----------------------------------===// -// -// 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 "config/linux/app.h" -#include "src/__support/OSUtil/syscall.h" -#include "src/__support/threads/thread.h" -#include "src/stdlib/atexit.h" -#include "src/stdlib/exit.h" -#include "src/string/memory_utils/memcpy_implementations.h" - -#include -#include -#include -#include -#include -#include -#include - -extern "C" int main(int, char **, char **); - -namespace __llvm_libc { - -#ifdef SYS_mmap2 -static constexpr long mmapSyscallNumber = SYS_mmap2; -#elif SYS_mmap -static constexpr long mmapSyscallNumber = SYS_mmap; -#else -#error "Target platform does not have SYS_mmap or SYS_mmap2 defined" -#endif - -AppProperties app; - -static ThreadAttributes main_thread_attrib; - -// TODO: The function is x86_64 specific. Move it to config/linux/app.h -// and generalize it. Also, dynamic loading is not handled currently. -void init_tls(TLSDescriptor &tls_descriptor) { - if (app.tls.size == 0) { - tls_descriptor.size = 0; - tls_descriptor.tp = 0; - return; - } - - // We will assume the alignment is always a power of two. - uintptr_t tlsSize = app.tls.size & -app.tls.align; - if (tlsSize != app.tls.size) - tlsSize += app.tls.align; - - // Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the - // address of the TLS block. So, we add more size to accomodate this address - // entry. - uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t); - - // We cannot call the mmap function here as the functions set errno on - // failure. Since errno is implemented via a thread local variable, we cannot - // use errno before TLS is setup. - long mmapRetVal = __llvm_libc::syscall_impl( - mmapSyscallNumber, nullptr, tlsSizeWithAddr, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - // We cannot check the return value with MAP_FAILED as that is the return - // of the mmap function and not the mmap syscall. - if (mmapRetVal < 0 && static_cast(mmapRetVal) > -app.pageSize) - __llvm_libc::syscall_impl(SYS_exit, 1); - uintptr_t *tlsAddr = reinterpret_cast(mmapRetVal); - - // x86_64 TLS faces down from the thread pointer with the first entry - // pointing to the address of the first real TLS byte. - uintptr_t endPtr = reinterpret_cast(tlsAddr) + tlsSize; - *reinterpret_cast(endPtr) = endPtr; - - __llvm_libc::inline_memcpy(reinterpret_cast(tlsAddr), - reinterpret_cast(app.tls.address), - app.tls.init_size); - - tls_descriptor = {tlsSizeWithAddr, uintptr_t(tlsAddr), endPtr}; - return; -} - -void cleanup_tls(uintptr_t addr, uintptr_t size) { - if (size == 0) - return; - __llvm_libc::syscall_impl(SYS_munmap, addr, size); -} - -// Sets the thread pointer to |val|. Returns true on success, false on failure. -static bool set_thread_ptr(uintptr_t val) { - return __llvm_libc::syscall_impl(SYS_arch_prctl, ARCH_SET_FS, val) == -1 - ? false - : 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 = fini_array_size; i > 0; --i) - reinterpret_cast(__fini_array_start[i - 1])(); -} - -} // namespace __llvm_libc - -using __llvm_libc::app; - -// TODO: Would be nice to use the aux entry structure from elf.h when available. -struct AuxEntry { - uint64_t type; - uint64_t value; -}; - -extern "C" void _start() { - // This TU is compiled with -fno-omit-frame-pointer. Hence, the previous value - // of the base pointer is pushed on to the stack. So, we step over it (the - // "+ 1" below) to get to the args. - app.args = reinterpret_cast<__llvm_libc::Args *>( - reinterpret_cast(__builtin_frame_address(0)) + 1); - - // The x86_64 ABI requires that the stack pointer is aligned to a 16-byte - // boundary. We align it here but we cannot use any local variables created - // before the following alignment. Best would be to not create any local - // variables before the alignment. Also, note that we are aligning the stack - // downwards as the x86_64 stack grows downwards. This ensures that we don't - // tread on argc, argv etc. - // NOTE: Compiler attributes for alignment do not help here as the stack - // pointer on entry to this _start function is controlled by the OS. In fact, - // compilers can generate code assuming the alignment as required by the ABI. - // If the stack pointers as setup by the OS are already aligned, then the - // following code is a NOP. - __asm__ __volatile__("andq $0xfffffffffffffff0, %%rsp\n\t" ::: "%rsp"); - __asm__ __volatile__("andq $0xfffffffffffffff0, %%rbp\n\t" ::: "%rbp"); - - auto tid = __llvm_libc::syscall_impl(SYS_gettid); - if (tid <= 0) - __llvm_libc::syscall_impl(SYS_exit, 1); - __llvm_libc::main_thread_attrib.tid = tid; - - // After the argv array, is a 8-byte long NULL value before the array of env - // values. The end of the env values is marked by another 8-byte long NULL - // value. We step over it (the "+ 1" below) to get to the env values. - uint64_t *env_ptr = app.args->argv + app.args->argc + 1; - uint64_t *env_end_marker = env_ptr; - app.envPtr = env_ptr; - while (*env_end_marker) - ++env_end_marker; - - // Initialize the POSIX global declared in unistd.h - environ = reinterpret_cast(env_ptr); - - // After the env array, is the aux-vector. The end of the aux-vector is - // denoted by an AT_NULL entry. - Elf64_Phdr *programHdrTable = nullptr; - uintptr_t programHdrCount; - for (AuxEntry *aux_entry = reinterpret_cast(env_end_marker + 1); - aux_entry->type != AT_NULL; ++aux_entry) { - switch (aux_entry->type) { - case AT_PHDR: - programHdrTable = reinterpret_cast(aux_entry->value); - break; - case AT_PHNUM: - programHdrCount = aux_entry->value; - break; - case AT_PAGESZ: - app.pageSize = aux_entry->value; - break; - default: - break; // TODO: Read other useful entries from the aux vector. - } - } - - app.tls.size = 0; - for (uintptr_t i = 0; i < programHdrCount; ++i) { - Elf64_Phdr *phdr = programHdrTable + i; - if (phdr->p_type != PT_TLS) - continue; - // TODO: p_vaddr value has to be adjusted for static-pie executables. - app.tls.address = phdr->p_vaddr; - app.tls.size = phdr->p_memsz; - app.tls.init_size = phdr->p_filesz; - app.tls.align = phdr->p_align; - } - - __llvm_libc::TLSDescriptor tls; - __llvm_libc::init_tls(tls); - if (tls.size != 0 && !__llvm_libc::set_thread_ptr(tls.tp)) - __llvm_libc::syscall_impl(SYS_exit, 1); - - __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib; - __llvm_libc::main_thread_attrib.atexit_callback_mgr = - __llvm_libc::internal::get_thread_atexit_callback_mgr(); - - // We want the fini array callbacks to be run after other atexit - // callbacks are run. So, we register them before running the init - // array callbacks as they can potentially register their own atexit - // callbacks. - __llvm_libc::atexit(&__llvm_libc::call_fini_array_callbacks); - - __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)); - - // TODO: TLS cleanup should be done after all other atexit callbacks - // are run. So, register a cleanup callback for it with atexit before - // everything else. - __llvm_libc::cleanup_tls(tls.addr, tls.size); - __llvm_libc::exit(retval); -} diff --git a/libc/startup/CMakeLists.txt b/libc/startup/CMakeLists.txt new file mode 100644 index 0000000..b4bbe81c --- /dev/null +++ b/libc/startup/CMakeLists.txt @@ -0,0 +1,3 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${LIBC_TARGET_OS}) +endif() diff --git a/libc/startup/linux/CMakeLists.txt b/libc/startup/linux/CMakeLists.txt new file mode 100644 index 0000000..f043ec5 --- /dev/null +++ b/libc/startup/linux/CMakeLists.txt @@ -0,0 +1,96 @@ +function(add_startup_object name) + cmake_parse_arguments( + "ADD_STARTUP_OBJECT" + "ALIAS" # Option argument + "SRC" # Single value arguments + "DEPENDS;COMPILE_OPTIONS" # Multi value arguments + ${ARGN} + ) + + get_fq_target_name(${name} fq_target_name) + get_fq_deps_list(fq_deps_list ${ADD_STARTUP_OBJECT_DEPENDS}) + if(ADD_STARTUP_OBJECT_ALIAS) + list(LENGTH ADD_STARTUP_OBJECT_DEPENDS deps_size) + if(NOT (${deps_size} EQUAL "1")) + message(FATAL_ERROR "A startup object alias should have exactly one dependency.") + endif() + list(GET ADD_STARTUP_OBJECT_DEPENDS 0 dep) + get_fq_dep_name(fq_dep_name ${dep}) + + add_custom_target(${fq_target_name}) + add_dependencies(${fq_target_name} ${fq_dep_name}) + get_target_property(startup_object ${fq_dep_name} STARTUP_OBJECT) + set_target_properties( + ${fq_target_name} + PROPERTIES + "TARGET_TYPE" "${OBJECT_LIBRARY_TARGET_TYPE}" + "STARTUP_OBJECT" "${startup_object}" + "OBJECT_FILES" "" + "DEPS" "${fq_dep_name}" + ) + return() + endif() + + add_object_library( + ${name}.__objects__ + SRCS ${ADD_STARTUP_OBJECT_SRC} + DEPENDS ${ADD_STARTUP_OBJECT_DEPENDS} + COMPILE_OPTIONS ${ADD_STARTUP_OBJECT_COMPILE_OPTIONS} + ) + + set(objfile ${LIBC_BUILD_DIR}/lib/${name}.o) + add_custom_command( + OUTPUT ${objfile} + COMMAND cp $ ${objfile} + DEPENDS $ + ) + add_custom_target( + ${fq_target_name} + DEPENDS ${objfile} + ) + set_target_properties( + ${fq_target_name} + PROPERTIES + "TARGET_TYPE" "${OBJECT_LIBRARY_TARGET_TYPE}" + "STARTUP_OBJECT" "${objfile}" + "OBJECT_FILES" "" + "DEPS" "${fq_target_name}.__objects__" + ) +endfunction() + +if(NOT (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})) + message(STATUS "Skipping startup for target architecture ${LIBC_TARGET_ARCHITECTURE}") + return() +endif() + +add_subdirectory(${LIBC_TARGET_ARCHITECTURE}) + +add_startup_object( + crt1 + ALIAS + DEPENDS + .${LIBC_TARGET_ARCHITECTURE}.crt1 +) + +add_startup_object( + crti + SRC + crti.cpp +) + +add_startup_object( + crtn + SRC + crtn.cpp +) + +add_custom_target(libc-startup) +set(startup_components crt1 crti crtn) +foreach(target IN LISTS startup_components) + set(fq_target_name libc.startup.linux.${target}) + add_dependencies(libc-startup ${fq_target_name}) + get_target_property(startup_object ${fq_target_name} STARTUP_OBJECT) + install(FILES ${startup_object} + DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT ${LIBC_COMPONENT}) +endforeach() diff --git a/libc/startup/linux/aarch64/CMakeLists.txt b/libc/startup/linux/aarch64/CMakeLists.txt new file mode 100644 index 0000000..14d6409 --- /dev/null +++ b/libc/startup/linux/aarch64/CMakeLists.txt @@ -0,0 +1,16 @@ +add_startup_object( + crt1 + SRC + start.cpp + DEPENDS + libc.config.linux.app_h + libc.include.sys_mman + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.stdlib.exit + libc.src.stdlib.atexit + libc.src.string.memory_utils.memcpy_implementation + COMPILE_OPTIONS + -fno-omit-frame-pointer + -ffreestanding # To avoid compiler warnings about calling the main function. +) diff --git a/libc/startup/linux/aarch64/start.cpp b/libc/startup/linux/aarch64/start.cpp new file mode 100644 index 0000000..b02ea7d --- /dev/null +++ b/libc/startup/linux/aarch64/start.cpp @@ -0,0 +1,230 @@ +//===-- Implementation of crt for aarch64 ---------------------------------===// +// +// 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 "config/linux/app.h" +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/threads/thread.h" +#include "src/stdlib/atexit.h" +#include "src/stdlib/exit.h" +#include "src/string/memory_utils/memcpy_implementations.h" + +#include + +#include +#include +#include +#include +#include +#include + +extern "C" int main(int, char **, char **); + +// Source documentation: +// https://github.com/ARM-software/abi-aa/tree/main/sysvabi64 + +namespace __llvm_libc { + +#ifdef SYS_mmap2 +static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2; +#elif SYS_mmap +static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap; +#else +#error "Target platform does not have SYS_mmap or SYS_mmap2 defined" +#endif + +AppProperties app; + +static ThreadAttributes main_thread_attrib; + +void init_tls(TLSDescriptor &tls_descriptor) { + if (app.tls.size == 0) { + tls_descriptor.size = 0; + tls_descriptor.tp = 0; + return; + } + + // aarch64 follows the variant 1 TLS layout: + // + // 1. First entry is the dynamic thread vector pointer + // 2. Second entry is a 8-byte reserved word. + // 3. Padding for alignment. + // 4. The TLS data from the ELF image. + // + // The thread pointer points to the first entry. + + const uintptr_t size_of_pointers = 2 * sizeof(uintptr_t); + uintptr_t padding = 0; + const uintptr_t ALIGNMENT_MASK = app.tls.align - 1; + uintptr_t diff = size_of_pointers & ALIGNMENT_MASK; + if (diff != 0) + padding += (ALIGNMENT_MASK - diff) + 1; + + uintptr_t alloc_size = size_of_pointers + padding + app.tls.size; + + // We cannot call the mmap function here as the functions set errno on + // failure. Since errno is implemented via a thread local variable, we cannot + // use errno before TLS is setup. + long mmap_ret_val = __llvm_libc::syscall_impl( + MMAP_SYSCALL_NUMBER, nullptr, alloc_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + // We cannot check the return value with MAP_FAILED as that is the return + // of the mmap function and not the mmap syscall. + if (mmap_ret_val < 0 && static_cast(mmap_ret_val) > -app.pageSize) + __llvm_libc::syscall_impl(SYS_exit, 1); + uintptr_t thread_ptr = uintptr_t(reinterpret_cast(mmap_ret_val)); + uintptr_t tls_addr = thread_ptr + size_of_pointers + padding; + __llvm_libc::inline_memcpy(reinterpret_cast(tls_addr), + reinterpret_cast(app.tls.address), + app.tls.init_size); + tls_descriptor.size = alloc_size; + tls_descriptor.addr = thread_ptr; + tls_descriptor.tp = thread_ptr; +} + +void cleanup_tls(uintptr_t addr, uintptr_t size) { + if (size == 0) + return; + __llvm_libc::syscall_impl(SYS_munmap, addr, size); +} + +static void set_thread_ptr(uintptr_t val) { __arm_wsr64("tpidr_el0", val); } + +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 = fini_array_size; i > 0; --i) + reinterpret_cast(__fini_array_start[i - 1])(); +} + +} // namespace __llvm_libc + +using __llvm_libc::app; + +// TODO: Would be nice to use the aux entry structure from elf.h when available. +struct AuxEntry { + uint64_t type; + uint64_t value; +}; + +__attribute__((noinline)) static void do_start() { + auto tid = __llvm_libc::syscall_impl(SYS_gettid); + if (tid <= 0) + __llvm_libc::syscall_impl(SYS_exit, 1); + __llvm_libc::main_thread_attrib.tid = tid; + + // After the argv array, is a 8-byte long NULL value before the array of env + // values. The end of the env values is marked by another 8-byte long NULL + // value. We step over it (the "+ 1" below) to get to the env values. + uint64_t *env_ptr = app.args->argv + app.args->argc + 1; + uint64_t *env_end_marker = env_ptr; + app.envPtr = env_ptr; + while (*env_end_marker) + ++env_end_marker; + + // Initialize the POSIX global declared in unistd.h + environ = reinterpret_cast(env_ptr); + + // After the env array, is the aux-vector. The end of the aux-vector is + // denoted by an AT_NULL entry. + Elf64_Phdr *programHdrTable = nullptr; + uintptr_t programHdrCount; + for (AuxEntry *aux_entry = reinterpret_cast(env_end_marker + 1); + aux_entry->type != AT_NULL; ++aux_entry) { + switch (aux_entry->type) { + case AT_PHDR: + programHdrTable = reinterpret_cast(aux_entry->value); + break; + case AT_PHNUM: + programHdrCount = aux_entry->value; + break; + case AT_PAGESZ: + app.pageSize = aux_entry->value; + break; + default: + break; // TODO: Read other useful entries from the aux vector. + } + } + + app.tls.size = 0; + for (uintptr_t i = 0; i < programHdrCount; ++i) { + Elf64_Phdr *phdr = programHdrTable + i; + if (phdr->p_type != PT_TLS) + continue; + // TODO: p_vaddr value has to be adjusted for static-pie executables. + app.tls.address = phdr->p_vaddr; + app.tls.size = phdr->p_memsz; + app.tls.init_size = phdr->p_filesz; + app.tls.align = phdr->p_align; + } + + __llvm_libc::TLSDescriptor tls; + __llvm_libc::init_tls(tls); + if (tls.size != 0) + __llvm_libc::set_thread_ptr(tls.tp); + + __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib; + __llvm_libc::main_thread_attrib.atexit_callback_mgr = + __llvm_libc::internal::get_thread_atexit_callback_mgr(); + + // We want the fini array callbacks to be run after other atexit + // callbacks are run. So, we register them before running the init + // array callbacks as they can potentially register their own atexit + // callbacks. + __llvm_libc::atexit(&__llvm_libc::call_fini_array_callbacks); + + __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)); + + // TODO: TLS cleanup should be done after all other atexit callbacks + // are run. So, register a cleanup callback for it with atexit before + // everything else. + __llvm_libc::cleanup_tls(tls.addr, tls.size); + __llvm_libc::exit(retval); +} + +extern "C" void _start() { + // Skip the Frame Pointer and the Link Register + // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + // Section 6.2.3. Note that this only works if the current function + // is not using any callee-saved registers (x19 to x28). If the + // function uses such registers, then their value is pushed on to the + // stack before the frame pointer an link register values. That breaks + // the assumption that stepping over the frame pointer and link register + // will take us to the previous stack pointer. That is the reason why the + // actual business logic of the startup code is pushed into a non-inline + // function do_start so that this function is free of any stack usage. + app.args = reinterpret_cast<__llvm_libc::Args *>( + reinterpret_cast(__builtin_frame_address(0)) + 2); + do_start(); +} diff --git a/libc/startup/linux/crti.cpp b/libc/startup/linux/crti.cpp new file mode 100644 index 0000000..e69de29 diff --git a/libc/startup/linux/crtn.cpp b/libc/startup/linux/crtn.cpp new file mode 100644 index 0000000..e69de29 diff --git a/libc/startup/linux/x86_64/CMakeLists.txt b/libc/startup/linux/x86_64/CMakeLists.txt new file mode 100644 index 0000000..75c8296 --- /dev/null +++ b/libc/startup/linux/x86_64/CMakeLists.txt @@ -0,0 +1,20 @@ +add_startup_object( + crt1 + SRC + start.cpp + DEPENDS + libc.config.linux.app_h + libc.include.sys_mman + libc.include.sys_syscall + libc.include.unistd + libc.src.__support.threads.thread + libc.src.__support.OSUtil.osutil + libc.src.stdlib.exit + libc.src.stdlib.atexit + libc.src.string.memory_utils.memcpy_implementation + libc.src.unistd.environ + COMPILE_OPTIONS + -fno-omit-frame-pointer + -ffreestanding # To avoid compiler warnings about calling the main function. + -fno-builtin +) diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp new file mode 100644 index 0000000..6ddb344 --- /dev/null +++ b/libc/startup/linux/x86_64/start.cpp @@ -0,0 +1,234 @@ +//===-- Implementation of crt for x86_64 ----------------------------------===// +// +// 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 "config/linux/app.h" +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/threads/thread.h" +#include "src/stdlib/atexit.h" +#include "src/stdlib/exit.h" +#include "src/string/memory_utils/memcpy_implementations.h" + +#include +#include +#include +#include +#include +#include +#include + +extern "C" int main(int, char **, char **); + +namespace __llvm_libc { + +#ifdef SYS_mmap2 +static constexpr long mmapSyscallNumber = SYS_mmap2; +#elif SYS_mmap +static constexpr long mmapSyscallNumber = SYS_mmap; +#else +#error "Target platform does not have SYS_mmap or SYS_mmap2 defined" +#endif + +AppProperties app; + +static ThreadAttributes main_thread_attrib; + +// TODO: The function is x86_64 specific. Move it to config/linux/app.h +// and generalize it. Also, dynamic loading is not handled currently. +void init_tls(TLSDescriptor &tls_descriptor) { + if (app.tls.size == 0) { + tls_descriptor.size = 0; + tls_descriptor.tp = 0; + return; + } + + // We will assume the alignment is always a power of two. + uintptr_t tlsSize = app.tls.size & -app.tls.align; + if (tlsSize != app.tls.size) + tlsSize += app.tls.align; + + // Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the + // address of the TLS block. So, we add more size to accomodate this address + // entry. + uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t); + + // We cannot call the mmap function here as the functions set errno on + // failure. Since errno is implemented via a thread local variable, we cannot + // use errno before TLS is setup. + long mmapRetVal = __llvm_libc::syscall_impl( + mmapSyscallNumber, nullptr, tlsSizeWithAddr, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + // We cannot check the return value with MAP_FAILED as that is the return + // of the mmap function and not the mmap syscall. + if (mmapRetVal < 0 && static_cast(mmapRetVal) > -app.pageSize) + __llvm_libc::syscall_impl(SYS_exit, 1); + uintptr_t *tlsAddr = reinterpret_cast(mmapRetVal); + + // x86_64 TLS faces down from the thread pointer with the first entry + // pointing to the address of the first real TLS byte. + uintptr_t endPtr = reinterpret_cast(tlsAddr) + tlsSize; + *reinterpret_cast(endPtr) = endPtr; + + __llvm_libc::inline_memcpy(reinterpret_cast(tlsAddr), + reinterpret_cast(app.tls.address), + app.tls.init_size); + + tls_descriptor = {tlsSizeWithAddr, uintptr_t(tlsAddr), endPtr}; + return; +} + +void cleanup_tls(uintptr_t addr, uintptr_t size) { + if (size == 0) + return; + __llvm_libc::syscall_impl(SYS_munmap, addr, size); +} + +// Sets the thread pointer to |val|. Returns true on success, false on failure. +static bool set_thread_ptr(uintptr_t val) { + return __llvm_libc::syscall_impl(SYS_arch_prctl, ARCH_SET_FS, val) == -1 + ? false + : 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 = fini_array_size; i > 0; --i) + reinterpret_cast(__fini_array_start[i - 1])(); +} + +} // namespace __llvm_libc + +using __llvm_libc::app; + +// TODO: Would be nice to use the aux entry structure from elf.h when available. +struct AuxEntry { + uint64_t type; + uint64_t value; +}; + +extern "C" void _start() { + // This TU is compiled with -fno-omit-frame-pointer. Hence, the previous value + // of the base pointer is pushed on to the stack. So, we step over it (the + // "+ 1" below) to get to the args. + app.args = reinterpret_cast<__llvm_libc::Args *>( + reinterpret_cast(__builtin_frame_address(0)) + 1); + + // The x86_64 ABI requires that the stack pointer is aligned to a 16-byte + // boundary. We align it here but we cannot use any local variables created + // before the following alignment. Best would be to not create any local + // variables before the alignment. Also, note that we are aligning the stack + // downwards as the x86_64 stack grows downwards. This ensures that we don't + // tread on argc, argv etc. + // NOTE: Compiler attributes for alignment do not help here as the stack + // pointer on entry to this _start function is controlled by the OS. In fact, + // compilers can generate code assuming the alignment as required by the ABI. + // If the stack pointers as setup by the OS are already aligned, then the + // following code is a NOP. + __asm__ __volatile__("andq $0xfffffffffffffff0, %%rsp\n\t" ::: "%rsp"); + __asm__ __volatile__("andq $0xfffffffffffffff0, %%rbp\n\t" ::: "%rbp"); + + auto tid = __llvm_libc::syscall_impl(SYS_gettid); + if (tid <= 0) + __llvm_libc::syscall_impl(SYS_exit, 1); + __llvm_libc::main_thread_attrib.tid = tid; + + // After the argv array, is a 8-byte long NULL value before the array of env + // values. The end of the env values is marked by another 8-byte long NULL + // value. We step over it (the "+ 1" below) to get to the env values. + uint64_t *env_ptr = app.args->argv + app.args->argc + 1; + uint64_t *env_end_marker = env_ptr; + app.envPtr = env_ptr; + while (*env_end_marker) + ++env_end_marker; + + // Initialize the POSIX global declared in unistd.h + environ = reinterpret_cast(env_ptr); + + // After the env array, is the aux-vector. The end of the aux-vector is + // denoted by an AT_NULL entry. + Elf64_Phdr *programHdrTable = nullptr; + uintptr_t programHdrCount; + for (AuxEntry *aux_entry = reinterpret_cast(env_end_marker + 1); + aux_entry->type != AT_NULL; ++aux_entry) { + switch (aux_entry->type) { + case AT_PHDR: + programHdrTable = reinterpret_cast(aux_entry->value); + break; + case AT_PHNUM: + programHdrCount = aux_entry->value; + break; + case AT_PAGESZ: + app.pageSize = aux_entry->value; + break; + default: + break; // TODO: Read other useful entries from the aux vector. + } + } + + app.tls.size = 0; + for (uintptr_t i = 0; i < programHdrCount; ++i) { + Elf64_Phdr *phdr = programHdrTable + i; + if (phdr->p_type != PT_TLS) + continue; + // TODO: p_vaddr value has to be adjusted for static-pie executables. + app.tls.address = phdr->p_vaddr; + app.tls.size = phdr->p_memsz; + app.tls.init_size = phdr->p_filesz; + app.tls.align = phdr->p_align; + } + + __llvm_libc::TLSDescriptor tls; + __llvm_libc::init_tls(tls); + if (tls.size != 0 && !__llvm_libc::set_thread_ptr(tls.tp)) + __llvm_libc::syscall_impl(SYS_exit, 1); + + __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib; + __llvm_libc::main_thread_attrib.atexit_callback_mgr = + __llvm_libc::internal::get_thread_atexit_callback_mgr(); + + // We want the fini array callbacks to be run after other atexit + // callbacks are run. So, we register them before running the init + // array callbacks as they can potentially register their own atexit + // callbacks. + __llvm_libc::atexit(&__llvm_libc::call_fini_array_callbacks); + + __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)); + + // TODO: TLS cleanup should be done after all other atexit callbacks + // are run. So, register a cleanup callback for it with atexit before + // everything else. + __llvm_libc::cleanup_tls(tls.addr, tls.size); + __llvm_libc::exit(retval); +} diff --git a/libc/test/integration/CMakeLists.txt b/libc/test/integration/CMakeLists.txt index 675f4c4..f891ed3 100644 --- a/libc/test/integration/CMakeLists.txt +++ b/libc/test/integration/CMakeLists.txt @@ -15,6 +15,6 @@ set_target_properties(libc_integration_test_dummy ARCHIVE_OUTPUT_NAME dummy ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) -add_subdirectory(loader) +add_subdirectory(startup) add_subdirectory(scudo) add_subdirectory(src) diff --git a/libc/test/integration/loader/CMakeLists.txt b/libc/test/integration/loader/CMakeLists.txt deleted file mode 100644 index 49f7eb7..0000000 --- a/libc/test/integration/loader/CMakeLists.txt +++ /dev/null @@ -1,62 +0,0 @@ -# A rule to add loader tests. When we have a complete loader, we should -# be able to use the add_libc_unittest rule or an extension of it. But, -# while the loader is getting built, we need to use a special rule like -# this. -function(add_loader_test target_name) - if(NOT CMAKE_HOST_UNIX) - message( - WARNING - "Loader tests currently assume a POSIX/Unix like environment and " - "may not work on your platform.") - endif() - - cmake_parse_arguments( - "ADD_LOADER_TEST" - "" # No option arguments - "SRC" # Single value arguments - "DEPENDS;ARGS;ENV" # Multivalue arguments. - ${ARGN} - ) - - get_fq_target_name(${target_name} fq_target_name) - add_executable( - ${fq_target_name} - EXCLUDE_FROM_ALL - ${ADD_LOADER_TEST_SRC} - ) - - set_target_properties(${fq_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - target_include_directories( - ${fq_target_name} - PRIVATE - ${LIBC_SOURCE_DIR} - ${LIBC_BUILD_DIR} - ${LIBC_BUILD_DIR}/include - ) - - if(ADD_LOADER_TEST_DEPENDS) - get_fq_deps_list(fq_deps_list ${ADD_LOADER_TEST_DEPENDS}) - add_dependencies(${fq_target_name} ${fq_deps_list}) - get_object_files_for_test(link_object_files has_skipped_entrypoint_list ${fq_deps_list}) - target_link_libraries(${fq_target_name} ${link_object_files}) - endif() - - target_link_options( - ${fq_target_name} - BEFORE PRIVATE - -nostdlib - ) - - add_custom_command( - TARGET ${fq_target_name} - POST_BUILD - COMMAND ${ADD_LOADER_TEST_ENV} $ ${ADD_LOADER_TEST_ARGS} - ) - - add_dependencies(libc_loader_tests ${fq_target_name}) -endfunction(add_loader_test) - -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) - add_subdirectory(${LIBC_TARGET_OS}) -endif() diff --git a/libc/test/integration/loader/linux/CMakeLists.txt b/libc/test/integration/loader/linux/CMakeLists.txt deleted file mode 100644 index 5138f7a..0000000 --- a/libc/test/integration/loader/linux/CMakeLists.txt +++ /dev/null @@ -1,62 +0,0 @@ -if(NOT (EXISTS ${LIBC_SOURCE_DIR}/loader/linux/${LIBC_TARGET_ARCHITECTURE})) - message("Skipping loader integration tests for target architecture ${LIBC_TARGET_ARCHITECTURE}.") - return() -endif() - -add_custom_target(libc-loader-tests) -add_dependencies(libc-integration-tests libc-loader-tests) - -add_integration_test( - loader_args_test - SUITE libc-loader-tests - LOADER - libc.loader.linux.crt1 - SRCS - args_test.cpp - ARGS - 1 2 3 - ENV - FRANCE=Paris - GERMANY=Berlin -) - -add_integration_test( - loader_no_envp_test - SUITE libc-loader-tests - LOADER - libc.loader.linux.crt1 - SRCS - main_without_envp.cpp -) - -add_integration_test( - loader_no_args_test - SUITE libc-loader-tests - LOADER - libc.loader.linux.crt1 - SRCS - main_without_args.cpp -) - -add_integration_test( - loader_tls_test - SUITE libc-loader-tests - LOADER - libc.loader.linux.crt1 - SRCS - tls_test.cpp - DEPENDS - libc.include.errno - libc.include.sys_mman - 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/args_test.cpp b/libc/test/integration/loader/linux/args_test.cpp deleted file mode 100644 index 6b99521..0000000 --- a/libc/test/integration/loader/linux/args_test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -//===-- Loader test to check args to main ---------------------------------===// -// -// 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" - -static bool my_streq(const char *lhs, const char *rhs) { - const char *l, *r; - for (l = lhs, r = rhs; *l != '\0' && *r != '\0'; ++l, ++r) - if (*l != *r) - return false; - - return *l == '\0' && *r == '\0'; -} - -TEST_MAIN(int argc, char **argv, char **envp) { - ASSERT_TRUE(argc == 4); - ASSERT_TRUE(my_streq(argv[1], "1")); - ASSERT_TRUE(my_streq(argv[2], "2")); - ASSERT_TRUE(my_streq(argv[3], "3")); - - bool found_france = false; - bool found_germany = false; - for (; *envp != nullptr; ++envp) { - if (my_streq(*envp, "FRANCE=Paris")) - found_france = true; - if (my_streq(*envp, "GERMANY=Berlin")) - found_germany = true; - } - - ASSERT_TRUE(found_france && found_germany); - - return 0; -} diff --git a/libc/test/integration/loader/linux/init_fini_array_test.cpp b/libc/test/integration/loader/linux/init_fini_array_test.cpp deleted file mode 100644 index b560fde..0000000 --- a/libc/test/integration/loader/linux/init_fini_array_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//===-- 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" - -#include - -int global_destroyed = false; - -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; - } - - ~A() { global_destroyed = true; } - - 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; -int preinitval = 0; - -__attribute__((constructor)) void set_initval() { - initval = INITVAL_INITIALIZER; -} -__attribute__((destructor(1))) void reset_initval() { - ASSERT_TRUE(global_destroyed); - ASSERT_EQ(preinitval, 0); - initval = 0; -} - -void set_preinitval() { preinitval = INITVAL_INITIALIZER; } -__attribute__((destructor(2))) void reset_preinitval() { - ASSERT_TRUE(global_destroyed); - ASSERT_EQ(initval, INITVAL_INITIALIZER); - 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; -} diff --git a/libc/test/integration/loader/linux/main_without_args.cpp b/libc/test/integration/loader/linux/main_without_args.cpp deleted file mode 100644 index 8c70c3d..0000000 --- a/libc/test/integration/loader/linux/main_without_args.cpp +++ /dev/null @@ -1,11 +0,0 @@ -//===-- Loader test for main without args ---------------------------------===// -// -// 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" - -TEST_MAIN() { return 0; } diff --git a/libc/test/integration/loader/linux/main_without_envp.cpp b/libc/test/integration/loader/linux/main_without_envp.cpp deleted file mode 100644 index 4127990..0000000 --- a/libc/test/integration/loader/linux/main_without_envp.cpp +++ /dev/null @@ -1,11 +0,0 @@ -//===-- Loader test for main without envp ---------------------------------===// -// -// 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" - -TEST_MAIN(int argc, char **argv) { return 0; } diff --git a/libc/test/integration/loader/linux/tls_test.cpp b/libc/test/integration/loader/linux/tls_test.cpp deleted file mode 100644 index 82141ce..0000000 --- a/libc/test/integration/loader/linux/tls_test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -//===-- Loader test to check if tls size is read correctly ----------------===// -// -// 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 "include/errno.h" -#include "include/sys/mman.h" - -#include "src/errno/llvmlibc_errno.h" -#include "src/sys/mman/mmap.h" -#include "utils/IntegrationTest/test.h" - -constexpr int threadLocalDataSize = 101; -_Thread_local int a[threadLocalDataSize] = {123}; - -TEST_MAIN(int argc, char **argv, char **envp) { - ASSERT_TRUE(a[0] == 123); - - for (int i = 1; i < threadLocalDataSize; ++i) - a[i] = i; - for (int i = 1; i < threadLocalDataSize; ++i) - ASSERT_TRUE(a[i] == i); - - // Call mmap with bad params so that an error value is - // set in errno. Since errno is implemented using a thread - // local var, this helps us test setting of errno and - // reading it back. - ASSERT_TRUE(llvmlibc_errno == 0); - void *addr = __llvm_libc::mmap(nullptr, 0, PROT_READ, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - ASSERT_TRUE(addr == MAP_FAILED); - ASSERT_TRUE(llvmlibc_errno == EINVAL); - - return 0; -} diff --git a/libc/test/integration/scudo/CMakeLists.txt b/libc/test/integration/scudo/CMakeLists.txt index 2e90c84..ca3e2c8 100644 --- a/libc/test/integration/scudo/CMakeLists.txt +++ b/libc/test/integration/scudo/CMakeLists.txt @@ -5,9 +5,9 @@ endif() # We use a special library consisting of only the SCUDO allocator # functions to link to the integration tests below. We could instead # link to libllvmlibc.a directly, but since libllvmlibc.a contains -# functions which depend on the LLVM libc loader, the integration -# test will have to link to the LLVM libc loader. LLVM libc's loader -# is not complete enough to allow this. It is also desireable to +# functions which depend on the LLVM libc startup system, the integration +# test will have to link to the LLVM libc startup system. LLVM libc's startup +# system is not complete enough to allow this. It is also desireable to # keep the dependencies as minimal as possible. add_entrypoint_library( libc_for_scudo_integration_test diff --git a/libc/test/integration/src/__support/threads/CMakeLists.txt b/libc/test/integration/src/__support/threads/CMakeLists.txt index 48e0e2c..592c0b3 100644 --- a/libc/test/integration/src/__support/threads/CMakeLists.txt +++ b/libc/test/integration/src/__support/threads/CMakeLists.txt @@ -11,8 +11,8 @@ add_integration_test( libc-support-threads-integration-tests SRCS thread_detach_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.src.__support.threads.mutex libc.src.__support.threads.thread @@ -24,8 +24,8 @@ add_integration_test( libc-support-threads-integration-tests SRCS thread_tls_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.src.__support.threads.thread ) diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt index 7215893..a6a1690 100644 --- a/libc/test/integration/src/pthread/CMakeLists.txt +++ b/libc/test/integration/src/pthread/CMakeLists.txt @@ -6,8 +6,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_mutex_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.pthread libc.src.errno.errno @@ -25,8 +25,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.pthread libc.src.pthread.pthread_create @@ -39,8 +39,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_equal_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.pthread libc.src.errno.errno @@ -60,8 +60,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_name_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.errno libc.include.pthread @@ -83,8 +83,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_exit_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.pthread libc.src.pthread.pthread_create @@ -98,8 +98,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_tss_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.pthread libc.src.pthread.pthread_create @@ -117,8 +117,8 @@ add_integration_test( libc-pthread-integration-tests SRCS pthread_once_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.pthread libc.src.pthread.pthread_once diff --git a/libc/test/integration/src/spawn/CMakeLists.txt b/libc/test/integration/src/spawn/CMakeLists.txt index dd0e8a3..d6307cb 100644 --- a/libc/test/integration/src/spawn/CMakeLists.txt +++ b/libc/test/integration/src/spawn/CMakeLists.txt @@ -26,8 +26,8 @@ add_integration_test( spawn-integration-tests SRCS posix_spawn_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc_posix_spawn_test_binary libc.test.integration.src.spawn.test_binary_properties diff --git a/libc/test/integration/src/stdio/CMakeLists.txt b/libc/test/integration/src/stdio/CMakeLists.txt index f0de50f..ed9af29 100644 --- a/libc/test/integration/src/stdio/CMakeLists.txt +++ b/libc/test/integration/src/stdio/CMakeLists.txt @@ -9,8 +9,8 @@ add_integration_test( stdio-integration-tests SRCS sprintf_size_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.src.stdio.sprintf ARGS @@ -26,8 +26,8 @@ add_integration_test( stdio-integration-tests SRCS sprintf_size_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 ARGS "%s %c %d" "First arg" diff --git a/libc/test/integration/src/stdlib/CMakeLists.txt b/libc/test/integration/src/stdlib/CMakeLists.txt index 01f5f6a..1f600f8 100644 --- a/libc/test/integration/src/stdlib/CMakeLists.txt +++ b/libc/test/integration/src/stdlib/CMakeLists.txt @@ -7,8 +7,8 @@ add_integration_test( stdlib-integration-tests SRCS getenv_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.src.stdlib.getenv ENV diff --git a/libc/test/integration/src/threads/CMakeLists.txt b/libc/test/integration/src/threads/CMakeLists.txt index 4dc82da..5f9e49e 100644 --- a/libc/test/integration/src/threads/CMakeLists.txt +++ b/libc/test/integration/src/threads/CMakeLists.txt @@ -6,8 +6,8 @@ add_integration_test( libc-threads-integration-tests SRCS mtx_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.errno.errno @@ -25,8 +25,8 @@ add_integration_test( libc-threads-integration-tests SRCS thrd_equal_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.threads.mtx_destroy @@ -45,8 +45,8 @@ add_integration_test( libc-threads-integration-tests SRCS thrd_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.errno.errno @@ -60,8 +60,8 @@ add_integration_test( libc-threads-integration-tests SRCS thrd_exit_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.threads.thrd_create @@ -75,8 +75,8 @@ add_integration_test( libc-threads-integration-tests SRCS tss_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.threads.thrd_create @@ -94,8 +94,8 @@ add_integration_test( libc-threads-integration-tests SRCS call_once_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.threads.call_once @@ -114,8 +114,8 @@ add_integration_test( libc-threads-integration-tests SRCS cnd_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.threads libc.src.threads.cnd_init diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt index 7631f5b..1abe016 100644 --- a/libc/test/integration/src/unistd/CMakeLists.txt +++ b/libc/test/integration/src/unistd/CMakeLists.txt @@ -7,8 +7,8 @@ add_integration_test( unistd-integration-tests SRCS getcwd_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.errno libc.src.__support.CPP.string_view @@ -22,8 +22,8 @@ add_integration_test( unistd-integration-tests SRCS fork_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc.include.errno libc.include.signal @@ -67,8 +67,8 @@ add_integration_test( unistd-integration-tests SRCS execv_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc_execv_test_normal_exit libc_execv_test_signal_exit @@ -86,8 +86,8 @@ add_integration_test( unistd-integration-tests SRCS execve_test.cpp - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS libc_execv_test_normal_exit libc_execv_test_signal_exit diff --git a/libc/test/integration/startup/CMakeLists.txt b/libc/test/integration/startup/CMakeLists.txt new file mode 100644 index 0000000..fb5d6bc --- /dev/null +++ b/libc/test/integration/startup/CMakeLists.txt @@ -0,0 +1,62 @@ +# A rule to add startup system tests. When we have a complete startup system, +# we should be able to use the add_libc_unittest rule or an extension of it. +# But, while the system is being developed, we need to use a special rule like +# this. +function(add_startup_test target_name) + if(NOT CMAKE_HOST_UNIX) + message( + WARNING + "Test for the startup system currently assume a POSIX/Unix like " + "environment and may not work on your platform.") + endif() + + cmake_parse_arguments( + "ADD_STARTUP_TEST" + "" # No option arguments + "SRC" # Single value arguments + "DEPENDS;ARGS;ENV" # Multivalue arguments. + ${ARGN} + ) + + get_fq_target_name(${target_name} fq_target_name) + add_executable( + ${fq_target_name} + EXCLUDE_FROM_ALL + ${ADD_STARTUP_TEST_SRC} + ) + + set_target_properties(${fq_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + target_include_directories( + ${fq_target_name} + PRIVATE + ${LIBC_SOURCE_DIR} + ${LIBC_BUILD_DIR} + ${LIBC_BUILD_DIR}/include + ) + + if(ADD_STARTUP_TEST_DEPENDS) + get_fq_deps_list(fq_deps_list ${ADD_STARTUP_TEST_DEPENDS}) + add_dependencies(${fq_target_name} ${fq_deps_list}) + get_object_files_for_test(link_object_files has_skipped_entrypoint_list ${fq_deps_list}) + target_link_libraries(${fq_target_name} ${link_object_files}) + endif() + + target_link_options( + ${fq_target_name} + BEFORE PRIVATE + -nostdlib + ) + + add_custom_command( + TARGET ${fq_target_name} + POST_BUILD + COMMAND ${ADD_STARTUP_TEST_ENV} $ ${ADD_STARTUP_TEST_ARGS} + ) + + add_dependencies(libc_startup_tests ${fq_target_name}) +endfunction(add_startup_test) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${LIBC_TARGET_OS}) +endif() diff --git a/libc/test/integration/startup/linux/CMakeLists.txt b/libc/test/integration/startup/linux/CMakeLists.txt new file mode 100644 index 0000000..675e8e3 --- /dev/null +++ b/libc/test/integration/startup/linux/CMakeLists.txt @@ -0,0 +1,62 @@ +if(NOT (EXISTS ${LIBC_SOURCE_DIR}/startup/linux/${LIBC_TARGET_ARCHITECTURE})) + message("Skipping startup integration tests for target architecture ${LIBC_TARGET_ARCHITECTURE}.") + return() +endif() + +add_custom_target(libc-startup-tests) +add_dependencies(libc-integration-tests libc-startup-tests) + +add_integration_test( + startup_args_test + SUITE libc-startup-tests + STARTUP + libc.startup.linux.crt1 + SRCS + args_test.cpp + ARGS + 1 2 3 + ENV + FRANCE=Paris + GERMANY=Berlin +) + +add_integration_test( + startup_no_envp_test + SUITE libc-startup-tests + STARTUP + libc.startup.linux.crt1 + SRCS + main_without_envp.cpp +) + +add_integration_test( + startup_no_args_test + SUITE libc-startup-tests + STARTUP + libc.startup.linux.crt1 + SRCS + main_without_args.cpp +) + +add_integration_test( + startup_tls_test + SUITE libc-startup-tests + STARTUP + libc.startup.linux.crt1 + SRCS + tls_test.cpp + DEPENDS + libc.include.errno + libc.include.sys_mman + libc.src.errno.errno + libc.src.sys.mman.mmap +) + +add_integration_test( + init_fini_array_test + SUITE libc-startup-tests + STARTUP + libc.startup.linux.crt1 + SRCS + init_fini_array_test.cpp +) diff --git a/libc/test/integration/startup/linux/args_test.cpp b/libc/test/integration/startup/linux/args_test.cpp new file mode 100644 index 0000000..6b99521 --- /dev/null +++ b/libc/test/integration/startup/linux/args_test.cpp @@ -0,0 +1,38 @@ +//===-- Loader test to check args to main ---------------------------------===// +// +// 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" + +static bool my_streq(const char *lhs, const char *rhs) { + const char *l, *r; + for (l = lhs, r = rhs; *l != '\0' && *r != '\0'; ++l, ++r) + if (*l != *r) + return false; + + return *l == '\0' && *r == '\0'; +} + +TEST_MAIN(int argc, char **argv, char **envp) { + ASSERT_TRUE(argc == 4); + ASSERT_TRUE(my_streq(argv[1], "1")); + ASSERT_TRUE(my_streq(argv[2], "2")); + ASSERT_TRUE(my_streq(argv[3], "3")); + + bool found_france = false; + bool found_germany = false; + for (; *envp != nullptr; ++envp) { + if (my_streq(*envp, "FRANCE=Paris")) + found_france = true; + if (my_streq(*envp, "GERMANY=Berlin")) + found_germany = true; + } + + ASSERT_TRUE(found_france && found_germany); + + return 0; +} diff --git a/libc/test/integration/startup/linux/init_fini_array_test.cpp b/libc/test/integration/startup/linux/init_fini_array_test.cpp new file mode 100644 index 0000000..b560fde --- /dev/null +++ b/libc/test/integration/startup/linux/init_fini_array_test.cpp @@ -0,0 +1,64 @@ +//===-- 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" + +#include + +int global_destroyed = false; + +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; + } + + ~A() { global_destroyed = true; } + + 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; +int preinitval = 0; + +__attribute__((constructor)) void set_initval() { + initval = INITVAL_INITIALIZER; +} +__attribute__((destructor(1))) void reset_initval() { + ASSERT_TRUE(global_destroyed); + ASSERT_EQ(preinitval, 0); + initval = 0; +} + +void set_preinitval() { preinitval = INITVAL_INITIALIZER; } +__attribute__((destructor(2))) void reset_preinitval() { + ASSERT_TRUE(global_destroyed); + ASSERT_EQ(initval, INITVAL_INITIALIZER); + 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; +} diff --git a/libc/test/integration/startup/linux/main_without_args.cpp b/libc/test/integration/startup/linux/main_without_args.cpp new file mode 100644 index 0000000..8c70c3d --- /dev/null +++ b/libc/test/integration/startup/linux/main_without_args.cpp @@ -0,0 +1,11 @@ +//===-- Loader test for main without args ---------------------------------===// +// +// 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" + +TEST_MAIN() { return 0; } diff --git a/libc/test/integration/startup/linux/main_without_envp.cpp b/libc/test/integration/startup/linux/main_without_envp.cpp new file mode 100644 index 0000000..4127990 --- /dev/null +++ b/libc/test/integration/startup/linux/main_without_envp.cpp @@ -0,0 +1,11 @@ +//===-- Loader test for main without envp ---------------------------------===// +// +// 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" + +TEST_MAIN(int argc, char **argv) { return 0; } diff --git a/libc/test/integration/startup/linux/tls_test.cpp b/libc/test/integration/startup/linux/tls_test.cpp new file mode 100644 index 0000000..82141ce --- /dev/null +++ b/libc/test/integration/startup/linux/tls_test.cpp @@ -0,0 +1,38 @@ +//===-- Loader test to check if tls size is read correctly ----------------===// +// +// 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 "include/errno.h" +#include "include/sys/mman.h" + +#include "src/errno/llvmlibc_errno.h" +#include "src/sys/mman/mmap.h" +#include "utils/IntegrationTest/test.h" + +constexpr int threadLocalDataSize = 101; +_Thread_local int a[threadLocalDataSize] = {123}; + +TEST_MAIN(int argc, char **argv, char **envp) { + ASSERT_TRUE(a[0] == 123); + + for (int i = 1; i < threadLocalDataSize; ++i) + a[i] = i; + for (int i = 1; i < threadLocalDataSize; ++i) + ASSERT_TRUE(a[i] == i); + + // Call mmap with bad params so that an error value is + // set in errno. Since errno is implemented using a thread + // local var, this helps us test setting of errno and + // reading it back. + ASSERT_TRUE(llvmlibc_errno == 0); + void *addr = __llvm_libc::mmap(nullptr, 0, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + ASSERT_TRUE(addr == MAP_FAILED); + ASSERT_TRUE(llvmlibc_errno == EINVAL); + + return 0; +} diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt index 426ab66..6a9ab8f 100644 --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -103,8 +103,8 @@ add_integration_test( libc-api-test SRCS ${public_test} - LOADER - libc.loader.linux.crt1 + STARTUP + libc.startup.linux.crt1 DEPENDS ${api-test-entrypoints} ) -- cgit v1.1