[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