diff options
Diffstat (limited to 'libc/startup/linux/aarch64/start.cpp')
| -rw-r--r-- | libc/startup/linux/aarch64/start.cpp | 215 |
1 files changed, 5 insertions, 210 deletions
diff --git a/libc/startup/linux/aarch64/start.cpp b/libc/startup/linux/aarch64/start.cpp index bc01582aeb49..d0a852687339 100644 --- a/libc/startup/linux/aarch64/start.cpp +++ b/libc/startup/linux/aarch64/start.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of crt for aarch64 ---------------------------------===// +//===-- Implementation of _start 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. @@ -6,213 +6,8 @@ // //===----------------------------------------------------------------------===// -#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/inline_memcpy.h" - -#include <arm_acle.h> - -#include <linux/auxvec.h> -#include <linux/elf.h> -#include <stdint.h> -#include <sys/mman.h> -#include <sys/syscall.h> -#include <unistd.h> - -extern "C" int main(int, char **, char **); - -// Source documentation: -// https://github.com/ARM-software/abi-aa/tree/main/sysvabi64 - -namespace LIBC_NAMESPACE { - -#ifdef SYS_mmap2 -static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2; -#elif SYS_mmap -static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap; -#else -#error "mmap and mmap2 syscalls not available." -#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 = LIBC_NAMESPACE::syscall_impl<long>( - 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<uintptr_t>(mmap_ret_val) > -app.page_size) - LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 1); - uintptr_t thread_ptr = uintptr_t(reinterpret_cast<uintptr_t *>(mmap_ret_val)); - uintptr_t tls_addr = thread_ptr + size_of_pointers + padding; - LIBC_NAMESPACE::inline_memcpy(reinterpret_cast<char *>(tls_addr), - reinterpret_cast<const char *>(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; - LIBC_NAMESPACE::syscall_impl<long>(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<InitCallback *>(__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<InitCallback *>(__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<FiniCallback *>(__fini_array_start[i - 1])(); -} - -} // namespace LIBC_NAMESPACE - -using LIBC_NAMESPACE::app; -using LIBC_NAMESPACE::AuxEntry; - -__attribute__((noinline)) static void do_start() { - auto tid = LIBC_NAMESPACE::syscall_impl<long>(SYS_gettid); - if (tid <= 0) - LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 1); - LIBC_NAMESPACE::main_thread_attrib.tid = static_cast<int>(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.env_ptr = env_ptr; - while (*env_end_marker) - ++env_end_marker; - - // Initialize the POSIX global declared in unistd.h - environ = reinterpret_cast<char **>(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 *program_hdr_table = nullptr; - uintptr_t program_hdr_count; - app.auxv_ptr = reinterpret_cast<AuxEntry *>(env_end_marker + 1); - for (auto *aux_entry = app.auxv_ptr; aux_entry->id != AT_NULL; ++aux_entry) { - switch (aux_entry->id) { - case AT_PHDR: - program_hdr_table = reinterpret_cast<Elf64_Phdr *>(aux_entry->value); - break; - case AT_PHNUM: - program_hdr_count = aux_entry->value; - break; - case AT_PAGESZ: - app.page_size = 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 < program_hdr_count; ++i) { - Elf64_Phdr *phdr = program_hdr_table + 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; - } - - // This descriptor has to be static since its cleanup function cannot - // capture the context. - static LIBC_NAMESPACE::TLSDescriptor tls; - LIBC_NAMESPACE::init_tls(tls); - if (tls.size != 0) - LIBC_NAMESPACE::set_thread_ptr(tls.tp); - - LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib; - LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr = - LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr(); - // We register the cleanup_tls function to be the last atexit callback to be - // invoked. It will tear down the TLS. Other callbacks may depend on TLS (such - // as the stack protector canary). - LIBC_NAMESPACE::atexit( - []() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); }); - // 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. - LIBC_NAMESPACE::atexit(&LIBC_NAMESPACE::call_fini_array_callbacks); - - LIBC_NAMESPACE::call_init_array_callbacks( - static_cast<int>(app.args->argc), - reinterpret_cast<char **>(app.args->argv), - reinterpret_cast<char **>(env_ptr)); - - int retval = main(static_cast<int>(app.args->argc), - reinterpret_cast<char **>(app.args->argv), - reinterpret_cast<char **>(env_ptr)); - - LIBC_NAMESPACE::exit(retval); -} - -extern "C" void _start() { +#include "startup/linux/do_start.h" +extern "C" [[noreturn]] 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 @@ -223,7 +18,7 @@ extern "C" void _start() { // 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<LIBC_NAMESPACE::Args *>( + LIBC_NAMESPACE::app.args = reinterpret_cast<LIBC_NAMESPACE::Args *>( reinterpret_cast<uintptr_t *>(__builtin_frame_address(0)) + 2); - do_start(); + LIBC_NAMESPACE::do_start(); } |
