https://github.com/jasonmolenda created 
https://github.com/llvm/llvm-project/pull/153911

I've wanted a utility to create a corefile for test purposes given a bit of 
memory and regsters, for a while.  I've written a few API tests over the years 
that needed exactly this capability -- we have several one-off Mach-O corefile 
creator utility in the API testsuite to do this.  But it's a lot of boilerplate 
when you only want to specify some register contents and memory contents, to 
create an API test.

This adds yaml2mach-core, a tool that should build on any system, takes a yaml 
description of register values for one or more threads, optionally memory 
values for one or more memory regions, and can take a list of UUIDs that will 
be added as LC_NOTE "load binary" metadata to the corefile so binaries can be 
loaded into virtual address space in a test scenario.

The format of the yaml file looks like

cpu: armv7m
endian: little
threads:
  - regsets: - flavor: gpr registers: [{name: sp, value: 0x2000fe70}, {name: 
r7, value: 0x2000fe80}, {name: pc, value: 0x0020392c}, {name: lr, value: 
0x0020392d}]

memory-regions:
  - addr: 0x2000fe70 UInt32: [ 0x0000002a, 0x20010e58, 0x00203923, 0x00000001, 
0x2000fe88, 0x00203911, 0x2000ffdc, 0xfffffff9 ]
  - addr: 0x203910 UInt8: [ 0xf8, 0xb5, 0x04, 0xaf, 0x06, 0x4c, 0x07, 0x49, 
0x74, 0xf0, 0x2e, 0xf8, 0x01, 0xac, 0x74, 0xf0 ]

and that's all that is needed to specify a corefile where four register values 
are specified (the others will be set to 0), and two memory regions will be 
emitted.

The memory can be specified as an array of UInt8, UInt32, or UInt64, I 
anticipate that some of these corefiles may have stack values constructed 
manually and it may be simpler for a human to write them in a particular 
grouping of values.

Accepting "endian" is probably a boondoggle that won't ever come to any use, 
and honestly I don't 100% know what the correct byte layout would be for a big 
endian Mach-O file any more.  In a RISC-V discussion a month ago, it was noted 
that register byte layout will be little endian even when there is a big endian 
defined format for RV, so memory would be byteswapped but registers would not.  
It may have been better not to pretend to support this, but on the other hand 
it might be neat to be able to generate a big endian test case simply.

I needed this utility for an upcoming patch for ARM Cortex-M processors, to 
create a test for the change.  I took the opportunity to remove two of the 
"trivial mach-o corefile" creator utilities I've written in the past, which 
also restricted the tests to only run on Darwin systems because I was using the 
system headers for Mach-O constant values.

rdar://110663219

>From 69ed12fb21bbdde89bf43b1356c5542912655318 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Fri, 15 Aug 2025 18:23:10 -0700
Subject: [PATCH] [lldb] Add utility to create Mach-O corefile from YAML desc

I've wanted a utility to create a corefile for test purposes given
a bit of memory and regsters, for a while.  I've written a few API
tests over the years that needed exactly this capability -- we have
several one-off Mach-O corefile creator utility in the API testsuite
to do this.  But it's a lot of boilerplate when you only want to
specify some register contents and memory contents, to create an
API test.

This adds yaml2mach-core, a tool that should build on any system,
takes a yaml description of register values for one or more threads,
optionally memory values for one or more memory regions, and can
take a list of UUIDs that will be added as LC_NOTE "load binary"
metadata to the corefile so binaries can be loaded into virtual
address space in a test scenario.

The format of the yaml file looks like

cpu: armv7m
endian: little
threads:
  - regsets:
      - flavor: gpr
        registers: [{name: sp, value: 0x2000fe70}, {name: r7, value: 
0x2000fe80},
                    {name: pc, value: 0x0020392c}, {name: lr, value: 
0x0020392d}]

memory-regions:
  - addr: 0x2000fe70
    UInt32: [
      0x0000002a, 0x20010e58, 0x00203923, 0x00000001,
      0x2000fe88, 0x00203911, 0x2000ffdc, 0xfffffff9
    ]
  - addr: 0x203910
    UInt8: [
      0xf8, 0xb5, 0x04, 0xaf, 0x06, 0x4c, 0x07, 0x49,
      0x74, 0xf0, 0x2e, 0xf8, 0x01, 0xac, 0x74, 0xf0
    ]

and that's all that is needed to specify a corefile where four register
values are specified (the others will be set to 0), and two memory
regions will be emitted.

The memory can be specified as an array of UInt8, UInt32, or UInt64,
I anticipate that some of these corefiles may have stack values
constructed manually and it may be simpler for a human to write
them in a particular grouping of values.

Accepting "endian" is probably a boondoggle that won't ever come
to any use, and honestly I don't 100% know what the correct byte
layout would be for a big endian Mach-O file any more.  In a RISC-V
discussion a month ago, it was noted that register byte layout will
be little endian even when there is a big endian defined format for
RV, so memory would be byteswapped but registers would not.  It may
have been better not to pretend to support this, but on the other
hand it might be neat to be able to generate a big endian test case
simply.

I needed this utility for an upcoming patch for ARM Cortex-M processors,
to create a test for the change.  I took the opportunity to remove two
of the "trivial mach-o corefile" creator utilities I've written in the
past, which also restricted the tests to only run on Darwin systems
because I was using the system headers for Mach-O constant values.

rdar://110663219
---
 .../Python/lldbsuite/test/configuration.py    |  10 +
 lldb/packages/Python/lldbsuite/test/dotest.py |   1 +
 .../Python/lldbsuite/test/lldbtest.py         |  15 +
 .../ObjectFile/JSON/ObjectFileJSON.cpp        |  36 +++
 .../Plugins/ObjectFile/JSON/ObjectFileJSON.h  |   3 +
 .../API/macosx/arm-corefile-regctx/Makefile   |   6 -
 .../TestArmMachoCorefileRegctx.py             |  17 +-
 .../API/macosx/arm-corefile-regctx/arm64.yaml |  31 ++
 .../macosx/arm-corefile-regctx/armv7m.yaml    |  37 +++
 .../create-arm-corefiles.cpp                  | 266 ------------------
 .../test/API/macosx/riscv32-corefile/Makefile |   7 -
 .../riscv32-corefile/TestRV32MachOCorefile.py |  18 +-
 .../create-empty-riscv-corefile.cpp           | 116 --------
 .../riscv32-corefile/riscv32-registers.yaml   |  47 ++++
 lldb/tools/CMakeLists.txt                     |   2 +
 lldb/tools/yaml2macho-core/CMakeLists.txt     |  13 +
 lldb/tools/yaml2macho-core/CoreSpec.h         |  56 ++++
 lldb/tools/yaml2macho-core/LCNoteWriter.cpp   |  68 +++++
 lldb/tools/yaml2macho-core/LCNoteWriter.h     |  23 ++
 lldb/tools/yaml2macho-core/MemoryWriter.cpp   |  57 ++++
 lldb/tools/yaml2macho-core/MemoryWriter.h     |  22 ++
 lldb/tools/yaml2macho-core/ThreadWriter.cpp   | 190 +++++++++++++
 lldb/tools/yaml2macho-core/ThreadWriter.h     |  19 ++
 lldb/tools/yaml2macho-core/Utility.cpp        |  57 ++++
 lldb/tools/yaml2macho-core/Utility.h          |  23 ++
 lldb/tools/yaml2macho-core/main.cpp           | 223 +++++++++++++++
 lldb/tools/yaml2macho-core/yaml2corespec.cpp  | 131 +++++++++
 lldb/tools/yaml2macho-core/yaml2corespec.h    |  16 ++
 28 files changed, 1099 insertions(+), 411 deletions(-)
 delete mode 100644 lldb/test/API/macosx/arm-corefile-regctx/Makefile
 create mode 100644 lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml
 create mode 100644 lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml
 delete mode 100644 
lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp
 delete mode 100644 lldb/test/API/macosx/riscv32-corefile/Makefile
 delete mode 100644 
lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp
 create mode 100644 lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml
 create mode 100644 lldb/tools/yaml2macho-core/CMakeLists.txt
 create mode 100644 lldb/tools/yaml2macho-core/CoreSpec.h
 create mode 100644 lldb/tools/yaml2macho-core/LCNoteWriter.cpp
 create mode 100644 lldb/tools/yaml2macho-core/LCNoteWriter.h
 create mode 100644 lldb/tools/yaml2macho-core/MemoryWriter.cpp
 create mode 100644 lldb/tools/yaml2macho-core/MemoryWriter.h
 create mode 100644 lldb/tools/yaml2macho-core/ThreadWriter.cpp
 create mode 100644 lldb/tools/yaml2macho-core/ThreadWriter.h
 create mode 100644 lldb/tools/yaml2macho-core/Utility.cpp
 create mode 100644 lldb/tools/yaml2macho-core/Utility.h
 create mode 100644 lldb/tools/yaml2macho-core/main.cpp
 create mode 100644 lldb/tools/yaml2macho-core/yaml2corespec.cpp
 create mode 100644 lldb/tools/yaml2macho-core/yaml2corespec.h

diff --git a/lldb/packages/Python/lldbsuite/test/configuration.py 
b/lldb/packages/Python/lldbsuite/test/configuration.py
index 5e3810992d172..1a9f25d66843a 100644
--- a/lldb/packages/Python/lldbsuite/test/configuration.py
+++ b/lldb/packages/Python/lldbsuite/test/configuration.py
@@ -64,6 +64,9 @@
 # Path to the yaml2obj tool. Not optional.
 yaml2obj = None
 
+# Path to the yaml2macho-core tool. Not optional.
+yaml2macho_core = None
+
 # The arch might dictate some specific CFLAGS to be passed to the toolchain to 
build
 # the inferior programs.  The global variable cflags_extras provides a hook to 
do
 # just that.
@@ -174,3 +177,10 @@ def get_yaml2obj_path():
     """
     if yaml2obj and os.path.lexists(yaml2obj):
         return yaml2obj
+
+def get_yaml2macho_core_path():
+    """
+    Get the path to the yaml2macho-core tool.
+    """
+    if yaml2macho_core and os.path.lexists(yaml2macho_core):
+        return yaml2macho_core
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py 
b/lldb/packages/Python/lldbsuite/test/dotest.py
index 47a3c2ed2fc9d..89b6807b41075 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -280,6 +280,7 @@ def parseOptionsAndInitTestdirs():
         configuration.llvm_tools_dir = args.llvm_tools_dir
         configuration.filecheck = shutil.which("FileCheck", 
path=args.llvm_tools_dir)
         configuration.yaml2obj = shutil.which("yaml2obj", 
path=args.llvm_tools_dir)
+        configuration.yaml2macho_core = shutil.which("yaml2macho-core", 
path=args.llvm_tools_dir)
 
     if not configuration.get_filecheck_path():
         logging.warning("No valid FileCheck executable; some tests may 
fail...")
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py 
b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 0fc85fcc4d2d6..599b019f0df8c 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1702,6 +1702,21 @@ def yaml2obj(self, yaml_path, obj_path, max_size=None):
             command += ["--max-size=%d" % max_size]
         self.runBuildCommand(command)
 
+    def yaml2macho_core(self, yaml_path, obj_path, uuids=None):
+        """
+        Create a Mach-O corefile at the given path from a yaml file.
+
+        Throws subprocess.CalledProcessError if the object could not be 
created.
+        """
+        yaml2macho_core_bin = configuration.get_yaml2macho_core_path()
+        if not yaml2macho_core_bin:
+            self.assertTrue(False, "No valid yaml2macho-core executable 
specified")
+        if uuids != None:
+          command = [yaml2macho_core_bin, "-i", yaml_path, "-o", obj_path, 
"-u", uuids]
+        else:
+          command = [yaml2macho_core_bin, "-i", yaml_path, "-o", obj_path]
+        self.runBuildCommand(command)
+
     def cleanup(self, dictionary=None):
         """Platform specific way to do cleanup after build."""
         module = builder_module()
diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp 
b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp
index cb8ba05d461d4..0aff98078120e 100644
--- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp
+++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp
@@ -12,6 +12,7 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Target.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 #include "llvm/ADT/DenseSet.h"
@@ -233,6 +234,41 @@ void ObjectFileJSON::CreateSections(SectionList 
&unified_section_list) {
   }
 }
 
+bool ObjectFileJSON::SetLoadAddress(Target &target, lldb::addr_t value,
+                                    bool value_is_offset) {
+  Log *log(GetLog(LLDBLog::DynamicLoader));
+  if (!m_sections_up)
+    return true;
+
+  const bool warn_multiple = true;
+
+  addr_t slide = value;
+  if (!value_is_offset) {
+    addr_t lowest_addr = LLDB_INVALID_ADDRESS;
+    for (const SectionSP &section_sp : *m_sections_up) {
+      addr_t section_load_addr = section_sp->GetFileAddress();
+      lowest_addr = std::min(lowest_addr, section_load_addr);
+    }
+    if (lowest_addr == LLDB_INVALID_ADDRESS)
+      return false;
+    slide = value - lowest_addr;
+  }
+
+  // Apply slide to each section's file address.
+  for (const SectionSP &section_sp : *m_sections_up) {
+    addr_t section_load_addr = section_sp->GetFileAddress();
+    if (section_load_addr != LLDB_INVALID_ADDRESS) {
+      LLDB_LOGF(
+          log,
+          "ObjectFileJSON::SetLoadAddress section %s to load addr 0x%" PRIx64,
+          section_sp->GetName().AsCString(), section_load_addr + slide);
+      target.SetSectionLoadAddress(section_sp, section_load_addr + slide,
+                                   warn_multiple);
+    }
+  }
+  return true;
+}
+
 bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp,
                                      lldb::addr_t data_offset,
                                      lldb::addr_t data_length) {
diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h 
b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h
index b72565f468862..029c8ff188934 100644
--- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h
+++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h
@@ -86,6 +86,9 @@ class ObjectFileJSON : public ObjectFile {
 
   Strata CalculateStrata() override { return eStrataUser; }
 
+  bool SetLoadAddress(Target &target, lldb::addr_t value,
+                      bool value_is_offset) override;
+
   static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset,
                               lldb::addr_t length);
 
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/Makefile 
b/lldb/test/API/macosx/arm-corefile-regctx/Makefile
deleted file mode 100644
index e1d0354441cd4..0000000000000
--- a/lldb/test/API/macosx/arm-corefile-regctx/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-MAKE_DSYM := NO
-
-CXX_SOURCES := create-arm-corefiles.cpp
-
-include Makefile.rules
-
diff --git 
a/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py 
b/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py
index 6754288a65e1a..a2890cdfeaa44 100644
--- a/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py
+++ b/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py
@@ -13,20 +13,14 @@
 class TestArmMachoCorefileRegctx(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    @skipUnlessDarwin
-    def setUp(self):
-        TestBase.setUp(self)
-        self.build()
-        self.create_corefile = self.getBuildArtifact("a.out")
-        self.corefile = self.getBuildArtifact("core")
-
     def test_armv7_corefile(self):
         ### Create corefile
-        retcode = call(self.create_corefile + " armv7 " + self.corefile, 
shell=True)
+        corefile = self.getBuildArtifact("core")
+        self.yaml2macho_core("armv7m.yaml", corefile)
 
         target = self.dbg.CreateTarget("")
         err = lldb.SBError()
-        process = target.LoadCore(self.corefile)
+        process = target.LoadCore(corefile)
         self.assertTrue(process.IsValid())
         thread = process.GetSelectedThread()
         frame = thread.GetSelectedFrame()
@@ -50,11 +44,12 @@ def test_armv7_corefile(self):
 
     def test_arm64_corefile(self):
         ### Create corefile
-        retcode = call(self.create_corefile + " arm64 " + self.corefile, 
shell=True)
+        corefile = self.getBuildArtifact("core")
+        self.yaml2macho_core("arm64.yaml", corefile)
 
         target = self.dbg.CreateTarget("")
         err = lldb.SBError()
-        process = target.LoadCore(self.corefile)
+        process = target.LoadCore(corefile)
         self.assertTrue(process.IsValid())
         thread = process.GetSelectedThread()
         frame = thread.GetSelectedFrame()
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml 
b/lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml
new file mode 100644
index 0000000000000..4c23b69302a02
--- /dev/null
+++ b/lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml
@@ -0,0 +1,31 @@
+cpu: arm64
+endian: little
+threads:
+  # (lldb) reg read
+  # % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: 
" $2 "},"}'
+  - regsets:
+      - flavor: gpr
+        registers: [
+           {name: x0, value: 0x0000000000000001}, {name: x1, value: 
0x000000016fdff3c0},
+           {name: x2, value: 0x000000016fdff3d0}, {name: x3, value: 
0x000000016fdff510},
+           {name: x4, value: 0x0000000000000000}, {name: x5, value: 
0x0000000000000000},
+           {name: x6, value: 0x0000000000000000}, {name: x7, value: 
0x0000000000000000},
+           {name: x8, value: 0x000000010000d910}, {name: x9, value: 
0x0000000000000001},
+           {name: x10, value: 0xe1e88de000000000}, {name: x11, value: 
0x0000000000000003},
+           {name: x12, value: 0x0000000000000148}, {name: x13, value: 
0x0000000000004000},
+           {name: x14, value: 0x0000000000000008}, {name: x15, value: 
0x0000000000000000},
+           {name: x16, value: 0x0000000000000000}, {name: x17, value: 
0x0000000100003f5c},
+           {name: x18, value: 0x0000000000000000}, {name: x19, value: 
0x0000000100003f5c},
+           {name: x20, value: 0x000000010000c000}, {name: x21, value: 
0x000000010000d910},
+           {name: x22, value: 0x000000016fdff250}, {name: x23, value: 
0x000000018ce12366},
+           {name: x24, value: 0x000000016fdff1d0}, {name: x25, value: 
0x0000000000000001},
+           {name: x26, value: 0x0000000000000000}, {name: x27, value: 
0x0000000000000000},
+           {name: x28, value: 0x0000000000000000}, {name: fp, value: 
0x000000016fdff3a0},
+           {name: lr, value: 0x000000018cd97f28}, {name: sp, value: 
0x000000016fdff140},
+           {name: pc, value: 0x0000000100003f5c}, {name: cpsr, value: 
0x80001000}
+        ]
+      - flavor: exc
+        registers: [ {name: far, value: 0x0000000100003f5c}, 
+                     {name: esr, value: 0xf2000000}, 
+                     {name: exception, value: 0x00000000}
+                   ]
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml 
b/lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml
new file mode 100644
index 0000000000000..1351056ed0999
--- /dev/null
+++ b/lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml
@@ -0,0 +1,37 @@
+cpu: armv7m
+endian: little
+threads:
+  # (lldb) reg read
+  # % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: 
" $2 "},"}'
+  - regsets:
+      - flavor: gpr
+        registers: [
+          {name: r0, value: 0x00010000}, {name: r1, value: 0x00020000},
+          {name: r2, value: 0x00030000}, {name: r3, value: 0x00040000},
+          {name: r4, value: 0x00050000}, {name: r5, value: 0x00060000},
+          {name: r6, value: 0x00070000}, {name: r7, value: 0x00080000},
+          {name: r8, value: 0x00090000}, {name: r9, value: 0x000a0000},
+          {name: r10, value: 0x000b0000}, {name: r11, value: 0x000c0000},
+          {name: r12, value: 0x000d0000}, {name: sp, value: 0x000e0000},
+          {name: lr, value: 0x000f0000}, {name: pc, value: 0x00100000},
+          {name: cpsr, value: 0x00110000}
+        ]
+      - flavor: exc
+        registers: [ {name: far, value: 0x00003f5c},
+                     {name: esr, value: 0xf2000000},
+                     {name: exception, value: 0x00000000}
+                   ]
+
+memory-regions:
+  # $sp is 0x000e0000, have bytes surrounding that address
+  - addr: 0x000dffe0
+    UInt8: [
+            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
+            0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+            0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+            0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+            0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 
+            0x3f
+           ]
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp 
b/lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp
deleted file mode 100644
index db39f12ecfb7e..0000000000000
--- a/lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-#include <mach-o/loader.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-#include <vector>
-
-
-// Normally these are picked up by including <mach/thread_status.h>
-// but that does a compile time check for the build host arch and
-// only defines the ARM register context constants when building on
-// an arm system.  We're creating fake corefiles, and might be
-// creating them on an intel system.
-#ifndef ARM_THREAD_STATE
-#define ARM_THREAD_STATE 1
-#endif
-#ifndef ARM_THREAD_STATE_COUNT
-#define ARM_THREAD_STATE_COUNT 17
-#endif
-#ifndef ARM_EXCEPTION_STATE
-#define ARM_EXCEPTION_STATE 3
-#endif
-#ifndef ARM_EXCEPTION_STATE_COUNT
-#define ARM_EXCEPTION_STATE_COUNT 3
-#endif
-#ifndef ARM_THREAD_STATE64
-#define ARM_THREAD_STATE64 6
-#endif
-#ifndef ARM_THREAD_STATE64_COUNT
-#define ARM_THREAD_STATE64_COUNT 68
-#endif
-#ifndef ARM_EXCEPTION_STATE64
-#define ARM_EXCEPTION_STATE64 7
-#endif
-#ifndef ARM_EXCEPTION_STATE64_COUNT
-#define ARM_EXCEPTION_STATE64_COUNT 4
-#endif
-
-union uint32_buf {
-  uint8_t bytebuf[4];
-  uint32_t val;
-};
-
-union uint64_buf {
-  uint8_t bytebuf[8];
-  uint64_t val;
-};
-
-void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
-  uint64_buf conv;
-  conv.val = val;
-  for (int i = 0; i < 8; i++)
-    buf.push_back(conv.bytebuf[i]);
-}
-
-void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
-  uint32_buf conv;
-  conv.val = val;
-  for (int i = 0; i < 4; i++)
-    buf.push_back(conv.bytebuf[i]);
-}
-
-std::vector<uint8_t> armv7_lc_thread_load_command() {
-  std::vector<uint8_t> data;
-  add_uint32(data, LC_THREAD);              // thread_command.cmd
-  add_uint32(data, 104);                    // thread_command.cmdsize
-  add_uint32(data, ARM_THREAD_STATE);       // thread_command.flavor
-  add_uint32(data, ARM_THREAD_STATE_COUNT); // thread_command.count
-  add_uint32(data, 0x00010000);             // r0
-  add_uint32(data, 0x00020000);             // r1
-  add_uint32(data, 0x00030000);             // r2
-  add_uint32(data, 0x00040000);             // r3
-  add_uint32(data, 0x00050000);             // r4
-  add_uint32(data, 0x00060000);             // r5
-  add_uint32(data, 0x00070000);             // r6
-  add_uint32(data, 0x00080000);             // r7
-  add_uint32(data, 0x00090000);             // r8
-  add_uint32(data, 0x000a0000);             // r9
-  add_uint32(data, 0x000b0000);             // r10
-  add_uint32(data, 0x000c0000);             // r11
-  add_uint32(data, 0x000d0000);             // r12
-  add_uint32(data, 0x000e0000);             // sp
-  add_uint32(data, 0x000f0000);             // lr
-  add_uint32(data, 0x00100000);             // pc
-  add_uint32(data, 0x00110000);             // cpsr
-
-  add_uint32(data, ARM_EXCEPTION_STATE);       // thread_command.flavor
-  add_uint32(data, ARM_EXCEPTION_STATE_COUNT); // thread_command.count
-  add_uint32(data, 0x00003f5c);                // far
-  add_uint32(data, 0xf2000000);                // esr
-  add_uint32(data, 0x00000000);                // exception
-
-  return data;
-}
-
-std::vector<uint8_t> arm64_lc_thread_load_command() {
-  std::vector<uint8_t> data;
-  add_uint32(data, LC_THREAD);                // thread_command.cmd
-  add_uint32(data, 312);                      // thread_command.cmdsize
-  add_uint32(data, ARM_THREAD_STATE64);       // thread_command.flavor
-  add_uint32(data, ARM_THREAD_STATE64_COUNT); // thread_command.count
-  add_uint64(data, 0x0000000000000001);       // x0
-  add_uint64(data, 0x000000016fdff3c0);       // x1
-  add_uint64(data, 0x000000016fdff3d0);       // x2
-  add_uint64(data, 0x000000016fdff510);       // x3
-  add_uint64(data, 0x0000000000000000);       // x4
-  add_uint64(data, 0x0000000000000000);       // x5
-  add_uint64(data, 0x0000000000000000);       // x6
-  add_uint64(data, 0x0000000000000000);       // x7
-  add_uint64(data, 0x000000010000d910);       // x8
-  add_uint64(data, 0x0000000000000001);       // x9
-  add_uint64(data, 0xe1e88de000000000);       // x10
-  add_uint64(data, 0x0000000000000003);       // x11
-  add_uint64(data, 0x0000000000000148);       // x12
-  add_uint64(data, 0x0000000000004000);       // x13
-  add_uint64(data, 0x0000000000000008);       // x14
-  add_uint64(data, 0x0000000000000000);       // x15
-  add_uint64(data, 0x0000000000000000);       // x16
-  add_uint64(data, 0x0000000100003f5c);       // x17
-  add_uint64(data, 0x0000000000000000);       // x18
-  add_uint64(data, 0x0000000100003f5c);       // x19
-  add_uint64(data, 0x000000010000c000);       // x20
-  add_uint64(data, 0x000000010000d910);       // x21
-  add_uint64(data, 0x000000016fdff250);       // x22
-  add_uint64(data, 0x000000018ce12366);       // x23
-  add_uint64(data, 0x000000016fdff1d0);       // x24
-  add_uint64(data, 0x0000000000000001);       // x25
-  add_uint64(data, 0x0000000000000000);       // x26
-  add_uint64(data, 0x0000000000000000);       // x27
-  add_uint64(data, 0x0000000000000000);       // x28
-  add_uint64(data, 0x000000016fdff3a0);       // fp
-  add_uint64(data, 0x000000018cd97f28);       // lr
-  add_uint64(data, 0x000000016fdff140);       // sp
-  add_uint64(data, 0x0000000100003f5c);       // pc
-  add_uint32(data, 0x80001000);               // cpsr
-
-  add_uint32(data, 0x00000000); // padding
-
-  add_uint32(data, ARM_EXCEPTION_STATE64);       // thread_command.flavor
-  add_uint32(data, ARM_EXCEPTION_STATE64_COUNT); // thread_command.count
-  add_uint64(data, 0x0000000100003f5c);          // far
-  add_uint32(data, 0xf2000000);                  // esr
-  add_uint32(data, 0x00000000);                  // exception
-
-  return data;
-}
-
-std::vector<uint8_t> lc_segment(uint32_t fileoff,
-                                uint32_t lc_segment_data_size) {
-  std::vector<uint8_t> data;
-  // 0x000e0000 is the value of $sp in the armv7 LC_THREAD
-  uint32_t start_vmaddr = 0x000e0000 - (lc_segment_data_size / 2);
-  add_uint32(data, LC_SEGMENT);                     // segment_command.cmd
-  add_uint32(data, sizeof(struct segment_command)); // segment_command.cmdsize
-  for (int i = 0; i < 16; i++)
-    data.push_back(0);                    // segment_command.segname[16]
-  add_uint32(data, start_vmaddr);         // segment_command.vmaddr
-  add_uint32(data, lc_segment_data_size); // segment_command.vmsize
-  add_uint32(data, fileoff);              // segment_command.fileoff
-  add_uint32(data, lc_segment_data_size); // segment_command.filesize
-  add_uint32(data, 3);                    // segment_command.maxprot
-  add_uint32(data, 3);                    // segment_command.initprot
-  add_uint32(data, 0);                    // segment_command.nsects
-  add_uint32(data, 0);                    // segment_command.flags
-
-  return data;
-}
-
-enum arch { unspecified, armv7, arm64 };
-
-int main(int argc, char **argv) {
-  if (argc != 3) {
-    fprintf(stderr,
-            "usage: create-arm-corefiles [armv7|arm64] <output-core-name>\n");
-    exit(1);
-  }
-
-  arch arch = unspecified;
-
-  if (strcmp(argv[1], "armv7") == 0)
-    arch = armv7;
-  else if (strcmp(argv[1], "arm64") == 0)
-    arch = arm64;
-  else {
-    fprintf(stderr, "unrecognized architecture %s\n", argv[1]);
-    exit(1);
-  }
-
-  // An array of load commands (in the form of byte arrays)
-  std::vector<std::vector<uint8_t>> load_commands;
-
-  // An array of corefile contents (page data, lc_note data, etc)
-  std::vector<uint8_t> payload;
-
-  // First add all the load commands / payload so we can figure out how large
-  // the load commands will actually be.
-  if (arch == armv7) {
-    load_commands.push_back(armv7_lc_thread_load_command());
-    load_commands.push_back(lc_segment(0, 0));
-  } else if (arch == arm64) {
-    load_commands.push_back(arm64_lc_thread_load_command());
-  }
-
-  int size_of_load_commands = 0;
-  for (const auto &lc : load_commands)
-    size_of_load_commands += lc.size();
-
-  int header_and_load_cmd_room =
-      sizeof(struct mach_header_64) + size_of_load_commands;
-
-  // Erase the load commands / payload now that we know how much space is
-  // needed, redo it.
-  load_commands.clear();
-  payload.clear();
-
-  int payload_fileoff = (header_and_load_cmd_room + 4096 - 1) & ~(4096 - 1);
-
-  const int lc_segment_data_size = 64;
-  if (arch == armv7) {
-    load_commands.push_back(armv7_lc_thread_load_command());
-    load_commands.push_back(lc_segment(payload_fileoff, lc_segment_data_size));
-  } else if (arch == arm64) {
-    load_commands.push_back(arm64_lc_thread_load_command());
-  }
-
-  if (arch == armv7)
-    for (int i = 0; i < lc_segment_data_size;
-         i++) // from segment_command.filesize
-      payload.push_back(i);
-
-  struct mach_header_64 mh;
-  int header_size;
-  if (arch == armv7) {
-    mh.magic = MH_MAGIC;
-    mh.cputype = CPU_TYPE_ARM;
-    mh.cpusubtype = CPU_SUBTYPE_ARM_V7M;
-    header_size = sizeof(struct mach_header);
-  } else if (arch == arm64) {
-    mh.magic = MH_MAGIC_64;
-    mh.cputype = CPU_TYPE_ARM64;
-    mh.cpusubtype = CPU_SUBTYPE_ARM64_ALL;
-    header_size = sizeof(struct mach_header_64);
-  }
-  mh.filetype = MH_CORE;
-  mh.ncmds = load_commands.size();
-  mh.sizeofcmds = size_of_load_commands;
-  mh.flags = 0;
-  mh.reserved = 0;
-
-  FILE *f = fopen(argv[2], "w");
-
-  if (f == nullptr) {
-    fprintf(stderr, "Unable to open file %s for writing\n", argv[2]);
-    exit(1);
-  }
-
-  fwrite(&mh, header_size, 1, f);
-
-  for (const auto &lc : load_commands)
-    fwrite(lc.data(), lc.size(), 1, f);
-
-  fseek(f, payload_fileoff, SEEK_SET);
-
-  fwrite(payload.data(), payload.size(), 1, f);
-
-  fclose(f);
-}
diff --git a/lldb/test/API/macosx/riscv32-corefile/Makefile 
b/lldb/test/API/macosx/riscv32-corefile/Makefile
deleted file mode 100644
index 04f268758d00c..0000000000000
--- a/lldb/test/API/macosx/riscv32-corefile/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-MAKE_DSYM := NO
-CXX_SOURCES := create-empty-riscv-corefile.cpp
-EXE := create-empty-riscv-corefile
-
-all: create-empty-riscv-corefile
-
-include Makefile.rules
diff --git a/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py 
b/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py
index 8d11821d38985..e35ed2d2b7799 100644
--- a/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py
+++ b/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py
@@ -13,18 +13,17 @@
 class TestRV32MachOCorefile(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    @skipUnlessDarwin
+    @no_debug_info_test
     def test_riscv32_gpr_corefile_registers(self):
-        self.build()
-        create_corefile = self.getBuildArtifact("create-empty-riscv-corefile")
         corefile = self.getBuildArtifact("core")
-        call(create_corefile + " " + corefile, shell=True)
+        self.yaml2macho_core("riscv32-registers.yaml", corefile)
+        # call(create_corefile + " " + corefile, shell=True)
 
         target = self.dbg.CreateTarget("")
         process = target.LoadCore(corefile)
 
         process = target.GetProcess()
-        self.assertEqual(process.GetNumThreads(), 1)
+        self.assertEqual(process.GetNumThreads(), 2)
 
         thread = process.GetThreadAtIndex(0)
         self.assertEqual(thread.GetNumFrames(), 1)
@@ -80,3 +79,12 @@ def test_riscv32_gpr_corefile_registers(self):
             val = idx | (idx << 8) | (idx << 16) | (idx << 24)
             
self.assertEqual(gpr_regs.GetChildAtIndex(idx).GetValueAsUnsigned(), val)
             idx = idx + 1
+
+        thread = process.GetThreadAtIndex(1)
+        self.assertEqual(thread.GetNumFrames(), 1)
+
+        frame = thread.GetFrameAtIndex(0)
+        gpr_regs = frame.registers.GetValueAtIndex(0)
+
+        self.assertEqual(gpr_regs.GetChildAtIndex(0).GetValueAsUnsigned(), 
0x90000000)
+        self.assertEqual(gpr_regs.GetChildAtIndex(32).GetValueAsUnsigned(), 
0x90202020)
diff --git 
a/lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp 
b/lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp
deleted file mode 100644
index 907cca3b70b41..0000000000000
--- a/lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <inttypes.h>
-#include <mach-o/loader.h>
-#include <mach/thread_status.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <string>
-#include <sys/errno.h>
-#include <uuid/uuid.h>
-#include <vector>
-
-#define CPU_TYPE_RISCV 24
-#define CPU_SUBTYPE_RISCV_ALL 0
-#define RV32_THREAD_STATE 2
-// x0-x31 + pc, all 32-bit
-#define RV32_THREAD_STATE_COUNT 33
-
-union uint32_buf {
-  uint8_t bytebuf[4];
-  uint32_t val;
-};
-
-union uint64_buf {
-  uint8_t bytebuf[8];
-  uint64_t val;
-};
-
-void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
-  uint64_buf conv;
-  conv.val = val;
-  for (int i = 0; i < 8; i++)
-    buf.push_back(conv.bytebuf[i]);
-}
-
-void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
-  uint32_buf conv;
-  conv.val = val;
-  for (int i = 0; i < 4; i++)
-    buf.push_back(conv.bytebuf[i]);
-}
-
-std::vector<uint8_t> lc_thread_load_command() {
-  std::vector<uint8_t> data;
-  add_uint32(data, LC_THREAD); // thread_command.cmd
-  add_uint32(data, 4 + 4 + 4 + 4 +
-                       (RV32_THREAD_STATE_COUNT * 4)); // 
thread_command.cmdsize
-  add_uint32(data, RV32_THREAD_STATE);                 // thread_command.flavor
-  add_uint32(data, RV32_THREAD_STATE_COUNT);           // thread_command.count
-  for (int i = 0; i < RV32_THREAD_STATE_COUNT; i++) {
-    add_uint32(data, i | (i << 8) | (i << 16) | (i << 24));
-  }
-  return data;
-}
-
-int main(int argc, char **argv) {
-  if (argc != 2) {
-    fprintf(stderr,
-            "usage: create-empty-riscv-corefile output-corefile-name\n");
-    exit(1);
-  }
-
-  cpu_type_t cputype = CPU_TYPE_RISCV;
-  cpu_subtype_t cpusubtype = CPU_SUBTYPE_RISCV_ALL;
-
-  // An array of load commands (in the form of byte arrays)
-  std::vector<std::vector<uint8_t>> load_commands;
-
-  // An array of corefile contents (page data, lc_note data, etc)
-  std::vector<uint8_t> payload;
-
-  // First add all the load commands / payload so we can figure out how large
-  // the load commands will actually be.
-  load_commands.push_back(lc_thread_load_command());
-
-  int size_of_load_commands = 0;
-  for (const auto &lc : load_commands)
-    size_of_load_commands += lc.size();
-
-  int header_and_load_cmd_room =
-      sizeof(struct mach_header_64) + size_of_load_commands;
-
-  // Erase the load commands / payload now that we know how much space is
-  // needed, redo it.
-  load_commands.clear();
-  payload.clear();
-
-  load_commands.push_back(lc_thread_load_command());
-
-  struct mach_header mh;
-  mh.magic = MH_MAGIC;
-  mh.cputype = cputype;
-
-  mh.cpusubtype = cpusubtype;
-  mh.filetype = MH_CORE;
-  mh.ncmds = load_commands.size();
-  mh.sizeofcmds = size_of_load_commands;
-  mh.flags = 0;
-
-  FILE *f = fopen(argv[1], "w");
-
-  if (f == nullptr) {
-    fprintf(stderr, "Unable to open file %s for writing\n", argv[1]);
-    exit(1);
-  }
-
-  fwrite(&mh, sizeof(struct mach_header), 1, f);
-
-  for (const auto &lc : load_commands)
-    fwrite(lc.data(), lc.size(), 1, f);
-
-  fseek(f, header_and_load_cmd_room, SEEK_SET);
-
-  fwrite(payload.data(), payload.size(), 1, f);
-
-  fclose(f);
-}
diff --git a/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml 
b/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml
new file mode 100644
index 0000000000000..5862904759fa4
--- /dev/null
+++ b/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml
@@ -0,0 +1,47 @@
+cpu: riscv
+endian: little
+threads:
+  # (lldb) reg read
+  # % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: 
" $2 "},"}'
+  - regsets:
+      - flavor: gpr
+        registers: [
+          {name: zero, value: 0x00000000}, {name: ra, value: 0x01010101},
+          {name: sp, value: 0x02020202}, {name: gp, value: 0x03030303},
+          {name: tp, value: 0x04040404}, {name: t0, value: 0x05050505},
+          {name: t1, value: 0x06060606}, {name: t2, value: 0x07070707},
+          {name: fp, value: 0x08080808}, {name: s1, value: 0x09090909},
+          {name: a0, value: 0x0a0a0a0a}, {name: a1, value: 0x0b0b0b0b},
+          {name: a2, value: 0x0c0c0c0c}, {name: a3, value: 0x0d0d0d0d},
+          {name: a4, value: 0x0e0e0e0e}, {name: a5, value: 0x0f0f0f0f},
+          {name: a6, value: 0x10101010}, {name: a7, value: 0x11111111},
+          {name: s2, value: 0x12121212}, {name: s3, value: 0x13131313},
+          {name: s4, value: 0x14141414}, {name: s5, value: 0x15151515},
+          {name: s6, value: 0x16161616}, {name: s7, value: 0x17171717},
+          {name: s8, value: 0x18181818}, {name: s9, value: 0x19191919},
+          {name: s10, value: 0x1a1a1a1a}, {name: s11, value: 0x1b1b1b1b},
+          {name: t3, value: 0x1c1c1c1c}, {name: t4, value: 0x1d1d1d1d},
+          {name: t5, value: 0x1e1e1e1e}, {name: t6, value: 0x1f1f1f1f},
+          {name: pc, value: 0x20202020}
+        ]
+  - regsets:
+      - flavor: gpr
+        registers: [
+          {name: zero, value: 0x90000000}, {name: ra, value: 0x01010101},
+          {name: sp, value: 0x92020202}, {name: gp, value: 0x03030303},
+          {name: tp, value: 0x94040404}, {name: t0, value: 0x05050505},
+          {name: t1, value: 0x96060606}, {name: t2, value: 0x07070707},
+          {name: fp, value: 0x98080808}, {name: s1, value: 0x09090909},
+          {name: a0, value: 0x9a0a0a0a}, {name: a1, value: 0x0b0b0b0b},
+          {name: a2, value: 0x9c0c0c0c}, {name: a3, value: 0x0d0d0d0d},
+          {name: a4, value: 0x9e0e0e0e}, {name: a5, value: 0x0f0f0f0f},
+          {name: a6, value: 0x90101010}, {name: a7, value: 0x11111111},
+          {name: s2, value: 0x92121212}, {name: s3, value: 0x13131313},
+          {name: s4, value: 0x94141414}, {name: s5, value: 0x15151515},
+          {name: s6, value: 0x96161616}, {name: s7, value: 0x17171717},
+          {name: s8, value: 0x98181818}, {name: s9, value: 0x19191919},
+          {name: s10, value: 0x9a1a1a1a}, {name: s11, value: 0x1b1b1b1b},
+          {name: t3, value: 0x9c1c1c1c}, {name: t4, value: 0x1d1d1d1d},
+          {name: t5, value: 0x9e1e1e1e}, {name: t6, value: 0x1f1f1f1f},
+          {name: pc, value: 0x90202020}
+        ]
diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt
index e2f039527ad75..663208022f71e 100644
--- a/lldb/tools/CMakeLists.txt
+++ b/lldb/tools/CMakeLists.txt
@@ -27,3 +27,5 @@ endif()
 if (LLDB_CAN_USE_LLDB_SERVER)
   add_lldb_tool_subdirectory(lldb-server)
 endif()
+
+add_subdirectory(yaml2macho-core)
diff --git a/lldb/tools/yaml2macho-core/CMakeLists.txt 
b/lldb/tools/yaml2macho-core/CMakeLists.txt
new file mode 100644
index 0000000000000..a880e7dfcaa64
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_lldb_tool(yaml2macho-core
+    main.cpp
+    yaml2corespec.cpp
+    LCNoteWriter.cpp
+    MemoryWriter.cpp
+    ThreadWriter.cpp
+    Utility.cpp
+
+    LINK_COMPONENTS
+      Support
+    LINK_LIBS
+      ${LLDB_SYSTEM_LIBS}
+)
diff --git a/lldb/tools/yaml2macho-core/CoreSpec.h 
b/lldb/tools/yaml2macho-core/CoreSpec.h
new file mode 100644
index 0000000000000..6625599cd4ef1
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/CoreSpec.h
@@ -0,0 +1,56 @@
+//===-- CoreSpec.h 
--------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_CORESPEC_H
+#define YAML2MACHOCOREFILE_CORESPEC_H
+#include <string>
+#include <vector>
+
+struct RegisterNameAndValue {
+  std::string name;
+  uint64_t value;
+};
+
+enum RegisterFlavor { GPR = 0, FPR, EXC };
+
+struct RegisterSet {
+  RegisterFlavor flavor;
+  std::vector<RegisterNameAndValue> registers;
+};
+
+struct Thread {
+  std::vector<RegisterSet> regsets;
+};
+
+enum Endian { Big = 0, Little = 1 };
+
+enum MemoryType { UInt8 = 0, UInt32, UInt64 };
+
+struct MemoryRegion {
+  uint64_t addr;
+  MemoryType type;
+  uint32_t size;
+  // One of the following formats.
+  std::vector<uint8_t> bytes;
+  std::vector<uint32_t> words;
+  std::vector<uint64_t> doublewords;
+};
+
+struct CoreSpec {
+  uint32_t cputype;
+  uint32_t cpusubtype;
+  int wordsize;
+  Endian endian;
+
+  std::vector<Thread> threads;
+  std::vector<MemoryRegion> memory_regions;
+
+  CoreSpec() : cputype(0), cpusubtype(0), wordsize(0), endian(Endian::Little) 
{}
+};
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/LCNoteWriter.cpp 
b/lldb/tools/yaml2macho-core/LCNoteWriter.cpp
new file mode 100644
index 0000000000000..351774d63dea8
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/LCNoteWriter.cpp
@@ -0,0 +1,68 @@
+//===-- LCNoteWriter.cpp 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LCNoteWriter.h"
+#include "Utility.h"
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "llvm/BinaryFormat/MachO.h"
+
+bool ishex(char p) {
+  char upp = toupper(p);
+  if (isdigit(upp) || (upp >= 'A' && upp <= 'F'))
+    return true;
+  return false;
+}
+
+void create_lc_note_binary_load_cmd(const CoreSpec &spec,
+                                    std::vector<uint8_t> &cmds,
+                                    std::string uuid, uint64_t slide,
+                                    std::vector<uint8_t> &payload_bytes,
+                                    off_t data_offset) {
+
+  // Add the payload bytes to payload_bytes.
+  size_t starting_payload_size = payload_bytes.size();
+  add_uint32(spec, payload_bytes, 1); // version
+  // uuid_t uuid
+  const char *p = uuid.c_str();
+  while (*p && *(p + 1)) {
+    if (ishex(*p) && ishex(*(p + 1))) {
+      char byte[3] = {'\0', '\0', '\0'};
+      byte[0] = *p++;
+      byte[1] = *p++;
+      uint8_t val = strtoul(byte, nullptr, 16);
+      payload_bytes.push_back(val);
+    } else {
+      p++;
+    }
+  }
+  add_uint64(spec, payload_bytes, UINT64_MAX); // address
+  add_uint64(spec, payload_bytes, slide);      // slide
+  payload_bytes.push_back(0);                  // name_cstring
+
+  size_t payload_size = payload_bytes.size() - starting_payload_size;
+  // Pad out the entry to a 4-byte aligned size.
+  if (payload_bytes.size() % 4 != 0) {
+    size_t pad_bytes =
+        ((payload_bytes.size() + 4 - 1) & (~4 - 1)) - payload_bytes.size();
+    for (size_t i = 0; i < pad_bytes; i++)
+      payload_bytes.push_back(0);
+  }
+
+  // Add the load command bytes to cmds.
+  add_uint32(spec, cmds, llvm::MachO::LC_NOTE);
+  add_uint32(spec, cmds, sizeof(struct llvm::MachO::note_command));
+  char cmdname[16];
+  memset(cmdname, '\0', sizeof(cmdname));
+  strcpy(cmdname, "load binary");
+  for (int i = 0; i < 16; i++)
+    cmds.push_back(cmdname[i]);
+  add_uint64(spec, cmds, data_offset);
+  add_uint64(spec, cmds, payload_size);
+}
diff --git a/lldb/tools/yaml2macho-core/LCNoteWriter.h 
b/lldb/tools/yaml2macho-core/LCNoteWriter.h
new file mode 100644
index 0000000000000..dcbc39d58ae14
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/LCNoteWriter.h
@@ -0,0 +1,23 @@
+//===-- LCNoteWriter.h 
----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_LCNOTEWRITER_H
+#define YAML2MACHOCOREFILE_LCNOTEWRITER_H
+
+#include "CoreSpec.h"
+
+#include <stdio.h>
+#include <vector>
+
+void create_lc_note_binary_load_cmd(const CoreSpec &spec,
+                                    std::vector<uint8_t> &cmds,
+                                    std::string uuid, uint64_t slide,
+                                    std::vector<uint8_t> &payload_bytes,
+                                    off_t data_offset);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/MemoryWriter.cpp 
b/lldb/tools/yaml2macho-core/MemoryWriter.cpp
new file mode 100644
index 0000000000000..7788e0a0deb8f
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/MemoryWriter.cpp
@@ -0,0 +1,57 @@
+//===-- MemoryWriter.cpp 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MemoryWriter.h"
+#include "CoreSpec.h"
+#include "Utility.h"
+
+#include "llvm/BinaryFormat/MachO.h"
+
+void create_lc_segment_cmd(const CoreSpec &spec, std::vector<uint8_t> &cmds,
+                           const MemoryRegion &memory, off_t data_offset) {
+  if (spec.wordsize == 8) {
+    // Add the bytes for a segment_command_64 from <mach-o/loader.h>
+    add_uint32(spec, cmds, llvm::MachO::LC_SEGMENT_64);
+    add_uint32(spec, cmds, sizeof(struct llvm::MachO::segment_command_64));
+    for (int i = 0; i < 16; i++)
+      cmds.push_back(0);
+    add_uint64(spec, cmds, memory.addr); // segment_command_64.vmaddr
+    add_uint64(spec, cmds, memory.size); // segment_command_64.vmsize
+    add_uint64(spec, cmds, data_offset); // segment_command_64.fileoff
+    add_uint64(spec, cmds, memory.size); // segment_command_64.filesize
+  } else {
+    // Add the bytes for a segment_command from <mach-o/loader.h>
+    add_uint32(spec, cmds, llvm::MachO::LC_SEGMENT);
+    add_uint32(spec, cmds, sizeof(struct llvm::MachO::segment_command));
+    for (int i = 0; i < 16; i++)
+      cmds.push_back(0);
+    add_uint32(spec, cmds, memory.addr); // segment_command_64.vmaddr
+    add_uint32(spec, cmds, memory.size); // segment_command_64.vmsize
+    add_uint32(spec, cmds, data_offset); // segment_command_64.fileoff
+    add_uint32(spec, cmds, memory.size); // segment_command_64.filesize
+  }
+  add_uint32(spec, cmds, 3); // segment_command_64.maxprot
+  add_uint32(spec, cmds, 3); // segment_command_64.initprot
+  add_uint32(spec, cmds, 0); // segment_command_64.nsects
+  add_uint32(spec, cmds, 0); // segment_command_64.flags
+}
+
+void create_memory_bytes(const CoreSpec &spec, const MemoryRegion &memory,
+                         std::vector<uint8_t> &buf) {
+  if (memory.type == MemoryType::UInt8)
+    for (uint8_t byte : memory.bytes)
+      buf.push_back(byte);
+
+  if (memory.type == MemoryType::UInt32)
+    for (uint32_t word : memory.words)
+      add_uint32(spec, buf, word);
+
+  if (memory.type == MemoryType::UInt64)
+    for (uint64_t word : memory.doublewords)
+      add_uint64(spec, buf, word);
+}
diff --git a/lldb/tools/yaml2macho-core/MemoryWriter.h 
b/lldb/tools/yaml2macho-core/MemoryWriter.h
new file mode 100644
index 0000000000000..a97926acac269
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/MemoryWriter.h
@@ -0,0 +1,22 @@
+//===-- MemoryWriter.h 
----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_MEMORYWRITER_H
+#define YAML2MACHOCOREFILE_MEMORYWRITER_H
+
+#include "CoreSpec.h"
+
+#include <vector>
+
+void create_lc_segment_cmd(const CoreSpec &spec, std::vector<uint8_t> &cmds,
+                           const MemoryRegion &memory, off_t data_offset);
+
+void create_memory_bytes(const CoreSpec &spec, const MemoryRegion &memory,
+                         std::vector<uint8_t> &buf);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/ThreadWriter.cpp 
b/lldb/tools/yaml2macho-core/ThreadWriter.cpp
new file mode 100644
index 0000000000000..2fbb810b1f15d
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/ThreadWriter.cpp
@@ -0,0 +1,190 @@
+//===-- ThreadWriter.cpp 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ThreadWriter.h"
+#include "CoreSpec.h"
+#include "Utility.h"
+
+#include "llvm/BinaryFormat/MachO.h"
+
+#include <algorithm>
+#include <stdio.h>
+
+#define ARM_THREAD_STATE 1
+#define ARM_THREAD_STATE_COUNT 17
+#define ARM_EXCEPTION_STATE 3
+#define ARM_EXCEPTION_STATE_COUNT 3
+
+std::vector<RegisterNameAndValue>::const_iterator
+find_by_name(std::vector<RegisterNameAndValue>::const_iterator first,
+             std::vector<RegisterNameAndValue>::const_iterator last,
+             const char *name) {
+  for (; first != last; ++first)
+    if (first->name == name)
+      return first;
+  return last;
+}
+
+void add_reg_value(CoreSpec &spec, std::vector<uint8_t> &buf,
+                   const std::vector<RegisterNameAndValue> &registers,
+                   const char *regname, int regsize) {
+  const auto it = find_by_name(registers.begin(), registers.end(), regname);
+  if (it != registers.end()) {
+    if (regsize == 8)
+      add_uint64(spec, buf, it->value);
+    else
+      add_uint32(spec, buf, it->value);
+  } else {
+    if (regsize == 8)
+      add_uint64(spec, buf, 0);
+    else
+      add_uint32(spec, buf, 0);
+  }
+}
+
+void add_lc_threads_armv7(CoreSpec &spec,
+                          std::vector<std::vector<uint8_t>> &load_commands) {
+  for (const Thread &th : spec.threads) {
+    std::vector<uint8_t> lc;
+    int size_of_all_flavors = 0;
+    for (const RegisterSet &rs : th.regsets) {
+      if (rs.flavor == RegisterFlavor::GPR)
+        size_of_all_flavors += (ARM_THREAD_STATE_COUNT * 4);
+      if (rs.flavor == RegisterFlavor::EXC)
+        size_of_all_flavors += (ARM_EXCEPTION_STATE_COUNT * 4);
+    }
+    int cmdsize = 4 * 2;                  // cmd, cmdsize
+    cmdsize += 4 * 2 * th.regsets.size(); // flavor, count (per register 
flavor)
+    cmdsize += size_of_all_flavors;       // size of all the register set data
+
+    add_uint32(spec, lc, llvm::MachO::LC_THREAD); // thread_command.cmd
+    add_uint32(spec, lc, cmdsize);                // thread_command.cmdsize
+    for (const RegisterSet &rs : th.regsets) {
+      if (rs.flavor == RegisterFlavor::GPR) {
+        add_uint32(spec, lc, ARM_THREAD_STATE);       // thread_command.flavor
+        add_uint32(spec, lc, ARM_THREAD_STATE_COUNT); // thread_command.count
+        const char *names[] = {"r0",  "r1", "r2", "r3", "r4",   "r5",
+                               "r6",  "r7", "r8", "r9", "r10",  "r11",
+                               "r12", "sp", "lr", "pc", "cpsr", nullptr};
+        for (int i = 0; names[i]; i++)
+          add_reg_value(spec, lc, rs.registers, names[i], 4);
+      }
+      if (rs.flavor == RegisterFlavor::EXC) {
+        add_uint32(spec, lc, ARM_EXCEPTION_STATE); // thread_command.flavor
+        add_uint32(spec, lc, ARM_EXCEPTION_STATE_COUNT); // 
thread_command.count
+        const char *names[] = {"far", "esr", "exception", nullptr};
+        for (int i = 0; names[i]; i++)
+          add_reg_value(spec, lc, rs.registers, names[i], 4);
+      }
+    }
+    load_commands.push_back(lc);
+  }
+}
+
+#define ARM_THREAD_STATE64 6
+#define ARM_THREAD_STATE64_COUNT 68
+#define ARM_EXCEPTION_STATE64 7
+#define ARM_EXCEPTION_STATE64_COUNT 4
+
+void add_lc_threads_arm64(CoreSpec &spec,
+                          std::vector<std::vector<uint8_t>> &load_commands) {
+  for (const Thread &th : spec.threads) {
+    std::vector<uint8_t> lc;
+    int size_of_all_flavors = 0;
+    for (const RegisterSet &rs : th.regsets) {
+      if (rs.flavor == RegisterFlavor::GPR)
+        size_of_all_flavors += (ARM_THREAD_STATE64_COUNT * 4);
+      if (rs.flavor == RegisterFlavor::EXC)
+        size_of_all_flavors += (ARM_EXCEPTION_STATE64_COUNT * 4);
+    }
+    int cmdsize = 4 * 2;                  // cmd, cmdsize
+    cmdsize += 4 * 2 * th.regsets.size(); // flavor, count (per register 
flavor)
+    cmdsize += size_of_all_flavors;       // size of all the register set data
+
+    add_uint32(spec, lc, llvm::MachO::LC_THREAD); // thread_command.cmd
+    add_uint32(spec, lc, cmdsize);                // thread_command.cmdsize
+
+    for (const RegisterSet &rs : th.regsets) {
+      if (rs.flavor == RegisterFlavor::GPR) {
+        add_uint32(spec, lc, ARM_THREAD_STATE64);       // 
thread_command.flavor
+        add_uint32(spec, lc, ARM_THREAD_STATE64_COUNT); // thread_command.count
+        const char *names[] = {"x0",  "x1",  "x2",  "x3",  "x4",  "x5",   "x6",
+                               "x7",  "x8",  "x9",  "x10", "x11", "x12",  
"x13",
+                               "x14", "x15", "x16", "x17", "x18", "x19",  
"x20",
+                               "x21", "x22", "x23", "x24", "x25", "x26",  
"x27",
+                               "x28", "fp",  "lr",  "sp",  "pc",  nullptr};
+        for (int i = 0; names[i]; i++)
+          add_reg_value(spec, lc, rs.registers, names[i], 8);
+
+        // cpsr is a 4-byte reg
+        add_reg_value(spec, lc, rs.registers, "cpsr", 4);
+        // the 4 bytes of zeroes
+        add_uint32(spec, lc, 0);
+      }
+      if (rs.flavor == RegisterFlavor::EXC) {
+        add_uint32(spec, lc, ARM_EXCEPTION_STATE64); // thread_command.flavor
+        add_uint32(spec, lc,
+                   ARM_EXCEPTION_STATE64_COUNT); // thread_command.count
+        add_reg_value(spec, lc, rs.registers, "far", 8);
+        add_reg_value(spec, lc, rs.registers, "esr", 4);
+        add_reg_value(spec, lc, rs.registers, "exception", 4);
+      }
+    }
+    load_commands.push_back(lc);
+  }
+}
+
+#define RV32_THREAD_STATE 2
+#define RV32_THREAD_STATE_COUNT 33
+
+void add_lc_threads_riscv(CoreSpec &spec,
+                          std::vector<std::vector<uint8_t>> &load_commands) {
+  for (const Thread &th : spec.threads) {
+    std::vector<uint8_t> lc;
+    int size_of_all_flavors = 0;
+    for (const RegisterSet &rs : th.regsets) {
+      if (rs.flavor == RegisterFlavor::GPR)
+        size_of_all_flavors += (RV32_THREAD_STATE_COUNT * 4);
+    }
+    int cmdsize = 4 * 2;                  // cmd, cmdsize
+    cmdsize += 4 * 2 * th.regsets.size(); // flavor, count (per register 
flavor)
+    cmdsize += size_of_all_flavors;       // size of all the register set data
+
+    add_uint32(spec, lc, llvm::MachO::LC_THREAD); // thread_command.cmd
+    add_uint32(spec, lc, cmdsize);                // thread_command.cmdsize
+    for (const RegisterSet &rs : th.regsets) {
+      if (rs.flavor == RegisterFlavor::GPR) {
+        add_uint32(spec, lc, RV32_THREAD_STATE);       // thread_command.flavor
+        add_uint32(spec, lc, RV32_THREAD_STATE_COUNT); // thread_command.count
+        const char *names[] = {"zero", "ra", "sp", "gp", "tp", "t0",   "t1",
+                               "t2",   "fp", "s1", "a0", "a1", "a2",   "a3",
+                               "a4",   "a5", "a6", "a7", "s2", "s3",   "s4",
+                               "s5",   "s6", "s7", "s8", "s9", "s10",  "s11",
+                               "t3",   "t4", "t5", "t6", "pc", nullptr};
+        for (int i = 0; names[i]; i++)
+          add_reg_value(spec, lc, rs.registers, names[i], 4);
+      }
+    }
+    load_commands.push_back(lc);
+  }
+}
+
+void add_lc_threads(CoreSpec &spec,
+                    std::vector<std::vector<uint8_t>> &load_commands) {
+  if (spec.cputype == llvm::MachO::CPU_TYPE_ARM)
+    add_lc_threads_armv7(spec, load_commands);
+  else if (spec.cputype == llvm::MachO::CPU_TYPE_ARM64)
+    add_lc_threads_arm64(spec, load_commands);
+  else if (spec.cputype == llvm::MachO::CPU_TYPE_RISCV)
+    add_lc_threads_riscv(spec, load_commands);
+  else {
+    fprintf(stderr,
+            "Unrecognized cputype, could not write LC_THREAD.  Exiting.\n");
+    exit(1);
+  }
+}
diff --git a/lldb/tools/yaml2macho-core/ThreadWriter.h 
b/lldb/tools/yaml2macho-core/ThreadWriter.h
new file mode 100644
index 0000000000000..bd212139f1572
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/ThreadWriter.h
@@ -0,0 +1,19 @@
+//===-- ThreadWriter.h 
----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_THREADWRITER_H
+#define YAML2MACHOCOREFILE_THREADWRITER_H
+
+#include "CoreSpec.h"
+
+#include <vector>
+
+void add_lc_threads(CoreSpec &spec,
+                    std::vector<std::vector<uint8_t>> &load_commands);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/Utility.cpp 
b/lldb/tools/yaml2macho-core/Utility.cpp
new file mode 100644
index 0000000000000..cf9bde4196906
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/Utility.cpp
@@ -0,0 +1,57 @@
+//===-- Utility.cpp 
-------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Utility.h"
+#include "CoreSpec.h"
+
+void add_uint64_swap(std::vector<uint8_t> &buf, uint64_t val) {
+  for (int byte = 7; byte >= 0; byte--)
+    buf.push_back((val >> (byte * 8)) & 0xff);
+}
+void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
+  for (int byte = 0; byte < 8; byte++)
+    buf.push_back((val >> (byte * 8)) & 0xff);
+}
+
+void add_uint32_swap(std::vector<uint8_t> &buf, uint32_t val) {
+  for (int byte = 3; byte >= 0; byte--)
+    buf.push_back((val >> (byte * 8)) & 0xff);
+}
+
+void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
+  for (int byte = 0; byte < 4; byte++)
+    buf.push_back((val >> (byte * 8)) & 0xff);
+}
+
+void add_uint64(const CoreSpec &spec, std::vector<uint8_t> &buf, uint64_t val) 
{
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+  if (spec.endian == Endian::big)
+    add_uint64(buf, val);
+  else
+    add_uint64_swap(buf, val);
+#else
+  if (spec.endian == Endian::Little)
+    add_uint64(buf, val);
+  else
+    add_uint64_swap(buf, val);
+#endif
+}
+
+void add_uint32(const CoreSpec &spec, std::vector<uint8_t> &buf, uint32_t val) 
{
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+  if (spec.endian == Endian::big)
+    add_uint32(buf, val);
+  else
+    add_uint32_swap(buf, val);
+#else
+  if (spec.endian == Endian::Little)
+    add_uint32(buf, val);
+  else
+    add_uint32_swap(buf, val);
+#endif
+}
diff --git a/lldb/tools/yaml2macho-core/Utility.h 
b/lldb/tools/yaml2macho-core/Utility.h
new file mode 100644
index 0000000000000..d3e691d22f4ff
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/Utility.h
@@ -0,0 +1,23 @@
+//===-- Utility.h 
---------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_UTILITY_H
+#define YAML2MACHOCOREFILE_UTILITY_H
+
+#include "CoreSpec.h"
+#include <vector>
+
+void add_uint64(const CoreSpec &spec, std::vector<uint8_t> &buf, uint64_t val);
+void add_uint32(const CoreSpec &spec, std::vector<uint8_t> &buf, uint32_t val);
+
+void add_uint64_swap(std::vector<uint8_t> &buf, uint64_t val);
+void add_uint64(std::vector<uint8_t> &buf, uint64_t val);
+void add_uint32_swap(std::vector<uint8_t> &buf, uint32_t val);
+void add_uint32(std::vector<uint8_t> &buf, uint32_t val);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/main.cpp 
b/lldb/tools/yaml2macho-core/main.cpp
new file mode 100644
index 0000000000000..f365c77ab0b2f
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/main.cpp
@@ -0,0 +1,223 @@
+//===-- main.cppp 
---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoreSpec.h"
+#include "LCNoteWriter.h"
+#include "MemoryWriter.h"
+#include "ThreadWriter.h"
+#include "Utility.h"
+#include "yaml2corespec.h"
+
+#include "llvm/BinaryFormat/MachO.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <string>
+#include <sys/stat.h>
+
+[[noreturn]] void print_help(void) {
+  fprintf(stderr, "Create a Mach-O corefile from a YAML register and memory "
+                  "description.\n");
+  fprintf(stderr, "Usage:\n");
+  fprintf(stderr, "   -i|--input <yaml spec>\n");
+  fprintf(stderr, "   -o|--output <corefile name>\n");
+  fprintf(stderr, "   -u|--uuids <uuid,uuid,uuid>\n");
+  fprintf(stderr, "   Add LC_NOTE 'load binary' for those UUIDs, "
+                  "at slide 0.\n");
+  exit(1);
+}
+
+std::vector<std::string> get_fields_from_delimited_string(std::string str,
+                                                          const char delim) {
+  std::vector<std::string> result;
+  std::string::size_type prev = std::string::npos;
+  std::string::size_type next = str.find(delim);
+  if (str.empty()) {
+    return result;
+  }
+  if (next == std::string::npos) {
+    result.push_back(str);
+  } else {
+    result.push_back(std::string(str, 0, next));
+    prev = next;
+    while ((next = str.find(delim, prev + 1)) != std::string::npos) {
+      result.push_back(std::string(str, prev + 1, next - prev - 1));
+      prev = next;
+    }
+    result.push_back(std::string(str, prev + 1));
+  }
+  return result;
+}
+
+int main(int argc, char **argv) {
+
+  const char *const short_opts = "i:o:u:h";
+  const option long_opts[] = {{"input", required_argument, nullptr, 'i'},
+                              {"output", required_argument, nullptr, 'o'},
+                              {"uuids", required_argument, nullptr, 'u'},
+                              {"help", no_argument, nullptr, 'h'},
+                              {nullptr, no_argument, nullptr, 0}};
+
+  std::optional<std::string> infile, outfile;
+  std::optional<std::vector<std::string>> uuids;
+  while (true) {
+    const auto opt = getopt_long(argc, argv, short_opts, long_opts, nullptr);
+    if (opt == -1)
+      break;
+    switch (opt) {
+    case 'i':
+      infile = optarg;
+      break;
+    case 'o':
+      outfile = optarg;
+      break;
+    case 'u':
+      uuids = get_fields_from_delimited_string(optarg, ',');
+      break;
+    case 'h':
+      print_help();
+    }
+  }
+
+  if (!infile || !outfile)
+    print_help();
+
+  struct stat sb;
+
+  if (stat(infile->c_str(), &sb) == -1) {
+    fprintf(stderr, "Unable to stat %s, exiting\n", infile->c_str());
+    exit(1);
+  }
+
+  FILE *input = fopen(infile->c_str(), "r");
+  if (!input) {
+    fprintf(stderr, "Unable to open %s, exiting\n", infile->c_str());
+    exit(1);
+  }
+  auto file_corespec = std::make_unique<char[]>(sb.st_size);
+  if (fread(file_corespec.get(), sb.st_size, 1, input) != 1) {
+    fprintf(stderr, "Unable to read all of %s, exiting\n", infile->c_str());
+    exit(1);
+  }
+  CoreSpec spec = from_yaml(file_corespec.get(), sb.st_size);
+  fclose(input);
+
+  // An array of load commands
+  std::vector<std::vector<uint8_t>> load_commands;
+
+  // An array of corefile contents (memory regions)
+  std::vector<uint8_t> payload;
+
+  // First add all the load commands / payload so we can figure out how large
+  // the load commands will be.
+
+  add_lc_threads(spec, load_commands);
+  for (size_t i = 0; i < spec.memory_regions.size(); i++) {
+    std::vector<uint8_t> segment_command_bytes;
+    create_lc_segment_cmd(spec, segment_command_bytes, spec.memory_regions[i],
+                          0);
+    load_commands.push_back(segment_command_bytes);
+  }
+
+  if (uuids)
+    for (const std::string &uuid : *uuids) {
+      std::vector<uint8_t> segment_command_bytes;
+      std::vector<uint8_t> payload_bytes;
+      create_lc_note_binary_load_cmd(spec, segment_command_bytes, uuid, 0,
+                                     payload_bytes, 0);
+      load_commands.push_back(segment_command_bytes);
+    }
+
+  off_t size_of_load_commands = 0;
+  for (const auto &lc : load_commands)
+    size_of_load_commands += lc.size();
+
+  off_t header_and_load_cmd_room =
+      sizeof(llvm::MachO::mach_header_64) + size_of_load_commands;
+  off_t initial_payload_fileoff = header_and_load_cmd_room;
+  initial_payload_fileoff = (initial_payload_fileoff + 4096 - 1) & ~(4096 - 1);
+  off_t payload_fileoff = initial_payload_fileoff;
+
+  // Erase the load commands / payload now that we know how much space is
+  // needed, redo it with real values.
+  load_commands.clear();
+  payload.clear();
+
+  add_lc_threads(spec, load_commands);
+  for (size_t i = 0; i < spec.memory_regions.size(); i++) {
+    std::vector<uint8_t> segment_command_bytes;
+    create_lc_segment_cmd(spec, segment_command_bytes, spec.memory_regions[i],
+                          payload_fileoff);
+    load_commands.push_back(segment_command_bytes);
+    payload_fileoff += spec.memory_regions[i].size;
+    payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+  }
+
+  std::vector<uint8_t> lc_note_payload_bytes;
+  if (uuids) {
+    off_t starting_fileoff_to_lcnote_payload = payload_fileoff;
+    for (const std::string &uuid : *uuids) {
+      std::vector<uint8_t> segment_command_bytes;
+      create_lc_note_binary_load_cmd(spec, segment_command_bytes, uuid, 0,
+                                     lc_note_payload_bytes, payload_fileoff);
+      payload_fileoff =
+          starting_fileoff_to_lcnote_payload + lc_note_payload_bytes.size();
+      load_commands.push_back(segment_command_bytes);
+    }
+    payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+  }
+
+  FILE *f = fopen(outfile->c_str(), "w");
+  if (f == nullptr) {
+    fprintf(stderr, "Unable to open file %s for writing\n", outfile->c_str());
+    exit(1);
+  }
+
+  std::vector<uint8_t> mh;
+  // Write the fields of a mach_header_64 struct
+  if (spec.wordsize == 8)
+    add_uint32(spec, mh, llvm::MachO::MH_MAGIC_64); // magic
+  else
+    add_uint32(spec, mh, llvm::MachO::MH_MAGIC); // magic
+  add_uint32(spec, mh, spec.cputype);            // cputype
+  add_uint32(spec, mh, spec.cpusubtype);         // cpusubtype
+  add_uint32(spec, mh, llvm::MachO::MH_CORE);    // filetype
+  add_uint32(spec, mh, load_commands.size());    // ncmds
+  add_uint32(spec, mh, size_of_load_commands);   // sizeofcmds
+  add_uint32(spec, mh, 0);                       // flags
+  if (spec.wordsize == 8)
+    add_uint32(spec, mh, 0); // reserved
+
+  fwrite(mh.data(), mh.size(), 1, f);
+
+  for (const auto &lc : load_commands)
+    fwrite(lc.data(), lc.size(), 1, f);
+
+  // Reset the payload offset back to the first one.
+  payload_fileoff = initial_payload_fileoff;
+  if (spec.memory_regions.size() > 0) {
+    for (size_t i = 0; i < spec.memory_regions.size(); i++) {
+      std::vector<uint8_t> bytes;
+      create_memory_bytes(spec, spec.memory_regions[i], bytes);
+      fseek(f, payload_fileoff, SEEK_SET);
+      fwrite(bytes.data(), bytes.size(), 1, f);
+
+      payload_fileoff += bytes.size();
+      payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+    }
+  }
+
+  if (lc_note_payload_bytes.size() > 0) {
+    fseek(f, payload_fileoff, SEEK_SET);
+    fwrite(lc_note_payload_bytes.data(), lc_note_payload_bytes.size(), 1, f);
+    payload_fileoff += lc_note_payload_bytes.size();
+    payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+  }
+
+  fclose(f);
+}
diff --git a/lldb/tools/yaml2macho-core/yaml2corespec.cpp 
b/lldb/tools/yaml2macho-core/yaml2corespec.cpp
new file mode 100644
index 0000000000000..e611074d5378b
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/yaml2corespec.cpp
@@ -0,0 +1,131 @@
+//===-- yaml2corespec.cpp 
-------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoreSpec.h"
+
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/YAMLTraits.h"
+
+#include <stdio.h>
+#include <string>
+
+using llvm::yaml::Input;
+using llvm::yaml::IO;
+using llvm::yaml::MappingTraits;
+
+template <> struct MappingTraits<RegisterNameAndValue> {
+  static void mapping(IO &io, RegisterNameAndValue &name_value) {
+    io.mapRequired("name", name_value.name);
+    io.mapRequired("value", name_value.value);
+  }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(RegisterNameAndValue)
+
+template <> struct llvm::yaml::ScalarEnumerationTraits<RegisterFlavor> {
+  static void enumeration(IO &io, RegisterFlavor &flavor) {
+    io.enumCase(flavor, "gpr", RegisterFlavor::GPR);
+    io.enumCase(flavor, "fpr", RegisterFlavor::FPR);
+    io.enumCase(flavor, "exc", RegisterFlavor::EXC);
+  }
+};
+
+template <> struct MappingTraits<RegisterSet> {
+  static void mapping(IO &io, RegisterSet &regset) {
+    std::string flavor;
+    io.mapRequired("flavor", regset.flavor);
+    io.mapRequired("registers", regset.registers);
+  }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(RegisterSet)
+
+template <> struct MappingTraits<Thread> {
+  static void mapping(IO &io, Thread &thread) {
+    io.mapRequired("regsets", thread.regsets);
+  }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(Thread)
+
+template <> struct llvm::yaml::ScalarEnumerationTraits<Endian> {
+  static void enumeration(IO &io, Endian &endian) {
+    io.enumCase(endian, "big", Endian::Big);
+    io.enumCase(endian, "little", Endian::Little);
+  }
+};
+
+template <> struct MappingTraits<MemoryRegion> {
+  static void mapping(IO &io, MemoryRegion &memory) {
+    io.mapRequired("addr", memory.addr);
+    io.mapOptional("UInt8", memory.bytes);
+    io.mapOptional("UInt32", memory.words);
+    io.mapOptional("UInt64", memory.doublewords);
+
+    if (memory.bytes.size()) {
+      memory.type = MemoryType::UInt8;
+      memory.size = memory.bytes.size();
+    } else if (memory.words.size()) {
+      memory.type = MemoryType::UInt32;
+      memory.size = memory.words.size() * 4;
+    } else if (memory.doublewords.size()) {
+      memory.type = MemoryType::UInt64;
+      memory.size = memory.doublewords.size() * 8;
+    }
+  }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(MemoryRegion)
+
+template <> struct MappingTraits<CoreSpec> {
+  static void mapping(IO &io, CoreSpec &corespec) {
+    std::string cpuname;
+    io.mapRequired("cpu", cpuname);
+    if (cpuname == "armv7m") {
+      corespec.cputype = llvm::MachO::CPU_TYPE_ARM;
+      corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM_V7M;
+    } else if (cpuname == "armv7") {
+      corespec.cputype = llvm::MachO::CPU_TYPE_ARM;
+      corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM_ALL;
+    } else if (cpuname == "riscv") {
+      corespec.cputype = llvm::MachO::CPU_TYPE_RISCV;
+      corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_RISCV_ALL;
+    } else if (cpuname == "arm64") {
+      corespec.cputype = llvm::MachO::CPU_TYPE_ARM64;
+      corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM64_ALL;
+    } else {
+      fprintf(stderr, "Unrecognized cpu name %s, exiting.\n", cpuname.c_str());
+      exit(1);
+    }
+    io.mapRequired("endian", corespec.endian);
+    io.mapOptional("threads", corespec.threads);
+    io.mapOptional("memory-regions", corespec.memory_regions);
+    if (corespec.cputype == llvm::MachO::CPU_TYPE_ARM ||
+        corespec.cputype == llvm::MachO::CPU_TYPE_RISCV)
+      corespec.wordsize = 4;
+    else if (corespec.cputype == llvm::MachO::CPU_TYPE_ARM64)
+      corespec.wordsize = 8;
+    else {
+      fprintf(stderr,
+              "Unrecognized cputype, could not set wordsize, exiting.\n");
+      exit(1);
+    }
+  }
+};
+
+CoreSpec from_yaml(char *buf, size_t len) {
+  llvm::StringRef file_corespec_strref(buf, len);
+
+  Input yin(file_corespec_strref);
+
+  CoreSpec v;
+  yin >> v;
+
+  if (yin.error()) {
+    fprintf(stderr, "Unable to parse YAML, exiting\n");
+    exit(1);
+  }
+
+  return v;
+}
diff --git a/lldb/tools/yaml2macho-core/yaml2corespec.h 
b/lldb/tools/yaml2macho-core/yaml2corespec.h
new file mode 100644
index 0000000000000..7eef9a9129bbc
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/yaml2corespec.h
@@ -0,0 +1,16 @@
+//===-- yaml2corespec.h 
---------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_YAML2CORESPEC_H
+#define YAML2MACHOCOREFILE_YAML2CORESPEC_H
+
+#include "CoreSpec.h"
+
+CoreSpec from_yaml(char *buf, size_t len);
+
+#endif

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to