//===-- 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 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; const 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 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 combined RegisterInfo array, one RegisterSet at a time. for (size_t combined_regset_idx = 0; combined_regset_idx < m_register_sets.size(); combined_regset_idx++) { uint32_t registers_this_regset = 0; // Copy all LC_THREAD RegisterInfos that have a value into our // combined RegisterInfo array. (the LC_THREAD RegisterContext // may describe registers that were not provided in this thread) // // LC_THREAD register set indexes are identical to the combined // register set indexes. The combined register set array may have // additional entries. if (combined_regset_idx < core_thread_regctx_sp->GetRegisterSetCount()) { const RegisterSet *regset = core_thread_regctx_sp->GetRegisterSet(combined_regset_idx); // Copy all the registers that have values in. 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++; } } } // Copy all the metadata RegisterInfos into our combined combined // RegisterInfo array. // The metadata may add registers to one of the LC_THREAD register sets, // or its own newly added register sets. metadata_regset_to_combined_regset // has the association of the RegisterSet indexes between the two. 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()); 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) { if (reg < m_register_infos.size()) return &m_register_infos[reg]; return nullptr; } size_t RegisterContextUnifiedCore::GetRegisterSetCount() { return m_register_sets.size(); } const RegisterSet *RegisterContextUnifiedCore::GetRegisterSet(size_t set) { if (set < m_register_sets.size()) return &m_register_sets[set]; return nullptr; } bool RegisterContextUnifiedCore::ReadRegister( const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value) { if (!reg_info) return false; if (ProcessSP process_sp = m_thread.GetProcess()) { 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; }