diff options
author | Siva Chandra Reddy <sivachandra@google.com> | 2020-03-18 12:46:33 -0700 |
---|---|---|
committer | Siva Chandra Reddy <sivachandra@google.com> | 2020-03-25 10:12:35 -0700 |
commit | f6ccb4fef24948003f1327c0e31a93ae4754c5d6 (patch) | |
tree | 1fd3eb0de8aa5179810ffe646112ee81de682603 /libc/loader | |
parent | bb4da94e5b5f64ea68197de9be44c7c5a4c91ce7 (diff) | |
download | llvm-f6ccb4fef24948003f1327c0e31a93ae4754c5d6.zip llvm-f6ccb4fef24948003f1327c0e31a93ae4754c5d6.tar.gz llvm-f6ccb4fef24948003f1327c0e31a93ae4754c5d6.tar.bz2 |
[libc] Add a simple x86_64 linux loader.
This adds a very simple loader. This will be extended to a full loader
in future patches. A utility rule to add unittests has been added to
serve us while we are building out the full loader.
Reviewers: abrachet, phosek
Differential Revision: https://reviews.llvm.org/D76412
Diffstat (limited to 'libc/loader')
-rw-r--r-- | libc/loader/CMakeLists.txt | 3 | ||||
-rw-r--r-- | libc/loader/linux/CMakeLists.txt | 34 | ||||
-rw-r--r-- | libc/loader/linux/x86_64/CMakeLists.txt | 11 | ||||
-rw-r--r-- | libc/loader/linux/x86_64/start.cpp | 65 |
4 files changed, 113 insertions, 0 deletions
diff --git a/libc/loader/CMakeLists.txt b/libc/loader/CMakeLists.txt new file mode 100644 index 0000000..b4bbe81c --- /dev/null +++ b/libc/loader/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/loader/linux/CMakeLists.txt b/libc/loader/linux/CMakeLists.txt new file mode 100644 index 0000000..4ef4b88 --- /dev/null +++ b/libc/loader/linux/CMakeLists.txt @@ -0,0 +1,34 @@ +function(add_loader_object name) + cmake_parse_arguments( + "ADD_LOADER_OBJECT" + "" # No option arguments + "SRC" # Single value arguments + "DEPENDS;COMPILE_OPTIONS" # Multi value arguments + ${ARGN} + ) + add_object( + ${name}_object + SRC ${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 $<TARGET_OBJECTS:${name}_object> ${objfile} + DEPENDS $<TARGET_OBJECTS:${name}_object> + ) + add_custom_target( + ${name} + DEPENDS ${objfile} + ) + set_target_properties( + ${name} + PROPERTIES + "TARGET_TYPE" "LOADER_OBJECT" + "OBJECT_FILE" ${objfile} + ) +endfunction() + +add_subdirectory(${LIBC_TARGET_MACHINE}) diff --git a/libc/loader/linux/x86_64/CMakeLists.txt b/libc/loader/linux/x86_64/CMakeLists.txt new file mode 100644 index 0000000..e7d7ee5 --- /dev/null +++ b/libc/loader/linux/x86_64/CMakeLists.txt @@ -0,0 +1,11 @@ +add_loader_object( + crt1 + SRC + start.cpp + DEPENDS + linux_syscall_h + sys_syscall_h + COMPILE_OPTIONS + -fno-omit-frame-pointer + -ffreestanding # To avoid compiler warnings about calling the main function. +) diff --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp new file mode 100644 index 0000000..57af0b1 --- /dev/null +++ b/libc/loader/linux/x86_64/start.cpp @@ -0,0 +1,65 @@ +//===------------------ 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/syscall.h" +#include "include/sys/syscall.h" + +#include <linux/auxvec.h> +#include <stdint.h> + +extern "C" int main(int, char **, char **); + +struct Args { + // At the language level, argc is an int. But we use uint64_t as the x86_64 + // ABI specifies it as an 8 byte value. + uint64_t argc; + + // At the language level, argv is a char** value. However, we use uint64_t as + // the x86_64 ABI specifies the argv vector be an |argc| long array of 8-byte + // values. Even though a flexible length array would be more suitable here, we + // set the array length to 1 to avoid a compiler warning about it being a C99 + // extension. Length of 1 is not really wrong as |argc| is guaranteed to be + // atleast 1, and there is an 8-byte null entry at the end of the argv array. + 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<uintptr_t *>(__builtin_frame_address(0)); + + // 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. + Args *args = reinterpret_cast<Args *>(frame_ptr + 1); + + // 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<AuxEntry *>(env_end_marker + 1); + aux_entry->type != AT_NULL; ++aux_entry) { + // TODO: Read the aux vector and store necessary information in a libc wide + // data structure. + } + + __llvm_libc::syscall(SYS_exit, + main(args->argc, reinterpret_cast<char **>(args->argv), + reinterpret_cast<char **>(env_ptr))); +} |