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

William Huynh via libc-commits libc-commits at lists.llvm.org
Thu Jul 31 03:09:19 PDT 2025


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

>From ad377724798116f5c3b219436555afa64f113393 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 1/5] [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           |  9 +--
 libc/startup/baremetal/fini.h             | 16 ++++
 libc/startup/baremetal/init.cpp           |  9 +--
 libc/startup/baremetal/init.h             | 18 +++++
 7 files changed, 211 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 276fe33876825..4deead2b9e21d 100644
--- a/libc/startup/baremetal/CMakeLists.txt
+++ b/libc/startup/baremetal/CMakeLists.txt
@@ -1,3 +1,33 @@
+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
@@ -5,6 +35,8 @@ add_entrypoint_object(
   DEPENDS
     libc.hdr.stdint_proxy
     libc.src.__support.common
+  HDRS
+    init.h
 )
 
 add_entrypoint_object(
@@ -14,4 +46,32 @@ add_entrypoint_object(
   DEPENDS
     libc.hdr.stdint_proxy
     libc.src.__support.common
+  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 64af842572ecd..159dae8f1128d 100644
--- a/libc/startup/baremetal/fini.cpp
+++ b/libc/startup/baremetal/fini.cpp
@@ -6,17 +6,16 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "startup/baremetal/fini.h"
+
+#include "startup/baremetal/fini.h"
+
 #include "hdr/stdint_proxy.h"
 #include "src/__support/macros/config.h"
 #include <stddef.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 995609c1bf412..4d67fa84e557f 100644
--- a/libc/startup/baremetal/init.cpp
+++ b/libc/startup/baremetal/init.cpp
@@ -7,18 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "hdr/stdint_proxy.h"
+#include "startup/baremetal/init.h"
+
 #include "src/__support/macros/config.h"
 #include <stddef.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"

>From b8c1bbf959fe139e803fded085ab94d936eeb7d9 Mon Sep 17 00:00:00 2001
From: William Huynh <William.Huynh at arm.com>
Date: Tue, 22 Jul 2025 13:10:33 +0100
Subject: [PATCH 2/5] Apply suggestions from @vhscampos

---
 libc/startup/baremetal/CMakeLists.txt     | 1 -
 libc/startup/baremetal/arm/CMakeLists.txt | 1 +
 libc/startup/baremetal/arm/start.cpp      | 3 ---
 3 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/libc/startup/baremetal/CMakeLists.txt b/libc/startup/baremetal/CMakeLists.txt
index 4deead2b9e21d..12db526eed70a 100644
--- a/libc/startup/baremetal/CMakeLists.txt
+++ b/libc/startup/baremetal/CMakeLists.txt
@@ -74,4 +74,3 @@ foreach(target IN LISTS startup_components)
           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
index 3f6c4041341f4..b1539f3bfb4f2 100644
--- a/libc/startup/baremetal/arm/CMakeLists.txt
+++ b/libc/startup/baremetal/arm/CMakeLists.txt
@@ -12,5 +12,6 @@ add_startup_object(
   COMPILE_OPTIONS
     -ffreestanding # To avoid compiler warnings about calling the main function.
     -fno-builtin
+    -Wno-global-constructors # To allow vector table initialization
 )
 get_fq_target_name(crt1 fq_name)
diff --git a/libc/startup/baremetal/arm/start.cpp b/libc/startup/baremetal/arm/start.cpp
index b40e7116c4001..67af586434e2a 100644
--- a/libc/startup/baremetal/arm/start.cpp
+++ b/libc/startup/baremetal/arm/start.cpp
@@ -48,8 +48,6 @@ void SysTick_Handler() {}
 // 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)) = {
@@ -72,7 +70,6 @@ const HandlerType vector_table[]
                                // Unused
 };
 } // extern "C"
-#pragma clang diagnostic pop
 
 namespace LIBC_NAMESPACE_DECL {
 [[noreturn]] void do_start() {

>From 51704576cada227fb0e2ee7c84729b3023029aeb Mon Sep 17 00:00:00 2001
From: William Huynh <williamhuynh2002 at gmail.com>
Date: Wed, 23 Jul 2025 09:42:27 +0100
Subject: [PATCH 3/5] Update libc/startup/baremetal/arm/start.cpp

Co-authored-by: Michael Jones <michaelrj at google.com>
---
 libc/startup/baremetal/arm/start.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/startup/baremetal/arm/start.cpp b/libc/startup/baremetal/arm/start.cpp
index 67af586434e2a..123efc4750941 100644
--- a/libc/startup/baremetal/arm/start.cpp
+++ b/libc/startup/baremetal/arm/start.cpp
@@ -76,8 +76,8 @@ namespace LIBC_NAMESPACE_DECL {
   // 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_NAMESPACE::memcpy(__data_start, __data_source, (uintptr_t)__data_size);
+  LIBC_NAMESPACE::memset(__bss_start, '\0', (uintptr_t)__bss_size);
   __libc_init_array();
 
   _platform_init();

>From 9cfb320a67664cb8ede8050fb6b8ee3a8e736263 Mon Sep 17 00:00:00 2001
From: William Huynh <William.Huynh at arm.com>
Date: Wed, 23 Jul 2025 11:24:32 +0100
Subject: [PATCH 4/5] Apply suggestions from @vhscampos 2

---
 libc/startup/baremetal/CMakeLists.txt     | 16 +++++++---------
 libc/startup/baremetal/arm/CMakeLists.txt |  1 -
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/libc/startup/baremetal/CMakeLists.txt b/libc/startup/baremetal/CMakeLists.txt
index 12db526eed70a..b70a065743418 100644
--- a/libc/startup/baremetal/CMakeLists.txt
+++ b/libc/startup/baremetal/CMakeLists.txt
@@ -65,12 +65,10 @@ add_startup_object(
 )
 
 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()
+
+set(fq_target_name libc.startup.baremetal.${LIBC_TARGET_ARCHITECTURE}.crt1)
+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)
diff --git a/libc/startup/baremetal/arm/CMakeLists.txt b/libc/startup/baremetal/arm/CMakeLists.txt
index b1539f3bfb4f2..f75bd893c68bc 100644
--- a/libc/startup/baremetal/arm/CMakeLists.txt
+++ b/libc/startup/baremetal/arm/CMakeLists.txt
@@ -14,4 +14,3 @@ add_startup_object(
     -fno-builtin
     -Wno-global-constructors # To allow vector table initialization
 )
-get_fq_target_name(crt1 fq_name)

>From f2138be237e8b1c9054cd667d99e40e75b84d849 Mon Sep 17 00:00:00 2001
From: William Huynh <William.Huynh at arm.com>
Date: Thu, 31 Jul 2025 11:08:04 +0100
Subject: [PATCH 5/5] Add suggestion from @petrhosek and rebase

---
 libc/startup/baremetal/CMakeLists.txt | 1 +
 libc/startup/baremetal/fini.cpp       | 3 ---
 libc/startup/baremetal/fini.h         | 2 +-
 libc/startup/baremetal/init.cpp       | 1 -
 libc/startup/baremetal/init.h         | 2 +-
 5 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libc/startup/baremetal/CMakeLists.txt b/libc/startup/baremetal/CMakeLists.txt
index b70a065743418..e361000f788ea 100644
--- a/libc/startup/baremetal/CMakeLists.txt
+++ b/libc/startup/baremetal/CMakeLists.txt
@@ -1,3 +1,4 @@
+# TODO: Use generic "add_startup_object" https://github.com/llvm/llvm-project/issues/133156 
 function(add_startup_object name)
   cmake_parse_arguments(
     "ADD_STARTUP_OBJECT"
diff --git a/libc/startup/baremetal/fini.cpp b/libc/startup/baremetal/fini.cpp
index 159dae8f1128d..66714e2f8f92d 100644
--- a/libc/startup/baremetal/fini.cpp
+++ b/libc/startup/baremetal/fini.cpp
@@ -8,9 +8,6 @@
 
 #include "startup/baremetal/fini.h"
 
-#include "startup/baremetal/fini.h"
-
-#include "hdr/stdint_proxy.h"
 #include "src/__support/macros/config.h"
 #include <stddef.h>
 
diff --git a/libc/startup/baremetal/fini.h b/libc/startup/baremetal/fini.h
index c3d53601d92cc..74e9601983a33 100644
--- a/libc/startup/baremetal/fini.h
+++ b/libc/startup/baremetal/fini.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <stdint.h>
+#include "hdr/stdint_proxy.h"
 
 extern "C" {
 extern uintptr_t __fini_array_start[];
diff --git a/libc/startup/baremetal/init.cpp b/libc/startup/baremetal/init.cpp
index 4d67fa84e557f..89065fd364ed9 100644
--- a/libc/startup/baremetal/init.cpp
+++ b/libc/startup/baremetal/init.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "hdr/stdint_proxy.h"
 #include "startup/baremetal/init.h"
 
 #include "src/__support/macros/config.h"
diff --git a/libc/startup/baremetal/init.h b/libc/startup/baremetal/init.h
index 2b294e33c98e6..6b545db3976da 100644
--- a/libc/startup/baremetal/init.h
+++ b/libc/startup/baremetal/init.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <stdint.h>
+#include "hdr/stdint_proxy.h"
 
 extern "C" {
 extern uintptr_t __preinit_array_start[];



More information about the libc-commits mailing list