blob: 449f85bc3f52ae82307e821bfa80678fa86c9a1e [file]
//===-- 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;
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<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 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;
}