[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 17:19:40 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:
(it would also be possible to convert the JSON into a proper RegisterContext -- we've got register sets, register infos, and register values -- and that would simplify these copies into our unified register infos. But that's basically what this class is doing already, in addition to unifying the two sources of registers at the same time)
https://github.com/llvm/llvm-project/pull/144627
More information about the lldb-commits
mailing list