#include "lldb/Expression/DWARFExpression.h"
#include <cinttypes>
#include <optional>
#include <vector>
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Utility/DataEncoder.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/VMRange.h"
#include "lldb/Host/Host.h"
#include "lldb/Utility/Endian.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackID.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::dwarf;
using namespace lldb_private::plugin::dwarf;
DWARFExpression::DWARFExpression() : m_data() {}
DWARFExpression::DWARFExpression(const DataExtractor &data) : m_data(data) {}
DWARFExpression::~DWARFExpression() = default;
bool DWARFExpression::IsValid() const { return m_data.GetByteSize() > 0; }
void DWARFExpression::UpdateValue(uint64_t const_value,
lldb::offset_t const_value_byte_size,
uint8_t addr_byte_size) {
if (!const_value_byte_size)
return;
m_data.SetData(
DataBufferSP(new DataBufferHeap(&const_value, const_value_byte_size)));
m_data.SetByteOrder(endian::InlHostByteOrder());
m_data.SetAddressByteSize(addr_byte_size);
}
void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
ABI *abi) const {
auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr;
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum,
bool IsEH) -> llvm::StringRef {
if (!MCRegInfo)
return {};
if (std::optional<unsigned> LLVMRegNum =
MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH))
if (const char *RegName = MCRegInfo->getName(*LLVMRegNum))
return llvm::StringRef(RegName);
return {};
};
llvm::DIDumpOptions DumpOpts;
DumpOpts.GetNameForDWARFReg = GetRegName;
llvm::DWARFExpression(m_data.GetAsLLVM(), m_data.GetAddressByteSize())
.print(s->AsRawOstream(), DumpOpts, nullptr);
}
RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; }
void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
m_reg_kind = reg_kind;
}
static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
lldb::RegisterKind reg_kind,
uint32_t reg_num, Value &value) {
if (reg_ctx == nullptr)
return llvm::createStringError("no register context in frame");
const uint32_t native_reg =
reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
if (native_reg == LLDB_INVALID_REGNUM)
return llvm::createStringError(
"unable to convert register kind=%u reg_num=%u to a native "
"register number",
reg_kind, reg_num);
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(native_reg);
RegisterValue reg_value;
if (reg_ctx->ReadRegister(reg_info, reg_value)) {
if (reg_value.GetScalarValue(value.GetScalar())) {
value.SetValueType(Value::ValueType::Scalar);
value.SetContext(Value::ContextType::RegisterInfo,
const_cast<RegisterInfo *>(reg_info));
return llvm::Error::success();
}
return llvm::createStringError(
"register %s can't be converted to a scalar value", reg_info->name);
}
return llvm::createStringError("register %s is not available",
reg_info->name);
}
static offset_t GetOpcodeDataSize(const DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op, const DWARFUnit *dwarf_cu) {
lldb::offset_t offset = data_offset;
switch (op) {
case DW_OP_addr:
case DW_OP_call_ref:
return data.GetAddressByteSize();
case DW_OP_deref:
case DW_OP_dup:
case DW_OP_drop:
case DW_OP_over:
case DW_OP_swap:
case DW_OP_rot:
case DW_OP_xderef:
case DW_OP_abs:
case DW_OP_and:
case DW_OP_div:
case DW_OP_minus:
case DW_OP_mod:
case DW_OP_mul:
case DW_OP_neg:
case DW_OP_not:
case DW_OP_or:
case DW_OP_plus:
case DW_OP_shl:
case DW_OP_shr:
case DW_OP_shra:
case DW_OP_xor:
case DW_OP_eq:
case DW_OP_ge:
case DW_OP_gt:
case DW_OP_le:
case DW_OP_lt:
case DW_OP_ne:
case DW_OP_lit0:
case DW_OP_lit1:
case DW_OP_lit2:
case DW_OP_lit3:
case DW_OP_lit4:
case DW_OP_lit5:
case DW_OP_lit6:
case DW_OP_lit7:
case DW_OP_lit8:
case DW_OP_lit9:
case DW_OP_lit10:
case DW_OP_lit11:
case DW_OP_lit12:
case DW_OP_lit13:
case DW_OP_lit14:
case DW_OP_lit15:
case DW_OP_lit16:
case DW_OP_lit17:
case DW_OP_lit18:
case DW_OP_lit19:
case DW_OP_lit20:
case DW_OP_lit21:
case DW_OP_lit22:
case DW_OP_lit23:
case DW_OP_lit24:
case DW_OP_lit25:
case DW_OP_lit26:
case DW_OP_lit27:
case DW_OP_lit28:
case DW_OP_lit29:
case DW_OP_lit30:
case DW_OP_lit31:
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31:
case DW_OP_nop:
case DW_OP_push_object_address:
case DW_OP_form_tls_address:
case DW_OP_call_frame_cfa:
case DW_OP_stack_value:
case DW_OP_GNU_push_tls_address:
return 0;
case DW_OP_const1u:
case DW_OP_const1s:
case DW_OP_pick:
case DW_OP_deref_size:
case DW_OP_xderef_size:
return 1;
case DW_OP_const2u:
case DW_OP_const2s:
case DW_OP_skip:
case DW_OP_bra:
case DW_OP_call2:
return 2;
case DW_OP_const4u:
case DW_OP_const4s:
case DW_OP_call4:
return 4;
case DW_OP_const8u:
case DW_OP_const8s:
return 8;
case DW_OP_addrx:
case DW_OP_constu:
case DW_OP_consts:
case DW_OP_plus_uconst:
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31:
case DW_OP_regx:
case DW_OP_fbreg:
case DW_OP_piece:
case DW_OP_GNU_addr_index:
case DW_OP_GNU_const_index:
data.Skip_LEB128(&offset);
return offset - data_offset;
case DW_OP_bregx:
case DW_OP_bit_piece:
data.Skip_LEB128(&offset);
data.Skip_LEB128(&offset);
return offset - data_offset;
case DW_OP_implicit_value:
{
uint64_t block_len = data.Skip_LEB128(&offset);
offset += block_len;
return offset - data_offset;
}
case DW_OP_GNU_entry_value:
#ifdef MS_DEBUGGER
case DW_OP_convert:
#endif
case DW_OP_entry_value:
{
uint64_t subexpr_len = data.GetULEB128(&offset);
return (offset - data_offset) + subexpr_len;
}
default:
if (!dwarf_cu) {
return LLDB_INVALID_OFFSET;
}
return dwarf_cu->GetSymbolFileDWARF().GetVendorDWARFOpcodeSize(
data, data_offset, op);
}
}
lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu,
bool &error) const {
error = false;
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_addr)
return m_data.GetAddress(&offset);
if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) {
uint64_t index = m_data.GetULEB128(&offset);
if (dwarf_cu)
return dwarf_cu->ReadAddressFromDebugAddrSection(index);
error = true;
break;
}
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET) {
error = true;
break;
}
offset += op_arg_size;
}
return LLDB_INVALID_ADDRESS;
}
bool DWARFExpression::Update_DW_OP_addr(const DWARFUnit *dwarf_cu,
lldb::addr_t file_addr) {
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_addr) {
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(),
m_data.GetByteOrder(), addr_byte_size);
if (encoder.PutAddress(offset, file_addr) == UINT32_MAX)
return false;
m_data.SetData(encoder.GetDataBuffer());
return true;
}
if (op == DW_OP_addrx) {
llvm::ArrayRef data_before_op = m_data.GetData().take_front(offset - 1);
const lldb::offset_t old_offset = offset;
m_data.GetULEB128(&offset);
if (old_offset == offset)
return false;
llvm::ArrayRef data_after_op = m_data.GetData().drop_front(offset);
DataEncoder encoder(m_data.GetByteOrder(), m_data.GetAddressByteSize());
encoder.AppendData(data_before_op);
encoder.AppendU8(DW_OP_addr);
encoder.AppendAddress(file_addr);
encoder.AppendData(data_after_op);
m_data.SetData(encoder.GetDataBuffer());
return true;
}
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
break;
offset += op_arg_size;
}
return false;
}
bool DWARFExpression::ContainsThreadLocalStorage(
const DWARFUnit *dwarf_cu) const {
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address)
return true;
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
offset += op_arg_size;
}
return false;
}
bool DWARFExpression::LinkThreadLocalStorage(
const DWARFUnit *dwarf_cu,
std::function<lldb::addr_t(lldb::addr_t file_addr)> const
&link_address_callback) {
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(),
m_data.GetByteOrder(), addr_byte_size);
lldb::offset_t offset = 0;
lldb::offset_t const_offset = 0;
lldb::addr_t const_value = 0;
size_t const_byte_size = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
bool decoded_data = false;
switch (op) {
case DW_OP_const4u:
const_offset = offset;
const_value = m_data.GetU32(&offset);
decoded_data = true;
const_byte_size = 4;
break;
case DW_OP_const8u:
const_offset = offset;
const_value = m_data.GetU64(&offset);
decoded_data = true;
const_byte_size = 8;
break;
case DW_OP_form_tls_address:
case DW_OP_GNU_push_tls_address:
if (const_byte_size > 0) {
lldb::addr_t linked_file_addr = link_address_callback(const_value);
if (linked_file_addr == LLDB_INVALID_ADDRESS)
return false;
if (encoder.PutUnsigned(const_offset, const_byte_size,
linked_file_addr) == UINT32_MAX)
return false;
}
break;
default:
const_offset = 0;
const_value = 0;
const_byte_size = 0;
break;
}
if (!decoded_data) {
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
else
offset += op_arg_size;
}
}
m_data.SetData(encoder.GetDataBuffer());
return true;
}
static llvm::Error Evaluate_DW_OP_entry_value(std::vector<Value> &stack,
ExecutionContext *exe_ctx,
RegisterContext *reg_ctx,
const DataExtractor &opcodes,
lldb::offset_t &opcode_offset,
Log *log) {
if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) {
return llvm::createStringError("no exe/reg context");
}
StackFrame *current_frame = exe_ctx->GetFramePtr();
Thread *thread = exe_ctx->GetThreadPtr();
if (!current_frame || !thread)
return llvm::createStringError("no current frame/thread");
Target &target = exe_ctx->GetTargetRef();
StackFrameSP parent_frame = nullptr;
addr_t return_pc = LLDB_INVALID_ADDRESS;
uint32_t current_frame_idx = current_frame->GetFrameIndex();
for (uint32_t parent_frame_idx = current_frame_idx + 1;;parent_frame_idx++) {
parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx);
if (!parent_frame)
break;
if (return_pc == LLDB_INVALID_ADDRESS) {
return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
LLDB_LOG(log, "immediate ancestor with pc = {0:x}", return_pc);
}
if (parent_frame->IsInlined())
continue;
break;
}
if (!parent_frame || !parent_frame->GetRegisterContext()) {
return llvm::createStringError("no parent frame with reg ctx");
}
Function *parent_func =
parent_frame->GetSymbolContext(eSymbolContextFunction).function;
if (!parent_func)
return llvm::createStringError("no parent function");
Function *current_func =
current_frame->GetSymbolContext(eSymbolContextFunction).function;
if (!current_func)
return llvm::createStringError("no current function");
CallEdge *call_edge = nullptr;
ModuleList &modlist = target.GetImages();
ExecutionContext parent_exe_ctx = *exe_ctx;
parent_exe_ctx.SetFrameSP(parent_frame);
if (!parent_frame->IsArtificial()) {
call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target);
if (!call_edge) {
return llvm::createStringError(
llvm::formatv("no call edge for retn-pc = {0:x} in parent frame {1}",
return_pc, parent_func->GetName()));
}
Function *callee_func = call_edge->GetCallee(modlist, parent_exe_ctx);
if (callee_func != current_func) {
return llvm::createStringError(
"ambiguous call sequence, can't find real parent frame");
}
} else {
for (auto &edge : parent_func->GetTailCallingEdges()) {
if (edge->GetCallee(modlist, parent_exe_ctx) == current_func) {
call_edge = edge.get();
break;
}
}
}
if (!call_edge)
return llvm::createStringError("no unambiguous edge from parent "
"to current function");
const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset);
const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len);
if (!subexpr_data)
return llvm::createStringError("subexpr could not be read");
const CallSiteParameter *matched_param = nullptr;
for (const CallSiteParameter ¶m : call_edge->GetCallSiteParameters()) {
DataExtractor param_subexpr_extractor;
if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor))
continue;
lldb::offset_t param_subexpr_offset = 0;
const void *param_subexpr_data =
param_subexpr_extractor.GetData(¶m_subexpr_offset, subexpr_len);
if (!param_subexpr_data ||
param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0)
continue;
if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) {
matched_param = ¶m;
break;
}
}
if (!matched_param)
return llvm::createStringError("no matching call site param found");
const DWARFExpressionList ¶m_expr = matched_param->LocationInCaller;
llvm::Expected<Value> maybe_result = param_expr.Evaluate(
&parent_exe_ctx, parent_frame->GetRegisterContext().get(),
LLDB_INVALID_ADDRESS,
nullptr,
nullptr);
if (!maybe_result) {
LLDB_LOG(log,
"Evaluate_DW_OP_entry_value: call site param evaluation failed");
return maybe_result.takeError();
}
stack.push_back(*maybe_result);
return llvm::Error::success();
}
namespace {
enum LocationDescriptionKind {
Empty,
Memory,
Register,
Implicit
};
void UpdateValueTypeFromLocationDescription(Log *log, const DWARFUnit *dwarf_cu,
LocationDescriptionKind kind,
Value *value = nullptr) {
if (dwarf_cu && dwarf_cu->GetVersion() >= 4) {
const char *log_msg = "DWARF location description kind: %s";
switch (kind) {
case Empty:
LLDB_LOGF(log, log_msg, "Empty");
break;
case Memory:
LLDB_LOGF(log, log_msg, "Memory");
if (value->GetValueType() == Value::ValueType::Scalar)
value->SetValueType(Value::ValueType::LoadAddress);
break;
case Register:
LLDB_LOGF(log, log_msg, "Register");
value->SetValueType(Value::ValueType::Scalar);
break;
case Implicit:
LLDB_LOGF(log, log_msg, "Implicit");
if (value->GetValueType() == Value::ValueType::LoadAddress)
value->SetValueType(Value::ValueType::Scalar);
break;
}
}
}
}
static llvm::Expected<lldb::addr_t>
ResolveLoadAddress(ExecutionContext *exe_ctx, lldb::ModuleSP &module_sp,
const char *dw_op_type, lldb::addr_t file_addr,
Address &so_addr, bool check_sectionoffset = false) {
if (!module_sp)
return llvm::createStringError("need module to resolve file address for %s",
dw_op_type);
if (!module_sp->ResolveFileAddress(file_addr, so_addr))
return llvm::createStringError("failed to resolve file address in module");
const addr_t load_addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
if (load_addr == LLDB_INVALID_ADDRESS &&
(check_sectionoffset && !so_addr.IsSectionOffset()))
return llvm::createStringError("failed to resolve load address");
return load_addr;
}
static Scalar DerefSizeExtractDataHelper(uint8_t *addr_bytes,
size_t size_addr_bytes,
ByteOrder byte_order, size_t size) {
DataExtractor addr_data(addr_bytes, size_addr_bytes, byte_order, size);
lldb::offset_t addr_data_offset = 0;
if (size <= 8)
return addr_data.GetMaxU64(&addr_data_offset, size);
else
return addr_data.GetAddress(&addr_data_offset);
}
llvm::Expected<Value> DWARFExpression::Evaluate(
ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::ModuleSP module_sp, const DataExtractor &opcodes,
const DWARFUnit *dwarf_cu, const lldb::RegisterKind reg_kind,
const Value *initial_value_ptr, const Value *object_address_ptr
#ifdef MS_DEBUGGER
, const DWARFExpressionList *expr_list) {
#else
) {
#endif
if (opcodes.GetByteSize() == 0)
return llvm::createStringError(
"no location, value may have been optimized out");
std::vector<Value> stack;
Process *process = nullptr;
StackFrame *frame = nullptr;
Target *target = nullptr;
if (exe_ctx) {
process = exe_ctx->GetProcessPtr();
frame = exe_ctx->GetFramePtr();
target = exe_ctx->GetTargetPtr();
}
if (reg_ctx == nullptr && frame)
reg_ctx = frame->GetRegisterContext().get();
if (initial_value_ptr)
stack.push_back(*initial_value_ptr);
lldb::offset_t offset = 0;
Value tmp;
uint32_t reg_num;
uint64_t op_piece_offset = 0;
Value pieces;
Log *log = GetLog(LLDBLog::Expressions);
auto to_generic = [&](auto v) {
bool is_signed = std::is_signed<decltype(v)>::value;
return Scalar(llvm::APSInt(
llvm::APInt(8 * opcodes.GetAddressByteSize(), v, is_signed),
!is_signed));
};
LocationDescriptionKind dwarf4_location_description_kind = Memory;
while (opcodes.ValidOffset(offset)) {
const lldb::offset_t op_offset = offset;
const uint8_t op = opcodes.GetU8(&offset);
if (log && log->GetVerbose()) {
size_t count = stack.size();
LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:",
(uint64_t)count);
for (size_t i = 0; i < count; ++i) {
StreamString new_value;
new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
stack[i].Dump(&new_value);
LLDB_LOGF(log, " %s", new_value.GetData());
}
LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset,
DW_OP_value_to_name(op));
}
if (std::optional<unsigned> arity =
llvm::dwarf::OperationArity(static_cast<LocationAtom>(op))) {
if (stack.size() < *arity)
return llvm::createStringError(
"%s needs at least %d stack entries (stack has %d entries)",
DW_OP_value_to_name(op), *arity, stack.size());
}
switch (op) {
case DW_OP_addr:
stack.push_back(Scalar(opcodes.GetAddress(&offset)));
if (target &&
target->GetArchitecture().GetCore() == ArchSpec::eCore_wasm32) {
stack.back().SetValueType(Value::ValueType::LoadAddress);
} else {
stack.back().SetValueType(Value::ValueType::FileAddress);
}
break;
case DW_OP_deref: {
if (stack.empty())
return llvm::createStringError(
"expression stack empty for DW_OP_deref");
Value::ValueType value_type = stack.back().GetValueType();
switch (value_type) {
case Value::ValueType::HostAddress: {
void *src = (void *)stack.back().GetScalar().ULongLong();
intptr_t ptr;
::memcpy(&ptr, src, sizeof(void *));
stack.back().GetScalar() = ptr;
stack.back().ClearContext();
} break;
case Value::ValueType::FileAddress: {
auto file_addr = stack.back().GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
Address so_addr;
auto maybe_load_addr = ResolveLoadAddress(
exe_ctx, module_sp, "DW_OP_deref", file_addr, so_addr);
if (!maybe_load_addr)
return maybe_load_addr.takeError();
stack.back().GetScalar() = *maybe_load_addr;
}
[[fallthrough]];
case Value::ValueType::Scalar:
stack.back().SetValueType(Value::ValueType::LoadAddress);
[[fallthrough]];
case Value::ValueType::LoadAddress:
if (exe_ctx) {
if (process) {
lldb::addr_t pointer_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
Status error;
lldb::addr_t pointer_value =
process->ReadPointerFromMemory(pointer_addr, error);
if (pointer_value != LLDB_INVALID_ADDRESS) {
if (ABISP abi_sp = process->GetABI())
pointer_value = abi_sp->FixCodeAddress(pointer_value);
stack.back().GetScalar() = pointer_value;
stack.back().ClearContext();
} else {
return llvm::createStringError(
"Failed to dereference pointer from 0x%" PRIx64
" for DW_OP_deref: %s\n",
pointer_addr, error.AsCString());
}
} else {
return llvm::createStringError("NULL process for DW_OP_deref");
}
} else {
return llvm::createStringError(
"NULL execution context for DW_OP_deref");
}
break;
case Value::ValueType::Invalid:
return llvm::createStringError("invalid value type for DW_OP_deref");
}
} break;
case DW_OP_deref_size: {
if (stack.empty()) {
return llvm::createStringError(
"expression stack empty for DW_OP_deref_size");
}
uint8_t size = opcodes.GetU8(&offset);
if (size > 8) {
return llvm::createStringError(
"Invalid address size for DW_OP_deref_size: %d\n", size);
}
Value::ValueType value_type = stack.back().GetValueType();
switch (value_type) {
case Value::ValueType::HostAddress: {
void *src = (void *)stack.back().GetScalar().ULongLong();
intptr_t ptr;
::memcpy(&ptr, src, sizeof(void *));
switch (size) {
case 1:
ptr = ptr & 0xff;
break;
case 2:
ptr = ptr & 0xffff;
break;
case 3:
ptr = ptr & 0xffffff;
break;
case 4:
ptr = ptr & 0xffffffff;
break;
case 5:
ptr = (intptr_t)ptr & 0xffffffffffULL;
break;
case 6:
ptr = (intptr_t)ptr & 0xffffffffffffULL;
break;
case 7:
ptr = (intptr_t)ptr & 0xffffffffffffffULL;
break;
default:
break;
}
stack.back().GetScalar() = ptr;
stack.back().ClearContext();
} break;
case Value::ValueType::FileAddress: {
auto file_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
Address so_addr;
auto maybe_load_addr = ResolveLoadAddress(
exe_ctx, module_sp, "DW_OP_deref_size", file_addr, so_addr,
true);
if (!maybe_load_addr)
return maybe_load_addr.takeError();
addr_t load_addr = *maybe_load_addr;
if (load_addr == LLDB_INVALID_ADDRESS && so_addr.IsSectionOffset()) {
uint8_t addr_bytes[8];
Status error;
if (target &&
target->ReadMemory(so_addr, &addr_bytes, size, error,
false) == size) {
ObjectFile *objfile = module_sp->GetObjectFile();
stack.back().GetScalar() = DerefSizeExtractDataHelper(
addr_bytes, size, objfile->GetByteOrder(), size);
stack.back().ClearContext();
break;
} else {
return llvm::createStringError(
"Failed to dereference pointer for DW_OP_deref_size: "
"%s\n",
error.AsCString());
}
}
stack.back().GetScalar() = load_addr;
}
[[fallthrough]];
case Value::ValueType::Scalar:
case Value::ValueType::LoadAddress:
if (exe_ctx) {
if (process) {
lldb::addr_t pointer_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
uint8_t addr_bytes[sizeof(lldb::addr_t)];
Status error;
#ifdef MS_DEBUGGER
MemoryReaderParamClient param{};
param.arch_spec = module_sp ? module_sp->GetArchitecture() : ArchSpec();
param.address_class = expr_list ? expr_list->GetAddressClass() : DeviceAddressClass::NONE;
if (process->ReadMemory(pointer_addr, &addr_bytes, size, param, error) ==
size) {
#else
if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) ==
size) {
#endif
stack.back().GetScalar() =
DerefSizeExtractDataHelper(addr_bytes, sizeof(addr_bytes),
process->GetByteOrder(), size);
stack.back().ClearContext();
} else {
return llvm::createStringError(
"Failed to dereference pointer from 0x%" PRIx64
" for DW_OP_deref: %s\n",
pointer_addr, error.AsCString());
}
} else {
return llvm::createStringError("NULL process for DW_OP_deref_size");
}
} else {
return llvm::createStringError(
"NULL execution context for DW_OP_deref_size");
}
break;
case Value::ValueType::Invalid:
return llvm::createStringError("invalid value for DW_OP_deref_size");
}
} break;
case DW_OP_xderef_size:
return llvm::createStringError("unimplemented opcode: DW_OP_xderef_size");
case DW_OP_xderef:
#ifdef MS_DEBUGGER
if (expr_list) {
if (expr_list->GetAddressClass() != DeviceAddressClass::NONE) {
break;
}
}
#endif
return llvm::createStringError("unimplemented opcode: DW_OP_xderef");
case DW_OP_const1u:
stack.push_back(to_generic(opcodes.GetU8(&offset)));
break;
case DW_OP_const1s:
stack.push_back(to_generic((int8_t)opcodes.GetU8(&offset)));
break;
case DW_OP_const2u:
stack.push_back(to_generic(opcodes.GetU16(&offset)));
break;
case DW_OP_const2s:
stack.push_back(to_generic((int16_t)opcodes.GetU16(&offset)));
break;
case DW_OP_const4u:
stack.push_back(to_generic(opcodes.GetU32(&offset)));
break;
case DW_OP_const4s:
stack.push_back(to_generic((int32_t)opcodes.GetU32(&offset)));
break;
case DW_OP_const8u:
stack.push_back(to_generic(opcodes.GetU64(&offset)));
break;
case DW_OP_const8s:
stack.push_back(to_generic((int64_t)opcodes.GetU64(&offset)));
break;
case DW_OP_constu:
stack.push_back(Scalar(opcodes.GetULEB128(&offset)));
break;
case DW_OP_consts:
stack.push_back(Scalar(opcodes.GetSLEB128(&offset)));
break;
case DW_OP_dup:
if (stack.empty()) {
return llvm::createStringError("expression stack empty for DW_OP_dup");
} else
stack.push_back(stack.back());
break;
case DW_OP_drop:
if (stack.empty()) {
return llvm::createStringError("expression stack empty for DW_OP_drop");
} else
stack.pop_back();
break;
case DW_OP_over:
stack.push_back(stack[stack.size() - 2]);
break;
case DW_OP_pick: {
uint8_t pick_idx = opcodes.GetU8(&offset);
if (pick_idx < stack.size())
stack.push_back(stack[stack.size() - 1 - pick_idx]);
else {
return llvm::createStringError(
"Index %u out of range for DW_OP_pick.\n", pick_idx);
}
} break;
case DW_OP_swap:
tmp = stack.back();
stack.back() = stack[stack.size() - 2];
stack[stack.size() - 2] = tmp;
break;
case DW_OP_rot: {
size_t last_idx = stack.size() - 1;
Value old_top = stack[last_idx];
stack[last_idx] = stack[last_idx - 1];
stack[last_idx - 1] = stack[last_idx - 2];
stack[last_idx - 2] = old_top;
} break;
case DW_OP_abs:
if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) {
return llvm::createStringError(
"failed to take the absolute value of the first stack item");
}
break;
case DW_OP_and:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx);
break;
case DW_OP_div: {
tmp = stack.back();
if (tmp.ResolveValue(exe_ctx).IsZero())
return llvm::createStringError("divide by zero");
stack.pop_back();
Scalar divisor, dividend;
divisor = tmp.ResolveValue(exe_ctx);
dividend = stack.back().ResolveValue(exe_ctx);
divisor.MakeSigned();
dividend.MakeSigned();
stack.back() = dividend / divisor;
if (!stack.back().ResolveValue(exe_ctx).IsValid())
return llvm::createStringError("divide failed");
} break;
case DW_OP_minus:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx);
break;
case DW_OP_mod:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx);
break;
case DW_OP_mul:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx);
break;
case DW_OP_neg:
if (!stack.back().ResolveValue(exe_ctx).UnaryNegate())
return llvm::createStringError("unary negate failed");
break;
case DW_OP_not:
if (!stack.back().ResolveValue(exe_ctx).OnesComplement())
return llvm::createStringError("logical NOT failed");
break;
case DW_OP_or:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx);
break;
case DW_OP_plus:
tmp = stack.back();
stack.pop_back();
stack.back().GetScalar() += tmp.GetScalar();
break;
case DW_OP_plus_uconst: {
const uint64_t uconst_value = opcodes.GetULEB128(&offset);
stack.back().GetScalar() += uconst_value;
if (!stack.back().GetScalar().IsValid())
return llvm::createStringError("DW_OP_plus_uconst failed");
} break;
case DW_OP_shl:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx);
break;
case DW_OP_shr:
tmp = stack.back();
stack.pop_back();
if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical(
tmp.ResolveValue(exe_ctx)))
return llvm::createStringError("DW_OP_shr failed");
break;
case DW_OP_shra:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx);
break;
case DW_OP_xor:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx);
break;
case DW_OP_skip: {
int16_t skip_offset = (int16_t)opcodes.GetU16(&offset);
lldb::offset_t new_offset = offset + skip_offset;
if (new_offset <= opcodes.GetByteSize())
offset = new_offset;
else {
return llvm::createStringError(llvm::formatv(
"Invalid opcode offset in DW_OP_skip: {0}+({1}) > {2}", offset,
skip_offset, opcodes.GetByteSize()));
}
} break;
case DW_OP_bra: {
tmp = stack.back();
stack.pop_back();
int16_t bra_offset = (int16_t)opcodes.GetU16(&offset);
Scalar zero(0);
if (tmp.ResolveValue(exe_ctx) != zero) {
lldb::offset_t new_offset = offset + bra_offset;
if (new_offset <= opcodes.GetByteSize())
offset = new_offset;
else {
return llvm::createStringError(llvm::formatv(
"Invalid opcode offset in DW_OP_bra: {0}+({1}) > {2}", offset,
bra_offset, opcodes.GetByteSize()));
}
}
} break;
case DW_OP_eq:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx);
break;
case DW_OP_ge:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx);
break;
case DW_OP_gt:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx);
break;
case DW_OP_le:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx);
break;
case DW_OP_lt:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx);
break;
case DW_OP_ne:
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx);
break;
case DW_OP_lit0:
case DW_OP_lit1:
case DW_OP_lit2:
case DW_OP_lit3:
case DW_OP_lit4:
case DW_OP_lit5:
case DW_OP_lit6:
case DW_OP_lit7:
case DW_OP_lit8:
case DW_OP_lit9:
case DW_OP_lit10:
case DW_OP_lit11:
case DW_OP_lit12:
case DW_OP_lit13:
case DW_OP_lit14:
case DW_OP_lit15:
case DW_OP_lit16:
case DW_OP_lit17:
case DW_OP_lit18:
case DW_OP_lit19:
case DW_OP_lit20:
case DW_OP_lit21:
case DW_OP_lit22:
case DW_OP_lit23:
case DW_OP_lit24:
case DW_OP_lit25:
case DW_OP_lit26:
case DW_OP_lit27:
case DW_OP_lit28:
case DW_OP_lit29:
case DW_OP_lit30:
case DW_OP_lit31:
stack.push_back(to_generic(op - DW_OP_lit0));
break;
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31: {
dwarf4_location_description_kind = Register;
reg_num = op - DW_OP_reg0;
if (llvm::Error err =
ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp))
return err;
stack.push_back(tmp);
} break;
case DW_OP_regx: {
dwarf4_location_description_kind = Register;
reg_num = opcodes.GetULEB128(&offset);
Status read_err;
if (llvm::Error err =
ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp))
return err;
stack.push_back(tmp);
} break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31: {
reg_num = op - DW_OP_breg0;
if (llvm::Error err =
ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp))
return err;
int64_t breg_offset = opcodes.GetSLEB128(&offset);
tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
tmp.ClearContext();
stack.push_back(tmp);
stack.back().SetValueType(Value::ValueType::LoadAddress);
} break;
case DW_OP_bregx: {
reg_num = opcodes.GetULEB128(&offset);
if (llvm::Error err =
ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp))
return err;
int64_t breg_offset = opcodes.GetSLEB128(&offset);
tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
tmp.ClearContext();
stack.push_back(tmp);
stack.back().SetValueType(Value::ValueType::LoadAddress);
} break;
case DW_OP_fbreg:
if (exe_ctx) {
if (frame) {
Scalar value;
Status fb_err;
if (frame->GetFrameBaseValue(value, &fb_err)) {
int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
value += fbreg_offset;
stack.push_back(value);
stack.back().SetValueType(Value::ValueType::LoadAddress);
} else
return fb_err.ToError();
} else {
return llvm::createStringError(
"invalid stack frame in context for DW_OP_fbreg opcode");
}
} else {
return llvm::createStringError(
"NULL execution context for DW_OP_fbreg");
}
break;
case DW_OP_nop:
break;
case DW_OP_piece: {
LocationDescriptionKind piece_locdesc = dwarf4_location_description_kind;
dwarf4_location_description_kind = Memory;
const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
if (piece_byte_size > 0) {
Value curr_piece;
if (stack.empty()) {
UpdateValueTypeFromLocationDescription(
log, dwarf_cu, LocationDescriptionKind::Empty);
curr_piece.ResizeData(piece_byte_size);
::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size);
pieces.AppendDataToHostBuffer(curr_piece);
} else {
Status error;
Value curr_piece_source_value(stack.back());
stack.pop_back();
UpdateValueTypeFromLocationDescription(log, dwarf_cu, piece_locdesc,
&curr_piece_source_value);
const Value::ValueType curr_piece_source_value_type =
curr_piece_source_value.GetValueType();
Scalar &scalar = curr_piece_source_value.GetScalar();
const lldb::addr_t addr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
switch (curr_piece_source_value_type) {
case Value::ValueType::Invalid:
return llvm::createStringError("invalid value type");
case Value::ValueType::LoadAddress:
case Value::ValueType::FileAddress: {
if (target) {
if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
if (target->ReadMemory(addr, curr_piece.GetBuffer().GetBytes(),
piece_byte_size, error,
false) !=
piece_byte_size) {
const char *addr_type = (curr_piece_source_value_type ==
Value::ValueType::LoadAddress)
? "load"
: "file";
return llvm::createStringError(
"failed to read memory DW_OP_piece(%" PRIu64
") from %s address 0x%" PRIx64,
piece_byte_size, addr_type, addr);
}
} else {
return llvm::createStringError(
"failed to resize the piece memory buffer for "
"DW_OP_piece(%" PRIu64 ")",
piece_byte_size);
}
}
} break;
case Value::ValueType::HostAddress: {
return llvm::createStringError(
"failed to read memory DW_OP_piece(%" PRIu64
") from host address 0x%" PRIx64,
piece_byte_size, addr);
} break;
case Value::ValueType::Scalar: {
uint32_t bit_size = piece_byte_size * 8;
uint32_t bit_offset = 0;
if (!scalar.ExtractBitfield(
bit_size, bit_offset)) {
return llvm::createStringError(
"unable to extract %" PRIu64 " bytes from a %" PRIu64
" byte scalar value.",
piece_byte_size,
(uint64_t)curr_piece_source_value.GetScalar().GetByteSize());
}
llvm::APInt fail_value(1, 0, false);
llvm::APInt ap_int = scalar.UInt128(fail_value);
assert(ap_int.getBitWidth() >= bit_size);
llvm::ArrayRef<uint64_t> buf{ap_int.getRawData(),
ap_int.getNumWords()};
curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf));
} break;
}
if (op_piece_offset == 0) {
if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
return llvm::createStringError("failed to append piece data");
}
} else {
if (pieces.GetBuffer().GetByteSize() != op_piece_offset) {
return llvm::createStringError(
"DW_OP_piece for offset %" PRIu64
" but top of stack is of size %" PRIu64,
op_piece_offset, pieces.GetBuffer().GetByteSize());
}
if (pieces.AppendDataToHostBuffer(curr_piece) == 0)
return llvm::createStringError("failed to append piece data");
}
}
op_piece_offset += piece_byte_size;
}
} break;
case DW_OP_bit_piece:
if (stack.size() < 1) {
UpdateValueTypeFromLocationDescription(log, dwarf_cu,
LocationDescriptionKind::Empty);
dwarf4_location_description_kind = Memory;
return llvm::createStringError(
"expression stack needs at least 1 item for DW_OP_bit_piece");
} else {
UpdateValueTypeFromLocationDescription(
log, dwarf_cu, dwarf4_location_description_kind, &stack.back());
dwarf4_location_description_kind = Memory;
const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
switch (stack.back().GetValueType()) {
case Value::ValueType::Invalid:
return llvm::createStringError(
"unable to extract bit value from invalid value");
case Value::ValueType::Scalar: {
if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
piece_bit_offset)) {
return llvm::createStringError(
"unable to extract %" PRIu64 " bit value with %" PRIu64
" bit offset from a %" PRIu64 " bit scalar value.",
piece_bit_size, piece_bit_offset,
(uint64_t)(stack.back().GetScalar().GetByteSize() * 8));
}
} break;
case Value::ValueType::FileAddress:
case Value::ValueType::LoadAddress:
case Value::ValueType::HostAddress:
return llvm::createStringError(
"unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
", bit_offset = %" PRIu64 ") from an address value.",
piece_bit_size, piece_bit_offset);
}
}
break;
case DW_OP_implicit_value: {
dwarf4_location_description_kind = Implicit;
const uint32_t len = opcodes.GetULEB128(&offset);
const void *data = opcodes.GetData(&offset, len);
if (!data) {
LLDB_LOG(log, "Evaluate_DW_OP_implicit_value: could not be read data");
return llvm::createStringError("could not evaluate %s",
DW_OP_value_to_name(op));
}
Value result(data, len);
stack.push_back(result);
break;
}
case DW_OP_implicit_pointer: {
dwarf4_location_description_kind = Implicit;
return llvm::createStringError("Could not evaluate %s.",
DW_OP_value_to_name(op));
}
case DW_OP_push_object_address:
if (object_address_ptr)
stack.push_back(*object_address_ptr);
else {
return llvm::createStringError("DW_OP_push_object_address used without "
"specifying an object address");
}
break;
case DW_OP_call2:
return llvm::createStringError("unimplemented opcode DW_OP_call2");
case DW_OP_call4:
return llvm::createStringError("unimplemented opcode DW_OP_call4");
case DW_OP_stack_value:
dwarf4_location_description_kind = Implicit;
stack.back().SetValueType(Value::ValueType::Scalar);
break;
case DW_OP_convert: {
const uint64_t die_offset = opcodes.GetULEB128(&offset);
uint64_t bit_size;
bool sign;
if (die_offset == 0) {
if (!module_sp)
return llvm::createStringError("no module");
sign = false;
bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8;
if (!bit_size)
return llvm::createStringError("unspecified architecture");
} else {
const uint64_t abs_die_offset = die_offset + dwarf_cu->GetOffset();
DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(abs_die_offset);
if (!die)
return llvm::createStringError(
"cannot resolve DW_OP_convert type DIE");
uint64_t encoding =
die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user);
bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
if (!bit_size)
bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
if (!bit_size)
return llvm::createStringError(
"unsupported type size in DW_OP_convert");
switch (encoding) {
case DW_ATE_signed:
case DW_ATE_signed_char:
sign = true;
break;
case DW_ATE_unsigned:
case DW_ATE_unsigned_char:
sign = false;
break;
default:
return llvm::createStringError(
"unsupported encoding in DW_OP_convert");
}
}
Scalar &top = stack.back().ResolveValue(exe_ctx);
top.TruncOrExtendTo(bit_size, sign);
break;
}
case DW_OP_call_frame_cfa:
if (frame) {
StackID id = frame->GetStackID();
addr_t cfa = id.GetCallFrameAddress();
if (cfa != LLDB_INVALID_ADDRESS) {
stack.push_back(Scalar(cfa));
stack.back().SetValueType(Value::ValueType::LoadAddress);
} else {
return llvm::createStringError(
"stack frame does not include a canonical "
"frame address for DW_OP_call_frame_cfa "
"opcode");
}
} else {
return llvm::createStringError("unvalid stack frame in context for "
"DW_OP_call_frame_cfa opcode");
}
break;
case DW_OP_form_tls_address:
case DW_OP_GNU_push_tls_address: {
if (stack.size() < 1) {
if (op == DW_OP_form_tls_address)
return llvm::createStringError(
"DW_OP_form_tls_address needs an argument");
else
return llvm::createStringError(
"DW_OP_GNU_push_tls_address needs an argument");
}
if (!exe_ctx || !module_sp)
return llvm::createStringError("no context to evaluate TLS within");
Thread *thread = exe_ctx->GetThreadPtr();
if (!thread)
return llvm::createStringError("no thread to evaluate TLS within");
const addr_t tls_file_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
const addr_t tls_load_addr =
thread->GetThreadLocalData(module_sp, tls_file_addr);
if (tls_load_addr == LLDB_INVALID_ADDRESS)
return llvm::createStringError(
"no TLS data currently exists for this thread");
stack.back().GetScalar() = tls_load_addr;
stack.back().SetValueType(Value::ValueType::LoadAddress);
} break;
case DW_OP_addrx:
case DW_OP_GNU_addr_index: {
if (!dwarf_cu)
return llvm::createStringError("DW_OP_GNU_addr_index found without a "
"compile unit being specified");
uint64_t index = opcodes.GetULEB128(&offset);
lldb::addr_t value = dwarf_cu->ReadAddressFromDebugAddrSection(index);
stack.push_back(Scalar(value));
if (target &&
target->GetArchitecture().GetCore() == ArchSpec::eCore_wasm32) {
stack.back().SetValueType(Value::ValueType::LoadAddress);
} else {
stack.back().SetValueType(Value::ValueType::FileAddress);
}
} break;
case DW_OP_GNU_const_index: {
if (!dwarf_cu) {
return llvm::createStringError("DW_OP_GNU_const_index found without a "
"compile unit being specified");
}
uint64_t index = opcodes.GetULEB128(&offset);
lldb::addr_t value = dwarf_cu->ReadAddressFromDebugAddrSection(index);
stack.push_back(Scalar(value));
} break;
case DW_OP_GNU_entry_value:
case DW_OP_entry_value: {
if (llvm::Error err = Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx,
opcodes, offset, log))
return llvm::createStringError(
"could not evaluate DW_OP_entry_value: %s",
llvm::toString(std::move(err)).c_str());
break;
}
default:
if (dwarf_cu) {
if (dwarf_cu->GetSymbolFileDWARF().ParseVendorDWARFOpcode(
op, opcodes, offset, stack)) {
break;
}
}
return llvm::createStringError(llvm::formatv(
"Unhandled opcode {0} in DWARFExpression", LocationAtom(op)));
}
}
if (stack.empty()) {
if (pieces.GetBuffer().GetByteSize())
return pieces;
return llvm::createStringError("stack empty after evaluation");
}
UpdateValueTypeFromLocationDescription(
log, dwarf_cu, dwarf4_location_description_kind, &stack.back());
if (log && log->GetVerbose()) {
size_t count = stack.size();
LLDB_LOGF(log,
"Stack after operation has %" PRIu64 " values:", (uint64_t)count);
for (size_t i = 0; i < count; ++i) {
StreamString new_value;
new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
stack[i].Dump(&new_value);
LLDB_LOGF(log, " %s", new_value.GetData());
}
}
#ifdef MS_DEBUGGER
auto &result = stack.back();
if (expr_list) {
result.SetAddressClass(expr_list->GetAddressClass());
result.SetPointeeAddressClass(expr_list->GetPointeeAddressClass());
}
result.SetArchSpec(module_sp ? module_sp->GetArchitecture() : ArchSpec());
#endif
return stack.back();
}
bool DWARFExpression::ParseDWARFLocationList(
const DWARFUnit *dwarf_cu, const DataExtractor &data,
DWARFExpressionList *location_list) {
location_list->Clear();
std::unique_ptr<llvm::DWARFLocationTable> loctable_up =
dwarf_cu->GetLocationTable(data);
Log *log = GetLog(LLDBLog::Expressions);
auto lookup_addr =
[&](uint32_t index) -> std::optional<llvm::object::SectionedAddress> {
addr_t address = dwarf_cu->ReadAddressFromDebugAddrSection(index);
if (address == LLDB_INVALID_ADDRESS)
return std::nullopt;
return llvm::object::SectionedAddress{address};
};
auto process_list = [&](llvm::Expected<llvm::DWARFLocationExpression> loc) {
if (!loc) {
LLDB_LOG_ERROR(log, loc.takeError(), "{0}");
return true;
}
auto buffer_sp =
std::make_shared<DataBufferHeap>(loc->Expr.data(), loc->Expr.size());
DWARFExpression expr = DWARFExpression(DataExtractor(
buffer_sp, data.GetByteOrder(), data.GetAddressByteSize()));
location_list->AddExpression(loc->Range->LowPC, loc->Range->HighPC, expr);
return true;
};
llvm::Error error = loctable_up->visitAbsoluteLocationList(
0, llvm::object::SectionedAddress{dwarf_cu->GetBaseAddress()},
lookup_addr, process_list);
location_list->Sort();
if (error) {
LLDB_LOG_ERROR(log, std::move(error), "{0}");
return false;
}
return true;
}
bool DWARFExpression::MatchesOperand(
StackFrame &frame, const Instruction::Operand &operand) const {
using namespace OperandMatchers;
RegisterContextSP reg_ctx_sp = frame.GetRegisterContext();
if (!reg_ctx_sp) {
return false;
}
DataExtractor opcodes(m_data);
lldb::offset_t op_offset = 0;
uint8_t opcode = opcodes.GetU8(&op_offset);
if (opcode == DW_OP_fbreg) {
int64_t offset = opcodes.GetSLEB128(&op_offset);
DWARFExpressionList *fb_expr = frame.GetFrameBaseExpression(nullptr);
if (!fb_expr) {
return false;
}
auto recurse = [&frame, fb_expr](const Instruction::Operand &child) {
return fb_expr->MatchesOperand(frame, child);
};
if (!offset &&
MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
recurse)(operand)) {
return true;
}
return MatchUnaryOp(
MatchOpType(Instruction::Operand::Type::Dereference),
MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
MatchImmOp(offset), recurse))(operand);
}
bool dereference = false;
const RegisterInfo *reg = nullptr;
int64_t offset = 0;
if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) {
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0);
} else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) {
offset = opcodes.GetSLEB128(&op_offset);
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_breg0);
} else if (opcode == DW_OP_regx) {
uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
} else if (opcode == DW_OP_bregx) {
uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
offset = opcodes.GetSLEB128(&op_offset);
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
} else {
return false;
}
if (!reg) {
return false;
}
if (dereference) {
if (!offset &&
MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
MatchRegOp(*reg))(operand)) {
return true;
}
return MatchUnaryOp(
MatchOpType(Instruction::Operand::Type::Dereference),
MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
MatchRegOp(*reg),
MatchImmOp(offset)))(operand);
} else {
return MatchRegOp(*reg)(operand);
}
}