diff --git a/libc/loader/linux/aarch64/CMakeLists.txt b/libc/loader/linux/aarch64/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/loader/linux/aarch64/CMakeLists.txt @@ -0,0 +1,13 @@ +add_loader_object( + crt1 + SRC + start.cpp + DEPENDS + libc.config.linux.app_h + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.string.memcpy + 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 new file mode 100644 --- /dev/null +++ b/libc/loader/linux/aarch64/start.cpp @@ -0,0 +1,83 @@ +//===-- 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 "include/sys/syscall.h" +#include "src/__support/OSUtil/syscall.h" + +#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 { + +AppProperties app; + +} // namespace __llvm_libc + +using __llvm_libc::app; + +struct Args { + // In the ARM64 ABI, arguments are usually passed in registers. x0 is a + // doubleword register, so this is 64 bit. + uint64_t argc; + + // C++ Doesn't have flexible arrays: P1039 proposes to fix this, but for now + // we just fake it. Even if argc is zero, "argv[argc] shall be a null + // pointer" (ISO C 5.1.2.2.1) so one is fine. + uint64_t argv[1]; +}; + +// 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() { + uintptr_t *frame_ptr = + reinterpret_cast(__builtin_frame_address(0)); + + // Skip the Frame Pointer and the Link Register + // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + // Section 6.2.3 + + Args *args = reinterpret_cast(frame_ptr + 2); + + // 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 = args->argv + args->argc + 1; + uint64_t *env_end_marker = env_ptr; + while (*env_end_marker) + ++env_end_marker; + + // After the env array, is the aux-vector. The end of the aux-vector is + // denoted by an AT_NULL entry. + for (AuxEntry *aux_entry = reinterpret_cast(env_end_marker + 1); + aux_entry->type != AT_NULL; ++aux_entry) { + switch (aux_entry->type) { + case AT_PAGESZ: + app.pageSize = aux_entry->value; + break; + default: + break; // TODO: Read other useful entries from the aux vector. + } + } + + // TODO: Init TLS + + __llvm_libc::syscall(SYS_exit, + main(args->argc, reinterpret_cast(args->argv), + reinterpret_cast(env_ptr))); +}