diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -476,6 +476,10 @@
   set(CMAKE_BUILD_TYPE "COVERAGE" CACHE STRING "" FORCE)
 endif()
 
+if (LIBCXX_INCLUDE_TESTS)
+  find_program(QEMU_SYSTEM_ARM qemu-system-arm)
+endif()
+
 #===============================================================================
 # Setup Compiler Flags
 #===============================================================================
diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst
--- a/libcxx/docs/index.rst
+++ b/libcxx/docs/index.rst
@@ -130,7 +130,7 @@
 Linux                 i386, x86_64, arm, arm64  Only glibc-2.24 and later and no other libc is officially supported
 Windows               i386, x86_64              Both MSVC and MinGW style environments, ABI in MSVC environments is :doc:`unstable <DesignDocs/ABIVersioning>`
 AIX 7.2TL5+           powerpc, powerpc64
-Embedded (picolibc)   arm                       Support for building with picolibc is currently work-in-progress
+Embedded (picolibc)   arm
 ===================== ========================= ============================
 
 Generally speaking, libc++ should work on any platform that provides a fairly complete
diff --git a/libcxx/test/configs/armv7m-libc++.cfg.in b/libcxx/test/configs/armv7m-libc++.cfg.in
--- a/libcxx/test/configs/armv7m-libc++.cfg.in
+++ b/libcxx/test/configs/armv7m-libc++.cfg.in
@@ -22,9 +22,17 @@
     ' -Wl,--defsym=__stack_size=0x1000'
 ))
 config.substitutions.append(('%{exec}',
-    'true' # TODO use qemu-system-arm
+    '%{executor}'
+    ' --execdir %T'
+    ' @LIBCXX_SOURCE_DIR@/utils/run-qemu.sh'
+    ' @QEMU_SYSTEM_ARM@'
+    ' mps2-an385'
+    ' cortex-m3'
 ))
 
+# Long tests are prohibitively slow when run via emulation.
+config.long_tests = False
+
 # LIBCXX-PICOLIBC-FIXME is the feature name used to XFAIL the
 # initial picolibc failures until they can be properly diagnosed
 # and fixed. This allows easier detection of new test failures
diff --git a/libcxx/test/libcxx/selftest/convenience_substitutions/build_run.sh.cpp b/libcxx/test/libcxx/selftest/convenience_substitutions/build_run.sh.cpp
--- a/libcxx/test/libcxx/selftest/convenience_substitutions/build_run.sh.cpp
+++ b/libcxx/test/libcxx/selftest/convenience_substitutions/build_run.sh.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// Command line arguments not supported.
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
 // Make sure that we provide the %{build} and %{run} convenience substitutions.
 
 // RUN: %{build}
diff --git a/libcxx/test/libcxx/selftest/dsl/dsl.sh.py b/libcxx/test/libcxx/selftest/dsl/dsl.sh.py
--- a/libcxx/test/libcxx/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/libcxx/selftest/dsl/dsl.sh.py
@@ -6,8 +6,9 @@
 #
 # ===----------------------------------------------------------------------===##
 
-# Picolibc test executor is `true` at present.
-# XFAIL: LIBCXX-PICOLIBC-FIXME
+# With picolibc test_pass_arguments_to_program fails because
+# command line arguments are not supported.
+# UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
 # Note: We prepend arguments with 'x' to avoid thinking there are too few
 #       arguments in case an argument is an empty string.
diff --git a/libcxx/test/libcxx/selftest/pass.cpp/run-error.pass.cpp b/libcxx/test/libcxx/selftest/pass.cpp/run-error.pass.cpp
--- a/libcxx/test/libcxx/selftest/pass.cpp/run-error.pass.cpp
+++ b/libcxx/test/libcxx/selftest/pass.cpp/run-error.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 // XFAIL: *
 
 // Make sure the test DOES NOT pass if it fails at runtime.
diff --git a/libcxx/test/libcxx/selftest/pass.mm/run-error.pass.mm b/libcxx/test/libcxx/selftest/pass.mm/run-error.pass.mm
--- a/libcxx/test/libcxx/selftest/pass.mm/run-error.pass.mm
+++ b/libcxx/test/libcxx/selftest/pass.mm/run-error.pass.mm
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: objective-c++
-// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
 // XFAIL: *
 
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/complexity.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: c++03, c++11
 
+// qemu: fatal: Lockup: can't escalate 3 to HardFault (current priority -1)
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
 // <algorithm>
 
 // template<class Iter>
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
+// qemu: fatal: Lockup: can't escalate 3 to HardFault (current priority -1)
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
 // <algorithm>
 
 // template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/sort.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/sort.pass.cpp
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/sort.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/sort.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// This test appears to hang with picolibc & qemu.
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
 // <algorithm>
 
 // template<RandomAccessIterator Iter>
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/generic_category.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/generic_category.pass.cpp
--- a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/generic_category.pass.cpp
+++ b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/generic_category.pass.cpp
@@ -8,6 +8,7 @@
 
 // XFAIL: suse-linux-enterprise-server-11
 // XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{9|10|11|12}}
+// XFAIL: LIBCXX-PICOLIBC-FIXME
 
 // <system_error>
 
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp
--- a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp
+++ b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp
@@ -14,6 +14,7 @@
 
 // XFAIL: suse-linux-enterprise-server-11
 // XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{9|10|11|12}}
+// XFAIL: LIBCXX-PICOLIBC-FIXME
 
 #include <system_error>
 #include <cassert>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array14.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array14.pass.cpp
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array14.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array14.pass.cpp
@@ -9,7 +9,6 @@
 // test sized operator delete[] replacement.
 
 // UNSUPPORTED: sanitizer-new-delete, c++03, c++11
-// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
 // NOTE: Clang does not enable sized-deallocation in C++14 and beyond by
 // default. It is only enabled when -fsized-deallocation is given.
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete14.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete14.pass.cpp
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete14.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete14.pass.cpp
@@ -9,7 +9,6 @@
 // test sized operator delete replacement.
 
 // UNSUPPORTED: sanitizer-new-delete, c++03, c++11
-// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
 // NOTE: Clang does not enable sized-deallocation in C++14 and beyond by
 // default. It is only enabled when -fsized-deallocation is given.
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.statics/classic_table.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.statics/classic_table.pass.cpp
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.statics/classic_table.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.statics/classic_table.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
 // <locale>
 
 // template <> class ctype<char>
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.pass.cpp
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.pass.cpp
@@ -13,6 +13,7 @@
 // iter_type put(iter_type s, ios_base& iob, char_type fill, long double v) const;
 
 // XFAIL: win32-broken-printf-g-precision
+// XFAIL: LIBCXX-PICOLIBC-FIXME
 
 #include <locale>
 #include <ios>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
@@ -18,6 +18,9 @@
 // Serializing/deserializing the state of the RNG requires iostreams
 // UNSUPPORTED: no-localization
 
+// This test appears to hang with picolibc & qemu.
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
 #include <random>
 #include <numeric>
 #include <vector>
diff --git a/libcxx/test/std/time/time.clock/time.clock.file/now.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.file/now.pass.cpp
--- a/libcxx/test/std/time/time.clock/time.clock.file/now.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.file/now.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: availability-filesystem-missing
 
+// qemu: Unsupported SemiHosting SWI 0x30
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
 // <chrono>
 
 // file_clock
diff --git a/libcxx/utils/run-qemu.sh b/libcxx/utils/run-qemu.sh
new file mode 100755
--- /dev/null
+++ b/libcxx/utils/run-qemu.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# This script makes it easy to pass an image to qemu as the last
+# command line argument.
+
+# Usage:
+# run-qemu.sh QEMU_EXECUTABLE MACHINE CPU IMAGE
+
+QEMU_EXECUTABLE=$1
+MACHINE=$2
+CPU=$3
+IMAGE=$4
+
+"${QEMU_EXECUTABLE}" \
+    -chardev stdio,mux=on,id=stdio0 \
+    -semihosting-config enable=on,chardev=stdio0 \
+    -monitor none \
+    -serial none \
+    -machine ${MACHINE},accel=tcg \
+    -cpu ${CPU} \
+    -device loader,file="${IMAGE}",cpu-num=0 \
+    -nographic
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -240,6 +240,10 @@
 # Include macros for adding and removing libc++abi flags.
 include(HandleLibcxxabiFlags)
 
+if (LIBCXXABI_INCLUDE_TESTS)
+  find_program(QEMU_SYSTEM_ARM qemu-system-arm)
+endif()
+
 #===============================================================================
 # Setup Compiler Flags
 #===============================================================================
diff --git a/libcxxabi/test/configs/armv7m-libc++abi.cfg.in b/libcxxabi/test/configs/armv7m-libc++abi.cfg.in
--- a/libcxxabi/test/configs/armv7m-libc++abi.cfg.in
+++ b/libcxxabi/test/configs/armv7m-libc++abi.cfg.in
@@ -22,7 +22,12 @@
     ' -Wl,--defsym=__stack_size=0x1000'
 ))
 config.substitutions.append(('%{exec}',
-    'true' # TODO use qemu-system-arm
+    '%{executor}'
+    ' --execdir %T'
+    ' @LIBCXXABI_LIBCXX_PATH@/utils/run-qemu.sh'
+    ' @QEMU_SYSTEM_ARM@'
+    ' mps2-an385'
+    ' cortex-m3'
 ))
 
 # LIBCXX-PICOLIBC-FIXME is the feature name used to XFAIL the
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -151,6 +151,10 @@
 # Include macros for adding and removing libunwind flags.
 include(HandleLibunwindFlags)
 
+if (LIBUNWIND_INCLUDE_TESTS)
+  find_program(QEMU_SYSTEM_ARM qemu-system-arm)
+endif()
+
 #===============================================================================
 # Setup Compiler Flags
 #===============================================================================
diff --git a/libunwind/test/configs/armv7m-libunwind.cfg.in b/libunwind/test/configs/armv7m-libunwind.cfg.in
--- a/libunwind/test/configs/armv7m-libunwind.cfg.in
+++ b/libunwind/test/configs/armv7m-libunwind.cfg.in
@@ -22,7 +22,12 @@
     ' -Wl,--defsym=__stack_size=0x1000'
 ))
 config.substitutions.append(('%{exec}',
-    'true' # TODO use qemu-system-arm
+    '%{executor}'
+    ' --execdir %T'
+    ' @LIBUNWIND_LIBCXX_PATH@/utils/run-qemu.sh'
+    ' @QEMU_SYSTEM_ARM@'
+    ' mps2-an385'
+    ' cortex-m3'
 ))
 
 import os, site