[Lldb-commits] [lldb] [lldb][Mach-O] Allow "process metadata" LC_NOTE to supply registers (PR #144627)

via lldb-commits lldb-commits at lists.llvm.org
Tue Jun 17 19:19:34 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Jason Molenda (jasonmolenda)

<details>
<summary>Changes</summary>

The "process metadata" LC_NOTE allows for thread IDs to be specified in a Mach-O corefile.  This extends the JSON recognzied in that LC_NOTE to allow for additional registers to be supplied on a per-thread basis.

The registers included in a Mach-O corefile LC_THREAD load command can only be one of the register flavors that the kernel (xnu) defines in <mach/arm/thread_status.h> for arm64 -- the general purpose registers, floating point registers, exception registers.

JTAG style corefile producers may have access to many additional registers beyond these that EL0 programs typically use, for instance TCR_EL1 on AArch64, and people developing low level code need access to these registers.  This patch defines a format for including these registers for any thread.

The JSON in "process metadata" is a dictionary that must have a `threads` key.  The value is an array of entries, one per LC_THREAD in the Mach-O corefile.  The number of entries must match the LC_THREADs so they can be correctly associated.

Each thread's dictionary must have two keys, `sets`, and `registers`. `sets` is an array of register set names.  If a register set name matches one from the LC_THREAD core registers, any registers that are defined will be added to that register set.  e.g. metadata can add a register to the "General Purpose Registers" set that lldb shows users.

`registers` is an array of dictionaries, one per register.  Each register must have the keys `name`, `value`, `bitsize`, and `set`. It may provide additional keys like `alt-name`, that `DynamicRegisterInfo::SetRegisterInfo` recognizes.

This `sets` + `registers` formatting is the same that is used by the `target.process.python-os-plugin-path` script interface uses, both are parsed by `DynamicRegisterInfo`.  The one addition is that in this LC_NOTE metadata, each register must also have a `value` field, with the value provided in big-endian base 10, as usual with JSON.

In RegisterContextUnifiedCore, I combine the register sets & registers from the LC_THREAD for a specific thread, and the metadata sets & registers for that thread from the LC_NOTE.  Even if no LC_NOTE is present, this class ingests the LC_THREAD register contexts and reformats it to its internal stores before returning itself as the RegisterContex, instead of shortcutting and returning the core's native RegisterContext.  I could have gone either way with that, but in the end I decided if the code is correct, we should live on it always.

I added a test where we process save-core to create a userland corefile, then use a utility "add-lcnote" to strip the existing "process metadata" LC_NOTE that lldb put in it, and adds a new one from a JSON string.

rdar://74358787

---

Patch is 41.58 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144627.diff


11 Files Affected:

- (modified) lldb/include/lldb/Symbol/ObjectFile.h (+15-2) 
- (modified) lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp (+41-20) 
- (modified) lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h (+2) 
- (modified) lldb/source/Plugins/Process/mach-core/CMakeLists.txt (+1) 
- (added) lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.cpp (+293) 
- (added) lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.h (+57) 
- (modified) lldb/source/Plugins/Process/mach-core/ThreadMachCore.cpp (+42-13) 
- (added) lldb/test/API/macosx/lc-note/additional-registers/Makefile (+11) 
- (added) lldb/test/API/macosx/lc-note/additional-registers/TestMetadataRegisters.py (+100) 
- (added) lldb/test/API/macosx/lc-note/additional-registers/add-lcnote.cpp (+384) 
- (added) lldb/test/API/macosx/lc-note/additional-registers/main.c (+11) 


``````````diff
diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h
index 43567592dd447..1b9ae1fb31a69 100644
--- a/lldb/include/lldb/Symbol/ObjectFile.h
+++ b/lldb/include/lldb/Symbol/ObjectFile.h
@@ -18,6 +18,7 @@
 #include "lldb/Utility/Endian.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/FileSpecList.h"
+#include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/UUID.h"
 #include "lldb/lldb-private.h"
 #include "llvm/Support/Threading.h"
@@ -544,9 +545,9 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
     return false;
   }
 
-  /// Get metadata about threads from the corefile.
+  /// Get metadata about thread ids from the corefile.
   ///
-  /// The corefile may have metadata (e.g. a Mach-O "thread extrainfo"
+  /// The corefile may have metadata (e.g. a Mach-O "process metadata"
   /// LC_NOTE) which for the threads in the process; this method tries
   /// to retrieve them.
   ///
@@ -568,6 +569,18 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
     return false;
   }
 
+  /// Get process metadata from the corefile in a StructuredData dictionary.
+  ///
+  /// The corefile may have notes (e.g. a Mach-O "process metadata" LC_NOTE)
+  /// which provide metadata about the process and threads in a JSON or
+  /// similar format.
+  ///
+  /// \return
+  ///     A StructuredData object with the metadata in the note, if there is
+  ///     one.  An empty shared pointer is returned if not metadata is found,
+  ///     or a problem parsing it.
+  virtual StructuredData::ObjectSP GetCorefileProcessMetadata() { return {}; }
+
   virtual lldb::RegisterContextSP
   GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) {
     return lldb::RegisterContextSP();
diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index b1741926b74aa..aea45293095e7 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -5794,27 +5794,9 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(
     std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
 
     Log *log(GetLog(LLDBLog::Object | LLDBLog::Process | LLDBLog::Thread));
-    auto lc_notes = FindLC_NOTEByName("process metadata");
-    for (auto lc_note : lc_notes) {
-      offset_t payload_offset = std::get<0>(lc_note);
-      offset_t strsize = std::get<1>(lc_note);
-      std::string buf(strsize, '\0');
-      if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) {
-        LLDB_LOGF(log,
-                  "Unable to read %" PRIu64
-                  " bytes of 'process metadata' LC_NOTE JSON contents",
-                  strsize);
-        return false;
-      }
-      while (buf.back() == '\0')
-        buf.resize(buf.size() - 1);
-      StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(buf);
+    StructuredData::ObjectSP object_sp = GetCorefileProcessMetadata();
+    if (object_sp) {
       StructuredData::Dictionary *dict = object_sp->GetAsDictionary();
-      if (!dict) {
-        LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
-                       "get a dictionary.");
-        return false;
-      }
       StructuredData::Array *threads;
       if (!dict->GetValueForKeyAsArray("threads", threads) || !threads) {
         LLDB_LOGF(log,
@@ -5857,6 +5839,45 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(
   return false;
 }
 
+StructuredData::ObjectSP ObjectFileMachO::GetCorefileProcessMetadata() {
+  ModuleSP module_sp(GetModule());
+  if (!module_sp)
+    return {};
+
+  Log *log(GetLog(LLDBLog::Object | LLDBLog::Process | LLDBLog::Thread));
+  std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+  auto lc_notes = FindLC_NOTEByName("process metadata");
+  if (lc_notes.size() == 0)
+    return {};
+
+  if (lc_notes.size() > 1)
+    LLDB_LOGF(
+        log,
+        "Multiple 'process metadata' LC_NOTEs found, only using the first.");
+
+  offset_t payload_offset = std::get<0>(lc_notes[0]);
+  offset_t strsize = std::get<1>(lc_notes[0]);
+  std::string buf(strsize, '\0');
+  if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) {
+    LLDB_LOGF(log,
+              "Unable to read %" PRIu64
+              " bytes of 'process metadata' LC_NOTE JSON contents",
+              strsize);
+    return {};
+  }
+  while (buf.back() == '\0')
+    buf.resize(buf.size() - 1);
+  StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(buf);
+  StructuredData::Dictionary *dict = object_sp->GetAsDictionary();
+  if (!dict) {
+    LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
+                   "get a dictionary.");
+    return {};
+  }
+
+  return object_sp;
+}
+
 lldb::RegisterContextSP
 ObjectFileMachO::GetThreadContextAtIndex(uint32_t idx,
                                          lldb_private::Thread &thread) {
diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
index 7f67f5e04f1d6..7e3a6754dd0b8 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
@@ -133,6 +133,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
 
   bool GetCorefileThreadExtraInfos(std::vector<lldb::tid_t> &tids) override;
 
+  lldb_private::StructuredData::ObjectSP GetCorefileProcessMetadata() override;
+
   bool LoadCoreFileImages(lldb_private::Process &process) override;
 
   lldb::RegisterContextSP
diff --git a/lldb/source/Plugins/Process/mach-core/CMakeLists.txt b/lldb/source/Plugins/Process/mach-core/CMakeLists.txt
index a1ea85ec4c728..926f06d23c64b 100644
--- a/lldb/source/Plugins/Process/mach-core/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/mach-core/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_library(lldbPluginProcessMachCore PLUGIN
   ProcessMachCore.cpp
   ThreadMachCore.cpp
+  RegisterContextUnifiedCore.cpp
 
   LINK_COMPONENTS
     Support
diff --git a/lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.cpp b/lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.cpp
new file mode 100644
index 0000000000000..774c0ca605b3c
--- /dev/null
+++ b/lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.cpp
@@ -0,0 +1,293 @@
+//===-- RegisterContextUnifiedCore.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 "RegisterContextUnifiedCore.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/StructuredData.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextUnifiedCore::RegisterContextUnifiedCore(
+    Thread &thread, uint32_t concrete_frame_idx,
+    RegisterContextSP core_thread_regctx_sp,
+    StructuredData::ObjectSP metadata_thread_registers)
+    : RegisterContext(thread, concrete_frame_idx) {
+
+  ProcessSP process_sp(thread.GetProcess());
+  Target &target = process_sp->GetTarget();
+  StructuredData::Dictionary *metadata_registers_dict = nullptr;
+
+  // If we have thread metadata, check if the keys for register
+  // definitions are present; if not, clear the ObjectSP.
+  if (metadata_thread_registers &&
+      metadata_thread_registers->GetAsDictionary()->HasKey("register_info")) {
+    metadata_registers_dict = metadata_thread_registers->GetAsDictionary()
+                                  ->GetValueForKey("register_info")
+                                  ->GetAsDictionary();
+    if (metadata_registers_dict)
+      if (!metadata_registers_dict->HasKey("sets") ||
+          !metadata_registers_dict->HasKey("registers"))
+        metadata_registers_dict = nullptr;
+  }
+
+  // When creating a register set list from the two sources,
+  // the LC_THREAD aka core_thread_regctx_sp register sets
+  // will be used at the same indexes.
+  // Any additional sets named by the thread metadata registers
+  // will be added.  If the thread metadata registers specify
+  // a set with the same name, the already-used index from the
+  // core register context will be used.
+  std::map<size_t, size_t> metadata_regset_to_combined_regset;
+
+  // Calculate the total size of the register store buffer we need
+  // for all registers.  The corefile register definitions may include
+  // RegisterInfo descriptions of registers that aren't actually
+  // available.  For simplicity, calculate the size of all registers
+  // as if they are available, so we can maintain the same offsets into
+  // the buffer.
+  uint32_t core_buffer_end = 0;
+  for (size_t idx = 0; idx < core_thread_regctx_sp->GetRegisterCount(); idx++) {
+    const RegisterInfo *reginfo =
+        core_thread_regctx_sp->GetRegisterInfoAtIndex(idx);
+    core_buffer_end =
+        std::max(reginfo->byte_offset + reginfo->byte_size, core_buffer_end);
+  }
+
+  // Add metadata register sizes to the total buffer size.
+  uint32_t combined_buffer_end = core_buffer_end;
+  if (metadata_registers_dict) {
+    StructuredData::Array *registers = nullptr;
+    if (metadata_registers_dict->GetValueForKeyAsArray("registers", registers))
+      registers->ForEach(
+          [&combined_buffer_end](StructuredData::Object *ent) -> bool {
+            uint32_t bitsize;
+            if (!ent->GetAsDictionary()->GetValueForKeyAsInteger("bitsize",
+                                                                 bitsize))
+              return false;
+            combined_buffer_end += (bitsize / 8);
+            return true;
+          });
+  }
+  m_register_data.resize(combined_buffer_end, 0);
+
+  // Copy the core register values into our combined data buffer,
+  // skip registers that are contained within another (e.g. w0 vs. x0)
+  // and registers that return as "unavailable".
+  for (size_t idx = 0; idx < core_thread_regctx_sp->GetRegisterCount(); idx++) {
+    const RegisterInfo *reginfo =
+        core_thread_regctx_sp->GetRegisterInfoAtIndex(idx);
+    RegisterValue val;
+    if (!reginfo->value_regs &&
+        core_thread_regctx_sp->ReadRegister(reginfo, val))
+      memcpy(m_register_data.data() + reginfo->byte_offset, val.GetBytes(),
+             val.GetByteSize());
+  }
+
+  // Set 'offset' fields for each register definition into our combined
+  // register data buffer. DynamicRegisterInfo needs
+  // this field set to parse the JSON.
+  // Also copy the values of the registers into our register data buffer.
+  if (metadata_registers_dict) {
+    size_t offset = core_buffer_end;
+    ByteOrder byte_order = core_thread_regctx_sp->GetByteOrder();
+    StructuredData::Array *registers;
+    if (metadata_registers_dict->GetValueForKeyAsArray("registers", registers))
+      registers->ForEach([this, &offset,
+                          byte_order](StructuredData::Object *ent) -> bool {
+        uint64_t bitsize;
+        uint64_t value;
+        if (!ent->GetAsDictionary()->GetValueForKeyAsInteger("bitsize",
+                                                             bitsize))
+          return false;
+        if (!ent->GetAsDictionary()->GetValueForKeyAsInteger("value", value)) {
+          // we had a bitsize but no value, so move the offset forward I guess.
+          offset += (bitsize / 8);
+          return false;
+        }
+        ent->GetAsDictionary()->AddIntegerItem("offset", offset);
+        Status error;
+        switch (bitsize / 8) {
+        case 2: {
+          Scalar value_scalar((uint16_t)value);
+          value_scalar.GetAsMemoryData(m_register_data.data() + offset, 2,
+                                       byte_order, error);
+          offset += 2;
+        } break;
+        case 4: {
+          Scalar value_scalar((uint32_t)value);
+          value_scalar.GetAsMemoryData(m_register_data.data() + offset, 4,
+                                       byte_order, error);
+          offset += 4;
+        } break;
+        case 8: {
+          Scalar value_scalar((uint64_t)value);
+          value_scalar.GetAsMemoryData(m_register_data.data() + offset, 8,
+                                       byte_order, error);
+          offset += 8;
+        } break;
+        }
+        return true;
+      });
+  }
+
+  // Create a DynamicRegisterInfo from the metadata JSON.
+  std::unique_ptr<DynamicRegisterInfo> additional_reginfo_up;
+  if (metadata_registers_dict)
+    additional_reginfo_up = DynamicRegisterInfo::Create(
+        *metadata_registers_dict, target.GetArchitecture());
+
+  // Copy the core thread register sets into our combined register set list.
+  for (size_t idx = 0; idx < core_thread_regctx_sp->GetRegisterSetCount();
+       idx++) {
+    RegisterSet new_set;
+    const RegisterSet *old_set = core_thread_regctx_sp->GetRegisterSet(idx);
+    new_set.name = ConstString(old_set->name).AsCString();
+    if (old_set->short_name)
+      new_set.short_name = ConstString(old_set->short_name).AsCString();
+    else
+      new_set.short_name = nullptr;
+    m_register_sets.push_back(new_set);
+  }
+
+  // Set up our RegisterSet array.
+  // First copying all of the core thread register sets,
+  // then any additional unique register sets from the metadata.
+  if (additional_reginfo_up) {
+    for (size_t idx = 0; idx < additional_reginfo_up->GetNumRegisterSets();
+         idx++) {
+      bool found_match = false;
+      const RegisterSet *old_set = additional_reginfo_up->GetRegisterSet(idx);
+      for (size_t jdx = 0; jdx < m_register_sets.size(); jdx++) {
+        if (strcmp(m_register_sets[jdx].name, old_set->name) == 0) {
+          metadata_regset_to_combined_regset[idx] = jdx;
+          found_match = true;
+          break;
+        }
+      }
+      if (!found_match) {
+        RegisterSet new_set;
+        new_set.name = ConstString(old_set->name).AsCString();
+        if (old_set->short_name)
+          new_set.short_name = ConstString(old_set->short_name).AsCString();
+        else
+          new_set.short_name = nullptr;
+        metadata_regset_to_combined_regset[idx] = m_register_sets.size();
+        m_register_sets.push_back(new_set);
+      }
+    }
+  }
+
+  // Set up our RegisterInfo array, one RegisterSet at a time.
+  // The metadata registers may be declared to be in a core thread
+  // register set (e.g. "General Purpose Registers", so we scan
+  // both core registers and metadata registers should be examined
+  // when creating the combined register sets.
+  for (size_t combined_regset_idx = 0;
+       combined_regset_idx < m_register_sets.size(); combined_regset_idx++) {
+    uint32_t registers_this_regset = 0;
+    if (combined_regset_idx < core_thread_regctx_sp->GetRegisterSetCount()) {
+      const RegisterSet *regset =
+          core_thread_regctx_sp->GetRegisterSet(combined_regset_idx);
+      for (size_t j = 0; j < regset->num_registers; j++) {
+        uint32_t reg_idx = regset->registers[j];
+        const RegisterInfo *reginfo =
+            core_thread_regctx_sp->GetRegisterInfoAtIndex(reg_idx);
+        RegisterValue val;
+        if (!reginfo->value_regs &&
+            core_thread_regctx_sp->ReadRegister(reginfo, val)) {
+          m_regset_regnum_collection[combined_regset_idx].push_back(
+              m_register_infos.size());
+          m_register_infos.push_back(*reginfo);
+          registers_this_regset++;
+        }
+      }
+    }
+    if (additional_reginfo_up) {
+      // Find the register set in the metadata that matches this register
+      // set, then copy all its RegisterInfos.
+      for (size_t setidx = 0;
+           setidx < additional_reginfo_up->GetNumRegisterSets(); setidx++) {
+        if (metadata_regset_to_combined_regset[setidx] == combined_regset_idx) {
+          const RegisterSet *regset =
+              additional_reginfo_up->GetRegisterSet(setidx);
+          for (size_t j = 0; j < regset->num_registers; j++) {
+            uint32_t reg_idx = regset->registers[j];
+            const RegisterInfo *reginfo =
+                additional_reginfo_up->GetRegisterInfoAtIndex(reg_idx);
+            m_regset_regnum_collection[combined_regset_idx].push_back(
+                m_register_infos.size());
+            // register names in DynamicRegisterInfo are ConstString stored;
+            // we can reuse the char* pointers here without retaining the
+            // DynamicRegisterInfo.
+            m_register_infos.push_back(*reginfo);
+            registers_this_regset++;
+          }
+        }
+      }
+    }
+    m_register_sets[combined_regset_idx].num_registers = registers_this_regset;
+    m_register_sets[combined_regset_idx].registers =
+        m_regset_regnum_collection[combined_regset_idx].data();
+  }
+}
+
+size_t RegisterContextUnifiedCore::GetRegisterCount() {
+  return m_register_infos.size();
+}
+
+const RegisterInfo *
+RegisterContextUnifiedCore::GetRegisterInfoAtIndex(size_t reg) {
+  return &m_register_infos[reg];
+}
+
+size_t RegisterContextUnifiedCore::GetRegisterSetCount() {
+  return m_register_sets.size();
+}
+
+const RegisterSet *RegisterContextUnifiedCore::GetRegisterSet(size_t set) {
+  return &m_register_sets[set];
+}
+
+bool RegisterContextUnifiedCore::ReadRegister(
+    const lldb_private::RegisterInfo *reg_info,
+    lldb_private::RegisterValue &value) {
+  if (!reg_info)
+    return false;
+  ProcessSP process_sp(m_thread.GetProcess());
+  if (process_sp) {
+    DataExtractor regdata(m_register_data.data(), m_register_data.size(),
+                          process_sp->GetByteOrder(),
+                          process_sp->GetAddressByteSize());
+    offset_t offset = reg_info->byte_offset;
+    switch (reg_info->byte_size) {
+    case 2:
+      value.SetUInt16(regdata.GetU16(&offset));
+      break;
+    case 4:
+      value.SetUInt32(regdata.GetU32(&offset));
+      break;
+    case 8:
+      value.SetUInt64(regdata.GetU64(&offset));
+      break;
+    default:
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool RegisterContextUnifiedCore::WriteRegister(
+    const lldb_private::RegisterInfo *reg_info,
+    const lldb_private::RegisterValue &value) {
+  return false;
+}
diff --git a/lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.h b/lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.h
new file mode 100644
index 0000000000000..47e2fb7cac5a3
--- /dev/null
+++ b/lldb/source/Plugins/Process/mach-core/RegisterContextUnifiedCore.h
@@ -0,0 +1,57 @@
+//===-- RegisterContextUnifiedCore.h ----------------------------*- C++ -*-===//
+//
+// 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 LLDB_SOURCE_PLUGINS_PROCESS_REGISTERCONTEXT_UNIFIED_CORE_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_REGISTERCONTEXT_UNIFIED_CORE_H
+
+#include <string>
+#include <vector>
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextUnifiedCore : public RegisterContext {
+public:
+  RegisterContextUnifiedCore(
+      Thread &thread, uint32_t concrete_frame_idx,
+      lldb::RegisterContextSP core_thread_regctx_sp,
+      lldb_private::StructuredData::ObjectSP metadata_thread_registers);
+
+  void InvalidateAllRegisters() override{};
+
+  size_t GetRegisterCount() override;
+
+  const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+  size_t GetRegisterSetCount() override;
+
+  const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+  bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+                    lldb_private::RegisterValue &value) override;
+
+  bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+                     const lldb_private::RegisterValue &value...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/144627


More information about the lldb-commits mailing list