aboutsummaryrefslogtreecommitdiff
path: root/libc/loader
diff options
context:
space:
mode:
authorSiva Chandra Reddy <sivachandra@google.com>2020-03-18 12:46:33 -0700
committerSiva Chandra Reddy <sivachandra@google.com>2020-03-25 10:12:35 -0700
commitf6ccb4fef24948003f1327c0e31a93ae4754c5d6 (patch)
tree1fd3eb0de8aa5179810ffe646112ee81de682603 /libc/loader
parentbb4da94e5b5f64ea68197de9be44c7c5a4c91ce7 (diff)
downloadllvm-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.txt3
-rw-r--r--libc/loader/linux/CMakeLists.txt34
-rw-r--r--libc/loader/linux/x86_64/CMakeLists.txt11
-rw-r--r--libc/loader/linux/x86_64/start.cpp65
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)));
+}