diff options
Diffstat (limited to 'lldb/source/Expression/DWARFExpression.cpp')
| -rw-r--r-- | lldb/source/Expression/DWARFExpression.cpp | 3058 |
1 files changed, 3058 insertions, 0 deletions
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp new file mode 100644 index 000000000000..3789d9147737 --- /dev/null +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -0,0 +1,3058 @@ +//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===// +// +// 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 "lldb/Expression/DWARFExpression.h" + +#include <inttypes.h> + +#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/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 "Plugins/SymbolFile/DWARF/DWARFUnit.h" + +using namespace lldb; +using namespace lldb_private; + +static lldb::addr_t +ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, + uint32_t index) { + uint32_t index_size = dwarf_cu->GetAddressByteSize(); + dw_offset_t addr_base = dwarf_cu->GetAddrBase(); + lldb::offset_t offset = addr_base + index * index_size; + return dwarf_cu->GetSymbolFileDWARF() + .GetDWARFContext() + .getOrLoadAddrData() + .GetMaxU64(&offset, index_size); +} + +// DWARFExpression constructor +DWARFExpression::DWARFExpression() + : m_module_wp(), m_data(), m_dwarf_cu(nullptr), + m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {} + +DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp, + const DataExtractor &data, + const DWARFUnit *dwarf_cu) + : m_module_wp(), m_data(data), m_dwarf_cu(dwarf_cu), + m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) { + if (module_sp) + m_module_wp = module_sp; +} + +// Destructor +DWARFExpression::~DWARFExpression() {} + +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::offset_t offset, + lldb::offset_t length, + lldb::DescriptionLevel level, + ABI *abi) const { + llvm::DWARFExpression(DataExtractor(m_data, offset, length).GetAsLLVM(), + llvm::dwarf::DWARF_VERSION, m_data.GetAddressByteSize()) + .print(s->AsRawOstream(), abi ? &abi->GetMCRegisterInfo() : nullptr, + nullptr); +} + +void DWARFExpression::SetLocationListSlide(addr_t slide) { + m_loclist_slide = slide; +} + +int DWARFExpression::GetRegisterKind() { return m_reg_kind; } + +void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) { + m_reg_kind = reg_kind; +} + +bool DWARFExpression::IsLocationList() const { + return m_loclist_slide != LLDB_INVALID_ADDRESS; +} + +void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level, + addr_t location_list_base_addr, + ABI *abi) const { + if (IsLocationList()) { + // We have a location list + lldb::offset_t offset = 0; + uint32_t count = 0; + addr_t curr_base_addr = location_list_base_addr; + while (m_data.ValidOffset(offset)) { + addr_t begin_addr_offset = LLDB_INVALID_ADDRESS; + addr_t end_addr_offset = LLDB_INVALID_ADDRESS; + if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, + begin_addr_offset, end_addr_offset)) + break; + + if (begin_addr_offset == 0 && end_addr_offset == 0) + break; + + if (begin_addr_offset < end_addr_offset) { + if (count > 0) + s->PutCString(", "); + VMRange addr_range(curr_base_addr + begin_addr_offset, + curr_base_addr + end_addr_offset); + addr_range.Dump(s, 0, 8); + s->PutChar('{'); + lldb::offset_t location_length = m_data.GetU16(&offset); + DumpLocation(s, offset, location_length, level, abi); + s->PutChar('}'); + offset += location_length; + } else { + if ((m_data.GetAddressByteSize() == 4 && + (begin_addr_offset == UINT32_MAX)) || + (m_data.GetAddressByteSize() == 8 && + (begin_addr_offset == UINT64_MAX))) { + curr_base_addr = end_addr_offset + location_list_base_addr; + // We have a new base address + if (count > 0) + s->PutCString(", "); + *s << "base_addr = " << end_addr_offset; + } + } + + count++; + } + } else { + // We have a normal location that contains DW_OP location opcodes + DumpLocation(s, 0, m_data.GetByteSize(), level, abi); + } +} + +static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + uint32_t reg_num, Status *error_ptr, + Value &value) { + if (reg_ctx == nullptr) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); + } else { + uint32_t native_reg = + reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + if (native_reg == LLDB_INVALID_REGNUM) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Unable to convert register " + "kind=%u reg_num=%u to a native " + "register number.\n", + reg_kind, reg_num); + } else { + 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::eValueTypeScalar); + value.SetContext(Value::eContextTypeRegisterInfo, + const_cast<RegisterInfo *>(reg_info)); + if (error_ptr) + error_ptr->Clear(); + return true; + } else { + // If we get this error, then we need to implement a value buffer in + // the dwarf expression evaluation function... + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "register %s can't be converted to a scalar value", + reg_info->name); + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("register %s is not available", + reg_info->name); + } + } + } + return false; +} + +/// Return the length in bytes of the set of operands for \p op. No guarantees +/// are made on the state of \p data after this call. +static offset_t GetOpcodeDataSize(const DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) { + lldb::offset_t offset = data_offset; + switch (op) { + case DW_OP_addr: + case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3) + return data.GetAddressByteSize(); + + // Opcodes with no arguments + case DW_OP_deref: // 0x06 + case DW_OP_dup: // 0x12 + case DW_OP_drop: // 0x13 + case DW_OP_over: // 0x14 + case DW_OP_swap: // 0x16 + case DW_OP_rot: // 0x17 + case DW_OP_xderef: // 0x18 + case DW_OP_abs: // 0x19 + case DW_OP_and: // 0x1a + case DW_OP_div: // 0x1b + case DW_OP_minus: // 0x1c + case DW_OP_mod: // 0x1d + case DW_OP_mul: // 0x1e + case DW_OP_neg: // 0x1f + case DW_OP_not: // 0x20 + case DW_OP_or: // 0x21 + case DW_OP_plus: // 0x22 + case DW_OP_shl: // 0x24 + case DW_OP_shr: // 0x25 + case DW_OP_shra: // 0x26 + case DW_OP_xor: // 0x27 + case DW_OP_eq: // 0x29 + case DW_OP_ge: // 0x2a + case DW_OP_gt: // 0x2b + case DW_OP_le: // 0x2c + case DW_OP_lt: // 0x2d + case DW_OP_ne: // 0x2e + case DW_OP_lit0: // 0x30 + case DW_OP_lit1: // 0x31 + case DW_OP_lit2: // 0x32 + case DW_OP_lit3: // 0x33 + case DW_OP_lit4: // 0x34 + case DW_OP_lit5: // 0x35 + case DW_OP_lit6: // 0x36 + case DW_OP_lit7: // 0x37 + case DW_OP_lit8: // 0x38 + case DW_OP_lit9: // 0x39 + case DW_OP_lit10: // 0x3A + case DW_OP_lit11: // 0x3B + case DW_OP_lit12: // 0x3C + case DW_OP_lit13: // 0x3D + case DW_OP_lit14: // 0x3E + case DW_OP_lit15: // 0x3F + case DW_OP_lit16: // 0x40 + case DW_OP_lit17: // 0x41 + case DW_OP_lit18: // 0x42 + case DW_OP_lit19: // 0x43 + case DW_OP_lit20: // 0x44 + case DW_OP_lit21: // 0x45 + case DW_OP_lit22: // 0x46 + case DW_OP_lit23: // 0x47 + case DW_OP_lit24: // 0x48 + case DW_OP_lit25: // 0x49 + case DW_OP_lit26: // 0x4A + case DW_OP_lit27: // 0x4B + case DW_OP_lit28: // 0x4C + case DW_OP_lit29: // 0x4D + case DW_OP_lit30: // 0x4E + case DW_OP_lit31: // 0x4f + case DW_OP_reg0: // 0x50 + case DW_OP_reg1: // 0x51 + case DW_OP_reg2: // 0x52 + case DW_OP_reg3: // 0x53 + case DW_OP_reg4: // 0x54 + case DW_OP_reg5: // 0x55 + case DW_OP_reg6: // 0x56 + case DW_OP_reg7: // 0x57 + case DW_OP_reg8: // 0x58 + case DW_OP_reg9: // 0x59 + case DW_OP_reg10: // 0x5A + case DW_OP_reg11: // 0x5B + case DW_OP_reg12: // 0x5C + case DW_OP_reg13: // 0x5D + case DW_OP_reg14: // 0x5E + case DW_OP_reg15: // 0x5F + case DW_OP_reg16: // 0x60 + case DW_OP_reg17: // 0x61 + case DW_OP_reg18: // 0x62 + case DW_OP_reg19: // 0x63 + case DW_OP_reg20: // 0x64 + case DW_OP_reg21: // 0x65 + case DW_OP_reg22: // 0x66 + case DW_OP_reg23: // 0x67 + case DW_OP_reg24: // 0x68 + case DW_OP_reg25: // 0x69 + case DW_OP_reg26: // 0x6A + case DW_OP_reg27: // 0x6B + case DW_OP_reg28: // 0x6C + case DW_OP_reg29: // 0x6D + case DW_OP_reg30: // 0x6E + case DW_OP_reg31: // 0x6F + case DW_OP_nop: // 0x96 + case DW_OP_push_object_address: // 0x97 DWARF3 + case DW_OP_form_tls_address: // 0x9b DWARF3 + case DW_OP_call_frame_cfa: // 0x9c DWARF3 + case DW_OP_stack_value: // 0x9f DWARF4 + case DW_OP_GNU_push_tls_address: // 0xe0 GNU extension + return 0; + + // Opcodes with a single 1 byte arguments + case DW_OP_const1u: // 0x08 1 1-byte constant + case DW_OP_const1s: // 0x09 1 1-byte constant + case DW_OP_pick: // 0x15 1 1-byte stack index + case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved + case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved + return 1; + + // Opcodes with a single 2 byte arguments + case DW_OP_const2u: // 0x0a 1 2-byte constant + case DW_OP_const2s: // 0x0b 1 2-byte constant + case DW_OP_skip: // 0x2f 1 signed 2-byte constant + case DW_OP_bra: // 0x28 1 signed 2-byte constant + case DW_OP_call2: // 0x98 1 2-byte offset of DIE (DWARF3) + return 2; + + // Opcodes with a single 4 byte arguments + case DW_OP_const4u: // 0x0c 1 4-byte constant + case DW_OP_const4s: // 0x0d 1 4-byte constant + case DW_OP_call4: // 0x99 1 4-byte offset of DIE (DWARF3) + return 4; + + // Opcodes with a single 8 byte arguments + case DW_OP_const8u: // 0x0e 1 8-byte constant + case DW_OP_const8s: // 0x0f 1 8-byte constant + return 8; + + // All opcodes that have a single ULEB (signed or unsigned) argument + case DW_OP_addrx: // 0xa1 1 ULEB128 index + case DW_OP_constu: // 0x10 1 ULEB128 constant + case DW_OP_consts: // 0x11 1 SLEB128 constant + case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend + case DW_OP_breg0: // 0x70 1 ULEB128 register + case DW_OP_breg1: // 0x71 1 ULEB128 register + case DW_OP_breg2: // 0x72 1 ULEB128 register + case DW_OP_breg3: // 0x73 1 ULEB128 register + case DW_OP_breg4: // 0x74 1 ULEB128 register + case DW_OP_breg5: // 0x75 1 ULEB128 register + case DW_OP_breg6: // 0x76 1 ULEB128 register + case DW_OP_breg7: // 0x77 1 ULEB128 register + case DW_OP_breg8: // 0x78 1 ULEB128 register + case DW_OP_breg9: // 0x79 1 ULEB128 register + case DW_OP_breg10: // 0x7a 1 ULEB128 register + case DW_OP_breg11: // 0x7b 1 ULEB128 register + case DW_OP_breg12: // 0x7c 1 ULEB128 register + case DW_OP_breg13: // 0x7d 1 ULEB128 register + case DW_OP_breg14: // 0x7e 1 ULEB128 register + case DW_OP_breg15: // 0x7f 1 ULEB128 register + case DW_OP_breg16: // 0x80 1 ULEB128 register + case DW_OP_breg17: // 0x81 1 ULEB128 register + case DW_OP_breg18: // 0x82 1 ULEB128 register + case DW_OP_breg19: // 0x83 1 ULEB128 register + case DW_OP_breg20: // 0x84 1 ULEB128 register + case DW_OP_breg21: // 0x85 1 ULEB128 register + case DW_OP_breg22: // 0x86 1 ULEB128 register + case DW_OP_breg23: // 0x87 1 ULEB128 register + case DW_OP_breg24: // 0x88 1 ULEB128 register + case DW_OP_breg25: // 0x89 1 ULEB128 register + case DW_OP_breg26: // 0x8a 1 ULEB128 register + case DW_OP_breg27: // 0x8b 1 ULEB128 register + case DW_OP_breg28: // 0x8c 1 ULEB128 register + case DW_OP_breg29: // 0x8d 1 ULEB128 register + case DW_OP_breg30: // 0x8e 1 ULEB128 register + case DW_OP_breg31: // 0x8f 1 ULEB128 register + case DW_OP_regx: // 0x90 1 ULEB128 register + case DW_OP_fbreg: // 0x91 1 SLEB128 offset + case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed + case DW_OP_GNU_addr_index: // 0xfb 1 ULEB128 index + case DW_OP_GNU_const_index: // 0xfc 1 ULEB128 index + data.Skip_LEB128(&offset); + return offset - data_offset; + + // All opcodes that have a 2 ULEB (signed or unsigned) arguments + case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset + case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); + data.Skip_LEB128(&offset); + data.Skip_LEB128(&offset); + return offset - data_offset; + + case DW_OP_implicit_value: // 0x9e ULEB128 size followed by block of that size + // (DWARF4) + { + uint64_t block_len = data.Skip_LEB128(&offset); + offset += block_len; + return offset - data_offset; + } + + case DW_OP_entry_value: // 0xa3 ULEB128 size + variable-length block + { + uint64_t subexpr_len = data.GetULEB128(&offset); + return (offset - data_offset) + subexpr_len; + } + + default: + break; + } + return LLDB_INVALID_OFFSET; +} + +lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx, + bool &error) const { + error = false; + if (IsLocationList()) + return LLDB_INVALID_ADDRESS; + lldb::offset_t offset = 0; + uint32_t curr_op_addr_idx = 0; + while (m_data.ValidOffset(offset)) { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) { + const lldb::addr_t op_file_addr = m_data.GetAddress(&offset); + if (curr_op_addr_idx == op_addr_idx) + return op_file_addr; + else + ++curr_op_addr_idx; + } else if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) { + uint64_t index = m_data.GetULEB128(&offset); + if (curr_op_addr_idx == op_addr_idx) { + if (!m_dwarf_cu) { + error = true; + break; + } + + return ReadAddressFromDebugAddrSection(m_dwarf_cu, index); + } else + ++curr_op_addr_idx; + } else { + const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); + if (op_arg_size == LLDB_INVALID_OFFSET) { + error = true; + break; + } + offset += op_arg_size; + } + } + return LLDB_INVALID_ADDRESS; +} + +bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) { + if (IsLocationList()) + return false; + 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(); + // We have to make a copy of the data as we don't know if this data is + // from a read only memory mapped buffer, so we duplicate all of the data + // first, then modify it, and if all goes well, we then replace the data + // for this expression + + // So first we copy the data into a heap buffer + std::unique_ptr<DataBufferHeap> head_data_up( + new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize())); + + // Make en encoder so we can write the address into the buffer using the + // correct byte order (endianness) + DataEncoder encoder(head_data_up->GetBytes(), head_data_up->GetByteSize(), + m_data.GetByteOrder(), addr_byte_size); + + // Replace the address in the new buffer + if (encoder.PutMaxU64(offset, addr_byte_size, file_addr) == UINT32_MAX) + return false; + + // All went well, so now we can reset the data using a shared pointer to + // the heap data so "m_data" will now correctly manage the heap data. + m_data.SetData(DataBufferSP(head_data_up.release())); + return true; + } else { + const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); + if (op_arg_size == LLDB_INVALID_OFFSET) + break; + offset += op_arg_size; + } + } + return false; +} + +bool DWARFExpression::ContainsThreadLocalStorage() const { + // We are assuming for now that any thread local variable will not have a + // location list. This has been true for all thread local variables we have + // seen so far produced by any compiler. + if (IsLocationList()) + return false; + 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); + if (op_arg_size == LLDB_INVALID_OFFSET) + return false; + else + offset += op_arg_size; + } + return false; +} +bool DWARFExpression::LinkThreadLocalStorage( + lldb::ModuleSP new_module_sp, + std::function<lldb::addr_t(lldb::addr_t file_addr)> const + &link_address_callback) { + // We are assuming for now that any thread local variable will not have a + // location list. This has been true for all thread local variables we have + // seen so far produced by any compiler. + if (IsLocationList()) + return false; + + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + // We have to make a copy of the data as we don't know if this data is from a + // read only memory mapped buffer, so we duplicate all of the data first, + // then modify it, and if all goes well, we then replace the data for this + // expression + + // So first we copy the data into a heap buffer + std::shared_ptr<DataBufferHeap> heap_data_sp( + new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize())); + + // Make en encoder so we can write the address into the buffer using the + // correct byte order (endianness) + DataEncoder encoder(heap_data_sp->GetBytes(), heap_data_sp->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: + // Remember the const offset in case we later have a + // DW_OP_form_tls_address or DW_OP_GNU_push_tls_address + const_offset = offset; + const_value = m_data.GetU32(&offset); + decoded_data = true; + const_byte_size = 4; + break; + + case DW_OP_const8u: + // Remember the const offset in case we later have a + // DW_OP_form_tls_address or DW_OP_GNU_push_tls_address + 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: + // DW_OP_form_tls_address and DW_OP_GNU_push_tls_address must be preceded + // by a file address on the stack. We assume that DW_OP_const4u or + // DW_OP_const8u is used for these values, and we check that the last + // opcode we got before either of these was DW_OP_const4u or + // DW_OP_const8u. If so, then we can link the value accodingly. For + // Darwin, the value in the DW_OP_const4u or DW_OP_const8u is the file + // address of a structure that contains a function pointer, the pthread + // key and the offset into the data pointed to by the pthread key. So we + // must link this address and also set the module of this expression to + // the new_module_sp so we can resolve the file address correctly + 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; + // Replace the address in the new buffer + if (encoder.PutMaxU64(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); + if (op_arg_size == LLDB_INVALID_OFFSET) + return false; + else + offset += op_arg_size; + } + } + + // If we linked the TLS address correctly, update the module so that when the + // expression is evaluated it can resolve the file address to a load address + // and read the + // TLS data + m_module_wp = new_module_sp; + m_data.SetData(heap_data_sp); + return true; +} + +bool DWARFExpression::LocationListContainsAddress( + lldb::addr_t loclist_base_addr, lldb::addr_t addr) const { + if (addr == LLDB_INVALID_ADDRESS) + return false; + + if (IsLocationList()) { + lldb::offset_t offset = 0; + + if (loclist_base_addr == LLDB_INVALID_ADDRESS) + return false; + + while (m_data.ValidOffset(offset)) { + // We need to figure out what the value is for the location. + addr_t lo_pc = LLDB_INVALID_ADDRESS; + addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc, + hi_pc)) + break; + + if (lo_pc == 0 && hi_pc == 0) + break; + + if ((m_data.GetAddressByteSize() == 4 && (lo_pc == UINT32_MAX)) || + (m_data.GetAddressByteSize() == 8 && (lo_pc == UINT64_MAX))) { + loclist_base_addr = hi_pc + m_loclist_slide; + continue; + } + lo_pc += loclist_base_addr - m_loclist_slide; + hi_pc += loclist_base_addr - m_loclist_slide; + + if (lo_pc <= addr && addr < hi_pc) + return true; + + offset += m_data.GetU16(&offset); + } + } + return false; +} + +bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc, + lldb::offset_t &offset, + lldb::offset_t &length) { + offset = 0; + if (!IsLocationList()) { + length = m_data.GetByteSize(); + return true; + } + + if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) { + addr_t curr_base_addr = base_addr; + + while (m_data.ValidOffset(offset)) { + // We need to figure out what the value is for the location. + addr_t lo_pc = LLDB_INVALID_ADDRESS; + addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc, + hi_pc)) + break; + + if (lo_pc == 0 && hi_pc == 0) + break; + + if ((m_data.GetAddressByteSize() == 4 && (lo_pc == UINT32_MAX)) || + (m_data.GetAddressByteSize() == 8 && (lo_pc == UINT64_MAX))) { + curr_base_addr = hi_pc + m_loclist_slide; + continue; + } + + lo_pc += curr_base_addr - m_loclist_slide; + hi_pc += curr_base_addr - m_loclist_slide; + + length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) + return true; + + offset += length; + } + } + offset = LLDB_INVALID_OFFSET; + length = 0; + return false; +} + +bool DWARFExpression::DumpLocationForAddress(Stream *s, + lldb::DescriptionLevel level, + addr_t base_addr, addr_t address, + ABI *abi) { + lldb::offset_t offset = 0; + lldb::offset_t length = 0; + + if (GetLocation(base_addr, address, offset, length)) { + if (length > 0) { + DumpLocation(s, offset, length, level, abi); + return true; + } + } + return false; +} + +static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack, + ExecutionContext *exe_ctx, + RegisterContext *reg_ctx, + const DataExtractor &opcodes, + lldb::offset_t &opcode_offset, + Status *error_ptr, Log *log) { + // DW_OP_entry_value(sub-expr) describes the location a variable had upon + // function entry: this variable location is presumed to be optimized out at + // the current PC value. The caller of the function may have call site + // information that describes an alternate location for the variable (e.g. a + // constant literal, or a spilled stack value) in the parent frame. + // + // Example (this is pseudo-code & pseudo-DWARF, but hopefully illustrative): + // + // void child(int &sink, int x) { + // ... + // /* "x" gets optimized out. */ + // + // /* The location of "x" here is: DW_OP_entry_value($reg2). */ + // ++sink; + // } + // + // void parent() { + // int sink; + // + // /* + // * The callsite information emitted here is: + // * + // * DW_TAG_call_site + // * DW_AT_return_pc ... (for "child(sink, 123);") + // * DW_TAG_call_site_parameter (for "sink") + // * DW_AT_location ($reg1) + // * DW_AT_call_value ($SP - 8) + // * DW_TAG_call_site_parameter (for "x") + // * DW_AT_location ($reg2) + // * DW_AT_call_value ($literal 123) + // * + // * DW_TAG_call_site + // * DW_AT_return_pc ... (for "child(sink, 456);") + // * ... + // */ + // child(sink, 123); + // child(sink, 456); + // } + // + // When the program stops at "++sink" within `child`, the debugger determines + // the call site by analyzing the return address. Once the call site is found, + // the debugger determines which parameter is referenced by DW_OP_entry_value + // and evaluates the corresponding location for that parameter in `parent`. + + // 1. Find the function which pushed the current frame onto the stack. + if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no exe/reg context"); + return false; + } + + StackFrame *current_frame = exe_ctx->GetFramePtr(); + Thread *thread = exe_ctx->GetThreadPtr(); + if (!current_frame || !thread) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current frame/thread"); + return false; + } + + Target &target = exe_ctx->GetTargetRef(); + StackFrameSP parent_frame = nullptr; + addr_t return_pc = LLDB_INVALID_ADDRESS; + uint32_t current_frame_idx = current_frame->GetFrameIndex(); + uint32_t num_frames = thread->GetStackFrameCount(); + for (uint32_t parent_frame_idx = current_frame_idx + 1; + parent_frame_idx < num_frames; ++parent_frame_idx) { + parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx); + // Require a valid sequence of frames. + if (!parent_frame) + break; + + // Record the first valid return address, even if this is an inlined frame, + // in order to look up the associated call edge in the first non-inlined + // parent frame. + if (return_pc == LLDB_INVALID_ADDRESS) { + return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target); + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: immediate ancestor with pc = {0:x}", + return_pc); + } + + // If we've found an inlined frame, skip it (these have no call site + // parameters). + if (parent_frame->IsInlined()) + continue; + + // We've found the first non-inlined parent frame. + break; + } + if (!parent_frame || !parent_frame->GetRegisterContext()) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent frame with reg ctx"); + return false; + } + + Function *parent_func = + parent_frame->GetSymbolContext(eSymbolContextFunction).function; + if (!parent_func) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent function"); + return false; + } + + // 2. Find the call edge in the parent function responsible for creating the + // current activation. + Function *current_func = + current_frame->GetSymbolContext(eSymbolContextFunction).function; + if (!current_func) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current function"); + return false; + } + + CallEdge *call_edge = nullptr; + ModuleList &modlist = target.GetImages(); + if (!parent_frame->IsArtificial()) { + // If the parent frame is not artificial, the current activation may be + // produced by an ambiguous tail call. In this case, refuse to proceed. + call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target); + if (!call_edge) { + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: no call edge for retn-pc = {0:x} " + "in parent frame {1}", + return_pc, parent_func->GetName()); + return false; + } + Function *callee_func = call_edge->GetCallee(modlist); + if (callee_func != current_func) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, " + "can't find real parent frame"); + return false; + } + } else { + // The StackFrameList solver machinery has deduced that an unambiguous tail + // call sequence that produced the current activation. The first edge in + // the parent that points to the current function must be valid. + for (CallEdge &edge : parent_func->GetTailCallingEdges()) { + if (edge.GetCallee(modlist) == current_func) { + call_edge = &edge; + break; + } + } + } + if (!call_edge) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no unambiguous edge from parent " + "to current function"); + return false; + } + + // 3. Attempt to locate the DW_OP_entry_value expression in the set of + // available call site parameters. If found, evaluate the corresponding + // parameter in the context of the parent frame. + const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset); + const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len); + if (!subexpr_data) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: subexpr could not be read"); + return false; + } + + 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; + + // At this point, the DW_OP_entry_value sub-expression and the callee-side + // expression in the call site parameter are known to have the same length. + // Check whether they are equal. + // + // Note that an equality check is sufficient: the contents of the + // DW_OP_entry_value subexpression are only used to identify the right call + // site parameter in the parent, and do not require any special handling. + if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) { + matched_param = ¶m; + break; + } + } + if (!matched_param) { + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: no matching call site param found"); + return false; + } + + // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value + // subexpresion whenever llvm does. + Value result; + ExecutionContext parent_exe_ctx = *exe_ctx; + parent_exe_ctx.SetFrameSP(parent_frame); + const DWARFExpression ¶m_expr = matched_param->LocationInCaller; + if (!param_expr.Evaluate(&parent_exe_ctx, + parent_frame->GetRegisterContext().get(), + /*loclist_base_addr=*/LLDB_INVALID_ADDRESS, + /*initial_value_ptr=*/nullptr, + /*object_address_ptr=*/nullptr, result, error_ptr)) { + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: call site param evaluation failed"); + return false; + } + + stack.push_back(result); + return true; +} + +bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope, + lldb::addr_t loclist_base_load_addr, + const Value *initial_value_ptr, + const Value *object_address_ptr, Value &result, + Status *error_ptr) const { + ExecutionContext exe_ctx(exe_scope); + return Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, initial_value_ptr, + object_address_ptr, result, error_ptr); +} + +bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx, + RegisterContext *reg_ctx, + lldb::addr_t loclist_base_load_addr, + const Value *initial_value_ptr, + const Value *object_address_ptr, Value &result, + Status *error_ptr) const { + ModuleSP module_sp = m_module_wp.lock(); + + if (IsLocationList()) { + lldb::offset_t offset = 0; + addr_t pc; + StackFrame *frame = nullptr; + if (reg_ctx) + pc = reg_ctx->GetPC(); + else { + frame = exe_ctx->GetFramePtr(); + if (!frame) + return false; + RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); + if (!reg_ctx_sp) + return false; + pc = reg_ctx_sp->GetPC(); + } + + if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) { + if (pc == LLDB_INVALID_ADDRESS) { + if (error_ptr) + error_ptr->SetErrorString("Invalid PC in frame."); + return false; + } + + addr_t curr_loclist_base_load_addr = loclist_base_load_addr; + + while (m_data.ValidOffset(offset)) { + // We need to figure out what the value is for the location. + addr_t lo_pc = LLDB_INVALID_ADDRESS; + addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, + lo_pc, hi_pc)) + break; + + if (lo_pc == 0 && hi_pc == 0) + break; + + if ((m_data.GetAddressByteSize() == 4 && + (lo_pc == UINT32_MAX)) || + (m_data.GetAddressByteSize() == 8 && + (lo_pc == UINT64_MAX))) { + curr_loclist_base_load_addr = hi_pc + m_loclist_slide; + continue; + } + lo_pc += curr_loclist_base_load_addr - m_loclist_slide; + hi_pc += curr_loclist_base_load_addr - m_loclist_slide; + + uint16_t length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) { + return DWARFExpression::Evaluate( + exe_ctx, reg_ctx, module_sp, + DataExtractor(m_data, offset, length), m_dwarf_cu, m_reg_kind, + initial_value_ptr, object_address_ptr, result, error_ptr); + } + offset += length; + } + } + if (error_ptr) + error_ptr->SetErrorString("variable not available"); + return false; + } + + // Not a location list, just a single expression. + return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, m_data, + m_dwarf_cu, m_reg_kind, initial_value_ptr, + object_address_ptr, result, error_ptr); +} + +bool 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, + Value &result, Status *error_ptr) { + + if (opcodes.GetByteSize() == 0) { + if (error_ptr) + error_ptr->SetErrorString( + "no location, value may have been optimized out"); + return false; + } + std::vector<Value> stack; + + Process *process = nullptr; + StackFrame *frame = nullptr; + + if (exe_ctx) { + process = exe_ctx->GetProcessPtr(); + frame = exe_ctx->GetFramePtr(); + } + 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; + + /// Insertion point for evaluating multi-piece expression. + uint64_t op_piece_offset = 0; + Value pieces; // Used for DW_OP_piece + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + 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)); + } + + switch (op) { + // The DW_OP_addr operation has a single operand that encodes a machine + // address and whose size is the size of an address on the target machine. + case DW_OP_addr: + stack.push_back(Scalar(opcodes.GetAddress(&offset))); + stack.back().SetValueType(Value::eValueTypeFileAddress); + // Convert the file address to a load address, so subsequent + // DWARF operators can operate on it. + if (frame) + stack.back().ConvertToLoadAddress(module_sp.get(), + frame->CalculateTarget().get()); + break; + + // The DW_OP_addr_sect_offset4 is used for any location expressions in + // shared libraries that have a location like: + // DW_OP_addr(0x1000) + // If this address resides in a shared library, then this virtual address + // won't make sense when it is evaluated in the context of a running + // process where shared libraries have been slid. To account for this, this + // new address type where we can store the section pointer and a 4 byte + // offset. + // case DW_OP_addr_sect_offset4: + // { + // result_type = eResultTypeFileAddress; + // lldb::Section *sect = (lldb::Section + // *)opcodes.GetMaxU64(&offset, sizeof(void *)); + // lldb::addr_t sect_offset = opcodes.GetU32(&offset); + // + // Address so_addr (sect, sect_offset); + // lldb::addr_t load_addr = so_addr.GetLoadAddress(); + // if (load_addr != LLDB_INVALID_ADDRESS) + // { + // // We successfully resolve a file address to a load + // // address. + // stack.push_back(load_addr); + // break; + // } + // else + // { + // // We were able + // if (error_ptr) + // error_ptr->SetErrorStringWithFormat ("Section %s in + // %s is not currently loaded.\n", + // sect->GetName().AsCString(), + // sect->GetModule()->GetFileSpec().GetFilename().AsCString()); + // return false; + // } + // } + // break; + + // OPCODE: DW_OP_deref + // OPERANDS: none + // DESCRIPTION: Pops the top stack entry and treats it as an address. + // The value retrieved from that address is pushed. The size of the data + // retrieved from the dereferenced address is the size of an address on the + // target machine. + case DW_OP_deref: { + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_deref."); + return false; + } + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) { + case Value::eValueTypeHostAddress: { + 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::eValueTypeFileAddress: { + auto file_addr = stack.back().GetScalar().ULongLong( + LLDB_INVALID_ADDRESS); + if (!module_sp) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "need module to resolve file address for DW_OP_deref"); + return false; + } + Address so_addr; + if (!module_sp->ResolveFileAddress(file_addr, so_addr)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to resolve file address in module"); + return false; + } + addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr()); + if (load_Addr == LLDB_INVALID_ADDRESS) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to resolve load address"); + return false; + } + stack.back().GetScalar() = load_Addr; + stack.back().SetValueType(Value::eValueTypeLoadAddress); + // Fall through to load address code below... + } LLVM_FALLTHROUGH; + case Value::eValueTypeLoadAddress: + 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) { + stack.back().GetScalar() = pointer_value; + stack.back().ClearContext(); + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "Failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref: %s\n", + pointer_addr, error.AsCString()); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL process for DW_OP_deref.\n"); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } break; + + // OPCODE: DW_OP_deref_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top + // stack entry and treats it as an address. The value retrieved from that + // address is pushed. In the DW_OP_deref_size operation, however, the size + // in bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended to + // the size of an address on the target machine before being pushed on the + // expression stack. + case DW_OP_deref_size: { + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack empty for DW_OP_deref_size."); + return false; + } + uint8_t size = opcodes.GetU8(&offset); + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) { + case Value::eValueTypeHostAddress: { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy(&ptr, src, sizeof(void *)); + // I can't decide whether the size operand should apply to the bytes in + // their + // lldb-host endianness or the target endianness.. I doubt this'll ever + // come up but I'll opt for assuming big endian regardless. + 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; + // the casts are added to work around the case where intptr_t is a 32 + // bit quantity; + // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this + // program. + 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::eValueTypeLoadAddress: + 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; + if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == + size) { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), + process->GetByteOrder(), size); + lldb::offset_t addr_data_offset = 0; + switch (size) { + case 1: + stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); + break; + case 2: + stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); + break; + case 4: + stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); + break; + case 8: + stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); + break; + default: + stack.back().GetScalar() = + addr_data.GetPointer(&addr_data_offset); + } + stack.back().ClearContext(); + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "Failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref: %s\n", + pointer_addr, error.AsCString()); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL process for DW_OP_deref.\n"); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } break; + + // OPCODE: DW_OP_xderef_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at + // the top of the stack is treated as an address. The second stack entry is + // treated as an "address space identifier" for those architectures that + // support multiple address spaces. The top two stack elements are popped, + // a data item is retrieved through an implementation-defined address + // calculation and pushed as the new stack top. In the DW_OP_xderef_size + // operation, however, the size in bytes of the data retrieved from the + // dereferenced address is specified by the single operand. This operand is + // a 1-byte unsigned integral constant whose value may not be larger than + // the size of an address on the target machine. The data retrieved is zero + // extended to the size of an address on the target machine before being + // pushed on the expression stack. + case DW_OP_xderef_size: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); + return false; + // OPCODE: DW_OP_xderef + // OPERANDS: none + // DESCRIPTION: Provides an extended dereference mechanism. The entry at + // the top of the stack is treated as an address. The second stack entry is + // treated as an "address space identifier" for those architectures that + // support multiple address spaces. The top two stack elements are popped, + // a data item is retrieved through an implementation-defined address + // calculation and pushed as the new stack top. The size of the data + // retrieved from the dereferenced address is the size of an address on the + // target machine. + case DW_OP_xderef: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); + return false; + + // All DW_OP_constXXX opcodes have a single operand as noted below: + // + // Opcode Operand 1 + // DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s + // 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer + // constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u + // 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer + // constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s + // 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer + // constant DW_OP_consts signed LEB128 integer constant + case DW_OP_const1u: + stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset))); + break; + case DW_OP_const1s: + stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset))); + break; + case DW_OP_const2u: + stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset))); + break; + case DW_OP_const2s: + stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset))); + break; + case DW_OP_const4u: + stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset))); + break; + case DW_OP_const4s: + stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset))); + break; + case DW_OP_const8u: + stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset))); + break; + case DW_OP_const8s: + stack.push_back(Scalar((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; + + // OPCODE: DW_OP_dup + // OPERANDS: none + // DESCRIPTION: duplicates the value at the top of the stack + case DW_OP_dup: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); + return false; + } else + stack.push_back(stack.back()); + break; + + // OPCODE: DW_OP_drop + // OPERANDS: none + // DESCRIPTION: pops the value at the top of the stack + case DW_OP_drop: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); + return false; + } else + stack.pop_back(); + break; + + // OPCODE: DW_OP_over + // OPERANDS: none + // DESCRIPTION: Duplicates the entry currently second in the stack at + // the top of the stack. + case DW_OP_over: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_over."); + return false; + } else + stack.push_back(stack[stack.size() - 2]); + break; + + // OPCODE: DW_OP_pick + // OPERANDS: uint8_t index into the current stack + // DESCRIPTION: The stack entry with the specified index (0 through 255, + // inclusive) is pushed on the stack + 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 { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "Index %u out of range for DW_OP_pick.\n", pick_idx); + return false; + } + } break; + + // OPCODE: DW_OP_swap + // OPERANDS: none + // DESCRIPTION: swaps the top two stack entries. The entry at the top + // of the stack becomes the second stack entry, and the second entry + // becomes the top of the stack + case DW_OP_swap: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_swap."); + return false; + } else { + tmp = stack.back(); + stack.back() = stack[stack.size() - 2]; + stack[stack.size() - 2] = tmp; + } + break; + + // OPCODE: DW_OP_rot + // OPERANDS: none + // DESCRIPTION: Rotates the first three stack entries. The entry at + // the top of the stack becomes the third stack entry, the second entry + // becomes the top of the stack, and the third entry becomes the second + // entry. + case DW_OP_rot: + if (stack.size() < 3) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 3 items for DW_OP_rot."); + return false; + } else { + 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; + + // OPCODE: DW_OP_abs + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, interprets it as a signed + // value and pushes its absolute value. If the absolute value can not be + // represented, the result is undefined. + case DW_OP_abs: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_abs."); + return false; + } else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) { + if (error_ptr) + error_ptr->SetErrorString( + "Failed to take the absolute value of the first stack item."); + return false; + } + break; + + // OPCODE: DW_OP_and + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, performs a bitwise and + // operation on the two, and pushes the result. + case DW_OP_and: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_and."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_div + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, divides the former second + // entry by the former top of the stack using signed division, and pushes + // the result. + case DW_OP_div: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_div."); + return false; + } else { + tmp = stack.back(); + if (tmp.ResolveValue(exe_ctx).IsZero()) { + if (error_ptr) + error_ptr->SetErrorString("Divide by zero."); + return false; + } else { + stack.pop_back(); + stack.back() = + stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx); + if (!stack.back().ResolveValue(exe_ctx).IsValid()) { + if (error_ptr) + error_ptr->SetErrorString("Divide failed."); + return false; + } + } + } + break; + + // OPCODE: DW_OP_minus + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, subtracts the former top + // of the stack from the former second entry, and pushes the result. + case DW_OP_minus: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_minus."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_mod + // OPERANDS: none + // DESCRIPTION: pops the top two stack values and pushes the result of + // the calculation: former second stack entry modulo the former top of the + // stack. + case DW_OP_mod: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_mod."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_mul + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, multiplies them + // together, and pushes the result. + case DW_OP_mul: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_mul."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_neg + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its negation. + case DW_OP_neg: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_neg."); + return false; + } else { + if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) { + if (error_ptr) + error_ptr->SetErrorString("Unary negate failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_not + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its bitwise + // complement + case DW_OP_not: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_not."); + return false; + } else { + if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) { + if (error_ptr) + error_ptr->SetErrorString("Logical NOT failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_or + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs a bitwise or + // operation on the two, and pushes the result. + case DW_OP_or: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_or."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_plus + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, adds them together, and + // pushes the result. + case DW_OP_plus: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_plus."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().GetScalar() += tmp.GetScalar(); + } + break; + + // OPCODE: DW_OP_plus_uconst + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 + // constant operand and pushes the result. + case DW_OP_plus_uconst: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_plus_uconst."); + return false; + } else { + const uint64_t uconst_value = opcodes.GetULEB128(&offset); + // Implicit conversion from a UINT to a Scalar... + stack.back().GetScalar() += uconst_value; + if (!stack.back().GetScalar().IsValid()) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_plus_uconst failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_shl + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former + // second entry left by the number of bits specified by the former top of + // the stack, and pushes the result. + case DW_OP_shl: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_shl."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_shr + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right logically (filling with zero bits) by the number of bits + // specified by the former top of the stack, and pushes the result. + case DW_OP_shr: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_shr."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical( + tmp.ResolveValue(exe_ctx))) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_shr failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_shra + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right arithmetically (divide the magnitude by 2, keep the same + // sign for the result) by the number of bits specified by the former top + // of the stack, and pushes the result. + case DW_OP_shra: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_shra."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_xor + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs the bitwise + // exclusive-or operation on the two, and pushes the result. + case DW_OP_xor: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_xor."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_skip + // OPERANDS: int16_t + // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte + // signed integer constant. The 2-byte constant is the number of bytes of + // the DWARF expression to skip forward or backward from the current + // operation, beginning after the 2-byte constant. + case DW_OP_skip: { + int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); + lldb::offset_t new_offset = offset + skip_offset; + if (opcodes.ValidOffset(new_offset)) + offset = new_offset; + else { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); + return false; + } + } break; + + // OPCODE: DW_OP_bra + // OPERANDS: int16_t + // DESCRIPTION: A conditional branch. Its single operand is a 2-byte + // signed integer constant. This operation pops the top of stack. If the + // value popped is not the constant 0, the 2-byte constant operand is the + // number of bytes of the DWARF expression to skip forward or backward from + // the current operation, beginning after the 2-byte constant. + case DW_OP_bra: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_bra."); + return false; + } else { + 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 (opcodes.ValidOffset(new_offset)) + offset = new_offset; + else { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); + return false; + } + } + } + break; + + // OPCODE: DW_OP_eq + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // equals (==) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_eq: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_eq."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_ge + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than or equal to (>=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_ge: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_ge."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_gt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than (>) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_gt: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_gt."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_le + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than or equal to (<=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_le: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_le."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_lt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than (<) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_lt: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_lt."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_ne + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // not equal (!=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_ne: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_ne."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_litn + // OPERANDS: none + // DESCRIPTION: encode the unsigned literal values from 0 through 31. + // STACK RESULT: push the unsigned literal constant value onto the top + // of the stack. + 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(Scalar((uint64_t)(op - DW_OP_lit0))); + break; + + // OPCODE: DW_OP_regN + // OPERANDS: none + // DESCRIPTION: Push the value in register n on the top of the stack. + 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: { + reg_num = op - DW_OP_reg0; + + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } break; + // OPCODE: DW_OP_regx + // OPERANDS: + // ULEB128 literal operand that encodes the register. + // DESCRIPTION: Push the value in register on the top of the stack. + case DW_OP_regx: { + reg_num = opcodes.GetULEB128(&offset); + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } break; + + // OPCODE: DW_OP_bregN + // OPERANDS: + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + 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 (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, + tmp)) { + 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::eValueTypeLoadAddress); + } else + return false; + } break; + // OPCODE: DW_OP_bregx + // OPERANDS: 2 + // ULEB128 literal operand that encodes the register. + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + case DW_OP_bregx: { + reg_num = opcodes.GetULEB128(&offset); + + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, + tmp)) { + 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::eValueTypeLoadAddress); + } else + return false; + } break; + + case DW_OP_fbreg: + if (exe_ctx) { + if (frame) { + Scalar value; + if (frame->GetFrameBaseValue(value, error_ptr)) { + int64_t fbreg_offset = opcodes.GetSLEB128(&offset); + value += fbreg_offset; + stack.push_back(value); + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } else + return false; + } else { + if (error_ptr) + error_ptr->SetErrorString( + "Invalid stack frame in context for DW_OP_fbreg opcode."); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL execution context for DW_OP_fbreg.\n"); + return false; + } + + break; + + // OPCODE: DW_OP_nop + // OPERANDS: none + // DESCRIPTION: A place holder. It has no effect on the location stack + // or any of its values. + case DW_OP_nop: + break; + + // OPCODE: DW_OP_piece + // OPERANDS: 1 + // ULEB128: byte size of the piece + // DESCRIPTION: The operand describes the size in bytes of the piece of + // the object referenced by the DWARF expression whose result is at the top + // of the stack. If the piece is located in a register, but does not occupy + // the entire register, the placement of the piece within that register is + // defined by the ABI. + // + // Many compilers store a single variable in sets of registers, or store a + // variable partially in memory and partially in registers. DW_OP_piece + // provides a way of describing how large a part of a variable a particular + // DWARF expression refers to. + case DW_OP_piece: { + const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); + + if (piece_byte_size > 0) { + Value curr_piece; + + if (stack.empty()) { + // In a multi-piece expression, this means that the current piece is + // not available. Fill with zeros for now by resizing the data and + // appending it + curr_piece.ResizeData(piece_byte_size); + ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size); + pieces.AppendDataToHostBuffer(curr_piece); + } else { + Status error; + // Extract the current piece into "curr_piece" + Value curr_piece_source_value(stack.back()); + stack.pop_back(); + + const Value::ValueType curr_piece_source_value_type = + curr_piece_source_value.GetValueType(); + switch (curr_piece_source_value_type) { + case Value::eValueTypeLoadAddress: + if (process) { + if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { + lldb::addr_t load_addr = + curr_piece_source_value.GetScalar().ULongLong( + LLDB_INVALID_ADDRESS); + if (process->ReadMemory( + load_addr, curr_piece.GetBuffer().GetBytes(), + piece_byte_size, error) != piece_byte_size) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to read memory DW_OP_piece(%" PRIu64 + ") from 0x%" PRIx64, + piece_byte_size, load_addr); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to resize the piece memory buffer for " + "DW_OP_piece(%" PRIu64 ")", + piece_byte_size); + return false; + } + } + break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + if (error_ptr) { + lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong( + LLDB_INVALID_ADDRESS); + error_ptr->SetErrorStringWithFormat( + "failed to read memory DW_OP_piece(%" PRIu64 + ") from %s address 0x%" PRIx64, + piece_byte_size, curr_piece_source_value.GetValueType() == + Value::eValueTypeFileAddress + ? "file" + : "host", + addr); + } + return false; + + case Value::eValueTypeScalar: { + uint32_t bit_size = piece_byte_size * 8; + uint32_t bit_offset = 0; + if (!curr_piece_source_value.GetScalar().ExtractBitfield( + bit_size, bit_offset)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "unable to extract %" PRIu64 " bytes from a %" PRIu64 + " byte scalar value.", + piece_byte_size, + (uint64_t)curr_piece_source_value.GetScalar() + .GetByteSize()); + return false; + } + curr_piece = curr_piece_source_value; + } break; + + case Value::eValueTypeVector: { + if (curr_piece_source_value.GetVector().length >= piece_byte_size) + curr_piece_source_value.GetVector().length = piece_byte_size; + else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "unable to extract %" PRIu64 " bytes from a %" PRIu64 + " byte vector value.", + piece_byte_size, + (uint64_t)curr_piece_source_value.GetVector().length); + return false; + } + } break; + } + + // Check if this is the first piece? + if (op_piece_offset == 0) { + // This is the first piece, we should push it back onto the stack + // so subsequent pieces will be able to access this piece and add + // to it + if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { + if (error_ptr) + error_ptr->SetErrorString("failed to append piece data"); + return false; + } + } else { + // If this is the second or later piece there should be a value on + // the stack + if (pieces.GetBuffer().GetByteSize() != op_piece_offset) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "DW_OP_piece for offset %" PRIu64 + " but top of stack is of size %" PRIu64, + op_piece_offset, pieces.GetBuffer().GetByteSize()); + return false; + } + + if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { + if (error_ptr) + error_ptr->SetErrorString("failed to append piece data"); + return false; + } + } + op_piece_offset += piece_byte_size; + } + } + } break; + + case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); + if (stack.size() < 1) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_bit_piece."); + return false; + } else { + const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); + const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); + switch (stack.back().GetValueType()) { + case Value::eValueTypeScalar: { + if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, + piece_bit_offset)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "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)); + return false; + } + } break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + if (error_ptr) { + error_ptr->SetErrorStringWithFormat( + "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 + ", bit_offset = %" PRIu64 ") from an address value.", + piece_bit_size, piece_bit_offset); + } + return false; + + case Value::eValueTypeVector: + if (error_ptr) { + error_ptr->SetErrorStringWithFormat( + "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 + ", bit_offset = %" PRIu64 ") from a vector value.", + piece_bit_size, piece_bit_offset); + } + return false; + } + } + break; + + // OPCODE: DW_OP_push_object_address + // OPERANDS: none + // DESCRIPTION: Pushes the address of the object currently being + // evaluated as part of evaluation of a user presented expression. This + // object may correspond to an independent variable described by its own + // DIE or it may be a component of an array, structure, or class whose + // address has been dynamically determined by an earlier step during user + // expression evaluation. + case DW_OP_push_object_address: + if (object_address_ptr) + stack.push_back(*object_address_ptr); + else { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_push_object_address used without " + "specifying an object address"); + return false; + } + break; + + // OPCODE: DW_OP_call2 + // OPERANDS: + // uint16_t compile unit relative offset of a DIE + // DESCRIPTION: Performs subroutine calls during evaluation + // of a DWARF expression. The operand is the 2-byte unsigned offset of a + // debugging information entry in the current compilation unit. + // + // Operand interpretation is exactly like that for DW_FORM_ref2. + // + // This operation transfers control of DWARF expression evaluation to the + // DW_AT_location attribute of the referenced DIE. If there is no such + // attribute, then there is no effect. Execution of the DWARF expression of + // a DW_AT_location attribute may add to and/or remove from values on the + // stack. Execution returns to the point following the call when the end of + // the attribute is reached. Values on the stack at the time of the call + // may be used as parameters by the called expression and values left on + // the stack by the called expression may be used as return values by prior + // agreement between the calling and called expressions. + case DW_OP_call2: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2."); + return false; + // OPCODE: DW_OP_call4 + // OPERANDS: 1 + // uint32_t compile unit relative offset of a DIE + // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF + // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of + // a debugging information entry in the current compilation unit. + // + // Operand interpretation DW_OP_call4 is exactly like that for + // DW_FORM_ref4. + // + // This operation transfers control of DWARF expression evaluation to the + // DW_AT_location attribute of the referenced DIE. If there is no such + // attribute, then there is no effect. Execution of the DWARF expression of + // a DW_AT_location attribute may add to and/or remove from values on the + // stack. Execution returns to the point following the call when the end of + // the attribute is reached. Values on the stack at the time of the call + // may be used as parameters by the called expression and values left on + // the stack by the called expression may be used as return values by prior + // agreement between the calling and called expressions. + case DW_OP_call4: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4."); + return false; + + // OPCODE: DW_OP_stack_value + // OPERANDS: None + // DESCRIPTION: Specifies that the object does not exist in memory but + // rather is a constant value. The value from the top of the stack is the + // value to be used. This is the actual object value and not the location. + case DW_OP_stack_value: + stack.back().SetValueType(Value::eValueTypeScalar); + break; + + // OPCODE: DW_OP_convert + // OPERANDS: 1 + // A ULEB128 that is either a DIE offset of a + // DW_TAG_base_type or 0 for the generic (pointer-sized) type. + // + // DESCRIPTION: Pop the top stack element, convert it to a + // different type, and push the result. + case DW_OP_convert: { + if (stack.size() < 1) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_convert."); + return false; + } + const uint64_t die_offset = opcodes.GetULEB128(&offset); + Scalar::Type type = Scalar::e_void; + uint64_t bit_size; + if (die_offset == 0) { + // The generic type has the size of an address on the target + // machine and an unspecified signedness. Scalar has no + // "unspecified signedness", so we use unsigned types. + if (!module_sp) { + if (error_ptr) + error_ptr->SetErrorString("No module"); + return false; + } + bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8; + if (!bit_size) { + if (error_ptr) + error_ptr->SetErrorString("unspecified architecture"); + return false; + } + type = Scalar::GetBestTypeForBitSize(bit_size, false); + } else { + // Retrieve the type DIE that the value is being converted to. + // FIXME: the constness has annoying ripple effects. + DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(die_offset); + if (!die) { + if (error_ptr) + error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE"); + return false; + } + 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) { + if (error_ptr) + error_ptr->SetErrorString("Unsupported type size in DW_OP_convert"); + return false; + } + switch (encoding) { + case DW_ATE_signed: + case DW_ATE_signed_char: + type = Scalar::GetBestTypeForBitSize(bit_size, true); + break; + case DW_ATE_unsigned: + case DW_ATE_unsigned_char: + type = Scalar::GetBestTypeForBitSize(bit_size, false); + break; + default: + if (error_ptr) + error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert"); + return false; + } + } + if (type == Scalar::e_void) { + if (error_ptr) + error_ptr->SetErrorString("Unsupported pointer size"); + return false; + } + Scalar &top = stack.back().ResolveValue(exe_ctx); + top.TruncOrExtendTo(type, bit_size); + break; + } + + // OPCODE: DW_OP_call_frame_cfa + // OPERANDS: None + // DESCRIPTION: Specifies a DWARF expression that pushes the value of + // the canonical frame address consistent with the call frame information + // located in .debug_frame (or in the FDEs of the eh_frame section). + case DW_OP_call_frame_cfa: + if (frame) { + // Note that we don't have to parse FDEs because this DWARF expression + // is commonly evaluated with a valid stack frame. + StackID id = frame->GetStackID(); + addr_t cfa = id.GetCallFrameAddress(); + if (cfa != LLDB_INVALID_ADDRESS) { + stack.push_back(Scalar(cfa)); + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } else if (error_ptr) + error_ptr->SetErrorString("Stack frame does not include a canonical " + "frame address for DW_OP_call_frame_cfa " + "opcode."); + } else { + if (error_ptr) + error_ptr->SetErrorString("Invalid stack frame in context for " + "DW_OP_call_frame_cfa opcode."); + return false; + } + break; + + // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension + // opcode, DW_OP_GNU_push_tls_address) + // OPERANDS: none + // DESCRIPTION: Pops a TLS offset from the stack, converts it to + // an address in the current thread's thread-local storage block, and + // pushes it on the stack. + case DW_OP_form_tls_address: + case DW_OP_GNU_push_tls_address: { + if (stack.size() < 1) { + if (error_ptr) { + if (op == DW_OP_form_tls_address) + error_ptr->SetErrorString( + "DW_OP_form_tls_address needs an argument."); + else + error_ptr->SetErrorString( + "DW_OP_GNU_push_tls_address needs an argument."); + } + return false; + } + + if (!exe_ctx || !module_sp) { + if (error_ptr) + error_ptr->SetErrorString("No context to evaluate TLS within."); + return false; + } + + Thread *thread = exe_ctx->GetThreadPtr(); + if (!thread) { + if (error_ptr) + error_ptr->SetErrorString("No thread to evaluate TLS within."); + return false; + } + + // Lookup the TLS block address for this thread and module. + 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) { + if (error_ptr) + error_ptr->SetErrorString( + "No TLS data currently exists for this thread."); + return false; + } + + stack.back().GetScalar() = tls_load_addr; + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } break; + + // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.) + // OPERANDS: 1 + // ULEB128: index to the .debug_addr section + // DESCRIPTION: Pushes an address to the stack from the .debug_addr + // section with the base address specified by the DW_AT_addr_base attribute + // and the 0 based index is the ULEB128 encoded index. + case DW_OP_addrx: + case DW_OP_GNU_addr_index: { + if (!dwarf_cu) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a " + "compile unit being specified"); + return false; + } + uint64_t index = opcodes.GetULEB128(&offset); + lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); + stack.push_back(Scalar(value)); + stack.back().SetValueType(Value::eValueTypeFileAddress); + } break; + + // OPCODE: DW_OP_GNU_const_index + // OPERANDS: 1 + // ULEB128: index to the .debug_addr section + // DESCRIPTION: Pushes an constant with the size of a machine address to + // the stack from the .debug_addr section with the base address specified + // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128 + // encoded index. + case DW_OP_GNU_const_index: { + if (!dwarf_cu) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_GNU_const_index found without a " + "compile unit being specified"); + return false; + } + uint64_t index = opcodes.GetULEB128(&offset); + lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); + stack.push_back(Scalar(value)); + } break; + + case DW_OP_entry_value: { + if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset, + error_ptr, log)) { + LLDB_ERRORF(error_ptr, "Could not evaluate %s.", + DW_OP_value_to_name(op)); + return false; + } + break; + } + + default: + LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.", + DW_OP_value_to_name(op)); + break; + } + } + + if (stack.empty()) { + // Nothing on the stack, check if we created a piece value from DW_OP_piece + // or DW_OP_bit_piece opcodes + if (pieces.GetBuffer().GetByteSize()) { + result = pieces; + } else { + if (error_ptr) + error_ptr->SetErrorString("Stack empty after evaluation."); + return false; + } + } else { + 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()); + } + } + result = stack.back(); + } + return true; // Return true on success +} + +bool DWARFExpression::AddressRangeForLocationListEntry( + const DWARFUnit *dwarf_cu, const DataExtractor &debug_loc_data, + lldb::offset_t *offset_ptr, lldb::addr_t &low_pc, lldb::addr_t &high_pc) { + if (!debug_loc_data.ValidOffset(*offset_ptr)) + return false; + + DWARFExpression::LocationListFormat format = + dwarf_cu->GetSymbolFileDWARF().GetLocationListFormat(); + switch (format) { + case NonLocationList: + return false; + case RegularLocationList: + low_pc = debug_loc_data.GetAddress(offset_ptr); + high_pc = debug_loc_data.GetAddress(offset_ptr); + return true; + case SplitDwarfLocationList: + case LocLists: + switch (debug_loc_data.GetU8(offset_ptr)) { + case DW_LLE_end_of_list: + return false; + case DW_LLE_startx_endx: { + uint64_t index = debug_loc_data.GetULEB128(offset_ptr); + low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index); + index = debug_loc_data.GetULEB128(offset_ptr); + high_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index); + return true; + } + case DW_LLE_startx_length: { + uint64_t index = debug_loc_data.GetULEB128(offset_ptr); + low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index); + uint64_t length = (format == LocLists) + ? debug_loc_data.GetULEB128(offset_ptr) + : debug_loc_data.GetU32(offset_ptr); + high_pc = low_pc + length; + return true; + } + case DW_LLE_start_length: { + low_pc = debug_loc_data.GetAddress(offset_ptr); + high_pc = low_pc + debug_loc_data.GetULEB128(offset_ptr); + return true; + } + case DW_LLE_start_end: { + low_pc = debug_loc_data.GetAddress(offset_ptr); + high_pc = debug_loc_data.GetAddress(offset_ptr); + return true; + } + default: + // Not supported entry type + lldbassert(false && "Not supported location list type"); + return false; + } + } + assert(false && "Not supported location list type"); + return false; +} + +static bool print_dwarf_exp_op(Stream &s, const DataExtractor &data, + lldb::offset_t *offset_ptr, int address_size, + int dwarf_ref_size) { + uint8_t opcode = data.GetU8(offset_ptr); + DRC_class opcode_class; + uint64_t uint; + int64_t sint; + + int size; + + opcode_class = DW_OP_value_to_class(opcode) & (~DRC_DWARFv3); + + s.Printf("%s ", DW_OP_value_to_name(opcode)); + + /* Does this take zero parameters? If so we can shortcut this function. */ + if (opcode_class == DRC_ZEROOPERANDS) + return true; + + if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) { + uint = data.GetULEB128(offset_ptr); + sint = data.GetSLEB128(offset_ptr); + s.Printf("%" PRIu64 " %" PRIi64, uint, sint); + return true; + } + if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_entry_value) { + uint = data.GetULEB128(offset_ptr); + s.Printf("%" PRIu64 " ", uint); + return true; + } + if (opcode_class != DRC_ONEOPERAND) { + s.Printf("UNKNOWN OP %u", opcode); + return false; + } + + switch (opcode) { + case DW_OP_addr: + size = address_size; + break; + case DW_OP_const1u: + size = 1; + break; + case DW_OP_const1s: + size = -1; + break; + case DW_OP_const2u: + size = 2; + break; + case DW_OP_const2s: + size = -2; + break; + case DW_OP_const4u: + size = 4; + break; + case DW_OP_const4s: + size = -4; + break; + case DW_OP_const8u: + size = 8; + break; + case DW_OP_const8s: + size = -8; + break; + case DW_OP_constu: + size = 128; + break; + case DW_OP_consts: + size = -128; + break; + case DW_OP_fbreg: + size = -128; + 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: + size = -128; + break; + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + size = 1; + break; + case DW_OP_skip: + case DW_OP_bra: + size = -2; + break; + case DW_OP_call2: + size = 2; + break; + case DW_OP_call4: + size = 4; + break; + case DW_OP_call_ref: + size = dwarf_ref_size; + break; + case DW_OP_addrx: + case DW_OP_piece: + case DW_OP_plus_uconst: + case DW_OP_regx: + case DW_OP_GNU_addr_index: + case DW_OP_GNU_const_index: + case DW_OP_entry_value: + size = 128; + break; + default: + s.Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode); + return false; + } + + switch (size) { + case -1: + sint = (int8_t)data.GetU8(offset_ptr); + s.Printf("%+" PRIi64, sint); + break; + case -2: + sint = (int16_t)data.GetU16(offset_ptr); + s.Printf("%+" PRIi64, sint); + break; + case -4: + sint = (int32_t)data.GetU32(offset_ptr); + s.Printf("%+" PRIi64, sint); + break; + case -8: + sint = (int64_t)data.GetU64(offset_ptr); + s.Printf("%+" PRIi64, sint); + break; + case -128: + sint = data.GetSLEB128(offset_ptr); + s.Printf("%+" PRIi64, sint); + break; + case 1: + uint = data.GetU8(offset_ptr); + s.Printf("0x%2.2" PRIx64, uint); + break; + case 2: + uint = data.GetU16(offset_ptr); + s.Printf("0x%4.4" PRIx64, uint); + break; + case 4: + uint = data.GetU32(offset_ptr); + s.Printf("0x%8.8" PRIx64, uint); + break; + case 8: + uint = data.GetU64(offset_ptr); + s.Printf("0x%16.16" PRIx64, uint); + break; + case 128: + uint = data.GetULEB128(offset_ptr); + s.Printf("0x%" PRIx64, uint); + break; + } + + return true; +} + +bool DWARFExpression::PrintDWARFExpression(Stream &s, const DataExtractor &data, + int address_size, int dwarf_ref_size, + bool location_expression) { + int op_count = 0; + lldb::offset_t offset = 0; + while (data.ValidOffset(offset)) { + if (location_expression && op_count > 0) + return false; + if (op_count > 0) + s.PutCString(", "); + if (!print_dwarf_exp_op(s, data, &offset, address_size, dwarf_ref_size)) + return false; + op_count++; + } + + return true; +} + +void DWARFExpression::PrintDWARFLocationList( + Stream &s, const DWARFUnit *cu, const DataExtractor &debug_loc_data, + lldb::offset_t offset) { + uint64_t start_addr, end_addr; + uint32_t addr_size = DWARFUnit::GetAddressByteSize(cu); + s.SetAddressByteSize(DWARFUnit::GetAddressByteSize(cu)); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + while (debug_loc_data.ValidOffset(offset)) { + start_addr = debug_loc_data.GetMaxU64(&offset, addr_size); + end_addr = debug_loc_data.GetMaxU64(&offset, addr_size); + + if (start_addr == 0 && end_addr == 0) + break; + + s.PutCString("\n "); + s.Indent(); + if (cu) + s.AddressRange(start_addr + base_addr, end_addr + base_addr, + cu->GetAddressByteSize(), nullptr, ": "); + uint32_t loc_length = debug_loc_data.GetU16(&offset); + + DataExtractor locationData(debug_loc_data, offset, loc_length); + PrintDWARFExpression(s, locationData, addr_size, 4, false); + offset += loc_length; + } +} + +bool DWARFExpression::GetOpAndEndOffsets(StackFrame &frame, + lldb::offset_t &op_offset, + lldb::offset_t &end_offset) { + SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction); + if (!sc.function) { + return false; + } + + addr_t loclist_base_file_addr = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) { + return false; + } + + addr_t pc_file_addr = frame.GetFrameCodeAddress().GetFileAddress(); + lldb::offset_t opcodes_offset, opcodes_length; + if (!GetLocation(loclist_base_file_addr, pc_file_addr, opcodes_offset, + opcodes_length)) { + return false; + } + + if (opcodes_length == 0) { + return false; + } + + op_offset = opcodes_offset; + end_offset = opcodes_offset + opcodes_length; + return true; +} + +bool DWARFExpression::MatchesOperand(StackFrame &frame, + const Instruction::Operand &operand) { + using namespace OperandMatchers; + + lldb::offset_t op_offset; + lldb::offset_t end_offset; + if (!GetOpAndEndOffsets(frame, op_offset, end_offset)) { + return false; + } + + if (!m_data.ValidOffset(op_offset) || op_offset >= end_offset) { + return false; + } + + RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); + if (!reg_ctx_sp) { + return false; + } + + DataExtractor opcodes = m_data; + uint8_t opcode = opcodes.GetU8(&op_offset); + + if (opcode == DW_OP_fbreg) { + int64_t offset = opcodes.GetSLEB128(&op_offset); + + DWARFExpression *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); + } +} + |
