[compiler-rt] [libcxx] [libcxxabi] [libunwind] [llvm] for ohos llvm libcxx (PR #208390)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 9 01:17:03 PDT 2026


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #



More information about the cfe-commits mailing list