[libc-commits] [libc] Enable bare-metal hermetic tests on Arm (PR #207361)
via libc-commits
libc-commits at lists.llvm.org
Fri Jul 3 03:02:22 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Volodymyr Turanskyy (voltur01)
<details>
<summary>Changes</summary>
This provides missing components required to run LLVM libc hermetic tests, for example, with QEMU:
- Redirect of output, time and exit to semihosting.
- Linker script.
- Extra LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY setting for LIBC_CONF_MATH_OPTIMIZATIONS to make hardfp tests pass.
---
Full diff: https://github.com/llvm/llvm-project/pull/207361.diff
4 Files Affected:
- (added) libc/config/baremetal/arm/config.json (+7)
- (added) libc/test/UnitTest/ArmBaremetalTestUtils.cpp (+113)
- (modified) libc/test/UnitTest/CMakeLists.txt (+6-1)
- (added) libc/test/UnitTest/llvm-libc-baremetal.ld (+157)
``````````diff
diff --git a/libc/config/baremetal/arm/config.json b/libc/config/baremetal/arm/config.json
new file mode 100644
index 0000000000000..c35f32d0eacdf
--- /dev/null
+++ b/libc/config/baremetal/arm/config.json
@@ -0,0 +1,7 @@
+{
+ "math": {
+ "LIBC_CONF_MATH_OPTIMIZATIONS": {
+ "value": "(LIBC_MATH_SKIP_ACCURATE_PASS | LIBC_MATH_SMALL_TABLES | LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT | LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT | LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY)"
+ }
+ }
+}
diff --git a/libc/test/UnitTest/ArmBaremetalTestUtils.cpp b/libc/test/UnitTest/ArmBaremetalTestUtils.cpp
new file mode 100644
index 0000000000000..251ed5127b492
--- /dev/null
+++ b/libc/test/UnitTest/ArmBaremetalTestUtils.cpp
@@ -0,0 +1,113 @@
+//===-- Arm bare-metal hermetic test utilities ----------------------------===//
+//
+// 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 <llvm-libc-types/ssize_t.h>
+#include <stddef.h>
+#include <time.h>
+
+// Redirect stdout/stderr, time and exit from libc tests to semihosting
+
+namespace {
+// Semihosting constants and semihosting call wrapper.
+// https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst
+constexpr uint32_t SYS_OPEN = 0x01;
+constexpr uint32_t SYS_WRITE = 0x05;
+constexpr uint32_t SYS_CLOCK = 0x10;
+constexpr uint32_t SYS_TIME = 0x11;
+constexpr uint32_t SYS_EXIT = 0x18;
+
+constexpr uint32_t OPENMODE_W = 4;
+
+constexpr uint32_t ADP_Stopped_ApplicationExit = 0x20026;
+constexpr uint32_t ADP_Stopped_RunTimeErrorUnknown = 0x20023;
+
+#if defined(__thumb__) // T32
+#if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M'
+#define SEMIHOST_INSTRUCTION "bkpt #0xAB"
+#else
+#define SEMIHOST_INSTRUCTION "svc 0xab"
+#endif
+#else // A32
+#define SEMIHOST_INSTRUCTION "svc 0x123456"
+#endif
+
+long semihosting_call(long val, const void *ptr) {
+ register long v __asm__("r0") = val;
+ register const void *p __asm__("r1") = ptr;
+ __asm__ __volatile__(SEMIHOST_INSTRUCTION
+ : "+r"(v), "+r"(p)
+ :
+ : "memory", "cc");
+ return v;
+}
+} // namespace
+
+extern "C" {
+struct __llvm_libc_stdio_cookie {
+ int handle;
+};
+
+struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+
+static void stdio_open(struct __llvm_libc_stdio_cookie *cookie, size_t mode) {
+ const char std_stream_name[] = ":tt";
+ size_t args[3];
+ args[0] = reinterpret_cast<size_t>(std_stream_name);
+ args[1] = mode;
+ args[2] = static_cast<size_t>(sizeof(std_stream_name) - 1);
+ cookie->handle = semihosting_call(SYS_OPEN, args);
+}
+
+void _platform_init(void) {
+ stdio_open(&__llvm_libc_stdout_cookie, OPENMODE_W);
+ stdio_open(&__llvm_libc_stderr_cookie, OPENMODE_W);
+}
+
+void __llvm_libc_exit(int status) {
+ uint32_t semihosting_status = (status == 0) ? ADP_Stopped_ApplicationExit
+ : ADP_Stopped_RunTimeErrorUnknown;
+ semihosting_call(SYS_EXIT,
+ reinterpret_cast<const void *>(semihosting_status));
+ __builtin_unreachable(); // This semihosting call does not return.
+}
+
+ssize_t __llvm_libc_stdio_write(struct __llvm_libc_stdio_cookie *cookie,
+ const char *buf, size_t size) {
+ size_t args[4];
+ args[0] = static_cast<size_t>(cookie->handle);
+ args[1] = reinterpret_cast<size_t>(buf);
+ args[2] = size;
+ ssize_t retval = semihosting_call(SYS_WRITE, args);
+ if (retval >= 0)
+ retval = size - retval;
+ return retval;
+}
+
+bool __llvm_libc_timespec_get_active(struct timespec *ts) {
+ long retval = semihosting_call(SYS_CLOCK, 0);
+ if (retval == -1)
+ return false;
+
+ // Semihosting uses centiseconds.
+ ts->tv_sec = (retval / 100);
+ ts->tv_nsec = (retval % 100) * (1'000'000'000 / 100);
+ return true;
+}
+
+bool __llvm_libc_timespec_get_utc(struct timespec *ts) {
+ long retval = semihosting_call(SYS_TIME, 0);
+
+ // Semihosting uses seconds.
+ ts->tv_sec = retval;
+ ts->tv_nsec = 0;
+ return true;
+}
+} // extern "C"
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index aa245b2234793..9187ac41c8005 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -111,10 +111,15 @@ add_unittest_framework_library(
libc.hdr.stdint_proxy
)
+set(libc_hermetic_test_support_srcs HermeticTestUtils.cpp)
+if(LIBC_TARGET_OS_IS_BAREMETAL AND LIBC_TARGET_ARCHITECTURE_IS_ARM)
+ list(APPEND libc_hermetic_test_support_srcs ArmBaremetalTestUtils.cpp)
+endif()
+
add_unittest_framework_library(
LibcHermeticTestSupport
SRCS
- HermeticTestUtils.cpp
+ ${libc_hermetic_test_support_srcs}
DEPENDS
libc.hdr.stdint_proxy
)
diff --git a/libc/test/UnitTest/llvm-libc-baremetal.ld b/libc/test/UnitTest/llvm-libc-baremetal.ld
new file mode 100644
index 0000000000000..354d6a683cfb6
--- /dev/null
+++ b/libc/test/UnitTest/llvm-libc-baremetal.ld
@@ -0,0 +1,157 @@
+/*===-- Bare-metal LLVM libc test linker script ---------------------------===*\
+ *
+ * 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
+ *
+\*===----------------------------------------------------------------------===*/
+
+ASSERT(DEFINED(__boot_flash), "__boot_flash must be defined");
+ASSERT(DEFINED(__boot_flash_size), "__boot_flash_size must be defined");
+ASSERT(DEFINED(__flash), "__flash must be defined");
+ASSERT(DEFINED(__flash_size), "__flash_size must be defined");
+ASSERT(DEFINED(__ram), "__ram must be defined");
+ASSERT(DEFINED(__ram_size), "__ram_size must be defined");
+
+MEMORY {
+ boot_flash (rx) : ORIGIN = __boot_flash, LENGTH = __boot_flash_size
+ flash (rx) : ORIGIN = __flash, LENGTH = __flash_size
+ ram (rwx) : ORIGIN = __ram, LENGTH = __ram_size
+}
+
+SECTIONS {
+ PROVIDE(__stack = ORIGIN(ram) + LENGTH(ram));
+
+ .boot_flash : {
+ KEEP(*(.text.init.enter))
+ KEEP(*(.vectors))
+ KEEP(*(.vectors.*))
+ KEEP(*(.data.init.enter))
+ KEEP(*(SORT_BY_NAME(.init) SORT_BY_NAME(.init.*)))
+ } > boot_flash
+
+ .text : ALIGN(8) {
+ KEEP(*(.text.startup .text.startup.*))
+ KEEP(*(.init))
+ *(.text .text.* .gnu.linkonce.t.*)
+ KEEP(*(.fini))
+
+ . = ALIGN(8);
+ PROVIDE_HIDDEN(__preinit_array_start = .);
+ KEEP(*(.preinit_array))
+ KEEP(*(.preinit_array.*))
+ PROVIDE_HIDDEN(__preinit_array_end = .);
+
+ PROVIDE_HIDDEN(__init_array_start = .);
+ KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)))
+ KEEP(*(.init_array))
+ KEEP(*(SORT_BY_INIT_PRIORITY(.ctors.*)))
+ KEEP(*(.ctors))
+ PROVIDE_HIDDEN(__init_array_end = .);
+
+ PROVIDE_HIDDEN(__fini_array_start = .);
+ KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)))
+ KEEP(*(.fini_array))
+ KEEP(*(SORT_BY_INIT_PRIORITY(.dtors.*)))
+ KEEP(*(.dtors))
+ PROVIDE_HIDDEN(__fini_array_end = .);
+ } > flash
+
+ .rodata : ALIGN(8) {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ *(.srodata .srodata.*)
+ *(.rdata)
+ } > flash
+
+ .eh_frame_hdr : ALIGN(4) {
+ *(.eh_frame_hdr)
+ *(.eh_frame_entry .eh_frame_entry.*)
+ } > flash
+
+ .eh_frame : ALIGN(4) {
+ KEEP(*(.eh_frame))
+ KEEP(*(.eh_frame.*))
+ } > flash
+
+ .ARM.extab : ALIGN(4) {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ } > flash
+
+ .ARM.exidx : ALIGN(4) {
+ PROVIDE(__exidx_start = .);
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ PROVIDE(__exidx_end = .);
+ } > flash
+
+ .data : ALIGN(8) {
+ PROVIDE(__data_start = .);
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ . = ALIGN(8);
+ } > ram AT> flash
+ PROVIDE(__data_source = LOADADDR(.data));
+
+ .tdata : ALIGN(8) {
+ PROVIDE(__tdata_start = .);
+ *(.tdata .tdata.* .gnu.linkonce.td.*)
+ . = ALIGN(8);
+ PROVIDE(__tdata_end = .);
+ } > ram AT> flash
+ PROVIDE(__tdata_source = LOADADDR(.tdata));
+ PROVIDE(__data_end = __tdata_end);
+ PROVIDE(__tdata_source_end = LOADADDR(.tdata) + SIZEOF(.tdata));
+ PROVIDE(__data_source_end =
+ SIZEOF(.tdata) ? __tdata_source_end : LOADADDR(.data) + SIZEOF(.data));
+ PROVIDE(__edata = __data_end);
+ PROVIDE(_edata = __data_end);
+ PROVIDE(edata = __data_end);
+ PROVIDE(__data_size = __data_end - __data_start);
+ PROVIDE(__tdata_size = __tdata_end - __tdata_start);
+ PROVIDE(__data_source_size = __data_source_end - __data_source);
+
+ .tbss (NOLOAD) : ALIGN(8) {
+ PROVIDE(__tbss_start = .);
+ *(.tbss .tbss.* .gnu.linkonce.tb.*)
+ *(.tcommon)
+ . = ALIGN(8);
+ PROVIDE(__tbss_end = .);
+ } > ram
+
+ .bss (NOLOAD) : ALIGN(8) {
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(8);
+ PROVIDE(__bss_end = .);
+ } > ram
+ PROVIDE(__bss_start = SIZEOF(.tbss) ? ADDR(.tbss) : ADDR(.bss));
+ PROVIDE(__bss_size = __bss_end - __bss_start);
+
+ .noinit (NOLOAD) : ALIGN(8) {
+ *(.noinit .noinit.*)
+ } > ram
+
+ PROVIDE(__end = .);
+ PROVIDE(_end = .);
+ PROVIDE(end = .);
+
+ PROVIDE(__heap_start = .);
+ PROVIDE(__heap_end = __stack - (DEFINED(__stack_size) ? __stack_size : 0));
+ PROVIDE(__llvm_libc_heap_limit = __heap_end);
+
+ .heap (NOLOAD) : {
+ . += (DEFINED(__heap_size_min) ? __heap_size_min : 0);
+ } > ram
+
+ .stack (NOLOAD) : {
+ . += (DEFINED(__stack_size) ? __stack_size : 0);
+ } > ram
+
+ ASSERT(__data_size == __data_source_size,
+ "ERROR: .data/.tdata RAM size does not match flash load size")
+
+ /DISCARD/ : {
+ *(.note .note.*)
+ *(.comment)
+ }
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/207361
More information about the libc-commits
mailing list