| //===-- DWARFExpression.cpp -----------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // The DWARF expression opcodes evaluated in this file are defined by the DWARF | |
| // Debugging Information Format specification, available at: | |
| // | |
| // https://dwarfstd.org/ | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Expression/DWARFExpression.h" | |
| #include <cinttypes> | |
| #include <limits> | |
| #include <optional> | |
| #include <vector> | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/Value.h" | |
| #include "lldb/Utility/DataEncoder.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/RegisterValue.h" | |
| #include "lldb/Utility/Scalar.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "lldb/Host/Host.h" | |
| #include "lldb/Utility/Endian.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Target/ABI.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/RegisterContext.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/StackID.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" | |
| #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" | |
| #include "llvm/Support/ErrorExtras.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| using namespace lldb_private::plugin::dwarf; | |
| using namespace llvm::dwarf; | |
| namespace { | |
| /// The location description kinds described by the DWARF v5 | |
| /// specification. Composite locations are handled out-of-band and | |
| /// thus aren't part of the enum. | |
| enum LocationDescriptionKind { | |
| Empty, | |
| Memory, | |
| Register, | |
| Implicit | |
| /* Composite*/ | |
| }; | |
| /// Aggregates the inputs, derived pointers, and mutable evaluation state for | |
| /// a single DWARF expression evaluation. Passed by reference to every helper | |
| /// so they don't need to re-thread these individually. | |
| struct EvalContext { | |
| ExecutionContext *exe_ctx; | |
| RegisterContext *reg_ctx; | |
| lldb::ModuleSP module_sp; | |
| const DWARFExpression::Delegate *dwarf_cu; | |
| lldb::RegisterKind reg_kind; | |
| const Value *initial_value_ptr; | |
| const Value *object_address_ptr; | |
| Process *process = nullptr; | |
| Target *target = nullptr; | |
| StackFrame *frame = nullptr; | |
| /// Mutable evaluation state. | |
| /// @{ | |
| std::vector<Value> stack; | |
| Value pieces; | |
| uint64_t op_piece_offset = 0; | |
| LocationDescriptionKind loc_desc_kind = Memory; | |
| /// @} | |
| EvalContext(ExecutionContext *exe_ctx, RegisterContext *reg_ctx, | |
| lldb::ModuleSP module_sp, | |
| const DWARFExpression::Delegate *dwarf_cu, | |
| lldb::RegisterKind reg_kind, const Value *initial_value_ptr, | |
| const Value *object_address_ptr) | |
| : exe_ctx(exe_ctx), reg_ctx(reg_ctx), module_sp(std::move(module_sp)), | |
| dwarf_cu(dwarf_cu), reg_kind(reg_kind), | |
| initial_value_ptr(initial_value_ptr), | |
| object_address_ptr(object_address_ptr) { | |
| if (exe_ctx) { | |
| process = exe_ctx->GetProcessPtr(); | |
| frame = exe_ctx->GetFramePtr(); | |
| target = exe_ctx->GetTargetPtr(); | |
| } | |
| if (this->reg_ctx == nullptr && frame) | |
| this->reg_ctx = frame->GetRegisterContext().get(); | |
| } | |
| }; | |
| } // namespace | |
| // DWARFExpression constructor | |
| DWARFExpression::DWARFExpression() : m_data() {} | |
| DWARFExpression::DWARFExpression(const DataExtractor &data) : m_data(data) {} | |
| // Destructor | |
| DWARFExpression::~DWARFExpression() = default; | |
| bool DWARFExpression::IsValid() const { return m_data.GetByteSize() > 0; } | |
| void DWARFExpression::UpdateValue(uint64_t const_value, | |
| lldb::offset_t const_value_byte_size, | |
| uint8_t addr_byte_size) { | |
| if (!const_value_byte_size) | |
| return; | |
| m_data.SetData( | |
| DataBufferSP(new DataBufferHeap(&const_value, const_value_byte_size))); | |
| m_data.SetByteOrder(endian::InlHostByteOrder()); | |
| m_data.SetAddressByteSize(addr_byte_size); | |
| } | |
| void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, | |
| ABI *abi, | |
| llvm::DIDumpOptions options) const { | |
| auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr; | |
| auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, | |
| bool IsEH) -> llvm::StringRef { | |
| if (!MCRegInfo) | |
| return {}; | |
| if (std::optional<unsigned> LLVMRegNum = | |
| MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH)) | |
| if (const char *RegName = MCRegInfo->getName(*LLVMRegNum)) | |
| return llvm::StringRef(RegName); | |
| return {}; | |
| }; | |
| options.GetNameForDWARFReg = GetRegName; | |
| llvm::DWARFExpression E(m_data.GetAsLLVM(), m_data.GetAddressByteSize()); | |
| llvm::printDwarfExpression(&E, s->AsRawOstream(), options, nullptr); | |
| } | |
| RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } | |
| void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) { | |
| m_reg_kind = reg_kind; | |
| } | |
| llvm::Error | |
| DWARFExpression::ReadRegisterValueAsScalar(RegisterContext *reg_ctx, | |
| lldb::RegisterKind reg_kind, | |
| uint32_t reg_num, Value &value) { | |
| if (reg_ctx == nullptr) | |
| return llvm::createStringError("no register context in frame"); | |
| const uint32_t native_reg = | |
| reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); | |
| if (native_reg == LLDB_INVALID_REGNUM) | |
| return llvm::createStringError( | |
| "unable to convert register kind=%u reg_num=%u to a native " | |
| "register number", | |
| reg_kind, reg_num); | |
| const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(native_reg); | |
| RegisterValue reg_value; | |
| if (reg_ctx->ReadRegister(reg_info, reg_value)) { | |
| if (reg_value.GetScalarValue(value.GetScalar())) { | |
| value.SetValueType(Value::ValueType::Scalar); | |
| value.SetContext(Value::ContextType::RegisterInfo, | |
| const_cast<RegisterInfo *>(reg_info)); | |
| return llvm::Error::success(); | |
| } | |
| // If we get this error, then we need to implement a value buffer in | |
| // the dwarf expression evaluation function... | |
| return llvm::createStringError( | |
| "register %s can't be converted to a scalar value", reg_info->name); | |
| } | |
| return llvm::createStringError("register %s is not available", | |
| reg_info->name); | |
| } | |
| /// 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 lldb::offset_t | |
| GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset, | |
| const LocationAtom op, | |
| const DWARFExpression::Delegate *dwarf_cu) { | |
| lldb::offset_t offset = data_offset; | |
| switch (op) { | |
| // Only used in LLVM metadata. | |
| case DW_OP_LLVM_fragment: | |
| case DW_OP_LLVM_convert: | |
| case DW_OP_LLVM_tag_offset: | |
| case DW_OP_LLVM_entry_value: | |
| case DW_OP_LLVM_implicit_pointer: | |
| case DW_OP_LLVM_arg: | |
| case DW_OP_LLVM_extract_bits_sext: | |
| case DW_OP_LLVM_extract_bits_zext: | |
| break; | |
| // Vendor extensions: | |
| case DW_OP_HP_is_value: | |
| case DW_OP_HP_fltconst4: | |
| case DW_OP_HP_fltconst8: | |
| case DW_OP_HP_mod_range: | |
| case DW_OP_HP_unmod_range: | |
| case DW_OP_HP_tls: | |
| case DW_OP_INTEL_bit_piece: | |
| case DW_OP_WASM_location: | |
| case DW_OP_WASM_location_int: | |
| case DW_OP_APPLE_uninit: | |
| case DW_OP_PGI_omp_thread_num: | |
| case DW_OP_hi_user: | |
| case DW_OP_GNU_implicit_pointer: | |
| break; | |
| 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 | |
| case DW_OP_deref_type: // 0xa6 1 1-byte constant | |
| 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_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_convert: // 0xa8 1 ULEB128 offset | |
| case DW_OP_reinterpret: // 0xa9 1 ULEB128 offset | |
| case DW_OP_addrx: // 0xa1 1 ULEB128 index | |
| case DW_OP_constx: // 0xa2 1 ULEB128 index | |
| case DW_OP_xderef_type: // 0xa7 1 ULEB128 index | |
| 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); | |
| case DW_OP_regval_type: // 0xa5 ULEB128 + ULEB128 | |
| 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.GetULEB128(&offset); | |
| offset += block_len; | |
| return offset - data_offset; | |
| } | |
| case DW_OP_implicit_pointer: // 0xa0 4-byte (or 8-byte for DWARF 64) constant | |
| // + LEB128 | |
| { | |
| data.Skip_LEB128(&offset); | |
| return (dwarf_cu ? dwarf_cu->GetAddressByteSize() : 4) + offset - | |
| data_offset; | |
| } | |
| case DW_OP_GNU_entry_value: | |
| case DW_OP_entry_value: // 0xa3 ULEB128 size + variable-length block | |
| { | |
| uint64_t subexpr_len = data.GetULEB128(&offset); | |
| return (offset - data_offset) + subexpr_len; | |
| } | |
| case DW_OP_const_type: // 0xa4 ULEB128 + size + variable-length block | |
| { | |
| data.Skip_LEB128(&offset); | |
| uint8_t length = data.GetU8(&offset); | |
| return (offset - data_offset) + length; | |
| } | |
| case DW_OP_LLVM_user: // 0xe9: ULEB128 + variable length constant | |
| { | |
| uint64_t constants = data.GetULEB128(&offset); | |
| return (offset - data_offset) + constants; | |
| } | |
| } | |
| if (dwarf_cu) | |
| return dwarf_cu->GetVendorDWARFOpcodeSize(data, data_offset, op); | |
| return LLDB_INVALID_OFFSET; | |
| } | |
| static const char *DW_OP_value_to_name(uint32_t val) { | |
| static char invalid[100]; | |
| llvm::StringRef llvmstr = llvm::dwarf::OperationEncodingString(val); | |
| if (llvmstr.empty()) { | |
| snprintf(invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); | |
| return invalid; | |
| } | |
| return llvmstr.data(); | |
| } | |
| llvm::Expected<lldb::addr_t> DWARFExpression::GetLocation_DW_OP_addr( | |
| const DWARFExpression::Delegate *dwarf_cu) const { | |
| lldb::offset_t offset = 0; | |
| while (m_data.ValidOffset(offset)) { | |
| const LocationAtom op = static_cast<LocationAtom>(m_data.GetU8(&offset)); | |
| if (op == DW_OP_addr) | |
| return m_data.GetAddress(&offset); | |
| if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) { | |
| const uint64_t index = m_data.GetULEB128(&offset); | |
| if (dwarf_cu) | |
| return dwarf_cu->ReadAddressFromDebugAddrSection(index); | |
| return llvm::createStringError("cannot evaluate %s without a DWARF unit", | |
| DW_OP_value_to_name(op)); | |
| } | |
| const lldb::offset_t op_arg_size = | |
| GetOpcodeDataSize(m_data, offset, op, dwarf_cu); | |
| if (op_arg_size == LLDB_INVALID_OFFSET) | |
| return llvm::createStringError("cannot get opcode data size for %s", | |
| DW_OP_value_to_name(op)); | |
| offset += op_arg_size; | |
| } | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| bool DWARFExpression::Update_DW_OP_addr( | |
| const DWARFExpression::Delegate *dwarf_cu, lldb::addr_t file_addr) { | |
| lldb::offset_t offset = 0; | |
| while (m_data.ValidOffset(offset)) { | |
| const LocationAtom op = static_cast<LocationAtom>(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 | |
| // Make en encoder that contains a copy of the location expression data | |
| // so we can write the address into the buffer using the correct byte | |
| // order. | |
| DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(), | |
| m_data.GetByteOrder(), addr_byte_size); | |
| // Replace the address in the new buffer | |
| if (encoder.PutAddress(offset, 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(encoder.GetDataBuffer()); | |
| return true; | |
| } | |
| if (op == DW_OP_addrx) { | |
| // Replace DW_OP_addrx with DW_OP_addr, since we can't modify the | |
| // read-only debug_addr table. | |
| // Subtract one to account for the opcode. | |
| llvm::ArrayRef data_before_op = m_data.GetData().take_front(offset - 1); | |
| // Read the addrx index to determine how many bytes it needs. | |
| const lldb::offset_t old_offset = offset; | |
| m_data.GetULEB128(&offset); | |
| if (old_offset == offset) | |
| return false; | |
| llvm::ArrayRef data_after_op = m_data.GetData().drop_front(offset); | |
| DataEncoder encoder(m_data.GetByteOrder(), m_data.GetAddressByteSize()); | |
| encoder.AppendData(data_before_op); | |
| encoder.AppendU8(DW_OP_addr); | |
| encoder.AppendAddress(file_addr); | |
| encoder.AppendData(data_after_op); | |
| m_data.SetData(encoder.GetDataBuffer()); | |
| return true; | |
| } | |
| const lldb::offset_t op_arg_size = | |
| GetOpcodeDataSize(m_data, offset, op, dwarf_cu); | |
| if (op_arg_size == LLDB_INVALID_OFFSET) | |
| break; | |
| offset += op_arg_size; | |
| } | |
| return false; | |
| } | |
| bool DWARFExpression::ContainsThreadLocalStorage( | |
| const DWARFExpression::Delegate *dwarf_cu) const { | |
| lldb::offset_t offset = 0; | |
| while (m_data.ValidOffset(offset)) { | |
| const LocationAtom op = static_cast<LocationAtom>(m_data.GetU8(&offset)); | |
| if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address) | |
| return true; | |
| const lldb::offset_t op_arg_size = | |
| GetOpcodeDataSize(m_data, offset, op, dwarf_cu); | |
| if (op_arg_size == LLDB_INVALID_OFFSET) | |
| return false; | |
| offset += op_arg_size; | |
| } | |
| return false; | |
| } | |
| bool DWARFExpression::IsImplicit( | |
| const DWARFExpression::Delegate *dwarf_cu) const { | |
| lldb::offset_t offset = 0; | |
| while (m_data.ValidOffset(offset)) { | |
| const LocationAtom op = static_cast<LocationAtom>(m_data.GetU8(&offset)); | |
| switch (op) { | |
| // Implicit locations have no storage in the inferior. Composite locations | |
| // might, but we conservatively treat them as non-writable because LLDB | |
| // does not write their pieces back. | |
| case DW_OP_stack_value: | |
| case DW_OP_implicit_value: | |
| case DW_OP_implicit_pointer: | |
| case DW_OP_piece: | |
| case DW_OP_bit_piece: | |
| return true; | |
| default: | |
| break; | |
| } | |
| const lldb::offset_t op_arg_size = | |
| GetOpcodeDataSize(m_data, offset, op, dwarf_cu); | |
| if (op_arg_size == LLDB_INVALID_OFFSET) | |
| return false; | |
| offset += op_arg_size; | |
| } | |
| return false; | |
| } | |
| bool DWARFExpression::LinkThreadLocalStorage( | |
| const DWARFExpression::Delegate *dwarf_cu, | |
| std::function<lldb::addr_t(lldb::addr_t file_addr)> const | |
| &link_address_callback) { | |
| const uint32_t addr_byte_size = m_data.GetAddressByteSize(); | |
| // 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. | |
| // Make en encoder that contains a copy of the location expression data so we | |
| // can write the address into the buffer using the correct byte order. | |
| DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(), | |
| m_data.GetByteOrder(), addr_byte_size); | |
| lldb::offset_t offset = 0; | |
| lldb::offset_t const_offset = 0; | |
| lldb::addr_t const_value = 0; | |
| size_t const_byte_size = 0; | |
| while (m_data.ValidOffset(offset)) { | |
| const LocationAtom op = static_cast<LocationAtom>(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 accordingly. 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.PutUnsigned(const_offset, const_byte_size, | |
| linked_file_addr) == UINT32_MAX) | |
| return false; | |
| } | |
| break; | |
| default: | |
| const_offset = 0; | |
| const_value = 0; | |
| const_byte_size = 0; | |
| break; | |
| } | |
| if (!decoded_data) { | |
| const lldb::offset_t op_arg_size = | |
| GetOpcodeDataSize(m_data, offset, op, dwarf_cu); | |
| if (op_arg_size == LLDB_INVALID_OFFSET) | |
| return false; | |
| else | |
| offset += op_arg_size; | |
| } | |
| } | |
| m_data.SetData(encoder.GetDataBuffer()); | |
| return true; | |
| } | |
| static llvm::Error Evaluate_DW_OP_entry_value(EvalContext &eval_ctx, | |
| llvm::ArrayRef<uint8_t> subexpr) { | |
| Log *log = GetLog(LLDBLog::Expressions); | |
| // 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 ((!eval_ctx.exe_ctx || !eval_ctx.exe_ctx->HasTargetScope()) || | |
| !eval_ctx.reg_ctx) { | |
| return llvm::createStringError("no exe/reg context"); | |
| } | |
| StackFrame *current_frame = eval_ctx.exe_ctx->GetFramePtr(); | |
| Thread *thread = eval_ctx.exe_ctx->GetThreadPtr(); | |
| if (!current_frame || !thread) | |
| return llvm::createStringError("no current frame/thread"); | |
| Target &target = eval_ctx.exe_ctx->GetTargetRef(); | |
| StackFrameSP parent_frame = nullptr; | |
| addr_t return_pc = LLDB_INVALID_ADDRESS; | |
| uint32_t current_frame_idx = current_frame->GetFrameIndex(); | |
| for (uint32_t parent_frame_idx = current_frame_idx + 1;; parent_frame_idx++) { | |
| parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx); | |
| // If this is null, we're at the end of the stack. | |
| 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, "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()) { | |
| return llvm::createStringError("no parent frame with reg ctx"); | |
| } | |
| Function *parent_func = | |
| parent_frame->GetSymbolContext(eSymbolContextFunction).function; | |
| if (!parent_func) | |
| return llvm::createStringError("no parent function"); | |
| // 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) | |
| return llvm::createStringError("no current function"); | |
| CallEdge *call_edge = nullptr; | |
| ModuleList &modlist = target.GetImages(); | |
| ExecutionContext parent_exe_ctx = *eval_ctx.exe_ctx; | |
| parent_exe_ctx.SetFrameSP(parent_frame); | |
| 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) { | |
| return llvm::createStringErrorV( | |
| "no call edge for retn-pc = {0:x} in parent frame {1}", return_pc, | |
| parent_func->GetName()); | |
| } | |
| SymbolContext callee = call_edge->GetCallee(modlist, parent_exe_ctx); | |
| if (callee.function != current_func) { | |
| return llvm::createStringError( | |
| "ambiguous call sequence, can't find real parent frame"); | |
| } | |
| } 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 (auto &edge : parent_func->GetTailCallingEdges()) { | |
| if (edge->GetCallee(modlist, parent_exe_ctx).function == current_func) { | |
| call_edge = edge.get(); | |
| break; | |
| } | |
| } | |
| } | |
| if (!call_edge) | |
| return llvm::createStringError("no unambiguous edge from parent " | |
| "to current function"); | |
| // 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 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.size()); | |
| 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.size()) == 0) { | |
| matched_param = ¶m; | |
| break; | |
| } | |
| } | |
| if (!matched_param) | |
| return llvm::createStringError("no matching call site param found"); | |
| // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value | |
| // subexpresion whenever llvm does. | |
| const DWARFExpressionList ¶m_expr = matched_param->LocationInCaller; | |
| // Recurse through the public entry point so the call-site parameter is | |
| // evaluated with a fresh EvalContext (separate stack and piece state). | |
| llvm::Expected<Value> maybe_result = param_expr.Evaluate( | |
| &parent_exe_ctx, parent_frame->GetRegisterContext().get(), | |
| LLDB_INVALID_ADDRESS, | |
| /*initial_value_ptr=*/nullptr, | |
| /*object_address_ptr=*/nullptr); | |
| if (!maybe_result) { | |
| LLDB_LOG(log, | |
| "Evaluate_DW_OP_entry_value: call site param evaluation failed"); | |
| return maybe_result.takeError(); | |
| } | |
| eval_ctx.stack.push_back(*maybe_result); | |
| return llvm::Error::success(); | |
| } | |
| /// Adjust value's ValueType according to the kind of location description. | |
| static void UpdateValueTypeFromLocationDescription(EvalContext &eval_ctx, | |
| LocationDescriptionKind kind, | |
| Value *value = nullptr) { | |
| // Note that this function is conflating DWARF expressions with | |
| // DWARF location descriptions. Perhaps it would be better to define | |
| // a wrapper for DWARFExpression::Eval() that deals with DWARF | |
| // location descriptions (which consist of one or more DWARF | |
| // expressions). But doing this would mean we'd also need factor the | |
| // handling of DW_OP_(bit_)piece out of this function. | |
| if (eval_ctx.dwarf_cu && eval_ctx.dwarf_cu->GetVersion() >= 4) { | |
| Log *log = GetLog(LLDBLog::Expressions); | |
| const char *log_msg = "DWARF location description kind: %s"; | |
| switch (kind) { | |
| case Empty: | |
| LLDB_LOGF(log, log_msg, "Empty"); | |
| break; | |
| case Memory: | |
| LLDB_LOGF(log, log_msg, "Memory"); | |
| if (value->GetValueType() == Value::ValueType::Scalar) | |
| value->SetValueType(Value::ValueType::LoadAddress); | |
| break; | |
| case Register: | |
| LLDB_LOGF(log, log_msg, "Register"); | |
| value->SetValueType(Value::ValueType::Scalar); | |
| break; | |
| case Implicit: | |
| LLDB_LOGF(log, log_msg, "Implicit"); | |
| if (value->GetValueType() == Value::ValueType::LoadAddress) | |
| value->SetValueType(Value::ValueType::Scalar); | |
| break; | |
| } | |
| } | |
| } | |
| /// Helper function to move common code used to resolve a file address and turn | |
| /// into a load address. | |
| /// | |
| /// \param eval_ctx Evaluation context (provides exe_ctx and module_sp). | |
| /// \param dw_op_type C-style string used to vary the error output | |
| /// \param file_addr the file address we are trying to resolve and turn into a | |
| /// load address | |
| /// \param so_addr out parameter, will be set to load address or section offset | |
| /// \param check_sectionoffset bool which determines if having a section offset | |
| /// but not a load address is considerd a success | |
| /// \returns std::optional containing the load address if resolving and getting | |
| /// the load address succeed or an empty Optinal otherwise. If | |
| /// check_sectionoffset is true we consider LLDB_INVALID_ADDRESS a | |
| /// success if so_addr.IsSectionOffset() is true. | |
| static llvm::Expected<lldb::addr_t> | |
| ResolveLoadAddress(EvalContext &eval_ctx, const char *dw_op_type, | |
| lldb::addr_t file_addr, Address &so_addr, | |
| bool check_sectionoffset = false) { | |
| if (!eval_ctx.module_sp) | |
| return llvm::createStringError("need module to resolve file address for %s", | |
| dw_op_type); | |
| if (!eval_ctx.module_sp->ResolveFileAddress(file_addr, so_addr)) | |
| return llvm::createStringError("failed to resolve file address in module"); | |
| const addr_t load_addr = so_addr.GetLoadAddress(eval_ctx.target); | |
| if (load_addr == LLDB_INVALID_ADDRESS && | |
| (check_sectionoffset && !so_addr.IsSectionOffset())) | |
| return llvm::createStringError("failed to resolve load address"); | |
| return load_addr; | |
| } | |
| /// @brief Helper function to load sized data from a uint8_t buffer. | |
| /// | |
| /// @param addr_bytes The buffer containing raw data. | |
| /// @param size_addr_bytes How large is the underlying raw data. | |
| /// @param byte_order What is the byte order of the underlying data. | |
| /// @param size How much of the underlying data we want to use. | |
| /// @return The underlying data converted into a Scalar. | |
| static Scalar DerefSizeExtractDataHelper(uint8_t *addr_bytes, | |
| size_t size_addr_bytes, | |
| ByteOrder byte_order, size_t size) { | |
| DataExtractor addr_data(addr_bytes, size_addr_bytes, byte_order, size); | |
| lldb::offset_t addr_data_offset = 0; | |
| if (size <= 8) | |
| return addr_data.GetMaxU64(&addr_data_offset, size); | |
| return addr_data.GetAddress(&addr_data_offset); | |
| } | |
| static llvm::Error Evaluate_DW_OP_deref(EvalContext &eval_ctx, | |
| LocationAtom opcode, unsigned size, | |
| size_t size_addr_bytes) { | |
| const char *op_name = DW_OP_value_to_name(opcode); | |
| if (eval_ctx.stack.empty()) | |
| return llvm::createStringError("expression stack empty for %s", op_name); | |
| if (size == 0 || size > 8) | |
| return llvm::createStringError("Invalid address size for %s: %u", op_name, | |
| size); | |
| if (opcode == DW_OP_deref_size && size > size_addr_bytes) | |
| return llvm::createStringError( | |
| "DW_OP_deref_size size (%u) exceeds address size (%zu)", size, | |
| size_addr_bytes); | |
| // Deref a register or implicit location and truncate the value to `size` | |
| // bytes. See the corresponding comment in DW_OP_deref for more details on | |
| // why we deref these locations this way. | |
| if (eval_ctx.loc_desc_kind == Register || | |
| eval_ctx.loc_desc_kind == Implicit) { | |
| // Reset context to default values. | |
| eval_ctx.loc_desc_kind = Memory; | |
| eval_ctx.stack.back().ClearContext(); | |
| // Truncate the value on top of the stack to *size* bytes then | |
| // extend to the size of an address (e.g. generic type). | |
| Scalar scalar = eval_ctx.stack.back().GetScalar(); | |
| scalar.TruncOrExtendTo(size * 8, /*sign=*/false); | |
| scalar.TruncOrExtendTo(size_addr_bytes * 8, | |
| /*sign=*/false); | |
| eval_ctx.stack.back().GetScalar() = scalar; | |
| return llvm::Error::success(); | |
| } | |
| Value::ValueType value_type = eval_ctx.stack.back().GetValueType(); | |
| switch (value_type) { | |
| case Value::ValueType::HostAddress: { | |
| void *src = (void *)eval_ctx.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; | |
| } | |
| eval_ctx.stack.back().GetScalar() = ptr; | |
| eval_ctx.stack.back().ClearContext(); | |
| } break; | |
| case Value::ValueType::FileAddress: { | |
| auto file_addr = | |
| eval_ctx.stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); | |
| Address so_addr; | |
| auto maybe_load_addr = | |
| ResolveLoadAddress(eval_ctx, op_name, file_addr, so_addr, | |
| /*check_sectionoffset=*/true); | |
| if (!maybe_load_addr) | |
| return maybe_load_addr.takeError(); | |
| addr_t load_addr = *maybe_load_addr; | |
| if (load_addr == LLDB_INVALID_ADDRESS && so_addr.IsSectionOffset()) { | |
| uint8_t addr_bytes[8]; | |
| Status error; | |
| if (!eval_ctx.target || | |
| eval_ctx.target->ReadMemory(so_addr, &addr_bytes, size, error, | |
| /*force_live_memory=*/false) != size) | |
| return llvm::createStringError("failed to dereference pointer for %s: " | |
| "%s\n", | |
| op_name, error.AsCString()); | |
| ObjectFile *objfile = eval_ctx.module_sp->GetObjectFile(); | |
| eval_ctx.stack.back().GetScalar() = DerefSizeExtractDataHelper( | |
| addr_bytes, size, objfile->GetByteOrder(), size); | |
| eval_ctx.stack.back().ClearContext(); | |
| break; | |
| } | |
| eval_ctx.stack.back().GetScalar() = load_addr; | |
| // Fall through to load address promotion code below. | |
| } | |
| [[fallthrough]]; | |
| case Value::ValueType::Scalar: | |
| // Promote Scalar to LoadAddress and fall through. | |
| eval_ctx.stack.back().SetValueType(Value::ValueType::LoadAddress); | |
| [[fallthrough]]; | |
| case Value::ValueType::LoadAddress: { | |
| if (!eval_ctx.exe_ctx) | |
| return llvm::createStringError("no execution context for %s", op_name); | |
| if (!eval_ctx.process) | |
| return llvm::createStringError("no process for %s", op_name); | |
| lldb::addr_t pointer_addr = | |
| eval_ctx.stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); | |
| uint8_t addr_bytes[sizeof(lldb::addr_t)]; | |
| Status error; | |
| if (eval_ctx.process->ReadMemory(pointer_addr, &addr_bytes, size, error) != | |
| size) | |
| return llvm::createStringError( | |
| "failed to dereference pointer from 0x%" PRIx64 " for %s: %s\n", | |
| pointer_addr, op_name, error.AsCString()); | |
| eval_ctx.stack.back().GetScalar() = DerefSizeExtractDataHelper( | |
| addr_bytes, sizeof(addr_bytes), eval_ctx.process->GetByteOrder(), size); | |
| eval_ctx.stack.back().ClearContext(); | |
| } break; | |
| case Value::ValueType::Invalid: | |
| return llvm::createStringError("invalid value for %s", op_name); | |
| } | |
| // Both operations push a generic, address-sized result. The truncation to | |
| // `size` bytes is unnecessary here because the branches above already limit | |
| // the value to `size` bytes; it is only done for consistency with the | |
| // Register/Implicit path above. | |
| eval_ctx.stack.back().GetScalar().TruncOrExtendTo(size * 8, /*sign=*/false); | |
| eval_ctx.stack.back().GetScalar().TruncOrExtendTo(size_addr_bytes * 8, | |
| /*sign=*/false); | |
| return llvm::Error::success(); | |
| } | |
| static llvm::Error Evaluate_DW_OP_piece(EvalContext &eval_ctx, | |
| uint64_t piece_byte_size) { | |
| LocationDescriptionKind piece_locdesc = eval_ctx.loc_desc_kind; | |
| // Reset for the next piece. | |
| eval_ctx.loc_desc_kind = Memory; | |
| if (piece_byte_size == 0) | |
| return llvm::Error::success(); | |
| Value curr_piece; | |
| if (eval_ctx.stack.empty()) { | |
| UpdateValueTypeFromLocationDescription(eval_ctx, | |
| LocationDescriptionKind::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); | |
| // Note that "0" is not a correct value for the unknown bits. | |
| // It would be better to also return a mask of valid bits together | |
| // with the expression result, so the debugger can print missing | |
| // members as "<optimized out>" or something. | |
| ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size); | |
| eval_ctx.pieces.AppendDataToHostBuffer(curr_piece); | |
| } else { | |
| Status error; | |
| // Extract the current piece into "curr_piece" | |
| Value curr_piece_source_value(eval_ctx.stack.back()); | |
| eval_ctx.stack.pop_back(); | |
| UpdateValueTypeFromLocationDescription(eval_ctx, piece_locdesc, | |
| &curr_piece_source_value); | |
| const Value::ValueType curr_piece_source_value_type = | |
| curr_piece_source_value.GetValueType(); | |
| Scalar &scalar = curr_piece_source_value.GetScalar(); | |
| lldb::addr_t addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); | |
| switch (curr_piece_source_value_type) { | |
| case Value::ValueType::Invalid: | |
| return llvm::createStringError("invalid value type"); | |
| case Value::ValueType::FileAddress: | |
| if (eval_ctx.target) { | |
| curr_piece_source_value.ConvertToLoadAddress(eval_ctx.module_sp.get(), | |
| eval_ctx.target); | |
| addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); | |
| } else { | |
| return llvm::createStringError( | |
| "unable to convert file address 0x%" PRIx64 " to load address " | |
| "for DW_OP_piece(%" PRIu64 "): " | |
| "no target available", | |
| addr, piece_byte_size); | |
| } | |
| [[fallthrough]]; | |
| case Value::ValueType::LoadAddress: { | |
| if (eval_ctx.target) { | |
| if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { | |
| if (eval_ctx.target->ReadMemory( | |
| Address(addr), curr_piece.GetBuffer().GetBytes(), | |
| piece_byte_size, error, | |
| /*force_live_memory=*/false) != piece_byte_size) { | |
| const char *addr_type = | |
| (curr_piece_source_value_type == Value::ValueType::LoadAddress) | |
| ? "load" | |
| : "file"; | |
| return llvm::createStringError( | |
| "failed to read memory DW_OP_piece(%" PRIu64 | |
| ") from %s address 0x%" PRIx64, | |
| piece_byte_size, addr_type, addr); | |
| } | |
| } else { | |
| return llvm::createStringError( | |
| "failed to resize the piece memory buffer for " | |
| "DW_OP_piece(%" PRIu64 ")", | |
| piece_byte_size); | |
| } | |
| } | |
| } break; | |
| case Value::ValueType::HostAddress: { | |
| // DW_OP_implicit_value uses a host-address Value to own its bytes. For | |
| // a full-width piece, the address is only an implementation detail and | |
| // the backing bytes are the value of the piece. | |
| if (piece_locdesc == Implicit && | |
| curr_piece_source_value.GetBuffer().GetByteSize() == | |
| piece_byte_size) { | |
| curr_piece = curr_piece_source_value; | |
| break; | |
| } | |
| return llvm::createStringError( | |
| "failed to read memory DW_OP_piece(%" PRIu64 | |
| ") from host address 0x%" PRIx64, | |
| piece_byte_size, addr); | |
| } break; | |
| case Value::ValueType::Scalar: { | |
| uint32_t bit_size = piece_byte_size * 8; | |
| uint32_t bit_offset = 0; | |
| if (!scalar.ExtractBitfield(bit_size, bit_offset)) { | |
| return llvm::createStringError( | |
| "unable to extract %" PRIu64 " bytes from a %" PRIu64 | |
| " byte scalar value.", | |
| piece_byte_size, | |
| (uint64_t)curr_piece_source_value.GetScalar().GetByteSize()); | |
| } | |
| // We have seen a case where we have expression like: | |
| // DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28 | |
| // here we are assuming the compiler was trying to zero | |
| // extend the value that we should append to the buffer. | |
| scalar.TruncOrExtendTo(bit_size, /*sign=*/false); | |
| curr_piece.GetScalar() = scalar; | |
| } break; | |
| } | |
| // Check if this is the first piece? | |
| if (eval_ctx.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 (eval_ctx.pieces.AppendDataToHostBuffer(curr_piece) == 0) { | |
| return llvm::createStringError("failed to append piece data"); | |
| } | |
| } else { | |
| // If this is the second or later piece there should be a value on | |
| // the stack. | |
| if (eval_ctx.pieces.GetBuffer().GetByteSize() != | |
| eval_ctx.op_piece_offset) { | |
| return llvm::createStringError( | |
| "DW_OP_piece for offset %" PRIu64 | |
| " but top of stack is of size %" PRIu64, | |
| eval_ctx.op_piece_offset, | |
| eval_ctx.pieces.GetBuffer().GetByteSize()); | |
| } | |
| if (eval_ctx.pieces.AppendDataToHostBuffer(curr_piece) == 0) | |
| return llvm::createStringError("failed to append piece data"); | |
| } | |
| } | |
| eval_ctx.op_piece_offset += piece_byte_size; | |
| return llvm::Error::success(); | |
| } | |
| static llvm::Error Evaluate_DW_OP_convert(EvalContext &eval_ctx, | |
| uint64_t relative_die_offset) { | |
| uint64_t bit_size; | |
| llvm::dwarf::TypeKind encoding; | |
| if (relative_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 (!eval_ctx.module_sp) | |
| return llvm::createStringError("no module"); | |
| encoding = llvm::dwarf::DW_ATE_unsigned; | |
| bit_size = eval_ctx.module_sp->GetArchitecture().GetAddressByteSize() * 8; | |
| if (!bit_size) | |
| return llvm::createStringError("unspecified architecture"); | |
| } else { | |
| if (!eval_ctx.dwarf_cu) | |
| return llvm::createStringError( | |
| "DW_OP_convert with a DIE offset requires a DWARF unit"); | |
| auto bit_size_encoding_or_err = | |
| eval_ctx.dwarf_cu->GetDIEBitSizeAndEncoding(relative_die_offset); | |
| if (!bit_size_encoding_or_err) | |
| return bit_size_encoding_or_err.takeError(); | |
| bit_size = bit_size_encoding_or_err->first; | |
| encoding = bit_size_encoding_or_err->second; | |
| } | |
| Scalar &scalar = eval_ctx.stack.back().GetScalar(); | |
| if (encoding == llvm::dwarf::DW_ATE_float) { | |
| const llvm::fltSemantics *semantics; | |
| switch (bit_size) { | |
| case 32: | |
| semantics = &llvm::APFloat::IEEEsingle(); | |
| break; | |
| case 64: | |
| semantics = &llvm::APFloat::IEEEdouble(); | |
| break; | |
| case 80: | |
| semantics = &llvm::APFloat::x87DoubleExtended(); | |
| break; | |
| default: | |
| return llvm::createStringError("unsupported floating-point type size"); | |
| } | |
| if (scalar.GetType() == Scalar::e_float) { | |
| llvm::APFloat value = scalar.GetAPFloat(); | |
| bool loses_info; | |
| value.convert(*semantics, llvm::APFloat::rmNearestTiesToEven, | |
| &loses_info); | |
| scalar = Scalar(std::move(value)); | |
| } else if (!scalar.FloatPromote(*semantics)) { | |
| return llvm::createStringError("cannot convert value to floating point"); | |
| } | |
| return llvm::Error::success(); | |
| } | |
| const bool sign = encoding == llvm::dwarf::DW_ATE_signed || | |
| encoding == llvm::dwarf::DW_ATE_signed_char; | |
| if (scalar.GetType() == Scalar::e_float) { | |
| if (bit_size > std::numeric_limits<uint16_t>::max()) | |
| return llvm::createStringError("unsupported integer type size: %" PRIu64, | |
| bit_size); | |
| llvm::APSInt value(static_cast<unsigned>(bit_size), | |
| /*isUnsigned=*/!sign); | |
| bool is_exact; | |
| llvm::APFloat::opStatus status = scalar.GetAPFloat().convertToInteger( | |
| value, llvm::APFloat::rmTowardZero, &is_exact); | |
| if (status & llvm::APFloat::opInvalidOp) | |
| return llvm::createStringError( | |
| "cannot convert floating-point value to integer"); | |
| scalar = Scalar(std::move(value)); | |
| } else { | |
| scalar.TruncOrExtendTo(bit_size, sign); | |
| } | |
| return llvm::Error::success(); | |
| } | |
| static llvm::Error Evaluate_DW_OP_form_tls_address(EvalContext &eval_ctx, | |
| LocationAtom opcode) { | |
| if (eval_ctx.stack.empty()) | |
| return llvm::createStringError("%s needs an argument", | |
| opcode == DW_OP_form_tls_address | |
| ? "DW_OP_form_tls_address" | |
| : "DW_OP_GNU_push_tls_address"); | |
| if (!eval_ctx.exe_ctx || !eval_ctx.module_sp) | |
| return llvm::createStringError("no context to evaluate TLS within"); | |
| Thread *thread = eval_ctx.exe_ctx->GetThreadPtr(); | |
| if (!thread) | |
| return llvm::createStringError("no thread to evaluate TLS within"); | |
| // Lookup the TLS block address for this thread and module. | |
| const addr_t tls_file_addr = | |
| eval_ctx.stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); | |
| const addr_t tls_load_addr = | |
| thread->GetThreadLocalData(eval_ctx.module_sp, tls_file_addr); | |
| if (tls_load_addr == LLDB_INVALID_ADDRESS) | |
| return llvm::createStringError( | |
| "no TLS data currently exists for this thread"); | |
| eval_ctx.stack.back().GetScalar() = tls_load_addr; | |
| eval_ctx.stack.back().SetValueType(Value::ValueType::LoadAddress); | |
| return llvm::Error::success(); | |
| } | |
| static llvm::Error Evaluate_DW_OP_fbreg(EvalContext &eval_ctx, | |
| int64_t fbreg_offset) { | |
| if (!eval_ctx.exe_ctx) | |
| return llvm::createStringError("NULL execution context for DW_OP_fbreg"); | |
| if (!eval_ctx.frame) | |
| return llvm::createStringError( | |
| "invalid stack frame in context for DW_OP_fbreg opcode"); | |
| Scalar value; | |
| if (llvm::Error err = eval_ctx.frame->GetFrameBaseValue(value)) | |
| return err; | |
| value += fbreg_offset; | |
| eval_ctx.stack.push_back(value); | |
| eval_ctx.stack.back().SetValueType(Value::ValueType::LoadAddress); | |
| return llvm::Error::success(); | |
| } | |
| static llvm::Error Evaluate_DW_OP_call_frame_cfa(EvalContext &eval_ctx) { | |
| if (!eval_ctx.frame) | |
| return llvm::createStringError( | |
| "invalid stack frame in context for DW_OP_call_frame_cfa opcode"); | |
| // Note that we don't have to parse FDEs because this DWARF expression | |
| // is commonly evaluated with a valid stack frame. | |
| StackID id = eval_ctx.frame->GetStackID(); | |
| addr_t cfa = id.GetCallFrameAddressWithMetadata(); | |
| if (cfa == LLDB_INVALID_ADDRESS) | |
| return llvm::createStringError("stack frame does not include a canonical " | |
| "frame address for DW_OP_call_frame_cfa " | |
| "opcode"); | |
| eval_ctx.stack.push_back(Scalar(cfa)); | |
| eval_ctx.stack.back().SetValueType(Value::ValueType::LoadAddress); | |
| return llvm::Error::success(); | |
| } | |
| static llvm::Error CheckScalarOperandsHaveSameType(const Scalar &lhs, | |
| const Scalar &rhs, | |
| LocationAtom opcode, | |
| size_t address_size) { | |
| auto mismatch = [&](const char *what) { | |
| return llvm::createStringError("%s requires operands to have the same %s", | |
| DW_OP_value_to_name(opcode), what); | |
| }; | |
| // Scalar does not preserve the original DWARF DIE, but it does carry the | |
| // pieces of base-type information used by the evaluator: kind, size, and | |
| // integer signedness. | |
| if (lhs.GetType() != rhs.GetType()) | |
| return mismatch("type"); | |
| // Only integer scalars have signedness. Non-integer operands (e.g. floats) | |
| // have no further scalar type information to compare once kind and size | |
| // match. | |
| if (lhs.GetType() != Scalar::e_int) { | |
| if (lhs.GetByteSize() != rhs.GetByteSize()) | |
| return mismatch("size"); | |
| return llvm::Error::success(); | |
| } | |
| // DWARF generic values are address-sized integers with unspecified | |
| // signedness. LLDB does not explicitly preserve genericness on the | |
| // expression stack, so treat integers at least as wide as the generic type | |
| // as potentially generic and compatible with one another, regardless of | |
| // exact width or signedness. This keeps common expressions working: e.g. | |
| // DW_OP_breg produces a register-sized value while DW_OP_const* produces an | |
| // address-sized one, yet both are meant to be generic. DW_OP_constu and | |
| // DW_OP_consts also do not always use to_generic due to | |
| // https://github.com/llvm/llvm-project/issues/47431. A precise fix would | |
| // require tracking genericness directly, which is a larger type-system | |
| // change. | |
| if (address_size != 0 && lhs.GetByteSize() >= address_size && | |
| rhs.GetByteSize() >= address_size) | |
| return llvm::Error::success(); | |
| // For non-generic integer operands, size and signedness are part of the | |
| // base-type information preserved by Scalar, so require them to match. | |
| if (lhs.GetByteSize() != rhs.GetByteSize()) | |
| return mismatch("size"); | |
| if (lhs.IsSigned() != rhs.IsSigned()) | |
| return mismatch("signedness"); | |
| return llvm::Error::success(); | |
| } | |
| // Scalar does not preserve DWARF's generic type identifier. Since generic | |
| // values are address-sized integers, use the scalar kind and width as an | |
| // approximation. | |
| static bool IsPotentiallyGenericIntegerOperand(const Scalar &operand, | |
| size_t address_size) { | |
| return address_size != 0 && operand.GetType() == Scalar::e_int && | |
| operand.GetByteSize() == address_size; | |
| } | |
| llvm::Expected<Value> DWARFExpression::Evaluate( | |
| ExecutionContext *exe_ctx, RegisterContext *reg_ctx, | |
| lldb::ModuleSP module_sp, const DataExtractor &opcodes, | |
| const DWARFExpression::Delegate *dwarf_cu, | |
| const lldb::RegisterKind reg_kind, const Value *initial_value_ptr, | |
| const Value *object_address_ptr) { | |
| uint32_t address_size = opcodes.GetAddressByteSize(); | |
| llvm::DataExtractor expr_data = opcodes.GetAsLLVM(); | |
| llvm::DWARFExpression expr(expr_data, address_size); | |
| if (expr_data.size() == 0) | |
| return llvm::createStringError( | |
| "no location, value may have been optimized out"); | |
| EvalContext eval_ctx(exe_ctx, reg_ctx, std::move(module_sp), dwarf_cu, | |
| reg_kind, initial_value_ptr, object_address_ptr); | |
| Stack &stack = eval_ctx.stack; | |
| if (initial_value_ptr) | |
| stack.push_back(*initial_value_ptr); | |
| Value tmp; | |
| uint32_t reg_num; | |
| Log *log = GetLog(LLDBLog::Expressions); | |
| // A generic type is "an integral type that has the size of an address and an | |
| // unspecified signedness". For now, just use the signedness of the operand. | |
| // TODO: Implement a real typed stack, and store the genericness of the value | |
| // there. | |
| auto to_generic = [&](auto v) { | |
| // TODO: Avoid implicit trunc? | |
| // See https://github.com/llvm/llvm-project/issues/112510. | |
| bool is_signed = std::is_signed<decltype(v)>::value; | |
| return Scalar(llvm::APSInt( | |
| llvm::APInt(8 * address_size, v, is_signed, /*implicitTrunc=*/true), | |
| !is_signed)); | |
| }; | |
| llvm::DWARFExpression::iterator op = expr.begin(), op_end = expr.end(); | |
| while (op != op_end) { | |
| const uint64_t op_offset = op.getOffset(); | |
| const LocationAtom opcode = static_cast<LocationAtom>(op->getCode()); | |
| if (log && log->GetVerbose()) { | |
| size_t count = stack.size(); | |
| LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:", | |
| static_cast<uint64_t>(count)); | |
| for (size_t i = 0; i < count; ++i) { | |
| StreamString new_value; | |
| new_value.Printf("[%" PRIu64 "]", static_cast<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(opcode)); | |
| } | |
| if (std::optional<unsigned> arity = OperationArity(opcode)) { | |
| if (stack.size() < *arity) | |
| return llvm::createStringError( | |
| "%s needs at least %d stack entries (stack has %d entries)", | |
| DW_OP_value_to_name(opcode), *arity, stack.size()); | |
| } | |
| switch (opcode) { | |
| case DW_OP_addr: | |
| stack.push_back(to_generic(op->getRawOperand(0))); | |
| stack.back().SetValueType(Value::ValueType::FileAddress); | |
| break; | |
| case DW_OP_deref: { | |
| size_t size = address_size; | |
| if (llvm::Error err = Evaluate_DW_OP_deref(eval_ctx, opcode, size, size)) | |
| return err; | |
| } break; | |
| case DW_OP_deref_size: { | |
| size_t size = op->getRawOperand(0); | |
| if (llvm::Error err = | |
| Evaluate_DW_OP_deref(eval_ctx, opcode, size, address_size)) | |
| return err; | |
| } break; | |
| case DW_OP_const1u: | |
| stack.push_back(to_generic(op->getRawOperand(0))); | |
| break; | |
| case DW_OP_const1s: | |
| stack.push_back(to_generic(static_cast<int8_t>(op->getRawOperand(0)))); | |
| break; | |
| case DW_OP_const2u: | |
| stack.push_back(to_generic(op->getRawOperand(0))); | |
| break; | |
| case DW_OP_const2s: | |
| stack.push_back(to_generic(static_cast<int16_t>(op->getRawOperand(0)))); | |
| break; | |
| case DW_OP_const4u: | |
| stack.push_back(to_generic(op->getRawOperand(0))); | |
| break; | |
| case DW_OP_const4s: | |
| stack.push_back(to_generic(static_cast<int32_t>(op->getRawOperand(0)))); | |
| break; | |
| case DW_OP_const8u: | |
| stack.push_back(to_generic(op->getRawOperand(0))); | |
| break; | |
| case DW_OP_const8s: | |
| stack.push_back(to_generic(static_cast<int64_t>(op->getRawOperand(0)))); | |
| break; | |
| case DW_OP_constu: | |
| stack.push_back(to_generic(op->getRawOperand(0))); | |
| break; | |
| case DW_OP_consts: | |
| stack.push_back(to_generic(static_cast<int64_t>(op->getRawOperand(0)))); | |
| break; | |
| case DW_OP_dup: | |
| if (stack.empty()) { | |
| return llvm::createStringError("expression stack empty for DW_OP_dup"); | |
| } else | |
| stack.push_back(stack.back()); | |
| break; | |
| case DW_OP_drop: | |
| if (stack.empty()) { | |
| return llvm::createStringError("expression stack empty for DW_OP_drop"); | |
| } else | |
| stack.pop_back(); | |
| break; | |
| case DW_OP_over: | |
| stack.push_back(stack[stack.size() - 2]); | |
| break; | |
| case DW_OP_pick: { | |
| uint8_t pick_idx = op->getRawOperand(0); | |
| if (pick_idx < stack.size()) | |
| stack.push_back(stack[stack.size() - 1 - pick_idx]); | |
| else { | |
| return llvm::createStringError( | |
| "Index %u out of range for DW_OP_pick.\n", pick_idx); | |
| } | |
| } break; | |
| case DW_OP_swap: | |
| tmp = stack.back(); | |
| stack.back() = stack[stack.size() - 2]; | |
| stack[stack.size() - 2] = tmp; | |
| break; | |
| case DW_OP_rot: { | |
| size_t last_idx = stack.size() - 1; | |
| Value old_top = stack[last_idx]; | |
| stack[last_idx] = stack[last_idx - 1]; | |
| stack[last_idx - 1] = stack[last_idx - 2]; | |
| stack[last_idx - 2] = old_top; | |
| } break; | |
| case DW_OP_abs: | |
| if (!stack.back().GetScalar().AbsoluteValue()) { | |
| return llvm::createStringError( | |
| "failed to take the absolute value of the first stack item"); | |
| } | |
| break; | |
| case DW_OP_and: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = stack.back().GetScalar() & tmp.GetScalar(); | |
| break; | |
| case DW_OP_div: { | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| if (tmp.GetScalar().IsZero()) | |
| return llvm::createStringError("divide by zero"); | |
| stack.pop_back(); | |
| Scalar divisor, dividend; | |
| divisor = tmp.GetScalar(); | |
| dividend = stack.back().GetScalar(); | |
| divisor.MakeSigned(); | |
| dividend.MakeSigned(); | |
| stack.back() = dividend / divisor; | |
| if (!stack.back().GetScalar().IsValid()) | |
| return llvm::createStringError("divide failed"); | |
| } break; | |
| case DW_OP_minus: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = stack.back().GetScalar() - tmp.GetScalar(); | |
| break; | |
| case DW_OP_mod: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| if (IsPotentiallyGenericIntegerOperand(tmp.GetScalar(), address_size) && | |
| IsPotentiallyGenericIntegerOperand(stack.back().GetScalar(), | |
| address_size)) { | |
| tmp.GetScalar().MakeUnsigned(); | |
| stack.back().GetScalar().MakeUnsigned(); | |
| } | |
| stack.back().GetScalar() = stack.back().GetScalar() % tmp.GetScalar(); | |
| break; | |
| case DW_OP_mul: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = stack.back().GetScalar() * tmp.GetScalar(); | |
| break; | |
| case DW_OP_neg: | |
| if (!stack.back().GetScalar().UnaryNegate()) | |
| return llvm::createStringError("unary negate failed"); | |
| break; | |
| case DW_OP_not: | |
| if (!stack.back().GetScalar().OnesComplement()) | |
| return llvm::createStringError("logical NOT failed"); | |
| break; | |
| case DW_OP_or: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = stack.back().GetScalar() | tmp.GetScalar(); | |
| break; | |
| case DW_OP_plus: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() += tmp.GetScalar(); | |
| break; | |
| case DW_OP_plus_uconst: { | |
| const uint64_t uconst_value = op->getRawOperand(0); | |
| Scalar &operand = stack.back().GetScalar(); | |
| Scalar addend(uconst_value); | |
| // The addend is interpreted as the same type as the popped operand | |
| // (DWARF v5, 2.5.1.4). Give it the operand's exact integer type so | |
| // the addition keeps the operand's width and wraparound semantics | |
| // instead of promoting to a 64-bit unsigned value. | |
| if (operand.GetType() == Scalar::e_int) | |
| addend.TruncOrExtendTo(operand.GetAPSInt().getBitWidth(), | |
| operand.GetAPSInt().isSigned()); | |
| operand += addend; | |
| if (!operand.IsValid()) | |
| return llvm::createStringError("DW_OP_plus_uconst failed"); | |
| } break; | |
| case DW_OP_shl: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() <<= tmp.GetScalar(); | |
| break; | |
| case DW_OP_shr: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| if (!stack.back().GetScalar().ShiftRightLogical(tmp.GetScalar())) | |
| return llvm::createStringError("DW_OP_shr failed"); | |
| break; | |
| case DW_OP_shra: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() >>= tmp.GetScalar(); | |
| break; | |
| case DW_OP_xor: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = stack.back().GetScalar() ^ tmp.GetScalar(); | |
| break; | |
| case DW_OP_skip: { | |
| int16_t skip_offset = static_cast<int16_t>(op->getRawOperand(0)); | |
| lldb::offset_t new_offset = op->getEndOffset() + skip_offset; | |
| // New offset can point at the end of the data, in this case we should | |
| // terminate the DWARF expression evaluation (will happen in the loop | |
| // condition). | |
| if (new_offset <= expr_data.size()) { | |
| op = op.skipBytes(skip_offset); | |
| continue; | |
| } | |
| return llvm::createStringErrorV( | |
| "Invalid opcode offset in DW_OP_skip: {0}+({1}) > {2}", | |
| op->getEndOffset(), skip_offset, expr_data.size()); | |
| } | |
| case DW_OP_bra: { | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| int16_t bra_offset = static_cast<int16_t>(op->getRawOperand(0)); | |
| Scalar zero(0); | |
| if (tmp.GetScalar() != zero) { | |
| lldb::offset_t new_offset = op->getEndOffset() + bra_offset; | |
| // New offset can point at the end of the data, in this case we should | |
| // terminate the DWARF expression evaluation (will happen in the loop | |
| // condition). | |
| if (new_offset <= expr_data.size()) { | |
| op = op.skipBytes(bra_offset); | |
| continue; | |
| } | |
| return llvm::createStringErrorV( | |
| "Invalid opcode offset in DW_OP_bra: {0}+({1}) > {2}", | |
| op->getEndOffset(), bra_offset, expr_data.size()); | |
| } | |
| } break; | |
| case DW_OP_eq: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar() == tmp.GetScalar()); | |
| break; | |
| case DW_OP_ge: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| if (IsPotentiallyGenericIntegerOperand(tmp.GetScalar(), address_size) && | |
| IsPotentiallyGenericIntegerOperand(stack.back().GetScalar(), | |
| address_size)) { | |
| tmp.GetScalar().MakeSigned(); | |
| stack.back().GetScalar().MakeSigned(); | |
| } | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar() >= tmp.GetScalar()); | |
| break; | |
| case DW_OP_gt: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| if (IsPotentiallyGenericIntegerOperand(tmp.GetScalar(), address_size) && | |
| IsPotentiallyGenericIntegerOperand(stack.back().GetScalar(), | |
| address_size)) { | |
| tmp.GetScalar().MakeSigned(); | |
| stack.back().GetScalar().MakeSigned(); | |
| } | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar() > tmp.GetScalar()); | |
| break; | |
| case DW_OP_le: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| if (IsPotentiallyGenericIntegerOperand(tmp.GetScalar(), address_size) && | |
| IsPotentiallyGenericIntegerOperand(stack.back().GetScalar(), | |
| address_size)) { | |
| tmp.GetScalar().MakeSigned(); | |
| stack.back().GetScalar().MakeSigned(); | |
| } | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar() <= tmp.GetScalar()); | |
| break; | |
| case DW_OP_lt: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| if (IsPotentiallyGenericIntegerOperand(tmp.GetScalar(), address_size) && | |
| IsPotentiallyGenericIntegerOperand(stack.back().GetScalar(), | |
| address_size)) { | |
| tmp.GetScalar().MakeSigned(); | |
| stack.back().GetScalar().MakeSigned(); | |
| } | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar() < tmp.GetScalar()); | |
| break; | |
| case DW_OP_ne: | |
| if (llvm::Error err = CheckScalarOperandsHaveSameType( | |
| stack[stack.size() - 2].GetScalar(), stack.back().GetScalar(), | |
| opcode, address_size)) | |
| return err; | |
| tmp = stack.back(); | |
| stack.pop_back(); | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar() != tmp.GetScalar()); | |
| break; | |
| case DW_OP_lit0: | |
| case DW_OP_lit1: | |
| case DW_OP_lit2: | |
| case DW_OP_lit3: | |
| case DW_OP_lit4: | |
| case DW_OP_lit5: | |
| case DW_OP_lit6: | |
| case DW_OP_lit7: | |
| case DW_OP_lit8: | |
| case DW_OP_lit9: | |
| case DW_OP_lit10: | |
| case DW_OP_lit11: | |
| case DW_OP_lit12: | |
| case DW_OP_lit13: | |
| case DW_OP_lit14: | |
| case DW_OP_lit15: | |
| case DW_OP_lit16: | |
| case DW_OP_lit17: | |
| case DW_OP_lit18: | |
| case DW_OP_lit19: | |
| case DW_OP_lit20: | |
| case DW_OP_lit21: | |
| case DW_OP_lit22: | |
| case DW_OP_lit23: | |
| case DW_OP_lit24: | |
| case DW_OP_lit25: | |
| case DW_OP_lit26: | |
| case DW_OP_lit27: | |
| case DW_OP_lit28: | |
| case DW_OP_lit29: | |
| case DW_OP_lit30: | |
| case DW_OP_lit31: | |
| stack.push_back(to_generic(opcode - DW_OP_lit0)); | |
| break; | |
| case DW_OP_reg0: | |
| case DW_OP_reg1: | |
| case DW_OP_reg2: | |
| case DW_OP_reg3: | |
| case DW_OP_reg4: | |
| case DW_OP_reg5: | |
| case DW_OP_reg6: | |
| case DW_OP_reg7: | |
| case DW_OP_reg8: | |
| case DW_OP_reg9: | |
| case DW_OP_reg10: | |
| case DW_OP_reg11: | |
| case DW_OP_reg12: | |
| case DW_OP_reg13: | |
| case DW_OP_reg14: | |
| case DW_OP_reg15: | |
| case DW_OP_reg16: | |
| case DW_OP_reg17: | |
| case DW_OP_reg18: | |
| case DW_OP_reg19: | |
| case DW_OP_reg20: | |
| case DW_OP_reg21: | |
| case DW_OP_reg22: | |
| case DW_OP_reg23: | |
| case DW_OP_reg24: | |
| case DW_OP_reg25: | |
| case DW_OP_reg26: | |
| case DW_OP_reg27: | |
| case DW_OP_reg28: | |
| case DW_OP_reg29: | |
| case DW_OP_reg30: | |
| case DW_OP_reg31: { | |
| eval_ctx.loc_desc_kind = Register; | |
| reg_num = opcode - DW_OP_reg0; | |
| if (llvm::Error err = ReadRegisterValueAsScalar( | |
| eval_ctx.reg_ctx, eval_ctx.reg_kind, reg_num, tmp)) | |
| return err; | |
| stack.push_back(tmp); | |
| } break; | |
| case DW_OP_regx: { | |
| eval_ctx.loc_desc_kind = Register; | |
| reg_num = op->getRawOperand(0); | |
| Status read_err; | |
| if (llvm::Error err = ReadRegisterValueAsScalar( | |
| eval_ctx.reg_ctx, eval_ctx.reg_kind, reg_num, tmp)) | |
| return err; | |
| stack.push_back(tmp); | |
| } break; | |
| case DW_OP_breg0: | |
| case DW_OP_breg1: | |
| case DW_OP_breg2: | |
| case DW_OP_breg3: | |
| case DW_OP_breg4: | |
| case DW_OP_breg5: | |
| case DW_OP_breg6: | |
| case DW_OP_breg7: | |
| case DW_OP_breg8: | |
| case DW_OP_breg9: | |
| case DW_OP_breg10: | |
| case DW_OP_breg11: | |
| case DW_OP_breg12: | |
| case DW_OP_breg13: | |
| case DW_OP_breg14: | |
| case DW_OP_breg15: | |
| case DW_OP_breg16: | |
| case DW_OP_breg17: | |
| case DW_OP_breg18: | |
| case DW_OP_breg19: | |
| case DW_OP_breg20: | |
| case DW_OP_breg21: | |
| case DW_OP_breg22: | |
| case DW_OP_breg23: | |
| case DW_OP_breg24: | |
| case DW_OP_breg25: | |
| case DW_OP_breg26: | |
| case DW_OP_breg27: | |
| case DW_OP_breg28: | |
| case DW_OP_breg29: | |
| case DW_OP_breg30: | |
| case DW_OP_breg31: { | |
| reg_num = opcode - DW_OP_breg0; | |
| if (llvm::Error err = ReadRegisterValueAsScalar( | |
| eval_ctx.reg_ctx, eval_ctx.reg_kind, reg_num, tmp)) | |
| return err; | |
| int64_t breg_offset = op->getRawOperand(0); | |
| tmp.GetScalar() = to_generic(tmp.GetScalar().ULongLong()); | |
| tmp.GetScalar() += to_generic(breg_offset); | |
| tmp.ClearContext(); | |
| stack.push_back(tmp); | |
| stack.back().SetValueType(Value::ValueType::LoadAddress); | |
| } break; | |
| case DW_OP_bregx: { | |
| reg_num = op->getRawOperand(0); | |
| if (llvm::Error err = ReadRegisterValueAsScalar( | |
| eval_ctx.reg_ctx, eval_ctx.reg_kind, reg_num, tmp)) | |
| return err; | |
| int64_t breg_offset = op->getRawOperand(1); | |
| tmp.GetScalar() = to_generic(tmp.GetScalar().ULongLong()); | |
| tmp.GetScalar() += to_generic(breg_offset); | |
| tmp.ClearContext(); | |
| stack.push_back(tmp); | |
| stack.back().SetValueType(Value::ValueType::LoadAddress); | |
| } break; | |
| case DW_OP_fbreg: | |
| if (llvm::Error err = | |
| Evaluate_DW_OP_fbreg(eval_ctx, op->getRawOperand(0))) | |
| return err; | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar().ULongLong()); | |
| break; | |
| case DW_OP_nop: | |
| break; | |
| case DW_OP_piece: { | |
| if (llvm::Error err = | |
| Evaluate_DW_OP_piece(eval_ctx, op->getRawOperand(0))) | |
| return err; | |
| } break; | |
| case DW_OP_bit_piece: | |
| if (stack.size() < 1) { | |
| UpdateValueTypeFromLocationDescription(eval_ctx, | |
| LocationDescriptionKind::Empty); | |
| // Reset for the next piece. | |
| eval_ctx.loc_desc_kind = Memory; | |
| return llvm::createStringError( | |
| "expression stack needs at least 1 item for DW_OP_bit_piece"); | |
| } else { | |
| const LocationDescriptionKind piece_locdesc = eval_ctx.loc_desc_kind; | |
| UpdateValueTypeFromLocationDescription(eval_ctx, piece_locdesc, | |
| &stack.back()); | |
| // Reset for the next piece. | |
| eval_ctx.loc_desc_kind = Memory; | |
| const uint64_t piece_bit_size = op->getRawOperand(0); | |
| const uint64_t piece_bit_offset = op->getRawOperand(1); | |
| switch (stack.back().GetValueType()) { | |
| case Value::ValueType::Invalid: | |
| return llvm::createStringError( | |
| "unable to extract bit value from invalid value"); | |
| case Value::ValueType::Scalar: { | |
| if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, | |
| piece_bit_offset)) { | |
| return llvm::createStringError( | |
| "unable to extract %" PRIu64 " bit value with %" PRIu64 | |
| " bit offset from a %" PRIu64 " bit scalar value.", | |
| piece_bit_size, piece_bit_offset, | |
| (uint64_t)(stack.back().GetScalar().GetByteSize() * 8)); | |
| } | |
| } break; | |
| case Value::ValueType::FileAddress: | |
| case Value::ValueType::LoadAddress: | |
| return llvm::createStringError( | |
| "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 | |
| ", bit_offset = %" PRIu64 ") from an address value.", | |
| piece_bit_size, piece_bit_offset); | |
| case Value::ValueType::HostAddress: | |
| // As above, a full-width DW_OP_implicit_value piece refers to the | |
| // backing bytes, not the address of that backing storage. | |
| if (piece_locdesc == Implicit && piece_bit_offset == 0 && | |
| piece_bit_size % 8 == 0 && | |
| stack.back().GetBuffer().GetByteSize() == piece_bit_size / 8) | |
| break; | |
| return llvm::createStringError( | |
| "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 | |
| ", bit_offset = %" PRIu64 ") from an address value.", | |
| piece_bit_size, piece_bit_offset); | |
| } | |
| } | |
| break; | |
| case DW_OP_implicit_value: { | |
| eval_ctx.loc_desc_kind = Implicit; | |
| // The second operand is a sequence of bytes of the length specified by | |
| // the first operand. LLVM represents it as an offset to that sequence. | |
| const uint64_t block_size = op->getRawOperand(0); | |
| uint64_t block_offset = op->getRawOperand(1); | |
| llvm::Error error = llvm::Error::success(); | |
| llvm::StringRef block_data = | |
| expr_data.getBytes(&block_offset, block_size, &error); | |
| if (error) | |
| return error; | |
| Value result(block_data.data(), block_data.size()); | |
| stack.push_back(result); | |
| break; | |
| } | |
| case DW_OP_implicit_pointer: { | |
| eval_ctx.loc_desc_kind = Implicit; | |
| return llvm::createStringError("could not evaluate %s", | |
| DW_OP_value_to_name(opcode)); | |
| } | |
| case DW_OP_push_object_address: | |
| if (eval_ctx.object_address_ptr) | |
| stack.push_back(*eval_ctx.object_address_ptr); | |
| else { | |
| return llvm::createStringError("DW_OP_push_object_address used without " | |
| "specifying an object address"); | |
| } | |
| break; | |
| case DW_OP_stack_value: | |
| eval_ctx.loc_desc_kind = Implicit; | |
| stack.back().SetValueType(Value::ValueType::Scalar); | |
| break; | |
| case DW_OP_convert: | |
| if (llvm::Error err = | |
| Evaluate_DW_OP_convert(eval_ctx, op->getRawOperand(0))) | |
| return err; | |
| break; | |
| case DW_OP_call_frame_cfa: | |
| if (llvm::Error err = Evaluate_DW_OP_call_frame_cfa(eval_ctx)) | |
| return err; | |
| stack.back().GetScalar() = | |
| to_generic(stack.back().GetScalar().ULongLong()); | |
| break; | |
| case DW_OP_form_tls_address: | |
| case DW_OP_GNU_push_tls_address: | |
| if (llvm::Error err = Evaluate_DW_OP_form_tls_address(eval_ctx, opcode)) | |
| return err; | |
| break; | |
| case DW_OP_addrx: | |
| case DW_OP_GNU_addr_index: { | |
| if (!eval_ctx.dwarf_cu) | |
| return llvm::createStringError("DW_OP_GNU_addr_index found without a " | |
| "compile unit being specified"); | |
| uint64_t index = op->getRawOperand(0); | |
| lldb::addr_t value = | |
| eval_ctx.dwarf_cu->ReadAddressFromDebugAddrSection(index); | |
| stack.push_back(to_generic(value)); | |
| stack.back().SetValueType(Value::ValueType::FileAddress); | |
| } break; | |
| case DW_OP_GNU_const_index: { | |
| if (!eval_ctx.dwarf_cu) { | |
| return llvm::createStringError("DW_OP_GNU_const_index found without a " | |
| "compile unit being specified"); | |
| } | |
| uint64_t index = op->getRawOperand(0); | |
| lldb::addr_t value = | |
| eval_ctx.dwarf_cu->ReadAddressFromDebugAddrSection(index); | |
| stack.push_back(to_generic(value)); | |
| } break; | |
| case DW_OP_GNU_entry_value: | |
| case DW_OP_entry_value: { | |
| // Technically, DW_OP_entry_value has two operands, but LLVM represents | |
| // it as a single-operand operation (bug?). We can deal with this: the | |
| // second operand immediately follows the first, but have to be careful | |
| // when advancing the iterator, see the comment below. | |
| const uint64_t block_size = op->getRawOperand(0); | |
| uint64_t block_offset = op->getEndOffset(); | |
| llvm::Error error = llvm::Error::success(); | |
| llvm::ArrayRef<uint8_t> block_data = llvm::arrayRefFromStringRef( | |
| expr_data.getBytes(&block_offset, block_size, &error)); | |
| if (error) | |
| return error; | |
| if (llvm::Error err = Evaluate_DW_OP_entry_value(eval_ctx, block_data)) | |
| return llvm::createStringError( | |
| "could not evaluate DW_OP_entry_value: %s", | |
| llvm::toString(std::move(err)).c_str()); | |
| // We can't use `operator++` here because the iterator currently points | |
| // to the second operand. See the comment above. | |
| op = op.skipBytes(block_size); | |
| continue; | |
| } | |
| // These opcodes are decoded but not evaluated here. | |
| case DW_OP_xderef: | |
| case DW_OP_xderef_size: | |
| case DW_OP_call2: | |
| case DW_OP_call4: | |
| case DW_OP_call_ref: | |
| case DW_OP_constx: | |
| case DW_OP_const_type: | |
| case DW_OP_regval_type: | |
| case DW_OP_deref_type: | |
| case DW_OP_xderef_type: | |
| case DW_OP_reinterpret: | |
| case DW_OP_GNU_implicit_pointer: | |
| return llvm::createStringError("unimplemented opcode %s", | |
| DW_OP_value_to_name(opcode)); | |
| case DW_OP_LLVM_user: | |
| if (op->getSubCode() == DW_OP_LLVM_piece_end) { | |
| if (op->getEndOffset() != expr_data.size()) | |
| return llvm::createStringError( | |
| "DW_OP_LLVM_piece_end is only supported at the end of an " | |
| "expression"); | |
| // LLDB already constructs one composite in `pieces`, so a terminal | |
| // DW_OP_LLVM_piece_end is a no-op. TODO: Support multiple composites | |
| // and DW_OP_piece_end once LLVM implements DWARF 6 locations on the | |
| // stack. | |
| // Extension: | |
| // https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html | |
| // Standard: https://dwarfstd.org/issues/230524.1-orig.html | |
| break; | |
| } | |
| [[fallthrough]]; | |
| default: | |
| if (eval_ctx.dwarf_cu) { | |
| const uint64_t operands_offset = op_offset + 1; | |
| uint64_t offset = operands_offset; // Updated by the callee. | |
| if (eval_ctx.dwarf_cu->ParseVendorDWARFOpcode( | |
| opcode, expr_data, offset, eval_ctx.reg_ctx, eval_ctx.reg_kind, | |
| stack)) { | |
| // This is a little tricky. If LLVM knows about this vendor-specific | |
| // operation, `getEndOffset()` points past its last operand. If LLVM | |
| // knows nothing about this operation, `getEndOffset()` points to its | |
| // opcode. In both cases `offset` will point to the next operation, | |
| // but we can't use it directly because the only available mutating | |
| // method of `iterator` (not counting `operator++`) is `skipBytes()`. | |
| // So we calculate the offset and pass it to `skipBytes()`. | |
| assert(offset >= op->getEndOffset()); | |
| uint64_t offset_to_next_op = offset - op->getEndOffset(); | |
| op = op.skipBytes(offset_to_next_op); | |
| continue; | |
| } | |
| } | |
| return llvm::createStringErrorV("unhandled opcode {0} in DWARFExpression", | |
| opcode); | |
| } | |
| ++op; | |
| } | |
| 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 (eval_ctx.pieces.GetBuffer().GetByteSize()) | |
| return eval_ctx.pieces; | |
| return llvm::createStringError("stack empty after evaluation"); | |
| } | |
| UpdateValueTypeFromLocationDescription(eval_ctx, eval_ctx.loc_desc_kind, | |
| &stack.back()); | |
| if (log && log->GetVerbose()) { | |
| size_t count = stack.size(); | |
| LLDB_LOGF(log, "Stack after operation has %" PRIu64 " values:", | |
| static_cast<uint64_t>(count)); | |
| for (size_t i = 0; i < count; ++i) { | |
| StreamString new_value; | |
| new_value.Printf("[%" PRIu64 "]", static_cast<uint64_t>(i)); | |
| stack[i].Dump(&new_value); | |
| LLDB_LOGF(log, " %s", new_value.GetData()); | |
| } | |
| } | |
| return stack.back(); | |
| } | |
| bool DWARFExpression::MatchesOperand( | |
| StackFrame &frame, const Instruction::Operand &operand) const { | |
| using namespace OperandMatchers; | |
| RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); | |
| if (!reg_ctx_sp) { | |
| return false; | |
| } | |
| DataExtractor opcodes(m_data); | |
| lldb::offset_t op_offset = 0; | |
| uint8_t opcode = opcodes.GetU8(&op_offset); | |
| if (opcode == DW_OP_fbreg) { | |
| int64_t offset = opcodes.GetSLEB128(&op_offset); | |
| DWARFExpressionList *fb_expr = frame.GetFrameBaseExpression(nullptr); | |
| if (!fb_expr) { | |
| return false; | |
| } | |
| auto recurse = [&frame, fb_expr](const Instruction::Operand &child) { | |
| return fb_expr->MatchesOperand(frame, child); | |
| }; | |
| if (!offset && | |
| MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), | |
| recurse)(operand)) { | |
| return true; | |
| } | |
| return MatchUnaryOp( | |
| MatchOpType(Instruction::Operand::Type::Dereference), | |
| MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), | |
| MatchImmOp(offset), recurse))(operand); | |
| } | |
| bool dereference = false; | |
| const RegisterInfo *reg = nullptr; | |
| int64_t offset = 0; | |
| if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) { | |
| reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0); | |
| } else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) { | |
| offset = opcodes.GetSLEB128(&op_offset); | |
| reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_breg0); | |
| } else if (opcode == DW_OP_regx) { | |
| uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset)); | |
| reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num); | |
| } else if (opcode == DW_OP_bregx) { | |
| uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset)); | |
| offset = opcodes.GetSLEB128(&op_offset); | |
| reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num); | |
| } else { | |
| return false; | |
| } | |
| if (!reg) { | |
| return false; | |
| } | |
| if (dereference) { | |
| if (!offset && | |
| MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), | |
| MatchRegOp(*reg))(operand)) { | |
| return true; | |
| } | |
| return MatchUnaryOp( | |
| MatchOpType(Instruction::Operand::Type::Dereference), | |
| MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), | |
| MatchRegOp(*reg), MatchImmOp(offset)))(operand); | |
| } else { | |
| return MatchRegOp(*reg)(operand); | |
| } | |
| } |