Implement basic loader for Linux aarch64
authorJeff Bailey <jeffbailey@google.com>
Sat, 12 Feb 2022 19:01:37 +0000 (19:01 +0000)
committerJeff Bailey <jeffbailey@google.com>
Wed, 16 Feb 2022 03:42:44 +0000 (03:42 +0000)
This implements a basic arm64 loader for Linux, and all the currently
enabled linker tests pass.  TLS is not implemented, and functions
using it will have undefined behaviour.  Notably, the TLS test is
currently disabled on x86_64.

Much of the structure is copied from x86_64 to allow for a refactoring
of the start code between architectures.

Tested:
ninja libc_loader_tests on aarch64-linux.

Co-authored-by: Raman Tenneti <rtenneti@google.com>
Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D119641

libc/loader/linux/aarch64/CMakeLists.txt [new file with mode: 0644]
libc/loader/linux/aarch64/start.cpp [new file with mode: 0644]

diff --git a/libc/loader/linux/aarch64/CMakeLists.txt b/libc/loader/linux/aarch64/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c8fa53b
--- /dev/null
@@ -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 (file)
index 0000000..c8ee619
--- /dev/null
@@ -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 <linux/auxvec.h>
+#include <linux/elf.h>
+#include <stdint.h>
+
+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<uintptr_t *>(__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<Args *>(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<AuxEntry *>(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<char **>(args->argv),
+                            reinterpret_cast<char **>(env_ptr)));
+}