[libc-commits] [libc] [llvm] [libc] Add bare-metal Hexagon port (PR #217528)

via libc-commits libc-commits at lists.llvm.org
Fri Sep 25 07:08:05 PDT 2026


https://github.com/quic-k updated https://github.com/llvm/llvm-project/pull/217528

>From 31945e6338bcea47af3b4290175550304887f6ba Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Mon, 17 Aug 2026 19:46:48 +0530
Subject: [PATCH 1/6] [libc] Add bare-metal Hexagon port

Add support for llvm-libc on Hexagon bare-metal targets.

Recognize the hexagon architecture in CMake.
Add the Hexagon bare-metal config: entrypoints.txt and headers.txt, reusing
the common bare-metal config.json.
Add Hexagon startup code (crt1) with semihosting trap handling.
Add a Hexagon FEnvImpl and select it in FEnvImpl.h; define fenv_t for Hexagon.
Add Hexagon setjmp/longjmp, saving the callee-saved registers r16-r27 as
register pairs along with sp, fp and lr, and define __jmp_buf accordingly.

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 .../cmake/modules/LLVMLibCArchitectures.cmake |   4 +
 libc/config/baremetal/hexagon/entrypoints.txt | 998 ++++++++++++++++++
 libc/config/baremetal/hexagon/headers.txt     |  32 +
 libc/include/llvm-libc-types/__jmp_buf.h      |   6 +
 libc/include/llvm-libc-types/fenv_t.h         |   2 +
 libc/src/__support/FPUtil/FEnvImpl.h          |   2 +
 libc/src/__support/FPUtil/hexagon/FEnvImpl.h  | 215 ++++
 .../macros/properties/architectures.h         |   4 +
 libc/src/__support/memory_size.h              |   7 +-
 libc/src/setjmp/hexagon/CMakeLists.txt        |  19 +
 libc/src/setjmp/hexagon/longjmp.cpp           |  39 +
 libc/src/setjmp/hexagon/setjmp.cpp            |  40 +
 libc/startup/baremetal/hexagon/CMakeLists.txt |  12 +
 libc/startup/baremetal/hexagon/start.cpp      |  99 ++
 libc/test/UnitTest/CMakeLists.txt             |  30 +-
 .../UnitTest/HexagonBaremetalTestUtils.cpp    | 120 +++
 libc/test/UnitTest/LibcTest.cpp               |   8 +-
 libc/test/UnitTest/LibcTest.h                 |   2 +
 libc/test/UnitTest/llvm-libc-baremetal.ld     |   3 +
 libc/test/src/math/smoke/exp10m1f_test.cpp    |  11 +-
 20 files changed, 1636 insertions(+), 17 deletions(-)
 create mode 100644 libc/config/baremetal/hexagon/entrypoints.txt
 create mode 100644 libc/config/baremetal/hexagon/headers.txt
 create mode 100644 libc/src/__support/FPUtil/hexagon/FEnvImpl.h
 create mode 100644 libc/src/setjmp/hexagon/CMakeLists.txt
 create mode 100644 libc/src/setjmp/hexagon/longjmp.cpp
 create mode 100644 libc/src/setjmp/hexagon/setjmp.cpp
 create mode 100644 libc/startup/baremetal/hexagon/CMakeLists.txt
 create mode 100644 libc/startup/baremetal/hexagon/start.cpp
 create mode 100644 libc/test/UnitTest/HexagonBaremetalTestUtils.cpp

diff --git a/libc/cmake/modules/LLVMLibCArchitectures.cmake b/libc/cmake/modules/LLVMLibCArchitectures.cmake
index 436f863b445e5..e65ddff98269e 100644
--- a/libc/cmake/modules/LLVMLibCArchitectures.cmake
+++ b/libc/cmake/modules/LLVMLibCArchitectures.cmake
@@ -53,6 +53,8 @@ function(get_arch_and_system_from_triple triple arch_var sys_var)
     set(target_arch "nvptx")
   elseif(target_arch MATCHES "^spirv")
     set(target_arch "spirv")
+  elseif(target_arch MATCHES "^hexagon")
+    set(target_arch "hexagon")
   else()
     return()
   endif()
@@ -190,6 +192,8 @@ elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "spirv")
   set(LIBC_TARGET_ARCHITECTURE_IS_SPIRV TRUE)
 elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "power")
   set(LIBC_TARGET_ARCHITECTURE_IS_POWERPC TRUE)
+elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "hexagon")
+  set(LIBC_TARGET_ARCHITECTURE_IS_HEXAGON TRUE)
 else()
   message(FATAL_ERROR
     "Unsupported libc target architecture ${LIBC_TARGET_ARCHITECTURE}")
diff --git a/libc/config/baremetal/hexagon/entrypoints.txt b/libc/config/baremetal/hexagon/entrypoints.txt
new file mode 100644
index 0000000000000..c6244a2f4d728
--- /dev/null
+++ b/libc/config/baremetal/hexagon/entrypoints.txt
@@ -0,0 +1,998 @@
+set(TARGET_LIBC_ENTRYPOINTS
+    # assert.h entrypoints
+    libc.src.assert.__assert_fail
+
+    # compiler entrypoints (no corresponding header)
+    libc.src.compiler.__stack_chk_fail
+
+    # ctype.h entrypoints
+    libc.src.ctype.isalnum
+    libc.src.ctype.isalpha
+    libc.src.ctype.isascii
+    libc.src.ctype.isblank
+    libc.src.ctype.iscntrl
+    libc.src.ctype.isdigit
+    libc.src.ctype.isgraph
+    libc.src.ctype.islower
+    libc.src.ctype.isprint
+    libc.src.ctype.ispunct
+    libc.src.ctype.isspace
+    libc.src.ctype.isupper
+    libc.src.ctype.isxdigit
+    libc.src.ctype.toascii
+    libc.src.ctype.tolower
+    libc.src.ctype.toupper
+
+    # ctype.h entrypoints
+    libc.src.ctype.isalnum_l
+    libc.src.ctype.isalpha_l
+    libc.src.ctype.isblank_l
+    libc.src.ctype.iscntrl_l
+    libc.src.ctype.isdigit_l
+    libc.src.ctype.isgraph_l
+    libc.src.ctype.islower_l
+    libc.src.ctype.isprint_l
+    libc.src.ctype.ispunct_l
+    libc.src.ctype.isspace_l
+    libc.src.ctype.isupper_l
+    libc.src.ctype.isxdigit_l
+    libc.src.ctype.tolower_l
+    libc.src.ctype.toupper_l
+
+    # errno.h entrypoints
+    libc.src.errno.errno
+
+    # locale.h entrypoints
+    libc.src.locale.localeconv
+    libc.src.locale.duplocale
+    libc.src.locale.freelocale
+    libc.src.locale.localeconv
+    libc.src.locale.newlocale
+    libc.src.locale.setlocale
+    libc.src.locale.uselocale
+
+    # setjmp.h entrypoints
+    libc.src.setjmp.longjmp
+    libc.src.setjmp.setjmp
+
+    # string.h entrypoints
+    libc.src.string.memccpy
+    libc.src.string.memchr
+    libc.src.string.memcmp
+    libc.src.string.memcpy
+    libc.src.string.memmem
+    libc.src.string.memmove
+    libc.src.string.mempcpy
+    libc.src.string.memrchr
+    libc.src.string.memset
+    libc.src.string.memset_explicit
+    libc.src.string.stpcpy
+    libc.src.string.stpncpy
+    libc.src.string.strcasestr
+    libc.src.string.strcat
+    libc.src.string.strchr
+    libc.src.string.strchrnul
+    libc.src.string.strcmp
+    libc.src.string.strcoll
+    libc.src.string.strcpy
+    libc.src.string.strcspn
+    libc.src.string.strdup
+    libc.src.string.strerror
+    libc.src.string.strerror_r
+    libc.src.string.strlcat
+    libc.src.string.strlcpy
+    libc.src.string.strlen
+    libc.src.string.strncat
+    libc.src.string.strncmp
+    libc.src.string.strncpy
+    libc.src.string.strndup
+    libc.src.string.strnlen
+    libc.src.string.strnlen_s
+    libc.src.string.strpbrk
+    libc.src.string.strrchr
+    libc.src.string.strsep
+    libc.src.string.strspn
+    libc.src.string.strstr
+    libc.src.string.strtok
+    libc.src.string.strtok_r
+    libc.src.string.strxfrm
+
+    # string.h entrypoints
+    libc.src.string.strcoll_l
+    libc.src.string.strxfrm_l
+
+    # strings.h entrypoints
+    libc.src.strings.bcmp
+    libc.src.strings.bcopy
+    libc.src.strings.bzero
+    libc.src.strings.ffs
+    libc.src.strings.ffsl
+    libc.src.strings.ffsll
+    libc.src.strings.index
+    libc.src.strings.rindex
+    libc.src.strings.strcasecmp
+    libc.src.strings.strncasecmp
+
+    # strings.h entrypoints
+    libc.src.strings.strcasecmp_l
+    libc.src.strings.strncasecmp_l
+
+    # inttypes.h entrypoints
+    libc.src.inttypes.imaxabs
+    libc.src.inttypes.imaxdiv
+    libc.src.inttypes.strtoimax
+    libc.src.inttypes.strtoumax
+
+    # stdio.h entrypoints
+    libc.src.stdio.asprintf
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.fgetc
+    libc.src.stdio.fgets
+    libc.src.stdio.fprintf
+    libc.src.stdio.fputc
+    libc.src.stdio.fputs
+    libc.src.stdio.fread
+    libc.src.stdio.fscanf
+    libc.src.stdio.fwrite
+    libc.src.stdio.getchar
+    libc.src.stdio.printf
+    libc.src.stdio.putc
+    libc.src.stdio.putchar
+    libc.src.stdio.puts
+    libc.src.stdio.remove
+    libc.src.stdio.scanf
+    libc.src.stdio.snprintf
+    libc.src.stdio.sprintf
+    libc.src.stdio.sscanf
+    libc.src.stdio.stderr
+    libc.src.stdio.stdin
+    libc.src.stdio.stdout
+    libc.src.stdio.vasprintf
+    libc.src.stdio.vfprintf
+    libc.src.stdio.vfscanf
+    libc.src.stdio.vprintf
+    libc.src.stdio.vscanf
+    libc.src.stdio.vsnprintf
+    libc.src.stdio.vsprintf
+    libc.src.stdio.vsscanf
+
+    # stdbit.h entrypoints
+    libc.src.stdbit.stdc_bit_ceil_uc
+    libc.src.stdbit.stdc_bit_ceil_ui
+    libc.src.stdbit.stdc_bit_ceil_ul
+    libc.src.stdbit.stdc_bit_ceil_ull
+    libc.src.stdbit.stdc_bit_ceil_us
+    libc.src.stdbit.stdc_bit_floor_uc
+    libc.src.stdbit.stdc_bit_floor_ui
+    libc.src.stdbit.stdc_bit_floor_ul
+    libc.src.stdbit.stdc_bit_floor_ull
+    libc.src.stdbit.stdc_bit_floor_us
+    libc.src.stdbit.stdc_bit_width_uc
+    libc.src.stdbit.stdc_bit_width_ui
+    libc.src.stdbit.stdc_bit_width_ul
+    libc.src.stdbit.stdc_bit_width_ull
+    libc.src.stdbit.stdc_bit_width_us
+    libc.src.stdbit.stdc_count_ones_uc
+    libc.src.stdbit.stdc_count_ones_ui
+    libc.src.stdbit.stdc_count_ones_ul
+    libc.src.stdbit.stdc_count_ones_ull
+    libc.src.stdbit.stdc_count_ones_us
+    libc.src.stdbit.stdc_count_zeros_uc
+    libc.src.stdbit.stdc_count_zeros_ui
+    libc.src.stdbit.stdc_count_zeros_ul
+    libc.src.stdbit.stdc_count_zeros_ull
+    libc.src.stdbit.stdc_count_zeros_us
+    libc.src.stdbit.stdc_first_leading_one_uc
+    libc.src.stdbit.stdc_first_leading_one_ui
+    libc.src.stdbit.stdc_first_leading_one_ul
+    libc.src.stdbit.stdc_first_leading_one_ull
+    libc.src.stdbit.stdc_first_leading_one_us
+    libc.src.stdbit.stdc_first_leading_zero_uc
+    libc.src.stdbit.stdc_first_leading_zero_ui
+    libc.src.stdbit.stdc_first_leading_zero_ul
+    libc.src.stdbit.stdc_first_leading_zero_ull
+    libc.src.stdbit.stdc_first_leading_zero_us
+    libc.src.stdbit.stdc_first_trailing_one_uc
+    libc.src.stdbit.stdc_first_trailing_one_ui
+    libc.src.stdbit.stdc_first_trailing_one_ul
+    libc.src.stdbit.stdc_first_trailing_one_ull
+    libc.src.stdbit.stdc_first_trailing_one_us
+    libc.src.stdbit.stdc_first_trailing_zero_uc
+    libc.src.stdbit.stdc_first_trailing_zero_ui
+    libc.src.stdbit.stdc_first_trailing_zero_ul
+    libc.src.stdbit.stdc_first_trailing_zero_ull
+    libc.src.stdbit.stdc_first_trailing_zero_us
+    libc.src.stdbit.stdc_has_single_bit_uc
+    libc.src.stdbit.stdc_has_single_bit_ui
+    libc.src.stdbit.stdc_has_single_bit_ul
+    libc.src.stdbit.stdc_has_single_bit_ull
+    libc.src.stdbit.stdc_has_single_bit_us
+    libc.src.stdbit.stdc_leading_ones_uc
+    libc.src.stdbit.stdc_leading_ones_ui
+    libc.src.stdbit.stdc_leading_ones_ul
+    libc.src.stdbit.stdc_leading_ones_ull
+    libc.src.stdbit.stdc_leading_ones_us
+    libc.src.stdbit.stdc_leading_zeros_uc
+    libc.src.stdbit.stdc_leading_zeros_ui
+    libc.src.stdbit.stdc_leading_zeros_ul
+    libc.src.stdbit.stdc_leading_zeros_ull
+    libc.src.stdbit.stdc_leading_zeros_us
+    libc.src.stdbit.stdc_trailing_ones_uc
+    libc.src.stdbit.stdc_trailing_ones_ui
+    libc.src.stdbit.stdc_trailing_ones_ul
+    libc.src.stdbit.stdc_trailing_ones_ull
+    libc.src.stdbit.stdc_trailing_ones_us
+    libc.src.stdbit.stdc_trailing_zeros_uc
+    libc.src.stdbit.stdc_trailing_zeros_ui
+    libc.src.stdbit.stdc_trailing_zeros_ul
+    libc.src.stdbit.stdc_trailing_zeros_ull
+    libc.src.stdbit.stdc_trailing_zeros_us
+
+    # stdlib.h entrypoints
+    libc.src.stdlib._Exit
+    libc.src.stdlib.a64l
+    libc.src.stdlib.abort
+    libc.src.stdlib.abs
+    libc.src.stdlib.aligned_alloc
+    libc.src.stdlib.atexit
+    libc.src.stdlib.atof
+    libc.src.stdlib.atoi
+    libc.src.stdlib.atol
+    libc.src.stdlib.atoll
+    libc.src.stdlib.bsearch
+    libc.src.stdlib.calloc
+    libc.src.stdlib.div
+    libc.src.stdlib.exit
+    libc.src.stdlib.free
+    libc.src.stdlib.l64a
+    libc.src.stdlib.labs
+    libc.src.stdlib.ldiv
+    libc.src.stdlib.llabs
+    libc.src.stdlib.lldiv
+    libc.src.stdlib.malloc
+    libc.src.stdlib.malloc_usable_size
+    libc.src.stdlib.memalignment
+    libc.src.stdlib.qsort
+    libc.src.stdlib.qsort_r
+    libc.src.stdlib.rand
+    libc.src.stdlib.realloc
+    libc.src.stdlib.srand
+    # libc.src.stdlib.strfromd
+    # libc.src.stdlib.strfromf
+    # libc.src.stdlib.strfroml
+    libc.src.stdlib.strtod
+    libc.src.stdlib.strtof
+    libc.src.stdlib.strtol
+    libc.src.stdlib.strtold
+    libc.src.stdlib.strtoll
+    libc.src.stdlib.strtoul
+    libc.src.stdlib.strtoull
+
+    # stdlib.h entrypoints
+    libc.src.stdlib.strtod_l
+    libc.src.stdlib.strtof_l
+    libc.src.stdlib.strtol_l
+    libc.src.stdlib.strtold_l
+    libc.src.stdlib.strtoll_l
+    libc.src.stdlib.strtoul_l
+    libc.src.stdlib.strtoull_l
+
+    # time.h entrypoints
+    libc.src.time.asctime
+    libc.src.time.asctime_r
+    libc.src.time.ctime
+    libc.src.time.ctime_r
+    libc.src.time.difftime
+    libc.src.time.gmtime
+    libc.src.time.gmtime_r
+    libc.src.time.localtime
+    libc.src.time.localtime_r
+    libc.src.time.mktime
+    libc.src.time.strftime
+    libc.src.time.strftime_l
+    libc.src.time.timespec_get
+
+    # wchar.h entrypoints
+    libc.src.wchar.btowc
+    libc.src.wchar.wcslen
+    libc.src.wchar.wctob
+
+    # wctype.h entrypoints
+    libc.src.wctype.iswalpha
+    libc.src.wctype.iswgraph
+    libc.src.wctype.iswcntrl
+    libc.src.wctype.iswdigit
+    libc.src.wctype.iswupper
+    libc.src.wctype.iswalnum
+    libc.src.wctype.iswlower
+    libc.src.wctype.iswspace
+    libc.src.wctype.iswblank
+    libc.src.wctype.iswxdigit
+    libc.src.wctype.iswpunct
+    libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
+
+    # internal entrypoints
+    libc.startup.baremetal.init
+    libc.startup.baremetal.fini
+)
+
+set(TARGET_LIBM_ENTRYPOINTS
+    # complex.h entrypoints
+    libc.src.complex.cabs
+    libc.src.complex.cabsf
+    libc.src.complex.carg
+    libc.src.complex.cargf
+    libc.src.complex.creal
+    libc.src.complex.crealf
+    libc.src.complex.creall
+    libc.src.complex.cimag
+    libc.src.complex.cimagf
+    libc.src.complex.cimagl
+    libc.src.complex.conj
+    libc.src.complex.conjf
+    libc.src.complex.conjl
+    libc.src.complex.cproj
+    libc.src.complex.cprojf
+    libc.src.complex.cprojl
+
+    # fenv.h entrypoints
+    libc.src.fenv.feclearexcept
+    libc.src.fenv.fedisableexcept
+    libc.src.fenv.feenableexcept
+    libc.src.fenv.fegetenv
+    libc.src.fenv.fegetexcept
+    libc.src.fenv.fegetexceptflag
+    libc.src.fenv.fegetround
+    libc.src.fenv.feholdexcept
+    libc.src.fenv.feraiseexcept
+    libc.src.fenv.fesetenv
+    libc.src.fenv.fesetexcept
+    libc.src.fenv.fesetexceptflag
+    libc.src.fenv.fesetround
+    libc.src.fenv.fetestexcept
+    libc.src.fenv.fetestexceptflag
+    libc.src.fenv.feupdateenv
+
+    # math.h entrypoints
+    libc.src.math.acosf
+    libc.src.math.acoshf
+    libc.src.math.acospif
+    libc.src.math.asinf
+    libc.src.math.asinhf
+    libc.src.math.asinpi
+    libc.src.math.asinpif
+    libc.src.math.atan2
+    libc.src.math.atan2f
+    # libc.src.math.atan2l
+    libc.src.math.atan
+    libc.src.math.atanf
+    libc.src.math.atanhf
+    libc.src.math.canonicalize
+    libc.src.math.canonicalizef
+    libc.src.math.canonicalizel
+    libc.src.math.cbrt
+    libc.src.math.cbrtf
+    libc.src.math.cbrtbf16
+    libc.src.math.ceil
+    libc.src.math.ceilf
+    libc.src.math.ceill
+    libc.src.math.copysign
+    libc.src.math.copysignf
+    libc.src.math.copysignl
+    libc.src.math.cos
+    libc.src.math.cosf
+    libc.src.math.coshf
+    libc.src.math.cospif
+    libc.src.math.dfmal
+    libc.src.math.dmull
+    libc.src.math.dsqrtl
+    libc.src.math.daddl
+    libc.src.math.ddivl
+    libc.src.math.dsubl
+    libc.src.math.erff
+    libc.src.math.exp
+    libc.src.math.exp10
+    libc.src.math.exp10f
+    libc.src.math.exp10m1f
+    libc.src.math.exp2
+    libc.src.math.exp2f
+    libc.src.math.exp2m1f
+    libc.src.math.expf
+    libc.src.math.expm1
+    libc.src.math.expm1f
+    libc.src.math.fabs
+    libc.src.math.fabsf
+    libc.src.math.fabsl
+    libc.src.math.fadd
+    libc.src.math.faddl
+    libc.src.math.fadd
+    libc.src.math.fdim
+    libc.src.math.fdimf
+    libc.src.math.fdiml
+    libc.src.math.fdiv
+    libc.src.math.fdivl
+    libc.src.math.ffma
+    libc.src.math.ffmal
+    libc.src.math.floor
+    libc.src.math.floorf
+    libc.src.math.floorl
+    libc.src.math.fma
+    libc.src.math.fmabf16
+    libc.src.math.fmaf
+    libc.src.math.fmax
+    libc.src.math.fmaxf
+    libc.src.math.fmaximum
+    libc.src.math.fmaximum_mag
+    libc.src.math.fmaximum_mag_num
+    libc.src.math.fmaximum_mag_numf
+    libc.src.math.fmaximum_mag_numl
+    libc.src.math.fmaximum_magf
+    libc.src.math.fmaximum_magl
+    libc.src.math.fmaximum_num
+    libc.src.math.fmaximum_numf
+    libc.src.math.fmaximum_numl
+    libc.src.math.fmaximumf
+    libc.src.math.fmaximuml
+    libc.src.math.fmaxl
+    libc.src.math.fmin
+    libc.src.math.fminf
+    libc.src.math.fminimum
+    libc.src.math.fminimum_mag
+    libc.src.math.fminimum_mag_num
+    libc.src.math.fminimum_mag_numf
+    libc.src.math.fminimum_mag_numl
+    libc.src.math.fminimum_magf
+    libc.src.math.fminimum_magl
+    libc.src.math.fminimum_num
+    libc.src.math.fminimum_numf
+    libc.src.math.fminimum_numl
+    libc.src.math.fminimumf
+    libc.src.math.fminimuml
+    libc.src.math.fminl
+    libc.src.math.fmod
+    libc.src.math.fmodf
+    # libc.src.math.fmodl
+    libc.src.math.fmul
+    libc.src.math.fmull
+    libc.src.math.frexp
+    libc.src.math.frexpf
+    libc.src.math.frexpl
+    libc.src.math.fromfp
+    libc.src.math.fromfpf
+    libc.src.math.fromfpl
+    libc.src.math.fromfpx
+    libc.src.math.fromfpxf
+    libc.src.math.fromfpxl
+    libc.src.math.fsqrt
+    libc.src.math.fsqrtl
+    libc.src.math.fsub
+    libc.src.math.fsubl
+    libc.src.math.getpayload
+    libc.src.math.getpayloadf
+    libc.src.math.getpayloadl
+    libc.src.math.hypot
+    libc.src.math.hypotf
+    libc.src.math.ilogb
+    libc.src.math.ilogbf
+    libc.src.math.ilogbl
+    libc.src.math.iscanonical
+    libc.src.math.iscanonicalf
+    libc.src.math.iscanonicall
+    libc.src.math.isnan
+    libc.src.math.isnanf
+    libc.src.math.isnanl
+    libc.src.math.issignaling
+    libc.src.math.issignalingf
+    libc.src.math.issignalingl
+    libc.src.math.ldexp
+    libc.src.math.ldexpf
+    libc.src.math.ldexpl
+    libc.src.math.llogb
+    libc.src.math.llogbf
+    libc.src.math.llogbl
+    libc.src.math.llrint
+    libc.src.math.llrintf
+    libc.src.math.llrintl
+    libc.src.math.llround
+    libc.src.math.llroundf
+    libc.src.math.llroundl
+    libc.src.math.log
+    libc.src.math.log10
+    libc.src.math.log10f
+    libc.src.math.log1p
+    libc.src.math.log1pf
+    libc.src.math.log2
+    libc.src.math.log2f
+    libc.src.math.logb
+    libc.src.math.logbf
+    libc.src.math.logbl
+    libc.src.math.logf
+    libc.src.math.lrint
+    libc.src.math.lrintf
+    libc.src.math.lrintl
+    libc.src.math.lround
+    libc.src.math.lroundf
+    libc.src.math.lroundl
+    libc.src.math.modf
+    libc.src.math.modff
+    libc.src.math.modfl
+    libc.src.math.nan
+    libc.src.math.nanf
+    libc.src.math.nanl
+    libc.src.math.nearbyint
+    libc.src.math.nearbyintf
+    libc.src.math.nearbyintl
+    libc.src.math.nextafter
+    libc.src.math.nextafterf
+    libc.src.math.nextafterl
+    libc.src.math.nextdown
+    libc.src.math.nextdownf
+    libc.src.math.nextdownl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
+    libc.src.math.nextup
+    libc.src.math.nextupf
+    libc.src.math.nextupl
+    libc.src.math.pow
+    libc.src.math.powf
+    libc.src.math.remainder
+    libc.src.math.remainderf
+    libc.src.math.remainderl
+    libc.src.math.remquo
+    libc.src.math.remquof
+    libc.src.math.remquol
+    libc.src.math.rint
+    libc.src.math.rintf
+    libc.src.math.rintl
+    libc.src.math.round
+    libc.src.math.roundeven
+    libc.src.math.roundevenf
+    libc.src.math.roundevenl
+    libc.src.math.roundf
+    libc.src.math.roundl
+    libc.src.math.scalbln
+    libc.src.math.scalblnf
+    libc.src.math.scalblnl
+    libc.src.math.scalbn
+    libc.src.math.scalbnf
+    libc.src.math.scalbnl
+    libc.src.math.setpayload
+    libc.src.math.setpayloadf
+    libc.src.math.setpayloadl
+    libc.src.math.setpayloadsig
+    libc.src.math.setpayloadsigf
+    libc.src.math.setpayloadsigl
+    libc.src.math.sin
+    libc.src.math.sincos
+    libc.src.math.sincosf
+    libc.src.math.sinf
+    libc.src.math.sinhf
+    libc.src.math.sinpif
+    libc.src.math.sqrt
+    libc.src.math.sqrtf
+    libc.src.math.sqrtl
+    libc.src.math.tan
+    libc.src.math.tanf
+    libc.src.math.tanhf
+    libc.src.math.totalorder
+    libc.src.math.totalorderf
+    libc.src.math.totalorderl
+    libc.src.math.totalordermag
+    libc.src.math.totalordermagf
+    libc.src.math.totalordermagl
+    libc.src.math.trunc
+    libc.src.math.truncf
+    libc.src.math.truncl
+    libc.src.math.ufromfp
+    libc.src.math.ufromfpf
+    libc.src.math.ufromfpl
+    libc.src.math.ufromfpx
+    libc.src.math.ufromfpxf
+    libc.src.math.ufromfpxl
+)
+
+if(LIBC_TYPES_HAS_CFLOAT16)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # complex.h C23 _Complex _Float16 entrypoints
+    libc.src.complex.crealf16
+    libc.src.complex.cimagf16
+    libc.src.complex.conjf16
+    libc.src.complex.cprojf16
+  )
+endif()
+
+if(LIBC_TYPES_HAS_FLOAT16)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # math.h C23 _Float16 entrypoints
+    libc.src.math.acosf16
+    libc.src.math.acoshf16
+    libc.src.math.asinf16
+    libc.src.math.asinhf16
+    libc.src.math.atan2f16
+    libc.src.math.canonicalizef16
+    libc.src.math.cbrtf16
+    libc.src.math.ceilf16
+    libc.src.math.copysignf16
+    libc.src.math.cosf16
+    libc.src.math.coshf16
+    libc.src.math.cospif16
+    libc.src.math.exp10f16
+    libc.src.math.exp10m1f16
+    libc.src.math.exp2f16
+    libc.src.math.exp2m1f16
+    libc.src.math.expf16
+    libc.src.math.expm1f16
+    libc.src.math.f16add
+    libc.src.math.f16addf
+    libc.src.math.f16addl
+    libc.src.math.f16div
+    libc.src.math.f16divf
+    libc.src.math.f16divl
+    libc.src.math.f16fma
+    libc.src.math.f16fmaf
+    libc.src.math.f16fmal
+    libc.src.math.f16mul
+    libc.src.math.f16mulf
+    libc.src.math.f16mull
+    libc.src.math.f16sqrt
+    libc.src.math.f16sqrtf
+    libc.src.math.f16sqrtl
+    libc.src.math.f16sub
+    libc.src.math.f16subf
+    libc.src.math.f16subl
+    libc.src.math.fabsf16
+    libc.src.math.fdimf16
+    libc.src.math.floorf16
+    libc.src.math.fmaf16
+    libc.src.math.fmaxf16
+    libc.src.math.fmaximum_mag_numf16
+    libc.src.math.fmaximum_magf16
+    libc.src.math.fmaximum_numf16
+    libc.src.math.fmaximumf16
+    libc.src.math.fminf16
+    libc.src.math.fminimum_mag_numf16
+    libc.src.math.fminimum_magf16
+    libc.src.math.fminimum_numf16
+    libc.src.math.fminimumf16
+    libc.src.math.fmodf16
+    libc.src.math.frexpf16
+    libc.src.math.fromfpf16
+    libc.src.math.fromfpxf16
+    libc.src.math.getpayloadf16
+    libc.src.math.hypotf16
+    libc.src.math.ilogbf16
+    libc.src.math.iscanonicalf16
+    libc.src.math.isnanf16
+    libc.src.math.issignalingf16
+    libc.src.math.ldexpf16
+    libc.src.math.lgammaf16
+    libc.src.math.llogbf16
+    libc.src.math.llrintf16
+    libc.src.math.llroundf16
+    libc.src.math.log10f16
+    libc.src.math.log10p1f16
+    libc.src.math.log2f16
+    libc.src.math.log2p1f16
+    libc.src.math.logbf16
+    libc.src.math.logf16
+    libc.src.math.lrintf16
+    libc.src.math.lroundf16
+    libc.src.math.modff16
+    libc.src.math.nanf16
+    libc.src.math.nearbyintf16
+    libc.src.math.nextafterf16
+    libc.src.math.nextdownf16
+    libc.src.math.nexttowardf16
+    libc.src.math.nextupf16
+    libc.src.math.remainderf16
+    libc.src.math.remquof16
+    libc.src.math.rintf16
+    libc.src.math.roundevenf16
+    libc.src.math.roundf16
+    libc.src.math.scalblnf16
+    libc.src.math.scalbnf16
+    libc.src.math.setpayloadf16
+    libc.src.math.setpayloadsigf16
+    libc.src.math.sinf16
+    libc.src.math.sinhf16
+    libc.src.math.sinpif16
+    libc.src.math.sqrtf16
+    libc.src.math.tanf16
+    libc.src.math.tanhf16
+    libc.src.math.tanpif16
+    libc.src.math.totalorderf16
+    libc.src.math.totalordermagf16
+    libc.src.math.truncf16
+    libc.src.math.ufromfpf16
+    libc.src.math.ufromfpxf16
+  )
+
+  if(LIBC_TYPES_HAS_FLOAT128)
+    list(APPEND TARGET_LIBM_ENTRYPOINTS
+      # math.h C23 mixed _Float16 and _Float128 entrypoints
+      libc.src.math.f16addf128
+      libc.src.math.f16divf128
+      libc.src.math.f16fmaf128
+      libc.src.math.f16mulf128
+      libc.src.math.f16sqrtf128
+      libc.src.math.f16subf128
+    )
+  endif()
+endif()
+
+if(LIBC_TYPES_HAS_CFLOAT128)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # complex.h C23 _Complex _Float128 entrypoints
+    libc.src.complex.crealf128
+    libc.src.complex.cimagf128
+    libc.src.complex.conjf128
+    libc.src.complex.cprojf128
+  )
+endif()
+
+if(LIBC_TYPES_HAS_FLOAT128)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # math.h C23 _Float128 entrypoints
+    libc.src.math.atan2f128
+    libc.src.math.canonicalizef128
+    libc.src.math.ceilf128
+    libc.src.math.copysignf128
+    libc.src.math.daddf128
+    libc.src.math.ddivf128
+    libc.src.math.dfmaf128
+    libc.src.math.dmulf128
+    libc.src.math.dsqrtf128
+    libc.src.math.dsubf128
+    libc.src.math.fabsf128
+    libc.src.math.faddf128
+    libc.src.math.fdimf128
+    libc.src.math.fdivf128
+    libc.src.math.ffmaf128
+    libc.src.math.floorf128
+    libc.src.math.fmaxf128
+    libc.src.math.fmaximum_mag_numf128
+    libc.src.math.fmaximum_magf128
+    libc.src.math.fmaximum_numf128
+    libc.src.math.fmaximumf128
+    libc.src.math.fminf128
+    libc.src.math.fminimum_mag_numf128
+    libc.src.math.fminimum_magf128
+    libc.src.math.fminimum_numf128
+    libc.src.math.fminimumf128
+    # libc.src.math.fmodf128
+    libc.src.math.fmulf128
+    libc.src.math.frexpf128
+    libc.src.math.fromfpf128
+    libc.src.math.fromfpxf128
+    libc.src.math.fsqrtf128
+    libc.src.math.fsubf128
+    libc.src.math.getpayloadf128
+    libc.src.math.ilogbf128
+    libc.src.math.iscanonicalf128
+    libc.src.math.isnanf128
+    libc.src.math.issignalingf128
+    libc.src.math.ldexpf128
+    libc.src.math.llogbf128
+    libc.src.math.llrintf128
+    libc.src.math.llroundf128
+    libc.src.math.logbf128
+    libc.src.math.lrintf128
+    libc.src.math.lroundf128
+    libc.src.math.modff128
+    libc.src.math.nanf128
+    libc.src.math.nearbyintf128
+    libc.src.math.nextafterf128
+    libc.src.math.nextdownf128
+    libc.src.math.nextupf128
+    libc.src.math.remainderf128
+    libc.src.math.remquof128
+    libc.src.math.rintf128
+    libc.src.math.roundevenf128
+    libc.src.math.roundf128
+    libc.src.math.scalblnf128
+    libc.src.math.scalbnf128
+    libc.src.math.setpayloadf128
+    libc.src.math.setpayloadsigf128
+    libc.src.math.sqrtf128
+    libc.src.math.totalorderf128
+    libc.src.math.totalordermagf128
+    libc.src.math.truncf128
+    libc.src.math.ufromfpf128
+    libc.src.math.ufromfpxf128
+  )
+endif()
+
+list(APPEND TARGET_LIBM_ENTRYPOINTS
+  # bfloat16 entrypoints
+  libc.src.math.atanbf16
+  libc.src.math.acosbf16
+  libc.src.math.asinbf16
+  libc.src.math.bf16add
+  libc.src.math.bf16addf
+  libc.src.math.bf16addl
+  libc.src.math.bf16div
+  libc.src.math.bf16divf
+  libc.src.math.bf16divl
+  libc.src.math.bf16fma
+  libc.src.math.bf16fmaf
+  libc.src.math.bf16fmal
+  libc.src.math.bf16mul
+  libc.src.math.bf16mulf
+  libc.src.math.bf16mull
+  libc.src.math.bf16sub
+  libc.src.math.bf16subf
+  libc.src.math.bf16subl
+  libc.src.math.canonicalizebf16
+  libc.src.math.ceilbf16
+  libc.src.math.copysignbf16
+  libc.src.math.expbf16
+  libc.src.math.fabsbf16
+  libc.src.math.fdimbf16
+  libc.src.math.floorbf16
+  libc.src.math.fmaxbf16
+  libc.src.math.fmaximumbf16
+  libc.src.math.fmaximum_magbf16
+  libc.src.math.fmaximum_mag_numbf16
+  libc.src.math.fmaximum_numbf16
+  libc.src.math.fminbf16
+  libc.src.math.fminimumbf16
+  libc.src.math.fminimum_magbf16
+  libc.src.math.fminimum_mag_numbf16
+  libc.src.math.fminimum_numbf16
+  libc.src.math.fmodbf16
+  libc.src.math.frexpbf16
+  libc.src.math.fromfpbf16
+  libc.src.math.fromfpxbf16
+  libc.src.math.getpayloadbf16
+  libc.src.math.hypotbf16
+  libc.src.math.ilogbbf16
+  libc.src.math.iscanonicalbf16
+  libc.src.math.issignalingbf16
+  libc.src.math.ldexpbf16
+  libc.src.math.llogbbf16
+  libc.src.math.llrintbf16
+  libc.src.math.llroundbf16
+  libc.src.math.log_bf16
+  libc.src.math.logbbf16
+  libc.src.math.lrintbf16
+  libc.src.math.lroundbf16
+  libc.src.math.modfbf16
+  libc.src.math.nanbf16
+  libc.src.math.nearbyintbf16
+  libc.src.math.nextafterbf16
+  libc.src.math.nextdownbf16
+  libc.src.math.nexttowardbf16
+  libc.src.math.nextupbf16
+  libc.src.math.remainderbf16
+  libc.src.math.remquobf16
+  libc.src.math.rintbf16
+  libc.src.math.roundbf16
+  libc.src.math.roundevenbf16
+  libc.src.math.scalblnbf16
+  libc.src.math.scalbnbf16
+  libc.src.math.setpayloadbf16
+  libc.src.math.setpayloadsigbf16
+  libc.src.math.sqrtbf16
+  libc.src.math.tanbf16
+  libc.src.math.truncbf16
+  libc.src.math.totalorderbf16
+  libc.src.math.totalordermagbf16
+  libc.src.math.ufromfpbf16
+  libc.src.math.ufromfpxbf16
+)
+
+if(LIBC_TYPES_HAS_FLOAT128)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # math.h C++23 mixed bfloat16 and _Float128 entrypoints
+    libc.src.math.bf16addf128
+    libc.src.math.bf16divf128
+    libc.src.math.bf16fmaf128
+    libc.src.math.bf16mulf128
+    libc.src.math.bf16subf128
+  )
+endif()
+
+if(LIBC_COMPILER_HAS_FIXED_POINT)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # stdfix.h _Fract and _Accum entrypoints
+    libc.src.stdfix.abshk
+    libc.src.stdfix.abshr
+    libc.src.stdfix.absk
+    libc.src.stdfix.abslk
+    libc.src.stdfix.abslr
+    libc.src.stdfix.absr
+    libc.src.stdfix.exphk
+    libc.src.stdfix.expk
+    libc.src.stdfix.roundhk
+    libc.src.stdfix.roundhr
+    libc.src.stdfix.roundk
+    libc.src.stdfix.roundlk
+    libc.src.stdfix.roundlr
+    libc.src.stdfix.roundr
+    libc.src.stdfix.rounduhk
+    libc.src.stdfix.rounduhr
+    libc.src.stdfix.rounduk
+    libc.src.stdfix.roundulk
+    libc.src.stdfix.roundulr
+    libc.src.stdfix.roundur
+    libc.src.stdfix.sqrtuhk
+    libc.src.stdfix.sqrtuhr
+    libc.src.stdfix.sqrtuk
+    libc.src.stdfix.sqrtur
+    # libc.src.stdfix.sqrtulk
+    libc.src.stdfix.sqrtulr
+    libc.src.stdfix.uhksqrtus
+    libc.src.stdfix.uksqrtui
+    libc.src.stdfix.hrbits
+    libc.src.stdfix.uhrbits
+    libc.src.stdfix.rbits
+    libc.src.stdfix.urbits
+    libc.src.stdfix.lrbits
+    libc.src.stdfix.ulrbits
+    libc.src.stdfix.hkbits
+    libc.src.stdfix.uhkbits
+    libc.src.stdfix.kbits
+    libc.src.stdfix.ukbits
+    libc.src.stdfix.lkbits
+    libc.src.stdfix.ulkbits
+    libc.src.stdfix.bitshr
+    libc.src.stdfix.bitsr
+    libc.src.stdfix.bitslr
+    libc.src.stdfix.bitshk
+    libc.src.stdfix.bitsk
+    libc.src.stdfix.bitslk
+    libc.src.stdfix.bitsuhr
+    libc.src.stdfix.bitsur
+    libc.src.stdfix.bitsulr
+    libc.src.stdfix.bitsuhk
+    libc.src.stdfix.bitsuk
+    libc.src.stdfix.bitsulk
+    libc.src.stdfix.countlshr
+    libc.src.stdfix.countlsr
+    libc.src.stdfix.countlslr
+    libc.src.stdfix.countlshk
+    libc.src.stdfix.countlsk
+    libc.src.stdfix.countlslk
+    libc.src.stdfix.countlsuhr
+    libc.src.stdfix.countlsur
+    libc.src.stdfix.countlsulr
+    libc.src.stdfix.countlsuhk
+    libc.src.stdfix.countlsuk
+    libc.src.stdfix.countlsulk
+    libc.src.stdfix.idivr
+    libc.src.stdfix.idivlr
+    libc.src.stdfix.idivk
+    libc.src.stdfix.idivlk
+    libc.src.stdfix.idivur
+    libc.src.stdfix.idivulr
+    libc.src.stdfix.idivuk
+    libc.src.stdfix.idivulk
+    libc.src.stdfix.divir
+    libc.src.stdfix.divilr
+    libc.src.stdfix.divik
+    libc.src.stdfix.divilk
+    libc.src.stdfix.diviur
+    libc.src.stdfix.diviulr
+    libc.src.stdfix.diviuk
+    libc.src.stdfix.diviulk
+  )
+endif()
+
+if(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS)
+  list(APPEND TARGET_LIBC_ENTRYPOINTS
+    # regex.h entrypoints
+    libc.src.regex.regcomp
+    libc.src.regex.regexec
+    libc.src.regex.regerror
+    libc.src.regex.regfree
+  )
+endif()
+
+set(TARGET_LLVMLIBC_ENTRYPOINTS
+  ${TARGET_LIBC_ENTRYPOINTS}
+  ${TARGET_LIBM_ENTRYPOINTS}
+)
diff --git a/libc/config/baremetal/hexagon/headers.txt b/libc/config/baremetal/hexagon/headers.txt
new file mode 100644
index 0000000000000..a69660a97fdae
--- /dev/null
+++ b/libc/config/baremetal/hexagon/headers.txt
@@ -0,0 +1,32 @@
+set(TARGET_PUBLIC_HEADERS
+    libc.include.assert
+    libc.include.complex
+    libc.include.ctype
+    libc.include.elf
+    libc.include.errno
+    libc.include.features
+    libc.include.fenv
+    libc.include.float
+    libc.include.inttypes
+    libc.include.limits
+    libc.include.locale
+    libc.include.math
+    libc.include.setjmp
+    libc.include.stdbit
+    libc.include.stdckdint
+    libc.include.stdfix
+    libc.include.stdint
+    libc.include.stdio
+    libc.include.stdlib
+    libc.include.string
+    libc.include.strings
+    libc.include.sys_queue
+    libc.include.time
+    libc.include.uchar
+    libc.include.wchar
+    libc.include.wctype
+)
+
+if(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS)
+  list(APPEND TARGET_PUBLIC_HEADERS libc.include.regex)
+endif()
diff --git a/libc/include/llvm-libc-types/__jmp_buf.h b/libc/include/llvm-libc-types/__jmp_buf.h
index 266d2ffffa8b4..91f7305222c26 100644
--- a/libc/include/llvm-libc-types/__jmp_buf.h
+++ b/libc/include/llvm-libc-types/__jmp_buf.h
@@ -60,6 +60,12 @@ typedef struct {
 #if __ARM_FP
   long fopaque[8]; // d8-d15
 #endif
+#elif defined(__hexagon__)
+  // Callee-saved registers r16-r27, followed by sp (r29), fp (r30) and lr
+  // (r31). r16-r27 are saved and restored as register pairs using memd, which
+  // requires the buffer to be 8-byte aligned, hence long long rather than
+  // long. That also rounds the size up, leaving one word of tail padding.
+  long long opaque[8];
 #else
 #error "__jmp_buf not available for your target architecture."
 #endif
diff --git a/libc/include/llvm-libc-types/fenv_t.h b/libc/include/llvm-libc-types/fenv_t.h
index f9b10762a02b5..3fc6a92ec9a69 100644
--- a/libc/include/llvm-libc-types/fenv_t.h
+++ b/libc/include/llvm-libc-types/fenv_t.h
@@ -32,6 +32,8 @@ typedef struct {
 } fenv_t;
 #elif defined(__riscv)
 typedef unsigned int fenv_t;
+#elif defined(__hexagon__)
+typedef unsigned int fenv_t;
 #elif defined(__AMDGPU__) || defined(__NVPTX__) || defined(__SPIRV__)
 typedef struct {
   unsigned int __fpc;
diff --git a/libc/src/__support/FPUtil/FEnvImpl.h b/libc/src/__support/FPUtil/FEnvImpl.h
index 4c897e7243820..6a13286964eb4 100644
--- a/libc/src/__support/FPUtil/FEnvImpl.h
+++ b/libc/src/__support/FPUtil/FEnvImpl.h
@@ -119,6 +119,8 @@ LIBC_INLINE int set_env(const fenv_t *env) {
 #include "arm/FEnvImpl.h"
 #elif defined(LIBC_TARGET_ARCH_IS_ANY_RISCV) && defined(__riscv_flen)
 #include "riscv/FEnvImpl.h"
+#elif defined(LIBC_TARGET_ARCH_IS_HEXAGON)
+#include "hexagon/FEnvImpl.h"
 #else
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/__support/FPUtil/hexagon/FEnvImpl.h b/libc/src/__support/FPUtil/hexagon/FEnvImpl.h
new file mode 100644
index 0000000000000..15d081e54f8c4
--- /dev/null
+++ b/libc/src/__support/FPUtil/hexagon/FEnvImpl.h
@@ -0,0 +1,215 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the Hexagon implementation of floating-point
+/// environment manipulation functions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_HEXAGON_FENVIMPL_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_HEXAGON_FENVIMPL_H
+
+#include "hdr/fenv_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/fenv_t.h"
+#include "src/__support/macros/attributes.h" // For LIBC_INLINE_ASM
+#include "src/__support/macros/config.h"     // For LIBC_INLINE
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+
+// Hexagon keeps all floating-point state in the User Status Register (USR):
+//
+//   bits  5:1   sticky exception status flags
+//                 bit 1  FPINVF  invalid
+//                 bit 2  FPDBZF  divide-by-zero
+//                 bit 3  FPOVFF  overflow
+//                 bit 4  FPUNFF  underflow
+//                 bit 5  FPINPF  inexact
+//   bits 23:22   FPRND   rounding mode
+//                 00 round to nearest (ties to even)
+//                 01 toward zero
+//                 10 downward (toward -inf)
+//                 11 upward (toward +inf)
+//   bits 29:25   exception trap-enable bits (status flag << 24)
+//                 bit 25 FPINVE, bit 26 FPDBZE, bit 27 FPOVFE,
+//                 bit 28 FPUNFE, bit 29 FPINEE
+//
+// Note the hardware status/enable bit positions do NOT match the FE_* macro
+// values used by llvm-libc, so we translate between the two below.
+struct FEnv {
+  // Hardware status-flag bit positions within USR.
+  static constexpr uint32_t INVALID = 0x02;   // bit 1
+  static constexpr uint32_t DIVBYZERO = 0x04; // bit 2
+  static constexpr uint32_t OVERFLOW = 0x08;  // bit 3
+  static constexpr uint32_t UNDERFLOW = 0x10; // bit 4
+  static constexpr uint32_t INEXACT = 0x20;   // bit 5
+  static constexpr uint32_t STATUS_MASK =
+      INVALID | DIVBYZERO | OVERFLOW | UNDERFLOW | INEXACT;
+
+  // The trap-enable bits sit STATUS bits shifted up by this amount.
+  static constexpr uint32_t ENABLE_SHIFT = 24;
+  static constexpr uint32_t ENABLE_MASK = STATUS_MASK << ENABLE_SHIFT;
+
+  // Rounding mode field.
+  static constexpr uint32_t RND_SHIFT = 22;
+  static constexpr uint32_t RND_MASK = 0x3;
+  static constexpr uint32_t RND_TONEAREST = 0x0;
+  static constexpr uint32_t RND_TOWARDZERO = 0x1;
+  static constexpr uint32_t RND_DOWNWARD = 0x2;
+  static constexpr uint32_t RND_UPWARD = 0x3;
+
+  // Combined mask of all FP-relevant USR bits.  fegetenv/fesetenv only touch
+  // these so that non-FP USR fields (loop counters, prefetch state, etc.) are
+  // never disturbed.
+  static constexpr uint32_t FP_MASK =
+      STATUS_MASK | (RND_MASK << RND_SHIFT) | ENABLE_MASK;
+
+  LIBC_INLINE static uint32_t get_usr() {
+    uint32_t usr;
+    LIBC_INLINE_ASM("%0 = usr\n\t" : "=r"(usr));
+    return usr;
+  }
+
+  LIBC_INLINE static void set_usr(uint32_t usr) {
+    LIBC_INLINE_ASM("usr = %0\n\t" : : "r"(usr));
+  }
+
+  // Translate a set of FE_* exception macros into hardware status-flag bits.
+  LIBC_INLINE static uint32_t exception_macro_to_bits(int except) {
+    return ((except & FE_INVALID) ? INVALID : 0) |
+           ((except & FE_DIVBYZERO) ? DIVBYZERO : 0) |
+           ((except & FE_OVERFLOW) ? OVERFLOW : 0) |
+           ((except & FE_UNDERFLOW) ? UNDERFLOW : 0) |
+           ((except & FE_INEXACT) ? INEXACT : 0);
+  }
+
+  // Translate hardware status-flag bits back into FE_* exception macros.
+  LIBC_INLINE static int exception_bits_to_macro(uint32_t status) {
+    return ((status & INVALID) ? FE_INVALID : 0) |
+           ((status & DIVBYZERO) ? FE_DIVBYZERO : 0) |
+           ((status & OVERFLOW) ? FE_OVERFLOW : 0) |
+           ((status & UNDERFLOW) ? FE_UNDERFLOW : 0) |
+           ((status & INEXACT) ? FE_INEXACT : 0);
+  }
+};
+
+LIBC_INLINE int enable_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  uint32_t old_enabled = (usr >> FEnv::ENABLE_SHIFT) & FEnv::STATUS_MASK;
+  uint32_t new_enabled = old_enabled | FEnv::exception_macro_to_bits(excepts);
+  FEnv::set_usr((usr & ~FEnv::ENABLE_MASK) |
+                (new_enabled << FEnv::ENABLE_SHIFT));
+  return FEnv::exception_bits_to_macro(old_enabled);
+}
+
+LIBC_INLINE int disable_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  uint32_t old_enabled = (usr >> FEnv::ENABLE_SHIFT) & FEnv::STATUS_MASK;
+  uint32_t new_enabled = old_enabled & ~FEnv::exception_macro_to_bits(excepts);
+  FEnv::set_usr((usr & ~FEnv::ENABLE_MASK) |
+                (new_enabled << FEnv::ENABLE_SHIFT));
+  return FEnv::exception_bits_to_macro(old_enabled);
+}
+
+LIBC_INLINE int get_except() {
+  uint32_t enabled =
+      (FEnv::get_usr() >> FEnv::ENABLE_SHIFT) & FEnv::STATUS_MASK;
+  return FEnv::exception_bits_to_macro(enabled);
+}
+
+LIBC_INLINE int clear_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  usr &= ~FEnv::exception_macro_to_bits(excepts);
+  FEnv::set_usr(usr);
+  return 0;
+}
+
+LIBC_INLINE int test_except(int excepts) {
+  uint32_t to_test = FEnv::exception_macro_to_bits(excepts);
+  uint32_t status = FEnv::get_usr() & FEnv::STATUS_MASK;
+  return FEnv::exception_bits_to_macro(status & to_test);
+}
+
+LIBC_INLINE int set_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  FEnv::set_usr(usr | FEnv::exception_macro_to_bits(excepts));
+  return 0;
+}
+
+LIBC_INLINE int raise_except(int excepts) {
+  // Setting the sticky status flag triggers a trap if the corresponding
+  // enable bit is set; otherwise it simply records the exception.
+  uint32_t usr = FEnv::get_usr();
+  FEnv::set_usr(usr | FEnv::exception_macro_to_bits(excepts));
+  return 0;
+}
+
+LIBC_INLINE int get_round() {
+  uint32_t rm = (FEnv::get_usr() >> FEnv::RND_SHIFT) & FEnv::RND_MASK;
+  switch (rm) {
+  case FEnv::RND_TONEAREST:
+    return FE_TONEAREST;
+  case FEnv::RND_TOWARDZERO:
+    return FE_TOWARDZERO;
+  case FEnv::RND_DOWNWARD:
+    return FE_DOWNWARD;
+  case FEnv::RND_UPWARD:
+    return FE_UPWARD;
+  default:
+    return -1; // Error value.
+  }
+}
+
+LIBC_INLINE int set_round(int mode) {
+  uint32_t rm;
+  switch (mode) {
+  case FE_TONEAREST:
+    rm = FEnv::RND_TONEAREST;
+    break;
+  case FE_TOWARDZERO:
+    rm = FEnv::RND_TOWARDZERO;
+    break;
+  case FE_DOWNWARD:
+    rm = FEnv::RND_DOWNWARD;
+    break;
+  case FE_UPWARD:
+    rm = FEnv::RND_UPWARD;
+    break;
+  default:
+    return -1; // To indicate failure.
+  }
+  uint32_t usr = FEnv::get_usr();
+  FEnv::set_usr((usr & ~(FEnv::RND_MASK << FEnv::RND_SHIFT)) |
+                (rm << FEnv::RND_SHIFT));
+  return 0;
+}
+
+LIBC_INLINE int get_env(fenv_t *envp) {
+  uint32_t *state = reinterpret_cast<uint32_t *>(envp);
+  *state = FEnv::get_usr() & FEnv::FP_MASK;
+  return 0;
+}
+
+LIBC_INLINE int set_env(const fenv_t *envp) {
+  uint32_t usr = FEnv::get_usr();
+  if (envp == FE_DFL_ENV) {
+    // Default environment: round to nearest, no exceptions raised or enabled.
+    FEnv::set_usr(usr & ~FEnv::FP_MASK);
+    return 0;
+  }
+  uint32_t state = *reinterpret_cast<const uint32_t *>(envp);
+  // Preserve non-FP USR bits; only load the FP-relevant fields.
+  FEnv::set_usr((usr & ~FEnv::FP_MASK) | (state & FEnv::FP_MASK));
+  return 0;
+}
+
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_HEXAGON_FENVIMPL_H
diff --git a/libc/src/__support/macros/properties/architectures.h b/libc/src/__support/macros/properties/architectures.h
index f73fa7a7643ab..c1c425067ddd9 100644
--- a/libc/src/__support/macros/properties/architectures.h
+++ b/libc/src/__support/macros/properties/architectures.h
@@ -70,4 +70,8 @@
 #define LIBC_TARGET_ARCH_IS_ANY_RISCV
 #endif
 
+#if defined(__hexagon__)
+#define LIBC_TARGET_ARCH_IS_HEXAGON
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_ARCHITECTURES_H
diff --git a/libc/src/__support/memory_size.h b/libc/src/__support/memory_size.h
index 49c3598960885..f85956e0b11b3 100644
--- a/libc/src/__support/memory_size.h
+++ b/libc/src/__support/memory_size.h
@@ -47,8 +47,11 @@ class SafeMemSize {
     type result;
     if (LIBC_UNLIKELY((value | other.value) < 0)) {
       result = -1;
-    } else {
-      result = value + other.value;
+    } else if (LIBC_UNLIKELY(add_overflow(value, other.value, result))) {
+      // Signed addition would overflow. Computing `value + other.value`
+      // directly is undefined behavior, so detect the overflow with the
+      // builtin and mark the result invalid instead.
+      result = -1;
     }
     return SafeMemSize{result};
   }
diff --git a/libc/src/setjmp/hexagon/CMakeLists.txt b/libc/src/setjmp/hexagon/CMakeLists.txt
new file mode 100644
index 0000000000000..55c80b0bede0d
--- /dev/null
+++ b/libc/src/setjmp/hexagon/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_entrypoint_object(
+  setjmp
+  SRCS
+    setjmp.cpp
+  HDRS
+    ../setjmp_impl.h
+  DEPENDS
+    libc.hdr.types.jmp_buf
+)
+
+add_entrypoint_object(
+  longjmp
+  SRCS
+    longjmp.cpp
+  HDRS
+    ../longjmp.h
+  DEPENDS
+    libc.hdr.types.jmp_buf
+)
diff --git a/libc/src/setjmp/hexagon/longjmp.cpp b/libc/src/setjmp/hexagon/longjmp.cpp
new file mode 100644
index 0000000000000..457a6bc92907b
--- /dev/null
+++ b/libc/src/setjmp/hexagon/longjmp.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the Hexagon implementation of `longjmp`.
+//===----------------------------------------------------------------------===//
+
+#include "src/setjmp/longjmp.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Restore the registers saved by setjmp, see the comment there for the layout
+// of the buffer.
+[[gnu::naked]] LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf buf, int val)) {
+  asm(R"(
+      r17:16 = memd(r0+#0)
+      r19:18 = memd(r0+#8)
+      r21:20 = memd(r0+#16)
+      r23:22 = memd(r0+#24)
+      r25:24 = memd(r0+#32)
+      r27:26 = memd(r0+#40)
+      r29 = memw(r0+#48)
+      r30 = memw(r0+#52)
+      r31 = memw(r0+#56)
+
+      # return val ?: 1;
+      { p0 = cmp.eq(r1,#0); if (p0.new) r1 = #1 }
+      r0 = r1
+      jumpr r31)");
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/setjmp/hexagon/setjmp.cpp b/libc/src/setjmp/hexagon/setjmp.cpp
new file mode 100644
index 0000000000000..7fa6e6f83ff3f
--- /dev/null
+++ b/libc/src/setjmp/hexagon/setjmp.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the Hexagon implementation of `setjmp`.
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/setjmp/setjmp_impl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// The Hexagon ABI callee-saved registers are r16-r27. Those are stored as
+// register pairs, which requires the buffer to be 8-byte aligned. The stack
+// pointer (r29), frame pointer (r30) and link register (r31) are stored
+// individually; r28 is caller-saved so it does not need to be preserved.
+[[gnu::naked]] LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
+  asm(R"(
+      memd(r0+#0) = r17:16
+      memd(r0+#8) = r19:18
+      memd(r0+#16) = r21:20
+      memd(r0+#24) = r23:22
+      memd(r0+#32) = r25:24
+      memd(r0+#40) = r27:26
+      memw(r0+#48) = r29
+      memw(r0+#52) = r30
+      memw(r0+#56) = r31
+
+      # Return zero.
+      r0 = #0
+      jumpr r31)");
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/startup/baremetal/hexagon/CMakeLists.txt b/libc/startup/baremetal/hexagon/CMakeLists.txt
new file mode 100644
index 0000000000000..372731be765df
--- /dev/null
+++ b/libc/startup/baremetal/hexagon/CMakeLists.txt
@@ -0,0 +1,12 @@
+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
+)
diff --git a/libc/startup/baremetal/hexagon/start.cpp b/libc/startup/baremetal/hexagon/start.cpp
new file mode 100644
index 0000000000000..268820f8f1d42
--- /dev/null
+++ b/libc/startup/baremetal/hexagon/start.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the startup code for bare-metal Hexagon targets.
+//===----------------------------------------------------------------------===//
+
+#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"
+
+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 LIBC_NAMESPACE_DECL {
+
+[[noreturn]] void do_start() {
+  // TODO: This startup code is the MVP for running under the Hexagon
+  // simulator with semihosting. It does not configure the MMU or caches.
+
+  // Clear the Supervisor Status Register so that a subsequent trap0
+  // semihosting call is serviced by the monitor.
+  asm volatile("ssr = %0" : : "r"(0));
+
+  // 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() {
+  // Setup event vector base (needed for semihosting trap handling).
+  asm volatile("r0 = ##__llvm_libc_hexagon_event_vectors\n\t"
+               "evb = r0");
+  // Setup stack pointer, 16-byte aligned.
+  asm volatile("r0 = ##__stack\n\t"
+               "sp = and(r0, #-16)");
+  // Setup GP register for the small-data area.
+  asm volatile("r0 = ##_SDA_BASE_\n\t"
+               "gp = r0");
+  asm volatile("jump %0" : : "X"(LIBC_NAMESPACE::do_start));
+}
+} // extern "C"
+
+// The simulator services semihosting when trap0 is executed. Put the trap0
+// handler at event-vector slot 8; other slots are unexpected and spin.
+asm(".text\n\t"
+    ".p2align 4\n"
+    "__llvm_libc_hexagon_event_spin:\n\t"
+    "jump __llvm_libc_hexagon_event_spin\n"
+    "__llvm_libc_hexagon_event_trap0:\n\t"
+    "rte\n\t"
+    ".p2align 12, 0\n\t"
+    ".global __llvm_libc_hexagon_event_vectors\n"
+    "__llvm_libc_hexagon_event_vectors:\n\t"
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 0  reset
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 1  nmi
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 2  error
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 3  reserved
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 4  tlb miss x
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 5  reserved
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 6  tlb miss rw
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 7  reserved
+    "jump __llvm_libc_hexagon_event_trap0\n\t" // 8  trap0
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 9  trap1
+);
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index 99350d4415b39..7ff9262bcaa57 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -29,7 +29,7 @@ function(_create_unittest_framework_library fq_target_name)
       ${TEST_LIB_HDRS}
   )
   target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})
-  if(TARGET libc.src.time.clock)
+  if(libc.src.time.clock IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS)
     target_compile_definitions(${fq_target_name} PRIVATE TARGET_SUPPORTS_CLOCK)
   endif()
 
@@ -124,11 +124,6 @@ add_unittest_framework_library(
     .LibcTest
 )
 
-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()
-
 if(LIBC_TEST_SUBPROCESS_TESTS)
   set(libc_death_test_deps
     libc.hdr.stdint_proxy
@@ -163,14 +158,31 @@ else()
   )
 endif()
 
+set(libc_hermetic_test_support_srcs HermeticTestUtils.cpp)
+set(libc_hermetic_test_support_deps 
+  libc.hdr.errno_macros
+  libc.hdr.stdint_proxy
+  libc.src.__support.libc_errno
+)
+if(LIBC_TARGET_OS_IS_BAREMETAL)
+  if(LIBC_TARGET_ARCHITECTURE_IS_ARM)
+    list(APPEND libc_hermetic_test_support_srcs ArmBaremetalTestUtils.cpp)
+  elseif(LIBC_TARGET_ARCHITECTURE_IS_HEXAGON)
+    list(APPEND libc_hermetic_test_support_srcs HexagonBaremetalTestUtils.cpp)
+  endif()
+  list(APPEND libc_hermetic_test_support_deps
+    libc.hdr.types.size_t
+    libc.hdr.types.ssize_t
+    libc.hdr.types.struct_timespec
+  )
+endif()
+
 add_unittest_framework_library(
   HermeticTestUtils
   SRCS
     ${libc_hermetic_test_support_srcs}
   DEPENDS
-    libc.hdr.errno_macros
-    libc.hdr.stdint_proxy
-    libc.src.__support.libc_errno
+    ${libc_hermetic_test_support_deps}
 )
 
 add_header_library(
diff --git a/libc/test/UnitTest/HexagonBaremetalTestUtils.cpp b/libc/test/UnitTest/HexagonBaremetalTestUtils.cpp
new file mode 100644
index 0000000000000..c637ed187ec45
--- /dev/null
+++ b/libc/test/UnitTest/HexagonBaremetalTestUtils.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file contains test utilities for bare-metal Hexagon hermetic tests.
+///
+/// llvm-libc's bare-metal port defers all OS interaction to a small set of
+/// "vendor-provided" hook symbols (see
+/// libc/src/__support/OSUtil/baremetal/io.h and the bare-metal time sources).
+/// This file implements those hooks for the Hexagon simulator using Angel-style
+/// semihosting, i.e. a `trap0(#0)` with:
+///   R0 = system call code (also the return value)
+///   R1 = pointer to an argument block (also errno on failure)
+///   R2 = exit status (for SYS_EXIT only)
+/// The read/write calls return the number of bytes NOT transferred.
+///
+/// It is compiled into HermeticTestUtils so hermetic tests can run under
+/// the simulator. It is purely test-support code and is not intended for use as
+/// general-purpose semihosting support.
+//===----------------------------------------------------------------------===//
+
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "hdr/types/struct_timespec.h"
+
+namespace {
+
+enum HexagonSyscall {
+  SYS_WRITE = 5,
+  SYS_READ = 6,
+  SYS_TIME = 0x11,
+  SYS_EXIT = 24,
+};
+
+// Software interrupt used to enter the simulator monitor.
+#define HEXAGON_SWI "trap0(#0)"
+
+// Issue a semihosting call: R0 = code, R1 = pointer to argument block.
+// Returns the value the simulator places in R0.
+int hexagon_semihost(int code, size_t *args) {
+  register uintptr_t r0 __asm__("r0") = static_cast<uintptr_t>(code);
+  register uintptr_t r1 __asm__("r1") = reinterpret_cast<uintptr_t>(args);
+  __asm__ __volatile__(HEXAGON_SWI : "=r"(r0), "=r"(r1) : "r"(r0), "r"(r1));
+  return static_cast<int>(r0);
+}
+
+} // namespace
+
+extern "C" {
+
+// The cookie type/objects only need to exist; their contents encode the file
+// descriptor used by the simulator-backed implementation.
+struct __llvm_libc_stdio_cookie {
+  int fd;
+};
+
+__llvm_libc_stdio_cookie __llvm_libc_stdin_cookie = {0};
+__llvm_libc_stdio_cookie __llvm_libc_stdout_cookie = {1};
+__llvm_libc_stdio_cookie __llvm_libc_stderr_cookie = {2};
+
+ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, size_t size) {
+  int fd = cookie ? static_cast<__llvm_libc_stdio_cookie *>(cookie)->fd : 1;
+  size_t args[] = {
+      static_cast<size_t>(fd),
+      reinterpret_cast<size_t>(buf),
+      size,
+  };
+  int not_written = hexagon_semihost(SYS_WRITE, args);
+  if (not_written < 0)
+    return -1;
+  // The simulator returns the number of bytes NOT written.
+  return static_cast<ssize_t>(size) - not_written;
+}
+
+ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size) {
+  int fd = cookie ? static_cast<__llvm_libc_stdio_cookie *>(cookie)->fd : 0;
+  size_t args[] = {
+      static_cast<size_t>(fd),
+      reinterpret_cast<size_t>(buf),
+      size,
+  };
+  int not_read = hexagon_semihost(SYS_READ, args);
+  if (not_read < 0)
+    return -1;
+  // The simulator returns the number of bytes NOT read.
+  return static_cast<ssize_t>(size) - not_read;
+}
+
+[[noreturn]] void __llvm_libc_exit(int status) {
+  // The simulator reads the exit status from R2 and the syscall code from R0.
+  register uintptr_t r2 __asm__("r2") = static_cast<uintptr_t>(status);
+  register uintptr_t r0 __asm__("r0") = SYS_EXIT;
+  __asm__ __volatile__(HEXAGON_SWI : : "r"(r0), "r"(r2) : "memory");
+  __builtin_unreachable();
+}
+
+// The bare-metal config builds with LIBC_ERRNO_MODE_EXTERNAL, so the vendor
+// must provide storage for errno via this hook.
+int *__llvm_libc_errno() {
+  static int errno_storage;
+  return &errno_storage;
+}
+
+// Vendor hook used by the bare-metal `timespec_get()` implementation. The
+// Hexagon simulator's SYS_TIME call returns wall-clock time in whole seconds
+// (R0 = code on entry, R0 = seconds on return).
+bool __llvm_libc_timespec_get_utc(struct timespec *ts) {
+  register uintptr_t r0 __asm__("r0") = SYS_TIME;
+  __asm__ __volatile__(HEXAGON_SWI : "=r"(r0) : "r"(r0));
+  ts->tv_sec = static_cast<time_t>(r0);
+  ts->tv_nsec = 0;
+  return true;
+}
+
+} // extern "C"
diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp
index ca343bbf8a4b0..a00d0ff32f115 100644
--- a/libc/test/UnitTest/LibcTest.cpp
+++ b/libc/test/UnitTest/LibcTest.cpp
@@ -166,11 +166,15 @@ int Test::runTests(const TestOptions &Options) {
     tlog << green << "[ RUN      ] " << reset << TestName << '\n';
     RunContext Ctx;
     internal::current_context = &Ctx;
-    [[maybe_unused]] const uint64_t start_time = static_cast<uint64_t>(clock());
+#ifdef LIBC_TEST_USE_CLOCK
+    const uint64_t start_time = static_cast<uint64_t>(clock());
+#endif
     T->SetUp();
     T->Run();
     T->TearDown();
-    [[maybe_unused]] const uint64_t end_time = static_cast<uint64_t>(clock());
+#ifdef LIBC_TEST_USE_CLOCK
+    const uint64_t end_time = static_cast<uint64_t>(clock());
+#endif
     internal::current_context = nullptr;
     switch (Ctx.status()) {
     case RunContext::RunResult::Fail:
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index e00f00a818e06..be3574c21a730 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -530,6 +530,8 @@ CString libc_make_test_file_path_func(const char *file_name);
 // EXPECT_DEATH can appear in a test of any function, e.g. checking for a crash
 // if passing nullptr to the function. So it must be defined, even if it can't
 // do anything.
+#define EXPECT_EXITS(FUNC, EXIT)
+#define ASSERT_EXITS(FUNC, EXIT)
 #define EXPECT_DEATH(FUNC, SIG)
 #define ASSERT_DEATH(FUNC, SIG)
 
diff --git a/libc/test/UnitTest/llvm-libc-baremetal.ld b/libc/test/UnitTest/llvm-libc-baremetal.ld
index e88a68cf62aa4..2f84d934edfe1 100644
--- a/libc/test/UnitTest/llvm-libc-baremetal.ld
+++ b/libc/test/UnitTest/llvm-libc-baremetal.ld
@@ -80,6 +80,9 @@ SECTIONS {
 
   .data : ALIGN(8) {
     PROVIDE(__data_start = .);
+    /* Base of the small-data area. Targets with GP-relative small-data
+       addressing (e.g. Hexagon) load GP with this at startup. */
+    PROVIDE(_SDA_BASE_ = .);
     *(.data .data.* .gnu.linkonce.d.*)
     *(.sdata .sdata.* .gnu.linkonce.s.*)
     . = ALIGN(8);
diff --git a/libc/test/src/math/smoke/exp10m1f_test.cpp b/libc/test/src/math/smoke/exp10m1f_test.cpp
index 87320fa2fe2f0..6f51abbd19143 100644
--- a/libc/test/src/math/smoke/exp10m1f_test.cpp
+++ b/libc/test/src/math/smoke/exp10m1f_test.cpp
@@ -17,10 +17,13 @@ TEST_F(LlvmLibcExp10m1fTest, SpecialNumbers) {
   EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::exp10m1f(sNaN), FE_INVALID);
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_EQ(FPBits(aNaN).uintval(),
-            FPBits(LIBC_NAMESPACE::exp10m1f(aNaN)).uintval());
-  EXPECT_EQ(FPBits(neg_aNaN).uintval(),
-            FPBits(LIBC_NAMESPACE::exp10m1f(neg_aNaN)).uintval());
+  // Use EXPECT_FP_EQ (which treats any two NaNs as equal) rather than comparing
+  // raw bit patterns: exp10m1f(NaN) is computed as NaN + inf, and some targets
+  // (e.g. Hexagon) produce a non-canonical NaN encoding (0xFFFFFFFF) instead of
+  // the canonical quiet NaN. Both are valid NaNs, so only require NaN-ness
+  // here.
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::exp10m1f(aNaN));
+  EXPECT_FP_EQ(neg_aNaN, LIBC_NAMESPACE::exp10m1f(neg_aNaN));
   EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp10m1f(inf));
   EXPECT_FP_EQ_ALL_ROUNDING(-1.0f, LIBC_NAMESPACE::exp10m1f(neg_inf));
   EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::exp10m1f(zero));

>From 4c46050122ae35970225b0d7e9cc74cefd27030a Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Wed, 23 Sep 2026 10:15:29 +0530
Subject: [PATCH 2/6] [Github] Enable libc tests for Hexagon (baremetal)

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 .github/workflows/libc-fullbuild-tests.yml    | 13 +++++++-
 libc/CMakeLists.txt                           |  4 +++
 libc/cmake/caches/baremetal_common.cmake      | 18 +++++++++++
 .../caches/hexagon-unknown-none-elf.cmake     | 32 +++++++++++++++++++
 libc/cmake/modules/LLVMLibCTestRules.cmake    | 18 ++++++++---
 5 files changed, 80 insertions(+), 5 deletions(-)
 create mode 100644 libc/cmake/caches/hexagon-unknown-none-elf.cmake

diff --git a/.github/workflows/libc-fullbuild-tests.yml b/.github/workflows/libc-fullbuild-tests.yml
index 33def6ad88a75..a0fcccb59c7ac 100644
--- a/.github/workflows/libc-fullbuild-tests.yml
+++ b/.github/workflows/libc-fullbuild-tests.yml
@@ -116,6 +116,14 @@ jobs:
             cpp_compiler: clang++-23
             target: riscv32-unknown-elf
             testing: SKIP
+          - name: baremetal-hexagon
+            os: ubuntu-24.04
+            build_type: MinSizeRel
+            c_compiler: clang-23
+            cpp_compiler: clang++-23
+            target: hexagon-unknown-none-elf
+            build_builtins: ON
+            testing: RUN
           - name: amd-gpu
             os: ubuntu-24.04
             build_type: Release
@@ -171,7 +179,7 @@ jobs:
           "
         fi
 
-        if [[ "${{ matrix.include_scudo }}" == "ON" || "${{ matrix.build_fuzzing_tests }}" == "ON" ]]; then
+        if [[ "${{ matrix.include_scudo }}" == "ON" || "${{ matrix.build_fuzzing_tests }}" == "ON" || "${{ matrix.build_builtins }}" == "ON" ]]; then
           export RUNTIMES="$RUNTIMES;compiler-rt"
         fi
 
@@ -185,6 +193,9 @@ jobs:
 
         case "${{ matrix.name }}" in
           baremetal-* )
+            if [[ "${{ matrix.build_builtins }}" == "ON" ]]; then
+              export CMAKE_FLAGS="$CMAKE_FLAGS -DLLVM_ENABLE_RUNTIMES=$RUNTIMES"
+            fi
             cmake $CMAKE_FLAGS \
               -C /__w/llvm-project/llvm-project/libc/cmake/caches/${{ matrix.target }}.cmake
             ;;
diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 292e4c90a26a8..45c01a6be4a16 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -123,6 +123,10 @@ endif()
 
 set(LIBC_TEST_CMD "" CACHE STRING
   "The full test command in the form <command> binary=@BINARY@, if using another program to test (e.g. QEMU)")
+set(LIBC_TEST_BUILTINS_TARGET "" CACHE STRING
+  "CMake target of compiler-rt builtins to link into libc tests")
+set(LIBC_TEST_BUILTINS_LIBRARY "" CACHE FILEPATH
+  "Compiler runtime builtins library to link into libc tests")
 set(LIBC_TEST_HERMETIC_ONLY "" OFF CACHE BOOL "Only enable hermetic tests.")
 
 list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT ${LIBC_COMMON_TUNE_OPTIONS})
diff --git a/libc/cmake/caches/baremetal_common.cmake b/libc/cmake/caches/baremetal_common.cmake
index 0b75fbc702bca..f3b2b0b6fd551 100644
--- a/libc/cmake/caches/baremetal_common.cmake
+++ b/libc/cmake/caches/baremetal_common.cmake
@@ -21,3 +21,21 @@ set(LIBC_TARGET_TRIPLE ${RUNTIMES_TARGET_TRIPLE} CACHE STRING "")
 set(LLVM_LIBC_FULL_BUILD "ON" CACHE BOOL "")
 set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
 set(LLVM_ENABLE_WERROR ON CACHE BOOL "")
+
+# Bare-metal compiler-rt builtins configuration (active when compiler-rt is enabled in LLVM_ENABLE_RUNTIMES).
+set(COMPILER_RT_BUILD_BUILTINS ON CACHE BOOL "")
+set(COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL "")
+set(COMPILER_RT_BUILD_XRAY OFF CACHE BOOL "")
+set(COMPILER_RT_BUILD_LIBFUZZER OFF CACHE BOOL "")
+set(COMPILER_RT_BUILD_PROFILE OFF CACHE BOOL "")
+set(COMPILER_RT_BUILD_MEMPROF OFF CACHE BOOL "")
+set(COMPILER_RT_BUILD_ORC OFF CACHE BOOL "")
+set(COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
+set(COMPILER_RT_BUILTINS_ENABLE_PIC OFF CACHE BOOL "")
+set(COMPILER_RT_BAREMETAL_BUILD ON CACHE BOOL "")
+set(COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "")
+set(COMPILER_RT_DEFAULT_TARGET_ONLY ON CACHE BOOL "")
+
+if(NOT LIBC_TEST_BUILTINS_TARGET AND CMAKE_SYSTEM_PROCESSOR)
+  set(LIBC_TEST_BUILTINS_TARGET "clang_rt.builtins-${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "")
+endif()
diff --git a/libc/cmake/caches/hexagon-unknown-none-elf.cmake b/libc/cmake/caches/hexagon-unknown-none-elf.cmake
new file mode 100644
index 0000000000000..fd3ab8c761c9b
--- /dev/null
+++ b/libc/cmake/caches/hexagon-unknown-none-elf.cmake
@@ -0,0 +1,32 @@
+set(CMAKE_SYSTEM_PROCESSOR hexagon CACHE STRING "")
+set(RUNTIMES_TARGET_TRIPLE "hexagon-unknown-none-elf" CACHE STRING "")
+
+# flag to build compiler-rt builtins
+set(CMAKE_C_FLAGS "-mv68 -G0" CACHE STRING "")
+set(CMAKE_CXX_FLAGS "-mv68 -G0" CACHE STRING "")
+set(CMAKE_ASM_FLAGS "-mv68 -G0" CACHE STRING "")
+
+set(LLVM_ENABLE_RUNTIMES "libc;compiler-rt" CACHE STRING "")
+set(LIBC_TEST_BUILTINS_TARGET "clang_rt.builtins-hexagon" CACHE STRING "")
+
+set(LIBC_COMPILE_OPTIONS_DEFAULT
+    "-mv68;-G0;-fuse-init-array"
+    CACHE STRING "")
+set(LIBC_LINK_OPTIONS_DEFAULT
+    "-mv68;-G0"
+    CACHE STRING "")
+set(LIBC_TEST_COMPILE_OPTIONS_DEFAULT "-O0" CACHE STRING "")
+
+# The bare-metal linker script requires explicit memory-region definitions for
+# the test image.
+set(LIBC_TEST_LINK_OPTIONS_DEFAULT
+    "-mv68;-G0;-fuse-ld=lld;-T;${CMAKE_CURRENT_LIST_DIR}/../../test/UnitTest/llvm-libc-baremetal.ld;-Wl,--defsym=__boot_flash=0x00100000;-Wl,--defsym=__boot_flash_size=0x00001000;-Wl,--defsym=__flash=0x00101000;-Wl,--defsym=__flash_size=0x003ff000;-Wl,--defsym=__ram=0x00500000;-Wl,--defsym=__ram_size=0x00800000;-Wl,--defsym=__stack_size=0x00040000"
+    CACHE STRING "")
+set(LIBC_TEST_CMD
+    "qemu-system-hexagon -M sim -cpu v68 -kernel @BINARY@"
+    CACHE STRING "")
+
+include(${CMAKE_CURRENT_LIST_DIR}/baremetal_common.cmake)
+
+# Enable hermetic tests for the QEMU-backed configuration.
+set(LLVM_INCLUDE_TESTS ON CACHE BOOL "" FORCE)
diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index 917e0ce2a9b05..d6593efdd400a 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -644,8 +644,8 @@ function(add_integration_test test_name)
       "--cuda-path=${LIBC_CUDA_ROOT}")
   elseif(LIBC_CC_SUPPORTS_NOSTDLIBPP)
     set(link_options
-      -nolibc
       -nostartfiles
+      -nodefaultlibs
       -nostdlib++
       -static
       ${LIBC_LINK_OPTIONS_DEFAULT}
@@ -656,8 +656,8 @@ function(add_integration_test test_name)
     # Older version of gcc does not support `nostdlib++` flag.  We use
     # `nostdlib` and link against libgcc_s, which cannot be linked statically.
     set(link_options
-      -nolibc
       -nostartfiles
+      -nodefaultlibs
       -nostdlib
       ${LIBC_LINK_OPTIONS_DEFAULT}
       ${LIBC_TEST_LINK_OPTIONS_DEFAULT}
@@ -670,8 +670,13 @@ function(add_integration_test test_name)
     libc.startup.${LIBC_TARGET_OS}.crt1
     libc.test.IntegrationTest.test
     ${fq_target_name}.__libc__
+    ${LIBC_TEST_BUILTINS_LIBRARY}
     ${compiler_runtime}
   )
+  if(LIBC_TEST_BUILTINS_TARGET)
+    target_link_libraries(${fq_build_target_name} $<TARGET_FILE:${LIBC_TEST_BUILTINS_TARGET}>)
+    add_dependencies(${fq_build_target_name} ${LIBC_TEST_BUILTINS_TARGET})
+  endif()
   add_dependencies(${fq_build_target_name}
     libc.test.IntegrationTest.test
     ${INTEGRATION_TEST_DEPENDS})
@@ -958,8 +963,8 @@ function(add_libc_hermetic test_name)
       "--cuda-path=${LIBC_CUDA_ROOT}")
   elseif(LIBC_CC_SUPPORTS_NOSTDLIBPP)
     set(link_options
-      -nolibc
       -nostartfiles
+      -nodefaultlibs
       -nostdlib++
       -static
       ${LIBC_LINK_OPTIONS_DEFAULT}
@@ -976,8 +981,8 @@ function(add_libc_hermetic test_name)
     # Older version of gcc does not support `nostdlib++` flag.  We use
     # `nostdlib` and link against libgcc_s, which cannot be linked statically.
     set(link_options
-      -nolibc
       -nostartfiles
+      -nodefaultlibs
       -nostdlib
       ${LIBC_LINK_OPTIONS_DEFAULT}
       ${LIBC_TEST_LINK_OPTIONS_DEFAULT}
@@ -1007,8 +1012,13 @@ function(add_libc_hermetic test_name)
       ${HERMETIC_TEST_LINK_LIBRARIES}
       ${fq_target_name}.__libc__
       ${coverage_link_libs}
+      ${LIBC_TEST_BUILTINS_LIBRARY}
       ${compiler_runtime}
   )
+  if(LIBC_TEST_BUILTINS_TARGET)
+    target_link_libraries(${fq_build_target_name} PRIVATE $<TARGET_FILE:${LIBC_TEST_BUILTINS_TARGET}>)
+    add_dependencies(${fq_build_target_name} ${LIBC_TEST_BUILTINS_TARGET})
+  endif()
   add_dependencies(${fq_build_target_name} ${fq_deps_list})
 
   if(NOT HERMETIC_TEST_NO_RUN_POSTBUILD)

>From f6e241dc01383448a7cf3525479e6a142f19d3c2 Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Thu, 24 Sep 2026 13:48:47 +0530
Subject: [PATCH 3/6] [libc] Fix -Werror fails for Hexagon

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 .../caches/hexagon-unknown-none-elf.cmake     |  2 +-
 libc/test/UnitTest/LibcTest.h                 | 48 ++++++++++++++++---
 libc/test/UnitTest/llvm-libc-baremetal.ld     |  6 +++
 libc/test/shared/shared_math_test.cpp         |  6 +--
 libc/test/src/__support/File/CMakeLists.txt   |  1 +
 libc/test/src/__support/File/file_test.cpp    | 44 ++++++++++++-----
 libc/test/src/math/smoke/FrexpTest.h          |  6 +--
 libc/test/src/math/smoke/NearbyIntTest.h      | 10 ++--
 libc/test/src/stdfix/DiviFxTest.h             |  2 +-
 libc/test/src/stdfix/IdivFxTest.h             |  2 +-
 10 files changed, 94 insertions(+), 33 deletions(-)

diff --git a/libc/cmake/caches/hexagon-unknown-none-elf.cmake b/libc/cmake/caches/hexagon-unknown-none-elf.cmake
index fd3ab8c761c9b..e959e86352ca2 100644
--- a/libc/cmake/caches/hexagon-unknown-none-elf.cmake
+++ b/libc/cmake/caches/hexagon-unknown-none-elf.cmake
@@ -20,7 +20,7 @@ set(LIBC_TEST_COMPILE_OPTIONS_DEFAULT "-O0" CACHE STRING "")
 # The bare-metal linker script requires explicit memory-region definitions for
 # the test image.
 set(LIBC_TEST_LINK_OPTIONS_DEFAULT
-    "-mv68;-G0;-fuse-ld=lld;-T;${CMAKE_CURRENT_LIST_DIR}/../../test/UnitTest/llvm-libc-baremetal.ld;-Wl,--defsym=__boot_flash=0x00100000;-Wl,--defsym=__boot_flash_size=0x00001000;-Wl,--defsym=__flash=0x00101000;-Wl,--defsym=__flash_size=0x003ff000;-Wl,--defsym=__ram=0x00500000;-Wl,--defsym=__ram_size=0x00800000;-Wl,--defsym=__stack_size=0x00040000"
+    "-mv68;-G0;-T;${CMAKE_CURRENT_LIST_DIR}/../../test/UnitTest/llvm-libc-baremetal.ld;-Wl,--defsym=__boot_flash=0x00100000;-Wl,--defsym=__boot_flash_size=0x00001000;-Wl,--defsym=__flash=0x00101000;-Wl,--defsym=__flash_size=0x003ff000;-Wl,--defsym=__ram=0x00500000;-Wl,--defsym=__ram_size=0x00800000;-Wl,--defsym=__stack_size=0x00040000"
     CACHE STRING "")
 set(LIBC_TEST_CMD
     "qemu-system-hexagon -M sim -cpu v68 -kernel @BINARY@"
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index be3574c21a730..a8d0dfad76d53 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -513,8 +513,20 @@ CString libc_make_test_file_path_func(const char *file_name);
 
 #ifdef LIBC_TEST_SKIP_DEATH_TESTS
 
-#define EXPECT_DEATH(FUNC, SIG)
-#define ASSERT_DEATH(FUNC, SIG)
+#define EXPECT_DEATH(FUNC, SIG)                                                \
+  do {                                                                         \
+    if (false) {                                                               \
+      (FUNC)();                                                                \
+      (void)(SIG);                                                             \
+    }                                                                          \
+  } while (0)
+#define ASSERT_DEATH(FUNC, SIG)                                                \
+  do {                                                                         \
+    if (false) {                                                               \
+      (FUNC)();                                                                \
+      (void)(SIG);                                                             \
+    }                                                                          \
+  } while (0)
 
 #else
 
@@ -530,10 +542,34 @@ CString libc_make_test_file_path_func(const char *file_name);
 // EXPECT_DEATH can appear in a test of any function, e.g. checking for a crash
 // if passing nullptr to the function. So it must be defined, even if it can't
 // do anything.
-#define EXPECT_EXITS(FUNC, EXIT)
-#define ASSERT_EXITS(FUNC, EXIT)
-#define EXPECT_DEATH(FUNC, SIG)
-#define ASSERT_DEATH(FUNC, SIG)
+#define EXPECT_EXITS(FUNC, EXIT)                                               \
+  do {                                                                         \
+    if (false) {                                                               \
+      (FUNC)();                                                                \
+      (void)(EXIT);                                                            \
+    }                                                                          \
+  } while (0)
+#define ASSERT_EXITS(FUNC, EXIT)                                               \
+  do {                                                                         \
+    if (false) {                                                               \
+      (FUNC)();                                                                \
+      (void)(EXIT);                                                            \
+    }                                                                          \
+  } while (0)
+#define EXPECT_DEATH(FUNC, SIG)                                                \
+  do {                                                                         \
+    if (false) {                                                               \
+      (FUNC)();                                                                \
+      (void)(SIG);                                                             \
+    }                                                                          \
+  } while (0)
+#define ASSERT_DEATH(FUNC, SIG)                                                \
+  do {                                                                         \
+    if (false) {                                                               \
+      (FUNC)();                                                                \
+      (void)(SIG);                                                             \
+    }                                                                          \
+  } while (0)
 
 #endif // LIBC_TEST_SUBPROCESS_TESTS
 
diff --git a/libc/test/UnitTest/llvm-libc-baremetal.ld b/libc/test/UnitTest/llvm-libc-baremetal.ld
index 2f84d934edfe1..49a0f4b1d41e5 100644
--- a/libc/test/UnitTest/llvm-libc-baremetal.ld
+++ b/libc/test/UnitTest/llvm-libc-baremetal.ld
@@ -40,18 +40,24 @@ SECTIONS {
   } > flash
 
   .preinit_array : ALIGN(8) {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
     KEEP(*(.preinit_array))
     KEEP(*(.preinit_array.*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
   } > flash
 
   .init_array : ALIGN(8) {
+    PROVIDE_HIDDEN (__init_array_start = .);
     KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)))
     KEEP(*(.init_array))
+    PROVIDE_HIDDEN (__init_array_end = .);
   } > flash
 
   .fini_array : ALIGN(8) {
+    PROVIDE_HIDDEN (__fini_array_start = .);
     KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)))
     KEEP(*(.fini_array))
+    PROVIDE_HIDDEN (__fini_array_end = .);
   } > flash
 
   .rodata : ALIGN(8) {
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 7b785331f36c6..a30fc4a1dde0a 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -18,7 +18,7 @@ using LIBC_NAMESPACE::fputil::Float128;
 TEST(LlvmLibcSharedMathTest, AllFloat16) {
   using FPBits = LIBC_NAMESPACE::fputil::FPBits<float16>;
 
-  int exponent;
+  int exponent = 0;
   EXPECT_FP_EQ(0.0f16, LIBC_NAMESPACE::shared::acoshf16(1.0f16));
   EXPECT_FP_EQ(0.0f16, LIBC_NAMESPACE::shared::acospif16(1.0f16));
   EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::shared::rsqrtf16(1.0f16));
@@ -167,7 +167,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) {
 
 TEST(LlvmLibcSharedMathTest, AllFloat) {
   using FPBits = LIBC_NAMESPACE::fputil::FPBits<float>;
-  int exponent;
+  int exponent = 0;
 
   EXPECT_FP_EQ(0x1.921fb6p+0, LIBC_NAMESPACE::shared::acosf(0.0f));
   EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::shared::acoshf(1.0f));
@@ -631,7 +631,7 @@ TEST(LlvmLibcSharedMathTest, AllEmuFloat128) {
 
 TEST(LlvmLibcSharedMathTest, AllFloat128) {
   using FPBits = LIBC_NAMESPACE::fputil::FPBits<float128>;
-  int exponent;
+  int exponent = 0;
 
   EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::shared::ffmaf128(
                          float128(0.0), float128(0.0), float128(0.0)));
diff --git a/libc/test/src/__support/File/CMakeLists.txt b/libc/test/src/__support/File/CMakeLists.txt
index bd5fe622fede0..7432a9d740269 100644
--- a/libc/test/src/__support/File/CMakeLists.txt
+++ b/libc/test/src/__support/File/CMakeLists.txt
@@ -19,6 +19,7 @@ add_libc_test(
     libc.src.__support.CPP.new
     libc.src.__support.File.file
     libc.src.__support.error_or
+    libc.hdr.errno_macros
     libc.test.UnitTest.MemoryMatcher
 )
 
diff --git a/libc/test/src/__support/File/file_test.cpp b/libc/test/src/__support/File/file_test.cpp
index db475b946c7df..33f889e3070fd 100644
--- a/libc/test/src/__support/File/file_test.cpp
+++ b/libc/test/src/__support/File/file_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "hdr/errno_macros.h"
 #include "hdr/types/size_t.h"
 #include "hdr/wchar_macros.h"
 #include "src/__support/CPP/new.h"
@@ -18,6 +19,7 @@
 #include "test/UnitTest/Test.h"
 
 using MemoryView = LIBC_NAMESPACE::testing::MemoryView;
+using LIBC_NAMESPACE::Error;
 using LIBC_NAMESPACE::ErrorOr;
 using LIBC_NAMESPACE::File;
 using LIBC_NAMESPACE::FileIOResult;
@@ -97,13 +99,21 @@ FileIOResult StringFile::str_write(LIBC_NAMESPACE::File *f, const void *data,
 ErrorOr<off_t> StringFile::str_seek(LIBC_NAMESPACE::File *f, off_t offset,
                                     int whence) {
   StringFile *sf = static_cast<StringFile *>(f);
+  off_t new_pos;
   if (whence == SEEK_SET)
-    sf->pos = offset;
-  if (whence == SEEK_CUR)
-    sf->pos += offset;
-  if (whence == SEEK_END)
-    sf->pos = SIZE + offset;
-  return sf->pos;
+    new_pos = offset;
+  else if (whence == SEEK_CUR)
+    new_pos = static_cast<off_t>(sf->pos) + offset;
+  else if (whence == SEEK_END)
+    new_pos = static_cast<off_t>(SIZE) + offset;
+  else
+    return Error(EINVAL);
+
+  if (new_pos < 0 || new_pos > static_cast<off_t>(SIZE))
+    return Error(EINVAL);
+
+  sf->pos = static_cast<size_t>(new_pos);
+  return new_pos;
 }
 
 StringFile *new_string_file(char *buffer, size_t buflen, int bufmode,
@@ -503,7 +513,7 @@ TEST(LlvmLibcFileTest, WriteSplit) {
       new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w");
 
   static constexpr size_t AVAIL = 12;
-  f->seek(-AVAIL, SEEK_END);
+  f->seek(-static_cast<off_t>(AVAIL), SEEK_END);
 
   const char data[] = "hello";
   ASSERT_EQ(sizeof(data) - 1, f->write(data, sizeof(data) - 1).value);
@@ -814,13 +824,21 @@ class ShortWriteFile : public File {
   static ErrorOr<off_t> short_seek(LIBC_NAMESPACE::File *f, off_t offset,
                                    int whence) {
     ShortWriteFile *sf = static_cast<ShortWriteFile *>(f);
+    off_t new_pos;
     if (whence == SEEK_SET)
-      sf->pos = offset;
-    if (whence == SEEK_CUR)
-      sf->pos += offset;
-    if (whence == SEEK_END)
-      sf->pos = SIZE + offset;
-    return sf->pos;
+      new_pos = offset;
+    else if (whence == SEEK_CUR)
+      new_pos = static_cast<off_t>(sf->pos) + offset;
+    else if (whence == SEEK_END)
+      new_pos = static_cast<off_t>(SIZE) + offset;
+    else
+      return Error(EINVAL);
+
+    if (new_pos < 0 || new_pos > static_cast<off_t>(SIZE))
+      return Error(EINVAL);
+
+    sf->pos = static_cast<size_t>(new_pos);
+    return new_pos;
   }
 
   static int short_close(LIBC_NAMESPACE::File *f) {
diff --git a/libc/test/src/math/smoke/FrexpTest.h b/libc/test/src/math/smoke/FrexpTest.h
index 15c17828374c5..d68f6b983783c 100644
--- a/libc/test/src/math/smoke/FrexpTest.h
+++ b/libc/test/src/math/smoke/FrexpTest.h
@@ -19,7 +19,7 @@ class FrexpTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   typedef T (*FrexpFunc)(T, int *);
 
   void testSpecialNumbers(FrexpFunc func) {
-    int exponent;
+    int exponent = 0;
     EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN, &exponent));
 #ifdef LIBC_FREXP_INF_NAN_EXPONENT
     EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
@@ -43,7 +43,7 @@ class FrexpTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }
 
   void testPowersOfTwo(FrexpFunc func) {
-    int exponent;
+    int exponent = 0;
 
     EXPECT_FP_EQ_ALL_ROUNDING(T(0.5), func(T(1.0), &exponent));
     EXPECT_EQ(exponent, 1);
@@ -77,7 +77,7 @@ class FrexpTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }
 
   void testSomeIntegers(FrexpFunc func) {
-    int exponent;
+    int exponent = 0;
 
     EXPECT_FP_EQ_ALL_ROUNDING(T(0.75), func(T(24.0), &exponent));
     EXPECT_EQ(exponent, 5);
diff --git a/libc/test/src/math/smoke/NearbyIntTest.h b/libc/test/src/math/smoke/NearbyIntTest.h
index af7a7df3a068d..49cd063e49a5e 100644
--- a/libc/test/src/math/smoke/NearbyIntTest.h
+++ b/libc/test/src/math/smoke/NearbyIntTest.h
@@ -60,20 +60,20 @@ class NearbyIntTestTemplate : public LIBC_NAMESPACE::testing::Test {
     ASSERT_FP_EQ(func(-min_denormal), neg_zero);
   }
 
-  void testSubnormalTowardZero(NearbyIntFunc func) {
+  void testSubnormalTowardZero([[maybe_unused]] NearbyIntFunc func) {
     EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(func(min_denormal), zero);
     EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(func(-min_denormal), neg_zero);
   }
 
-  void testSubnormalToPosInf(NearbyIntFunc func) {
+  void testSubnormalToPosInf([[maybe_unused]] NearbyIntFunc func) {
     EXPECT_FP_EQ_ROUNDING_UPWARD(func(min_denormal), FPBits::one().get_val());
     EXPECT_FP_EQ_ROUNDING_UPWARD(func(-min_denormal), neg_zero);
   }
 
-  void testSubnormalToNegInf(NearbyIntFunc func) {
-    T negative_one = FPBits::one(Sign::NEG).get_val();
+  void testSubnormalToNegInf([[maybe_unused]] NearbyIntFunc func) {
     EXPECT_FP_EQ_ROUNDING_DOWNWARD(func(min_denormal), zero);
-    EXPECT_FP_EQ_ROUNDING_DOWNWARD(func(-min_denormal), negative_one);
+    EXPECT_FP_EQ_ROUNDING_DOWNWARD(func(-min_denormal),
+                                   FPBits::one(Sign::NEG).get_val());
   }
 };
 
diff --git a/libc/test/src/stdfix/DiviFxTest.h b/libc/test/src/stdfix/DiviFxTest.h
index 6bbfcd0f8e9fd..c74285692e108 100644
--- a/libc/test/src/stdfix/DiviFxTest.h
+++ b/libc/test/src/stdfix/DiviFxTest.h
@@ -151,7 +151,7 @@ class DiviFxTest : public LIBC_NAMESPACE::testing::Test {
     }
   }
 
-  void testInvalidNumbers(DiviFxFunc func) {
+  void testInvalidNumbers([[maybe_unused]] DiviFxFunc func) {
 
     EXPECT_DEATH([func] { func(1, zero); }, WITH_SIGNAL(-1));
     if constexpr (has_integral) {
diff --git a/libc/test/src/stdfix/IdivFxTest.h b/libc/test/src/stdfix/IdivFxTest.h
index 1fe6e5706b765..883669f0bf1c7 100644
--- a/libc/test/src/stdfix/IdivFxTest.h
+++ b/libc/test/src/stdfix/IdivFxTest.h
@@ -61,7 +61,7 @@ class IdivFxTest : public LIBC_NAMESPACE::testing::Test {
     }
   }
 
-  void testInvalidNumbers(IdivFxFunc func) {
+  void testInvalidNumbers([[maybe_unused]] IdivFxFunc func) {
     constexpr bool has_integral = (FXRep::INTEGRAL_LEN > 0);
 
     EXPECT_DEATH([func] { func(0.5, 0.0); }, WITH_SIGNAL(-1));

>From 2435acba2f7aec72c5af65f7895492a15bc394d5 Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Thu, 24 Sep 2026 21:16:26 +0530
Subject: [PATCH 4/6] [libc] Explicitly link compiler builtins for tests when
 using -nodefaultlibs

When -nodefaultlibs replaced -nolibc in test link options, compiler runtime
builtins were no longer implicitly linked by the compiler driver.
For targets without an explicit LIBC_TEST_BUILTINS_TARGET or
LIBC_TEST_BUILTINS_LIBRARY (e.g. Linux x86_64 and AArch64 in CI), query the
compiler for its builtins library (-print-libgcc-file-name) or fall back to
-lgcc so tests link cleanly. Also add bare-metal signal-macros.h for
freestanding environments.

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 libc/cmake/modules/LLVMLibCTestRules.cmake | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index d6593efdd400a..64f3d8754be9c 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -513,6 +513,28 @@ if(NOT MSVC AND NOT LIBC_CC_SUPPORTS_NOSTDLIBPP)
   string(STRIP ${LIBGCC_S_LOCATION} LIBGCC_S_LOCATION)
 endif()
 
+# Get the compiler runtime builtins library to be used in hermetic and integration tests
+# when -nodefaultlibs is in effect.
+if(NOT MSVC AND NOT LIBC_TEST_BUILTINS_LIBRARY AND NOT LIBC_TEST_BUILTINS_TARGET)
+  set(target_flags "")
+  if(CMAKE_C_COMPILER_TARGET)
+    list(APPEND target_flags "--target=${CMAKE_C_COMPILER_TARGET}")
+  endif()
+  execute_process(
+    COMMAND ${CMAKE_C_COMPILER} ${target_flags} ${LIBC_COMPILE_OPTIONS_DEFAULT} -print-libgcc-file-name
+    OUTPUT_VARIABLE default_builtins_file
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    RESULT_VARIABLE default_builtins_res
+  )
+  if(default_builtins_res EQUAL 0 AND default_builtins_file AND EXISTS "${default_builtins_file}")
+    set(LIBC_TEST_BUILTINS_LIBRARY "${default_builtins_file}" CACHE FILEPATH
+        "Compiler runtime builtins library to link into libc tests" FORCE)
+  elseif(NOT LIBC_TARGET_OS_IS_BAREMETAL)
+    set(LIBC_TEST_BUILTINS_LIBRARY "-lgcc" CACHE STRING
+        "Compiler runtime builtins library to link into libc tests" FORCE)
+  endif()
+endif()
+
 # DEPRECATED: Use add_hermetic_test instead.
 #
 # Rule to add an integration test. An integration test is like a unit test

>From 22d87954d7c9dc882c606df13903b1d5eff8f5c7 Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Thu, 24 Sep 2026 22:07:04 +0530
Subject: [PATCH 5/6] [libc] Skip stack_chk_guard_test on bare-metal targets

stack_chk_guard_test tests __stack_chk_fail via EXPECT_DEATH with
SIGABRT. Bare-metal targets do not have OS signal handling or subprocess
death test support, and do not provide signal headers. Skip this test on
bare-metal targets.

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 libc/test/src/compiler/CMakeLists.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libc/test/src/compiler/CMakeLists.txt b/libc/test/src/compiler/CMakeLists.txt
index eaff2906c216c..430f07a576e23 100644
--- a/libc/test/src/compiler/CMakeLists.txt
+++ b/libc/test/src/compiler/CMakeLists.txt
@@ -1,5 +1,9 @@
 add_custom_target(libc_stack_chk_guard_unittests)
 
+if(LIBC_TARGET_OS_IS_BAREMETAL)
+  return()
+endif()
+
 add_libc_test(
   stack_chk_guard_test
   SUITE

>From a4c7b83fab8aa6f05e7c1fe7b7e98de752b44bee Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Thu, 24 Sep 2026 22:54:46 +0530
Subject: [PATCH 6/6] [Github] Use unversioned lld for baremetal-hexagon in
 libc CI

Clang's Hexagon driver checks whether the linker executable name ends
with 'ld.lld' to determine if the linker is LLD. When -fuse-ld=lld-23 is
used, the check fails, causing Clang to assume GNU ld and pass
-mcpu=hexagonv68 which ld.lld rejects as an unknown emulation.

Ensure a symlink /usr/bin/ld.lld exists and set -DLLVM_USE_LINKER=lld
for baremetal-hexagon in CI.

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 .github/workflows/libc-fullbuild-tests.yml | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/libc-fullbuild-tests.yml b/.github/workflows/libc-fullbuild-tests.yml
index a0fcccb59c7ac..e2856bfbe526f 100644
--- a/.github/workflows/libc-fullbuild-tests.yml
+++ b/.github/workflows/libc-fullbuild-tests.yml
@@ -34,6 +34,7 @@ jobs:
             build_type: Debug
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: x86_64-unknown-linux-llvm
             include_scudo: ON
             testing: BUILD
@@ -42,6 +43,7 @@ jobs:
             build_type: Release
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: x86_64-unknown-linux-llvm
             include_scudo: ON
             build_fuzzing_tests: ON
@@ -50,6 +52,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: x86_64-unknown-linux-llvm
             include_scudo: ON
             testing: BUILD
@@ -58,6 +61,7 @@ jobs:
             build_type: Release
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: aarch64-unknown-linux-llvm
             include_scudo: ON
           - name: uefi-x86_64-clang
@@ -65,6 +69,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: x86_64-unknown-uefi-llvm
             testing: SKIP
           - name: baremetal-armv6m
@@ -72,6 +77,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: armv6m-none-eabi
             testing: SKIP
           - name: baremetal-armv7m
@@ -79,6 +85,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: armv7m-none-eabi
             testing: SKIP
           - name: baremetal-armv7em
@@ -86,6 +93,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: armv7em-none-eabi
             testing: SKIP
           - name: baremetal-armv8m-softfp
@@ -93,6 +101,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: armv8m.main-none-eabi
             testing: SKIP
           - name: baremetal-armv8m-hard
@@ -100,6 +109,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: armv8m.main-none-eabihf
             testing: SKIP
           - name: baremetal-armv8.1m
@@ -107,6 +117,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: armv8.1m.main-none-eabi
             testing: SKIP
           - name: baremetal-riscv32
@@ -114,6 +125,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: riscv32-unknown-elf
             testing: SKIP
           - name: baremetal-hexagon
@@ -121,6 +133,7 @@ jobs:
             build_type: MinSizeRel
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld
             target: hexagon-unknown-none-elf
             build_builtins: ON
             testing: RUN
@@ -129,6 +142,7 @@ jobs:
             build_type: Release
             c_compiler: clang-23
             cpp_compiler: clang++-23
+            linker: lld-23
             target: amdgcn-amd-amdhsa
             testing: SKIP
           # TODO: add back gcc build when it is fixed
@@ -167,7 +181,7 @@ jobs:
           -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
           -DCMAKE_C_COMPILER_LAUNCHER=sccache
           -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
-          -DLLVM_USE_LINKER=lld-23
+          -DLLVM_USE_LINKER=${{ matrix.linker }}
           -DCMAKE_INSTALL_PREFIX=/__w/llvm-project/llvm-project/install
           -DLIBC_COMPILE_OPTIONS_NATIVE=''
         "
@@ -196,6 +210,9 @@ jobs:
             if [[ "${{ matrix.build_builtins }}" == "ON" ]]; then
               export CMAKE_FLAGS="$CMAKE_FLAGS -DLLVM_ENABLE_RUNTIMES=$RUNTIMES"
             fi
+            if [[ "${{ matrix.name }}" == "baremetal-hexagon" ]]; then
+              sudo ln -sf /usr/bin/ld.lld-23 /usr/bin/ld.lld
+            fi
             cmake $CMAKE_FLAGS \
               -C /__w/llvm-project/llvm-project/libc/cmake/caches/${{ matrix.target }}.cmake
             ;;



More information about the libc-commits mailing list