[libc-commits] [libc] [libc] Draft implementation of crt0 (PR #146863)

William Huynh via libc-commits libc-commits at lists.llvm.org
Thu Jul 3 04:44:47 PDT 2025


https://github.com/saturn691 created https://github.com/llvm/llvm-project/pull/146863

This is based on https://github.com/arm/arm-toolchain/tree/5446a3cbf4ef73196ccce490fc9fbf87bee093aa/arm-software/embedded/llvmlibc-support/crt0 and what we discussed. I've been using this `crt0` to run my hermetic tests downstream for some time (I just keep it as a local patch). Remember that downstream, I also have to link in `-lsemihost` and my linker script, which we decided to hold off upstreaming, at least for the time being.

It is not the cleanest code, given I had to do `#pragma` to turn the warnings off. Furthermore, there are a lot of assumptions about the linker script, which work when I use my linker script. 

If you have a cleaner version of this, please also send your draft and then we can decide which version to use.

>From c5786244e9667fe056942d55583cab13f40f1ae2 Mon Sep 17 00:00:00 2001
From: William Huynh <William.Huynh at arm.com>
Date: Thu, 3 Jul 2025 12:35:30 +0100
Subject: [PATCH] [libc] Draft implementation of crt0

---
 libc/startup/baremetal/CMakeLists.txt     | 60 ++++++++++++++
 libc/startup/baremetal/arm/CMakeLists.txt | 16 ++++
 libc/startup/baremetal/arm/start.cpp      | 95 +++++++++++++++++++++++
 libc/startup/baremetal/fini.cpp           |  7 +-
 libc/startup/baremetal/fini.h             | 16 ++++
 libc/startup/baremetal/init.cpp           |  9 +--
 libc/startup/baremetal/init.h             | 18 +++++
 7 files changed, 209 insertions(+), 12 deletions(-)
 create mode 100644 libc/startup/baremetal/arm/CMakeLists.txt
 create mode 100644 libc/startup/baremetal/arm/start.cpp
 create mode 100644 libc/startup/baremetal/fini.h
 create mode 100644 libc/startup/baremetal/init.h

diff --git a/libc/startup/baremetal/CMakeLists.txt b/libc/startup/baremetal/CMakeLists.txt
index 4faced93fabe5..c5c74a0de5c3a 100644
--- a/libc/startup/baremetal/CMakeLists.txt
+++ b/libc/startup/baremetal/CMakeLists.txt
@@ -1,11 +1,71 @@
+function(add_startup_object name)
+  cmake_parse_arguments(
+    "ADD_STARTUP_OBJECT"
+    "ALIAS" # Option argument
+    "SRC"   # Single value arguments
+    "DEPENDS;COMPILE_OPTIONS" # Multi value arguments
+    ${ARGN}
+  )
+
+  get_fq_target_name(${name} fq_target_name)
+  if(ADD_STARTUP_OBJECT_ALIAS)
+    get_fq_deps_list(fq_dep_list ${ADD_STARTUP_OBJECT_DEPENDS})
+    add_library(${fq_target_name} ALIAS ${fq_dep_list})
+    return()
+  endif()
+
+  add_object_library(
+    ${name}
+    SRCS ${ADD_STARTUP_OBJECT_SRC}
+    COMPILE_OPTIONS ${ADD_STARTUP_OBJECT_COMPILE_OPTIONS}
+    ${ADD_STARTUP_OBJECT_UNPARSED_ARGUMENTS}
+    DEPENDS ${ADD_STARTUP_OBJECT_DEPENDS}
+  )
+  set_target_properties(
+    ${fq_target_name}
+    PROPERTIES
+      OUTPUT_NAME ${name}.o
+  )
+endfunction()
+
 add_entrypoint_object(
   init
   SRCS
     init.cpp
+  HDRS
+    init.h
 )
 
 add_entrypoint_object(
   fini
   SRCS
     fini.cpp
+  HDRS
+    fini.h
+)
+
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+  add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
+else()
+  message(WARNING "Cannot build 'crt1.o' for ${LIBC_TARGET_ARCHITECTURE} yet.")
+  return()
+endif()
+
+add_startup_object(
+  crt1
+  ALIAS
+  DEPENDS
+  .${LIBC_TARGET_ARCHITECTURE}.crt1
 )
+
+add_custom_target(libc-startup)
+set(startup_components crt1)
+foreach(target IN LISTS startup_components)
+  set(fq_target_name libc.startup.baremetal.${target})
+  add_dependencies(libc-startup ${fq_target_name})
+  install(FILES $<TARGET_OBJECTS:${fq_target_name}>
+          DESTINATION ${LIBC_INSTALL_LIBRARY_DIR}
+          RENAME $<TARGET_PROPERTY:${fq_target_name},OUTPUT_NAME>
+          COMPONENT libc)
+endforeach()
+
diff --git a/libc/startup/baremetal/arm/CMakeLists.txt b/libc/startup/baremetal/arm/CMakeLists.txt
new file mode 100644
index 0000000000000..3f6c4041341f4
--- /dev/null
+++ b/libc/startup/baremetal/arm/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
+)
+get_fq_target_name(crt1 fq_name)
diff --git a/libc/startup/baremetal/arm/start.cpp b/libc/startup/baremetal/arm/start.cpp
new file mode 100644
index 0000000000000..b40e7116c4001
--- /dev/null
+++ b/libc/startup/baremetal/arm/start.cpp
@@ -0,0 +1,95 @@
+//===-- Implementation of crt for arm -------------------------------------===//
+//
+// 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 "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 <stdint.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[];
+
+// Based on
+// https://developer.arm.com/documentation/107565/0101/Use-case-examples/Generic-Information/What-is-inside-a-program-image-/Vector-table
+void NMI_Handler() {}
+void HardFault_Handler() { LIBC_NAMESPACE::exit(1); }
+void MemManage_Handler() { LIBC_NAMESPACE::exit(1); }
+void BusFault_Handler() { LIBC_NAMESPACE::exit(1); }
+void UsageFault_Handler() { LIBC_NAMESPACE::exit(1); }
+void SVC_Handler() {}
+void DebugMon_Handler() {}
+void PendSV_Handler() {}
+void SysTick_Handler() {}
+
+// Architecturally the bottom 7 bits of VTOR are zero, meaning the vector table
+// has to be 128-byte aligned, however an implementation can require more bits
+// to be zero and Cortex-M23 can require up to 10, so 1024-byte align the vector
+// table.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+using HandlerType = void (*)(void);
+const HandlerType vector_table[]
+    __attribute__((section(".vectors"), aligned(1024), used)) = {
+        (HandlerType)&__stack, // SP
+        _start,                // Reset
+        NMI_Handler,           // NMI Handler
+        HardFault_Handler,     // Hard Fault Handlerß
+        MemManage_Handler,     // MPU Fault Han`dler
+        BusFault_Handler,      // Bus Fault Handler
+        UsageFault_Handler,    // Usage Fault Handler
+        0,                     // Reserved
+        0,                     // Reserved
+        0,                     // Reserved
+        0,                     // Reserved
+        SVC_Handler,           // SVC Handler
+        DebugMon_Handler,      // Debug Monitor Handler
+        0,                     // Reserved
+        PendSV_Handler,        // PendSV Handler
+        SysTick_Handler,       // SysTick Handler
+                               // Unused
+};
+} // extern "C"
+#pragma clang diagnostic pop
+
+namespace LIBC_NAMESPACE_DECL {
+[[noreturn]] void do_start() {
+  // FIXME: set up the QEMU test environment
+
+  // Perform the equivalent of scatterloading
+  memcpy(__data_start, __data_source, (uintptr_t)__data_size);
+  memset(__bss_start, '\0', (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" void _start() {
+  asm volatile("mov sp, %0" : : "r"(&__stack));
+  asm volatile("bl %0" : : "X"(LIBC_NAMESPACE::do_start));
+}
diff --git a/libc/startup/baremetal/fini.cpp b/libc/startup/baremetal/fini.cpp
index 263d7192cc607..5532512117ea6 100644
--- a/libc/startup/baremetal/fini.cpp
+++ b/libc/startup/baremetal/fini.cpp
@@ -6,17 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "startup/baremetal/fini.h"
+
 #include "src/__support/macros/config.h"
 #include <stddef.h>
 #include <stdint.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-extern "C" {
-extern uintptr_t __fini_array_start[];
-extern uintptr_t __fini_array_end[];
-}
-
 using FiniCallback = void(void);
 
 extern "C" void __libc_fini_array(void) {
diff --git a/libc/startup/baremetal/fini.h b/libc/startup/baremetal/fini.h
new file mode 100644
index 0000000000000..c3d53601d92cc
--- /dev/null
+++ b/libc/startup/baremetal/fini.h
@@ -0,0 +1,16 @@
+//===-- Implementation header of __libc_fini_array ------------------------===//
+//
+// 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 <stdint.h>
+
+extern "C" {
+extern uintptr_t __fini_array_start[];
+extern uintptr_t __fini_array_end[];
+
+void __libc_fini_array(void);
+} // extern "C"
diff --git a/libc/startup/baremetal/init.cpp b/libc/startup/baremetal/init.cpp
index ce387017c4972..98cca18102fef 100644
--- a/libc/startup/baremetal/init.cpp
+++ b/libc/startup/baremetal/init.cpp
@@ -6,19 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "startup/baremetal/init.h"
+
 #include "src/__support/macros/config.h"
 #include <stddef.h>
 #include <stdint.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-extern "C" {
-extern uintptr_t __preinit_array_start[];
-extern uintptr_t __preinit_array_end[];
-extern uintptr_t __init_array_start[];
-extern uintptr_t __init_array_end[];
-}
-
 using InitCallback = void(void);
 
 extern "C" void __libc_init_array(void) {
diff --git a/libc/startup/baremetal/init.h b/libc/startup/baremetal/init.h
new file mode 100644
index 0000000000000..2b294e33c98e6
--- /dev/null
+++ b/libc/startup/baremetal/init.h
@@ -0,0 +1,18 @@
+//===-- Implementation header of __libc_init_array ------------------------===//
+//
+// 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 <stdint.h>
+
+extern "C" {
+extern uintptr_t __preinit_array_start[];
+extern uintptr_t __preinit_array_end[];
+extern uintptr_t __init_array_start[];
+extern uintptr_t __init_array_end[];
+
+void __libc_init_array(void);
+} // extern "C"



More information about the libc-commits mailing list