Skip to content

Conversation

saturn691
Copy link
Contributor

This is required in hermetic testing downstream. It is not complete, and will not work on hardware, however it runs on QEMU, and can report a pass/fail on our tests.

@llvmbot llvmbot added the libc label Aug 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 21, 2025

@llvm/pr-subscribers-libc

Author: William Huynh (saturn691)

Changes

This is required in hermetic testing downstream. It is not complete, and will not work on hardware, however it runs on QEMU, and can report a pass/fail on our tests.


Full diff: https://github.com/llvm/llvm-project/pull/154789.diff

3 Files Affected:

  • (modified) libc/cmake/modules/LLVMLibCTestRules.cmake (+1-1)
  • (added) libc/startup/baremetal/aarch64/CMakeLists.txt (+16)
  • (added) libc/startup/baremetal/aarch64/start.cpp (+110)
diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index 267c32e956945..66ef3d3c1cb78 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -836,7 +836,7 @@ function(add_libc_hermetic test_name)
                    ${fq_deps_list})
   # TODO: currently the dependency chain is broken such that getauxval cannot properly
   # propagate to hermetic tests. This is a temporary workaround.
-  if (LIBC_TARGET_ARCHITECTURE_IS_AARCH64)
+  if (LIBC_TARGET_ARCHITECTURE_IS_AARCH64 AND NOT(LIBC_TARGET_OS_IS_BAREMETAL))
     target_link_libraries(
       ${fq_build_target_name}
       PRIVATE
diff --git a/libc/startup/baremetal/aarch64/CMakeLists.txt b/libc/startup/baremetal/aarch64/CMakeLists.txt
new file mode 100644
index 0000000000000..f75bd893c68bc
--- /dev/null
+++ b/libc/startup/baremetal/aarch64/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_startup_object(
+  crt1
+  SRC
+    start.cpp
+  DEPENDS
+    libc.src.stdlib.atexit
+    libc.src.stdlib.exit
+    libc.src.string.memcpy
+    libc.src.string.memset
+    libc.startup.baremetal.init
+    libc.startup.baremetal.fini
+  COMPILE_OPTIONS
+    -ffreestanding # To avoid compiler warnings about calling the main function.
+    -fno-builtin
+    -Wno-global-constructors # To allow vector table initialization
+)
diff --git a/libc/startup/baremetal/aarch64/start.cpp b/libc/startup/baremetal/aarch64/start.cpp
new file mode 100644
index 0000000000000..94d44c849ff9a
--- /dev/null
+++ b/libc/startup/baremetal/aarch64/start.cpp
@@ -0,0 +1,110 @@
+//===-- 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 "hdr/stdint_proxy.h"
+#include "src/__support/macros/config.h"
+#include "src/stdlib/atexit.h"
+#include "src/stdlib/exit.h"
+#include "src/string/memcpy.h"
+#include "src/string/memset.h"
+#include "startup/baremetal/fini.h"
+#include "startup/baremetal/init.h"
+
+#include <arm_acle.h>
+
+extern "C" {
+int main(int argc, char **argv);
+void _start();
+
+// Semihosting library initialisation if applicable. Required for printf, etc.
+[[gnu::weak]] void _platform_init() {}
+
+// These symbols are provided by the linker. The exact names are not defined by
+// a standard.
+extern uintptr_t __stack;
+extern uintptr_t __data_source[];
+extern uintptr_t __data_start[];
+extern uintptr_t __data_size[];
+extern uintptr_t __bss_start[];
+extern uintptr_t __bss_size[];
+} // extern "C"
+
+namespace {
+// The Arm ARM for the A-profile architecture (D14.1.5) defines the exceptions.
+// However, for simplicity, we don't bother logging, and just exit.
+void GenericException_Handler() { LIBC_NAMESPACE::exit(1); }
+
+// The AArch64 exception vector table has 16 entries, each of which is 128
+// bytes long, and contains code. The whole table must be 2048-byte aligned.
+// For our purposes, each entry just contains one branch instruction to the
+// exception reporting function, since we never want to resume after an
+// exception.
+[[gnu::section(".vectors"), gnu::aligned(2048), gnu::used, gnu::naked]]
+void vector_table() {
+#define VECTOR_TABLE_ENTRY                                                     \
+  asm(".balign 128");                                                          \
+  asm("B %0" : : "X"(GenericException_Handler));
+
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+  VECTOR_TABLE_ENTRY;
+}
+} // namespace
+
+namespace LIBC_NAMESPACE_DECL {
+
+[[noreturn]] void do_start() {
+  // TODO: This startup code is not extensive, but rather the MVP for QEMU
+  // testing.
+
+  // Set up exception handling
+  __arm_wsr64("VBAR_EL1", reinterpret_cast<uint64_t>(&vector_table));
+
+#ifdef __ARM_FP
+  // Do not trap FP/SME/SVE instructions
+  static constexpr uint64_t CPACR_SHIFT_FPEN = 20;
+  static constexpr uint64_t CPACR_SHIFT_SMEN = 24;
+  uint64_t cpacr = __arm_rsr64("CPACR_EL1");
+  cpacr |= (0x3 << CPACR_SHIFT_FPEN);
+  cpacr |= (0x3 << CPACR_SHIFT_SMEN);
+  __arm_wsr64("CPACR_EL1", cpacr);
+#endif
+
+  // Perform the equivalent of scatterloading
+  LIBC_NAMESPACE::memcpy(__data_start, __data_source,
+                         reinterpret_cast<uintptr_t>(__data_size));
+  LIBC_NAMESPACE::memset(__bss_start, '\0',
+                         reinterpret_cast<uintptr_t>(__bss_size));
+  __libc_init_array();
+
+  _platform_init();
+  LIBC_NAMESPACE::atexit(&__libc_fini_array);
+  LIBC_NAMESPACE::exit(main(0, 0));
+}
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+[[gnu::section(".text.init.enter"), gnu::naked]]
+void _start() {
+  asm volatile("mov sp, %0" : : "r"(&__stack));
+  asm volatile("bl %0" : : "X"(LIBC_NAMESPACE::do_start));
+}
+} // extern "C"

This is required in hermetic testing downstream. It is not
complete, and will not work on hardware, however it runs on QEMU,
and can report a pass/fail on our tests.
Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as an MVP.

If possible, please add TODOs on the parts that are incomplete

Copy link
Member

@RossComputerGuy RossComputerGuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@saturn691 saturn691 enabled auto-merge (squash) August 21, 2025 18:43
@saturn691 saturn691 merged commit 1b9e9e2 into llvm:main Aug 21, 2025
19 checks passed
@saturn691 saturn691 deleted the libc-start-aarch64 branch August 22, 2025 09:42
saturn691 added a commit to saturn691/arm-toolchain that referenced this pull request Aug 26, 2025
saturn691 added a commit to arm/arm-toolchain that referenced this pull request Aug 26, 2025
Enabled in llvm/llvm-project#154789

Minor changes to the CMake file, to select `qemu-system-aarch64` if
applicable. These tests don't pass, but they compile, so we enable them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants