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

Jason Molenda via lldb-commits lldb-commits at lists.llvm.org
Fri Jun 27 18:13:00 PDT 2025


================
@@ -0,0 +1,300 @@
+//===-- 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() &&
+      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 after them.  If the thread metadata
+  // specify a set with the same name as LC_THREAD, the already-used
+  // index from the core register context will be used in
+  // the RegisterInfo.
+  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;
+        int bytesize = bitsize / 8;
+        switch (bytesize) {
+        case 2: {
+          Scalar value_scalar((uint16_t)value);
+          value_scalar.GetAsMemoryData(m_register_data.data() + offset,
+                                       bytesize, byte_order, error);
+          offset += bytesize;
+        } break;
+        case 4: {
+          Scalar value_scalar((uint32_t)value);
+          value_scalar.GetAsMemoryData(m_register_data.data() + offset,
+                                       bytesize, byte_order, error);
+          offset += bytesize;
+        } break;
+        case 8: {
+          Scalar value_scalar((uint64_t)value);
+          value_scalar.GetAsMemoryData(m_register_data.data() + offset,
+                                       bytesize, byte_order, error);
+          offset += bytesize;
+        } 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());
+
+  // Put the RegisterSet names in the constant string pool,
+  // to sidestep lifetime issues of char*'s.
+  auto copy_regset_name = [](RegisterSet &dst, const RegisterSet &src) {
+    dst.name = ConstString(src.name).AsCString();
+    if (src.short_name)
+      dst.short_name = ConstString(src.short_name).AsCString();
+    else
+      dst.short_name = nullptr;
+  };
+
+  // Copy the core thread register sets into our combined register set list.
+  // RegisterSet indexes will be identical for the LC_THREAD RegisterContext.
+  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);
+    copy_regset_name(new_set, *old_set);
+    m_register_sets.push_back(new_set);
+  }
+
+  // Add any additional metadata RegisterSets to our combined RegisterSet array.
+  if (additional_reginfo_up) {
+    for (size_t idx = 0; idx < additional_reginfo_up->GetNumRegisterSets();
+         idx++) {
+      // See if this metadata RegisterSet name matches one already present
+      // from the LC_THREAD RegisterContext.
+      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;
+        }
+      }
+      // This metadata RegisterSet is a new one.
+      // Add it to the combined RegisterSet array.
+      if (!found_match) {
+        RegisterSet new_set;
+        copy_regset_name(new_set, *old_set);
+        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 when creating the
+  // combined RegisterInfo arrays.
+  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++;
+          }
----------------
jasonmolenda wrote:

Yeah lol, there's like 3 lines I could pull out of these two loops into a lambda.  

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


More information about the lldb-commits mailing list