diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Expression')
17 files changed, 10397 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp b/contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp new file mode 100644 index 000000000000..444e44b39289 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp @@ -0,0 +1,2403 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DWARFExpression.h" + +#include <cinttypes> + +#include <optional> +#include <vector> + +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/DataEncoder.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/VMRange.h" + +#include "lldb/Host/Host.h" +#include "lldb/Utility/Endian.h" + +#include "lldb/Symbol/Function.h" + +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" + +#include "Plugins/SymbolFile/DWARF/DWARFUnit.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +// DWARFExpression 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) const { + auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr; + auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, + bool IsEH) -> llvm::StringRef { + if (!MCRegInfo) + return {}; + if (std::optional<unsigned> LLVMRegNum = + MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH)) + if (const char *RegName = MCRegInfo->getName(*LLVMRegNum)) + return llvm::StringRef(RegName); + return {}; + }; + llvm::DIDumpOptions DumpOpts; + DumpOpts.GetNameForDWARFReg = GetRegName; + llvm::DWARFExpression(m_data.GetAsLLVM(), m_data.GetAddressByteSize()) + .print(s->AsRawOstream(), DumpOpts, nullptr); +} + +RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } + +void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) { + m_reg_kind = reg_kind; +} + +static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + uint32_t reg_num, Value &value) { + if (reg_ctx == nullptr) + return llvm::createStringError("no register context in frame"); + + const uint32_t native_reg = + reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + if (native_reg == LLDB_INVALID_REGNUM) + return llvm::createStringError( + "unable to convert register kind=%u reg_num=%u to a native " + "register number", + reg_kind, reg_num); + + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(native_reg); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) { + if (reg_value.GetScalarValue(value.GetScalar())) { + value.SetValueType(Value::ValueType::Scalar); + value.SetContext(Value::ContextType::RegisterInfo, + const_cast<RegisterInfo *>(reg_info)); + return llvm::Error::success(); + } + + // 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 offset_t GetOpcodeDataSize(const DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op, const DWARFUnit *dwarf_cu) { + lldb::offset_t offset = data_offset; + switch (op) { + case DW_OP_addr: + case DW_OP_call_ref: // 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_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; + } + + default: + if (!dwarf_cu) { + return LLDB_INVALID_OFFSET; + } + return dwarf_cu->GetSymbolFileDWARF().GetVendorDWARFOpcodeSize( + data, data_offset, op); + } +} + +lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, + bool &error) const { + error = false; + lldb::offset_t offset = 0; + while (m_data.ValidOffset(offset)) { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) + return m_data.GetAddress(&offset); + if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) { + uint64_t index = m_data.GetULEB128(&offset); + if (dwarf_cu) + return dwarf_cu->ReadAddressFromDebugAddrSection(index); + error = true; + break; + } + const offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); + if (op_arg_size == LLDB_INVALID_OFFSET) { + error = true; + break; + } + offset += op_arg_size; + } + return LLDB_INVALID_ADDRESS; +} + +bool DWARFExpression::Update_DW_OP_addr(const DWARFUnit *dwarf_cu, + lldb::addr_t file_addr) { + lldb::offset_t offset = 0; + while (m_data.ValidOffset(offset)) { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) { + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + // 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 offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); + if (op_arg_size == LLDB_INVALID_OFFSET) + break; + offset += op_arg_size; + } + return false; +} + +bool DWARFExpression::ContainsThreadLocalStorage( + const DWARFUnit *dwarf_cu) const { + lldb::offset_t offset = 0; + while (m_data.ValidOffset(offset)) { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address) + return true; + const offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); + if (op_arg_size == LLDB_INVALID_OFFSET) + return false; + offset += op_arg_size; + } + return false; +} +bool DWARFExpression::LinkThreadLocalStorage( + const DWARFUnit *dwarf_cu, + std::function<lldb::addr_t(lldb::addr_t file_addr)> const + &link_address_callback) { + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + // 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 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 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 offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); + if (op_arg_size == LLDB_INVALID_OFFSET) + return false; + else + offset += op_arg_size; + } + } + + m_data.SetData(encoder.GetDataBuffer()); + return true; +} + +static llvm::Error Evaluate_DW_OP_entry_value(std::vector<Value> &stack, + ExecutionContext *exe_ctx, + RegisterContext *reg_ctx, + const DataExtractor &opcodes, + lldb::offset_t &opcode_offset, + Log *log) { + // 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) { + return llvm::createStringError("no exe/reg context"); + } + + StackFrame *current_frame = exe_ctx->GetFramePtr(); + Thread *thread = exe_ctx->GetThreadPtr(); + if (!current_frame || !thread) + return llvm::createStringError("no current frame/thread"); + + Target &target = exe_ctx->GetTargetRef(); + StackFrameSP parent_frame = nullptr; + addr_t return_pc = LLDB_INVALID_ADDRESS; + uint32_t current_frame_idx = current_frame->GetFrameIndex(); + + for (uint32_t parent_frame_idx = current_frame_idx + 1;;parent_frame_idx++) { + parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx); + // If 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 = *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::createStringError( + llvm::formatv("no call edge for retn-pc = {0:x} in parent frame {1}", + return_pc, parent_func->GetName())); + } + Function *callee_func = call_edge->GetCallee(modlist, parent_exe_ctx); + if (callee_func != current_func) { + return llvm::createStringError( + "ambiguous call sequence, can't find real parent frame"); + } + } else { + // 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) == 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 uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset); + const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len); + if (!subexpr_data) + return llvm::createStringError("subexpr could not be read"); + + const CallSiteParameter *matched_param = nullptr; + for (const CallSiteParameter ¶m : call_edge->GetCallSiteParameters()) { + DataExtractor param_subexpr_extractor; + if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor)) + continue; + lldb::offset_t param_subexpr_offset = 0; + const void *param_subexpr_data = + param_subexpr_extractor.GetData(¶m_subexpr_offset, subexpr_len); + if (!param_subexpr_data || + param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0) + continue; + + // 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) + 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; + + 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(); + } + + stack.push_back(*maybe_result); + return llvm::Error::success(); +} + +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*/ +}; +/// Adjust value's ValueType according to the kind of location description. +void UpdateValueTypeFromLocationDescription(Log *log, const DWARFUnit *dwarf_cu, + 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 (dwarf_cu && dwarf_cu->GetVersion() >= 4) { + const char *log_msg = "DWARF location description kind: %s"; + switch (kind) { + case Empty: + LLDB_LOGF(log, log_msg, "Empty"); + break; + case Memory: + LLDB_LOGF(log, log_msg, "Memory"); + if (value->GetValueType() == Value::ValueType::Scalar) + value->SetValueType(Value::ValueType::LoadAddress); + break; + case Register: + LLDB_LOGF(log, log_msg, "Register"); + value->SetValueType(Value::ValueType::Scalar); + break; + case Implicit: + LLDB_LOGF(log, log_msg, "Implicit"); + if (value->GetValueType() == Value::ValueType::LoadAddress) + value->SetValueType(Value::ValueType::Scalar); + break; + } + } +} +} // namespace + +/// Helper function to move common code used to resolve a file address and turn +/// into a load address. +/// +/// \param exe_ctx Pointer to the execution context +/// \param module_sp shared_ptr contains the module if we have one +/// \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(ExecutionContext *exe_ctx, lldb::ModuleSP &module_sp, + const char *dw_op_type, lldb::addr_t file_addr, + Address &so_addr, bool check_sectionoffset = false) { + if (!module_sp) + return llvm::createStringError("need module to resolve file address for %s", + dw_op_type); + + if (!module_sp->ResolveFileAddress(file_addr, so_addr)) + return llvm::createStringError("failed to resolve file address in module"); + + const addr_t load_addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr()); + + if (load_addr == LLDB_INVALID_ADDRESS && + (check_sectionoffset && !so_addr.IsSectionOffset())) + return llvm::createStringError("failed to resolve load address"); + + return load_addr; +} + +/// Helper function to move common code used to load sized data from a uint8_t +/// buffer. +/// +/// \param addr_bytes uint8_t buffer containg raw data +/// \param size_addr_bytes how large is the underlying raw data +/// \param byte_order what is the byter order of the underlyig 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); + else + return addr_data.GetAddress(&addr_data_offset); +} + +llvm::Expected<Value> DWARFExpression::Evaluate( + ExecutionContext *exe_ctx, RegisterContext *reg_ctx, + lldb::ModuleSP module_sp, const DataExtractor &opcodes, + const DWARFUnit *dwarf_cu, const lldb::RegisterKind reg_kind, + const Value *initial_value_ptr, const Value *object_address_ptr) { + + if (opcodes.GetByteSize() == 0) + return llvm::createStringError( + "no location, value may have been optimized out"); + std::vector<Value> stack; + + Process *process = nullptr; + StackFrame *frame = nullptr; + Target *target = nullptr; + + if (exe_ctx) { + process = exe_ctx->GetProcessPtr(); + frame = exe_ctx->GetFramePtr(); + target = exe_ctx->GetTargetPtr(); + } + if (reg_ctx == nullptr && frame) + reg_ctx = frame->GetRegisterContext().get(); + + if (initial_value_ptr) + stack.push_back(*initial_value_ptr); + + lldb::offset_t offset = 0; + Value tmp; + uint32_t reg_num; + + /// Insertion point for evaluating multi-piece expression. + uint64_t op_piece_offset = 0; + Value pieces; // Used for DW_OP_piece + + 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) { + bool is_signed = std::is_signed<decltype(v)>::value; + return Scalar(llvm::APSInt( + llvm::APInt(8 * opcodes.GetAddressByteSize(), v, is_signed), + !is_signed)); + }; + + // The default kind is a memory location. This is updated by any + // operation that changes this, such as DW_OP_stack_value, and reset + // by composition operations like DW_OP_piece. + LocationDescriptionKind dwarf4_location_description_kind = Memory; + + while (opcodes.ValidOffset(offset)) { + const lldb::offset_t op_offset = offset; + const uint8_t op = opcodes.GetU8(&offset); + + if (log && log->GetVerbose()) { + size_t count = stack.size(); + LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:", + (uint64_t)count); + for (size_t i = 0; i < count; ++i) { + StreamString new_value; + new_value.Printf("[%" PRIu64 "]", (uint64_t)i); + stack[i].Dump(&new_value); + LLDB_LOGF(log, " %s", new_value.GetData()); + } + LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset, + DW_OP_value_to_name(op)); + } + + if (std::optional<unsigned> arity = + llvm::dwarf::OperationArity(static_cast<LocationAtom>(op))) { + if (stack.size() < *arity) + return llvm::createStringError( + "%s needs at least %d stack entries (stack has %d entries)", + DW_OP_value_to_name(op), *arity, stack.size()); + } + + switch (op) { + // 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))); + if (target && + target->GetArchitecture().GetCore() == ArchSpec::eCore_wasm32) { + // wasm file sections aren't mapped into memory, therefore addresses can + // never point into a file section and are always LoadAddresses. + stack.back().SetValueType(Value::ValueType::LoadAddress); + } else { + stack.back().SetValueType(Value::ValueType::FileAddress); + } + 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()) + return llvm::createStringError( + "expression stack empty for DW_OP_deref"); + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) { + case Value::ValueType::HostAddress: { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy(&ptr, src, sizeof(void *)); + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } break; + case Value::ValueType::FileAddress: { + auto file_addr = stack.back().GetScalar().ULongLong( + LLDB_INVALID_ADDRESS); + + Address so_addr; + auto maybe_load_addr = ResolveLoadAddress( + exe_ctx, module_sp, "DW_OP_deref", file_addr, so_addr); + + if (!maybe_load_addr) + return maybe_load_addr.takeError(); + + stack.back().GetScalar() = *maybe_load_addr; + // Fall through to load address promotion code below. + } + [[fallthrough]]; + case Value::ValueType::Scalar: + // Promote Scalar to LoadAddress and fall through. + stack.back().SetValueType(Value::ValueType::LoadAddress); + [[fallthrough]]; + case Value::ValueType::LoadAddress: + if (exe_ctx) { + if (process) { + lldb::addr_t pointer_addr = + stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + Status error; + lldb::addr_t pointer_value = + process->ReadPointerFromMemory(pointer_addr, error); + if (pointer_value != LLDB_INVALID_ADDRESS) { + if (ABISP abi_sp = process->GetABI()) + pointer_value = abi_sp->FixCodeAddress(pointer_value); + stack.back().GetScalar() = pointer_value; + stack.back().ClearContext(); + } else { + return llvm::createStringError( + "Failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref: %s\n", + pointer_addr, error.AsCString()); + } + } else { + return llvm::createStringError("NULL process for DW_OP_deref"); + } + } else { + return llvm::createStringError( + "NULL execution context for DW_OP_deref"); + } + break; + + case Value::ValueType::Invalid: + return llvm::createStringError("invalid value type for DW_OP_deref"); + } + + } break; + + // 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()) { + return llvm::createStringError( + "expression stack empty for DW_OP_deref_size"); + } + uint8_t size = opcodes.GetU8(&offset); + if (size > 8) { + return llvm::createStringError( + "Invalid address size for DW_OP_deref_size: %d\n", size); + } + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) { + case Value::ValueType::HostAddress: { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy(&ptr, src, sizeof(void *)); + // 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::ValueType::FileAddress: { + auto file_addr = + stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + Address so_addr; + auto maybe_load_addr = ResolveLoadAddress( + exe_ctx, module_sp, "DW_OP_deref_size", file_addr, so_addr, + /*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 (target && + target->ReadMemory(so_addr, &addr_bytes, size, error, + /*force_live_memory=*/false) == size) { + ObjectFile *objfile = module_sp->GetObjectFile(); + + stack.back().GetScalar() = DerefSizeExtractDataHelper( + addr_bytes, size, objfile->GetByteOrder(), size); + stack.back().ClearContext(); + break; + } else { + return llvm::createStringError( + "Failed to dereference pointer for DW_OP_deref_size: " + "%s\n", + error.AsCString()); + } + } + stack.back().GetScalar() = load_addr; + // Fall through to load address promotion code below. + } + + [[fallthrough]]; + case Value::ValueType::Scalar: + case Value::ValueType::LoadAddress: + if (exe_ctx) { + if (process) { + lldb::addr_t pointer_addr = + stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; + Status error; + if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == + size) { + + stack.back().GetScalar() = + DerefSizeExtractDataHelper(addr_bytes, sizeof(addr_bytes), + process->GetByteOrder(), size); + stack.back().ClearContext(); + } else { + return llvm::createStringError( + "Failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref: %s\n", + pointer_addr, error.AsCString()); + } + } else { + + return llvm::createStringError("NULL process for DW_OP_deref_size"); + } + } else { + return llvm::createStringError( + "NULL execution context for DW_OP_deref_size"); + } + break; + + case Value::ValueType::Invalid: + + return llvm::createStringError("invalid value for DW_OP_deref_size"); + } + + } break; + + // 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: + return llvm::createStringError("unimplemented opcode: DW_OP_xderef_size"); + // 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: + return llvm::createStringError("unimplemented opcode: DW_OP_xderef"); + + // 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(to_generic(opcodes.GetU8(&offset))); + break; + case DW_OP_const1s: + stack.push_back(to_generic((int8_t)opcodes.GetU8(&offset))); + break; + case DW_OP_const2u: + stack.push_back(to_generic(opcodes.GetU16(&offset))); + break; + case DW_OP_const2s: + stack.push_back(to_generic((int16_t)opcodes.GetU16(&offset))); + break; + case DW_OP_const4u: + stack.push_back(to_generic(opcodes.GetU32(&offset))); + break; + case DW_OP_const4s: + stack.push_back(to_generic((int32_t)opcodes.GetU32(&offset))); + break; + case DW_OP_const8u: + stack.push_back(to_generic(opcodes.GetU64(&offset))); + break; + case DW_OP_const8s: + stack.push_back(to_generic((int64_t)opcodes.GetU64(&offset))); + break; + // These should also use to_generic, but we can't do that due to a + // producer-side bug in llvm. See llvm.org/pr48087. + 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()) { + return llvm::createStringError("expression stack empty for DW_OP_dup"); + } 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()) { + return llvm::createStringError("expression stack empty for DW_OP_drop"); + } 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: + 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 { + return llvm::createStringError( + "Index %u out of range for DW_OP_pick.\n", pick_idx); + } + } 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: + 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: { + 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.back().ResolveValue(exe_ctx).AbsoluteValue()) { + return llvm::createStringError( + "failed to take the absolute value of the first stack item"); + } + 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: + 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: { + tmp = stack.back(); + if (tmp.ResolveValue(exe_ctx).IsZero()) + return llvm::createStringError("divide by zero"); + + stack.pop_back(); + Scalar divisor, dividend; + divisor = tmp.ResolveValue(exe_ctx); + dividend = stack.back().ResolveValue(exe_ctx); + divisor.MakeSigned(); + dividend.MakeSigned(); + stack.back() = dividend / divisor; + + if (!stack.back().ResolveValue(exe_ctx).IsValid()) + return llvm::createStringError("divide failed"); + } break; + + // 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: + 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: + 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: + 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.back().ResolveValue(exe_ctx).UnaryNegate()) + return llvm::createStringError("unary negate failed"); + break; + + // OPCODE: DW_OP_not + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its bitwise + // complement + case DW_OP_not: + if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) + return llvm::createStringError("logical NOT failed"); + 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: + 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: + 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: { + 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()) + return llvm::createStringError("DW_OP_plus_uconst failed"); + } 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: + 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: + tmp = stack.back(); + stack.pop_back(); + if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical( + tmp.ResolveValue(exe_ctx))) + return llvm::createStringError("DW_OP_shr failed"); + break; + + // 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: + 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: + 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; + // 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 <= opcodes.GetByteSize()) + offset = new_offset; + else { + return llvm::createStringError(llvm::formatv( + "Invalid opcode offset in DW_OP_skip: {0}+({1}) > {2}", offset, + skip_offset, opcodes.GetByteSize())); + } + } break; + + // 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: { + 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; + // 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 <= opcodes.GetByteSize()) + offset = new_offset; + else { + return llvm::createStringError(llvm::formatv( + "Invalid opcode offset in DW_OP_bra: {0}+({1}) > {2}", offset, + bra_offset, opcodes.GetByteSize())); + } + } + } break; + + // 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: + 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: + 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: + 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: + 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: + 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: + 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(to_generic(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: { + dwarf4_location_description_kind = Register; + reg_num = op - DW_OP_reg0; + + if (llvm::Error err = + ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp)) + return err; + stack.push_back(tmp); + } break; + // 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: { + dwarf4_location_description_kind = Register; + reg_num = opcodes.GetULEB128(&offset); + Status read_err; + if (llvm::Error err = + ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp)) + return err; + stack.push_back(tmp); + } break; + + // 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 (llvm::Error err = + ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp)) + return err; + + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType(Value::ValueType::LoadAddress); + } break; + // 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 (llvm::Error err = + ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, tmp)) + return err; + + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType(Value::ValueType::LoadAddress); + } break; + + case DW_OP_fbreg: + if (exe_ctx) { + if (frame) { + Scalar value; + Status fb_err; + if (frame->GetFrameBaseValue(value, &fb_err)) { + int64_t fbreg_offset = opcodes.GetSLEB128(&offset); + value += fbreg_offset; + stack.push_back(value); + stack.back().SetValueType(Value::ValueType::LoadAddress); + } else + return fb_err.ToError(); + } else { + return llvm::createStringError( + "invalid stack frame in context for DW_OP_fbreg opcode"); + } + } else { + return llvm::createStringError( + "NULL execution context for DW_OP_fbreg"); + } + + break; + + // 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: { + LocationDescriptionKind piece_locdesc = dwarf4_location_description_kind; + // Reset for the next piece. + dwarf4_location_description_kind = Memory; + + const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); + + if (piece_byte_size > 0) { + Value curr_piece; + + if (stack.empty()) { + UpdateValueTypeFromLocationDescription( + log, dwarf_cu, LocationDescriptionKind::Empty); + // 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); + pieces.AppendDataToHostBuffer(curr_piece); + } else { + Status error; + // Extract the current piece into "curr_piece" + Value curr_piece_source_value(stack.back()); + stack.pop_back(); + UpdateValueTypeFromLocationDescription(log, dwarf_cu, piece_locdesc, + &curr_piece_source_value); + + const Value::ValueType curr_piece_source_value_type = + curr_piece_source_value.GetValueType(); + Scalar &scalar = curr_piece_source_value.GetScalar(); + const lldb::addr_t addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); + switch (curr_piece_source_value_type) { + case Value::ValueType::Invalid: + return llvm::createStringError("invalid value type"); + case Value::ValueType::LoadAddress: + case Value::ValueType::FileAddress: { + if (target) { + if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { + if (target->ReadMemory(addr, curr_piece.GetBuffer().GetBytes(), + piece_byte_size, error, + /*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: { + 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()); + } + // Create curr_piece with bit_size. By default Scalar + // grows to the nearest host integer type. + llvm::APInt fail_value(1, 0, false); + llvm::APInt ap_int = scalar.UInt128(fail_value); + assert(ap_int.getBitWidth() >= bit_size); + llvm::ArrayRef<uint64_t> buf{ap_int.getRawData(), + ap_int.getNumWords()}; + curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf)); + } break; + } + + // 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) { + 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 (pieces.GetBuffer().GetByteSize() != op_piece_offset) { + return llvm::createStringError( + "DW_OP_piece for offset %" PRIu64 + " but top of stack is of size %" PRIu64, + op_piece_offset, pieces.GetBuffer().GetByteSize()); + } + + if (pieces.AppendDataToHostBuffer(curr_piece) == 0) + return llvm::createStringError("failed to append piece data"); + } + } + op_piece_offset += piece_byte_size; + } + } break; + + case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); + if (stack.size() < 1) { + UpdateValueTypeFromLocationDescription(log, dwarf_cu, + LocationDescriptionKind::Empty); + // Reset for the next piece. + dwarf4_location_description_kind = Memory; + return llvm::createStringError( + "expression stack needs at least 1 item for DW_OP_bit_piece"); + } else { + UpdateValueTypeFromLocationDescription( + log, dwarf_cu, dwarf4_location_description_kind, &stack.back()); + // Reset for the next piece. + dwarf4_location_description_kind = Memory; + const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); + const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); + switch (stack.back().GetValueType()) { + case Value::ValueType::Invalid: + return llvm::createStringError( + "unable to extract bit value from invalid value"); + case Value::ValueType::Scalar: { + if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, + piece_bit_offset)) { + return llvm::createStringError( + "unable to extract %" PRIu64 " bit value with %" PRIu64 + " bit offset from a %" PRIu64 " bit scalar value.", + piece_bit_size, piece_bit_offset, + (uint64_t)(stack.back().GetScalar().GetByteSize() * 8)); + } + } break; + + case Value::ValueType::FileAddress: + case Value::ValueType::LoadAddress: + case Value::ValueType::HostAddress: + return llvm::createStringError( + "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 + ", bit_offset = %" PRIu64 ") from an address value.", + piece_bit_size, piece_bit_offset); + } + } + break; + + // OPCODE: DW_OP_implicit_value + // OPERANDS: 2 + // ULEB128 size of the value block in bytes + // uint8_t* block bytes encoding value in target's memory + // representation + // DESCRIPTION: Value is immediately stored in block in the debug info with + // the memory representation of the target. + case DW_OP_implicit_value: { + dwarf4_location_description_kind = Implicit; + + const uint32_t len = opcodes.GetULEB128(&offset); + const void *data = opcodes.GetData(&offset, len); + + if (!data) { + LLDB_LOG(log, "Evaluate_DW_OP_implicit_value: could not be read data"); + return llvm::createStringError("could not evaluate %s", + DW_OP_value_to_name(op)); + } + + Value result(data, len); + stack.push_back(result); + break; + } + + case DW_OP_implicit_pointer: { + dwarf4_location_description_kind = Implicit; + return llvm::createStringError("Could not evaluate %s.", + DW_OP_value_to_name(op)); + } + + // 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 { + return llvm::createStringError("DW_OP_push_object_address used without " + "specifying an object address"); + } + 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: + return llvm::createStringError("unimplemented opcode DW_OP_call2"); + // 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: + return llvm::createStringError("unimplemented opcode DW_OP_call4"); + + // 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: + dwarf4_location_description_kind = Implicit; + stack.back().SetValueType(Value::ValueType::Scalar); + 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: { + const uint64_t die_offset = opcodes.GetULEB128(&offset); + uint64_t bit_size; + bool sign; + 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) + return llvm::createStringError("no module"); + sign = false; + bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8; + if (!bit_size) + return llvm::createStringError("unspecified architecture"); + } else { + // Retrieve the type DIE that the value is being converted to. This + // offset is compile unit relative so we need to fix it up. + const uint64_t abs_die_offset = die_offset + dwarf_cu->GetOffset(); + // FIXME: the constness has annoying ripple effects. + DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(abs_die_offset); + if (!die) + return llvm::createStringError( + "cannot resolve DW_OP_convert type DIE"); + uint64_t encoding = + die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user); + bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; + if (!bit_size) + bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0); + if (!bit_size) + return llvm::createStringError( + "unsupported type size in DW_OP_convert"); + switch (encoding) { + case DW_ATE_signed: + case DW_ATE_signed_char: + sign = true; + break; + case DW_ATE_unsigned: + case DW_ATE_unsigned_char: + sign = false; + break; + default: + return llvm::createStringError( + "unsupported encoding in DW_OP_convert"); + } + } + Scalar &top = stack.back().ResolveValue(exe_ctx); + top.TruncOrExtendTo(bit_size, sign); + break; + } + + // 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::ValueType::LoadAddress); + } else { + return llvm::createStringError( + "stack frame does not include a canonical " + "frame address for DW_OP_call_frame_cfa " + "opcode"); + } + } else { + return llvm::createStringError("unvalid stack frame in context for " + "DW_OP_call_frame_cfa opcode"); + } + break; + + // 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 (op == DW_OP_form_tls_address) + return llvm::createStringError( + "DW_OP_form_tls_address needs an argument"); + else + return llvm::createStringError( + "DW_OP_GNU_push_tls_address needs an argument"); + } + + if (!exe_ctx || !module_sp) + return llvm::createStringError("no context to evaluate TLS within"); + + Thread *thread = exe_ctx->GetThreadPtr(); + if (!thread) + return llvm::createStringError("no thread to evaluate TLS within"); + + // 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) + return llvm::createStringError( + "no TLS data currently exists for this thread"); + + stack.back().GetScalar() = tls_load_addr; + stack.back().SetValueType(Value::ValueType::LoadAddress); + } 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) + return llvm::createStringError("DW_OP_GNU_addr_index found without a " + "compile unit being specified"); + uint64_t index = opcodes.GetULEB128(&offset); + lldb::addr_t value = dwarf_cu->ReadAddressFromDebugAddrSection(index); + stack.push_back(Scalar(value)); + if (target && + target->GetArchitecture().GetCore() == ArchSpec::eCore_wasm32) { + // wasm file sections aren't mapped into memory, therefore addresses can + // never point into a file section and are always LoadAddresses. + stack.back().SetValueType(Value::ValueType::LoadAddress); + } else { + stack.back().SetValueType(Value::ValueType::FileAddress); + } + } 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) { + return llvm::createStringError("DW_OP_GNU_const_index found without a " + "compile unit being specified"); + } + uint64_t index = opcodes.GetULEB128(&offset); + lldb::addr_t value = dwarf_cu->ReadAddressFromDebugAddrSection(index); + stack.push_back(Scalar(value)); + } break; + + case DW_OP_GNU_entry_value: + case DW_OP_entry_value: { + if (llvm::Error err = Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, + opcodes, offset, log)) + return llvm::createStringError( + "could not evaluate DW_OP_entry_value: %s", + llvm::toString(std::move(err)).c_str()); + break; + } + + default: + if (dwarf_cu) { + if (dwarf_cu->GetSymbolFileDWARF().ParseVendorDWARFOpcode( + op, opcodes, offset, stack)) { + break; + } + } + return llvm::createStringError(llvm::formatv( + "Unhandled opcode {0} in DWARFExpression", LocationAtom(op))); + } + } + + if (stack.empty()) { + // 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()) + return pieces; + + return llvm::createStringError("stack empty after evaluation"); + } + + UpdateValueTypeFromLocationDescription( + log, dwarf_cu, dwarf4_location_description_kind, &stack.back()); + + if (log && log->GetVerbose()) { + size_t count = stack.size(); + LLDB_LOGF(log, + "Stack after operation has %" PRIu64 " values:", (uint64_t)count); + for (size_t i = 0; i < count; ++i) { + StreamString new_value; + new_value.Printf("[%" PRIu64 "]", (uint64_t)i); + stack[i].Dump(&new_value); + LLDB_LOGF(log, " %s", new_value.GetData()); + } + } + return stack.back(); +} + +bool DWARFExpression::ParseDWARFLocationList( + const DWARFUnit *dwarf_cu, const DataExtractor &data, + DWARFExpressionList *location_list) { + location_list->Clear(); + std::unique_ptr<llvm::DWARFLocationTable> loctable_up = + dwarf_cu->GetLocationTable(data); + Log *log = GetLog(LLDBLog::Expressions); + auto lookup_addr = + [&](uint32_t index) -> std::optional<llvm::object::SectionedAddress> { + addr_t address = dwarf_cu->ReadAddressFromDebugAddrSection(index); + if (address == LLDB_INVALID_ADDRESS) + return std::nullopt; + return llvm::object::SectionedAddress{address}; + }; + auto process_list = [&](llvm::Expected<llvm::DWARFLocationExpression> loc) { + if (!loc) { + LLDB_LOG_ERROR(log, loc.takeError(), "{0}"); + return true; + } + auto buffer_sp = + std::make_shared<DataBufferHeap>(loc->Expr.data(), loc->Expr.size()); + DWARFExpression expr = DWARFExpression(DataExtractor( + buffer_sp, data.GetByteOrder(), data.GetAddressByteSize())); + location_list->AddExpression(loc->Range->LowPC, loc->Range->HighPC, expr); + return true; + }; + llvm::Error error = loctable_up->visitAbsoluteLocationList( + 0, llvm::object::SectionedAddress{dwarf_cu->GetBaseAddress()}, + lookup_addr, process_list); + location_list->Sort(); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), "{0}"); + return false; + } + return true; +} + +bool DWARFExpression::MatchesOperand( + StackFrame &frame, const Instruction::Operand &operand) const { + using namespace OperandMatchers; + + RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); + if (!reg_ctx_sp) { + return false; + } + + DataExtractor opcodes(m_data); + + lldb::offset_t op_offset = 0; + uint8_t opcode = opcodes.GetU8(&op_offset); + + if (opcode == DW_OP_fbreg) { + int64_t offset = opcodes.GetSLEB128(&op_offset); + + DWARFExpressionList *fb_expr = frame.GetFrameBaseExpression(nullptr); + if (!fb_expr) { + return false; + } + + auto recurse = [&frame, fb_expr](const Instruction::Operand &child) { + return fb_expr->MatchesOperand(frame, child); + }; + + if (!offset && + MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), + recurse)(operand)) { + return true; + } + + return MatchUnaryOp( + MatchOpType(Instruction::Operand::Type::Dereference), + MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), + MatchImmOp(offset), recurse))(operand); + } + + bool dereference = false; + const RegisterInfo *reg = nullptr; + int64_t offset = 0; + + if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) { + reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0); + } else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) { + offset = opcodes.GetSLEB128(&op_offset); + reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_breg0); + } else if (opcode == DW_OP_regx) { + uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset)); + reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num); + } else if (opcode == DW_OP_bregx) { + uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset)); + offset = opcodes.GetSLEB128(&op_offset); + reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num); + } else { + return false; + } + + if (!reg) { + return false; + } + + if (dereference) { + if (!offset && + MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), + MatchRegOp(*reg))(operand)) { + return true; + } + + return MatchUnaryOp( + MatchOpType(Instruction::Operand::Type::Dereference), + MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), + MatchRegOp(*reg), + MatchImmOp(offset)))(operand); + } else { + return MatchRegOp(*reg)(operand); + } +} diff --git a/contrib/llvm-project/lldb/source/Expression/DWARFExpressionList.cpp b/contrib/llvm-project/lldb/source/Expression/DWARFExpressionList.cpp new file mode 100644 index 000000000000..7a5cf9f9a0be --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/DWARFExpressionList.cpp @@ -0,0 +1,240 @@ +//===-- DWARFExpressionList.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DWARFExpressionList.h" +#include "Plugins/SymbolFile/DWARF/DWARFUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" + +using namespace lldb; +using namespace lldb_private; + +bool DWARFExpressionList::IsAlwaysValidSingleExpr() const { + return GetAlwaysValidExpr() != nullptr; +} + +const DWARFExpression * DWARFExpressionList::GetAlwaysValidExpr() const { + if (m_exprs.GetSize() != 1) + return nullptr; + const auto *expr = m_exprs.GetEntryAtIndex(0); + if (expr->base == 0 && expr->size == LLDB_INVALID_ADDRESS) + return &expr->data; + return nullptr; +} + +bool DWARFExpressionList::AddExpression(addr_t base, addr_t end, + DWARFExpression expr) { + if (IsAlwaysValidSingleExpr() || base >= end) + return false; + m_exprs.Append({base, end - base, expr}); + return true; +} + +bool DWARFExpressionList::GetExpressionData(DataExtractor &data, + lldb::addr_t func_load_addr, + lldb::addr_t file_addr) const { + if (const DWARFExpression *expr = + GetExpressionAtAddress(func_load_addr, file_addr)) + return expr->GetExpressionData(data); + return false; +} + +bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr, + lldb::addr_t addr) const { + if (IsAlwaysValidSingleExpr()) + return true; + return GetExpressionAtAddress(func_load_addr, addr) != nullptr; +} + +const DWARFExpression * +DWARFExpressionList::GetExpressionAtAddress(lldb::addr_t func_load_addr, + lldb::addr_t load_addr) const { + if (const DWARFExpression *expr = GetAlwaysValidExpr()) + return expr; + if (func_load_addr == LLDB_INVALID_ADDRESS) + func_load_addr = m_func_file_addr; + addr_t addr = load_addr - func_load_addr + m_func_file_addr; + uint32_t index = m_exprs.FindEntryIndexThatContains(addr); + if (index == UINT32_MAX) + return nullptr; + return &m_exprs.GetEntryAtIndex(index)->data; +} + +DWARFExpression * +DWARFExpressionList::GetMutableExpressionAtAddress(lldb::addr_t func_load_addr, + lldb::addr_t load_addr) { + if (IsAlwaysValidSingleExpr()) + return &m_exprs.GetMutableEntryAtIndex(0)->data; + if (func_load_addr == LLDB_INVALID_ADDRESS) + func_load_addr = m_func_file_addr; + addr_t addr = load_addr - func_load_addr + m_func_file_addr; + uint32_t index = m_exprs.FindEntryIndexThatContains(addr); + if (index == UINT32_MAX) + return nullptr; + return &m_exprs.GetMutableEntryAtIndex(index)->data; +} + +bool DWARFExpressionList::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 (!IsAlwaysValidSingleExpr()) + return false; + + const DWARFExpression &expr = m_exprs.GetEntryRef(0).data; + return expr.ContainsThreadLocalStorage(m_dwarf_cu); +} + +bool DWARFExpressionList::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 (!IsAlwaysValidSingleExpr()) + return false; + + DWARFExpression &expr = m_exprs.GetEntryRef(0).data; + // 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 + if (expr.LinkThreadLocalStorage(m_dwarf_cu, link_address_callback)) + m_module_wp = new_module_sp; + return true; +} + +bool DWARFExpressionList::MatchesOperand( + StackFrame &frame, const Instruction::Operand &operand) const { + RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); + if (!reg_ctx_sp) { + return false; + } + const DWARFExpression *expr = nullptr; + if (IsAlwaysValidSingleExpr()) + expr = &m_exprs.GetEntryAtIndex(0)->data; + else { + SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction); + if (!sc.function) + return false; + + addr_t load_function_start = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + if (load_function_start == LLDB_INVALID_ADDRESS) + return false; + + addr_t pc = frame.GetFrameCodeAddressForSymbolication().GetFileAddress(); + expr = GetExpressionAtAddress(LLDB_INVALID_ADDRESS, pc); + } + if (!expr) + return false; + return expr->MatchesOperand(frame, operand); +} + +bool DWARFExpressionList::DumpLocations(Stream *s, lldb::DescriptionLevel level, + lldb::addr_t func_load_addr, + lldb::addr_t file_addr, + ABI *abi) const { + llvm::raw_ostream &os = s->AsRawOstream(); + llvm::ListSeparator separator; + if (const DWARFExpression *expr = GetAlwaysValidExpr()) { + expr->DumpLocation(s, level, abi); + return true; + } + for (const Entry &entry : *this) { + addr_t load_base = entry.GetRangeBase() + func_load_addr - m_func_file_addr; + addr_t load_end = entry.GetRangeEnd() + func_load_addr - m_func_file_addr; + if (file_addr != LLDB_INVALID_ADDRESS && + (file_addr < load_base || file_addr >= load_end)) + continue; + const auto &expr = entry.data; + DataExtractor data; + expr.GetExpressionData(data); + uint32_t addr_size = data.GetAddressByteSize(); + + os << separator; + os << "["; + os << llvm::format_hex(load_base, 2 + 2 * addr_size); + os << ", "; + os << llvm::format_hex(load_end, 2 + 2 * addr_size); + os << ") -> "; + expr.DumpLocation(s, level, abi); + if (file_addr != LLDB_INVALID_ADDRESS) + break; + } + return true; +} + +void DWARFExpressionList::GetDescription(Stream *s, + lldb::DescriptionLevel level, + ABI *abi) const { + llvm::raw_ostream &os = s->AsRawOstream(); + if (IsAlwaysValidSingleExpr()) { + m_exprs.Back()->data.DumpLocation(s, level, abi); + return; + } + os << llvm::format("0x%8.8" PRIx64 ": ", 0); + for (const Entry &entry : *this) { + const auto &expr = entry.data; + DataExtractor data; + expr.GetExpressionData(data); + uint32_t addr_size = data.GetAddressByteSize(); + os << "\n"; + os.indent(s->GetIndentLevel() + 2); + os << "["; + llvm::DWARFFormValue::dumpAddress(os, addr_size, entry.GetRangeBase()); + os << ", "; + llvm::DWARFFormValue::dumpAddress(os, addr_size, entry.GetRangeEnd()); + os << "): "; + expr.DumpLocation(s, level, abi); + } +} + +llvm::Expected<Value> DWARFExpressionList::Evaluate( + ExecutionContext *exe_ctx, RegisterContext *reg_ctx, + lldb::addr_t func_load_addr, const Value *initial_value_ptr, + const Value *object_address_ptr) const { + ModuleSP module_sp = m_module_wp.lock(); + DataExtractor data; + RegisterKind reg_kind; + DWARFExpression expr; + if (IsAlwaysValidSingleExpr()) { + expr = m_exprs.Back()->data; + } else { + Address pc; + StackFrame *frame = nullptr; + if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) { + if (exe_ctx) + frame = exe_ctx->GetFramePtr(); + if (!frame) + return llvm::createStringError("no frame"); + RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); + if (!reg_ctx_sp) + return llvm::createStringError("no register context"); + reg_ctx_sp->GetPCForSymbolication(pc); + } + + if (!pc.IsValid()) { + return llvm::createStringError("Invalid PC in frame."); + } + addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr()); + const DWARFExpression *entry = + GetExpressionAtAddress(func_load_addr, pc_load_addr); + if (!entry) + return llvm::createStringError("variable not available"); + expr = *entry; + } + expr.GetExpressionData(data); + reg_kind = expr.GetRegisterKind(); + return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, data, + m_dwarf_cu, reg_kind, initial_value_ptr, + object_address_ptr); +} diff --git a/contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp b/contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp new file mode 100644 index 000000000000..a8330138f3d5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp @@ -0,0 +1,87 @@ +//===-- DiagnosticManager.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DiagnosticManager.h" + +#include "llvm/Support/ErrorHandling.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb_private; + +void DiagnosticManager::Dump(Log *log) { + if (!log) + return; + + std::string str = GetString(); + + // GetString() puts a separator after each diagnostic. We want to remove the + // last '\n' because log->PutCString will add one for us. + + if (str.size() && str.back() == '\n') { + str.pop_back(); + } + + log->PutCString(str.c_str()); +} + +static const char *StringForSeverity(lldb::Severity severity) { + switch (severity) { + // this should be exhaustive + case lldb::eSeverityError: + return "error: "; + case lldb::eSeverityWarning: + return "warning: "; + case lldb::eSeverityInfo: + return ""; + } + llvm_unreachable("switch needs another case for lldb::Severity enum"); +} + +std::string DiagnosticManager::GetString(char separator) { + std::string ret; + llvm::raw_string_ostream stream(ret); + + for (const auto &diagnostic : Diagnostics()) { + llvm::StringRef severity = StringForSeverity(diagnostic->GetSeverity()); + stream << severity; + + llvm::StringRef message = diagnostic->GetMessage(); + std::string searchable_message = message.lower(); + auto severity_pos = message.find(severity); + stream << message.take_front(severity_pos); + + if (severity_pos != llvm::StringRef::npos) + stream << message.drop_front(severity_pos + severity.size()); + stream << separator; + } + + return ret; +} + +size_t DiagnosticManager::Printf(lldb::Severity severity, const char *format, + ...) { + StreamString ss; + + va_list args; + va_start(args, format); + size_t result = ss.PrintfVarArg(format, args); + va_end(args); + + AddDiagnostic(ss.GetString(), severity, eDiagnosticOriginLLDB); + + return result; +} + +void DiagnosticManager::PutString(lldb::Severity severity, + llvm::StringRef str) { + if (str.empty()) + return; + AddDiagnostic(str, severity, eDiagnosticOriginLLDB); +} diff --git a/contrib/llvm-project/lldb/source/Expression/Expression.cpp b/contrib/llvm-project/lldb/source/Expression/Expression.cpp new file mode 100644 index 000000000000..93f585edfce3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/Expression.cpp @@ -0,0 +1,28 @@ +//===-- Expression.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/Expression.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +Expression::Expression(Target &target) + : m_target_wp(target.shared_from_this()), + m_jit_start_addr(LLDB_INVALID_ADDRESS), + m_jit_end_addr(LLDB_INVALID_ADDRESS) { + // Can't make any kind of expression without a target. + assert(m_target_wp.lock()); +} + +Expression::Expression(ExecutionContextScope &exe_scope) + : m_target_wp(exe_scope.CalculateTarget()), + m_jit_start_addr(LLDB_INVALID_ADDRESS), + m_jit_end_addr(LLDB_INVALID_ADDRESS) { + assert(m_target_wp.lock()); +} diff --git a/contrib/llvm-project/lldb/source/Expression/ExpressionParser.cpp b/contrib/llvm-project/lldb/source/Expression/ExpressionParser.cpp new file mode 100644 index 000000000000..e9f7121c2499 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/ExpressionParser.cpp @@ -0,0 +1,72 @@ +//===-- ExpressionParser.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ExpressionParser.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +using namespace lldb; +using namespace lldb_private; + +Status ExpressionParser::PrepareForExecution( + addr_t &func_addr, addr_t &func_end, + std::shared_ptr<IRExecutionUnit> &execution_unit_sp, + ExecutionContext &exe_ctx, bool &can_interpret, + ExecutionPolicy execution_policy) { + Status status = + DoPrepareForExecution(func_addr, func_end, execution_unit_sp, exe_ctx, + can_interpret, execution_policy); + if (status.Success() && exe_ctx.GetProcessPtr() && exe_ctx.HasThreadScope()) + status = RunStaticInitializers(execution_unit_sp, exe_ctx); + + return status; +} + +Status +ExpressionParser::RunStaticInitializers(IRExecutionUnitSP &execution_unit_sp, + ExecutionContext &exe_ctx) { + Status err; + + if (!execution_unit_sp.get()) { + err.SetErrorString( + "can't run static initializers for a NULL execution unit"); + return err; + } + + if (!exe_ctx.HasThreadScope()) { + err.SetErrorString("can't run static initializers without a thread"); + return err; + } + + std::vector<addr_t> static_initializers; + + execution_unit_sp->GetStaticInitializers(static_initializers); + + for (addr_t static_initializer : static_initializers) { + EvaluateExpressionOptions options; + + ThreadPlanSP call_static_initializer(new ThreadPlanCallFunction( + exe_ctx.GetThreadRef(), Address(static_initializer), CompilerType(), + llvm::ArrayRef<addr_t>(), options)); + + DiagnosticManager execution_errors; + ExpressionResults results = + exe_ctx.GetThreadRef().GetProcess()->RunThreadPlan( + exe_ctx, call_static_initializer, options, execution_errors); + + if (results != eExpressionCompleted) { + err.SetErrorStringWithFormat("couldn't run static initializer: %s", + execution_errors.GetString().c_str()); + return err; + } + } + + return err; +} diff --git a/contrib/llvm-project/lldb/source/Expression/ExpressionTypeSystemHelper.cpp b/contrib/llvm-project/lldb/source/Expression/ExpressionTypeSystemHelper.cpp new file mode 100644 index 000000000000..dbac376f5f1c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/ExpressionTypeSystemHelper.cpp @@ -0,0 +1,13 @@ +//===-- ExpressionTypeSystemHelper.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ExpressionTypeSystemHelper.h" + +using namespace lldb_private; + +char ExpressionTypeSystemHelper::ID; diff --git a/contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp b/contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp new file mode 100644 index 000000000000..f0a28988822f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp @@ -0,0 +1,86 @@ +//===-- ExpressionVariable.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include <optional> + +using namespace lldb_private; + +char ExpressionVariable::ID; + +ExpressionVariable::ExpressionVariable() : m_flags(0) {} + +uint8_t *ExpressionVariable::GetValueBytes() { + std::optional<uint64_t> byte_size = m_frozen_sp->GetByteSize(); + if (byte_size && *byte_size) { + if (m_frozen_sp->GetDataExtractor().GetByteSize() < *byte_size) { + m_frozen_sp->GetValue().ResizeData(*byte_size); + m_frozen_sp->GetValue().GetData(m_frozen_sp->GetDataExtractor()); + } + return const_cast<uint8_t *>( + m_frozen_sp->GetDataExtractor().GetDataStart()); + } + return nullptr; +} + +char PersistentExpressionState::ID; + +PersistentExpressionState::PersistentExpressionState() = default; + +PersistentExpressionState::~PersistentExpressionState() = default; + +lldb::addr_t PersistentExpressionState::LookupSymbol(ConstString name) { + SymbolMap::iterator si = m_symbol_map.find(name.GetCString()); + + if (si != m_symbol_map.end()) + return si->second; + else + return LLDB_INVALID_ADDRESS; +} + +void PersistentExpressionState::RegisterExecutionUnit( + lldb::IRExecutionUnitSP &execution_unit_sp) { + Log *log = GetLog(LLDBLog::Expressions); + + m_execution_units.insert(execution_unit_sp); + + LLDB_LOGF(log, "Registering JITted Functions:\n"); + + for (const IRExecutionUnit::JittedFunction &jitted_function : + execution_unit_sp->GetJittedFunctions()) { + if (jitted_function.m_external && + jitted_function.m_name != execution_unit_sp->GetFunctionName() && + jitted_function.m_remote_addr != LLDB_INVALID_ADDRESS) { + m_symbol_map[jitted_function.m_name.GetCString()] = + jitted_function.m_remote_addr; + LLDB_LOGF(log, " Function: %s at 0x%" PRIx64 ".", + jitted_function.m_name.GetCString(), + jitted_function.m_remote_addr); + } + } + + LLDB_LOGF(log, "Registering JIIted Symbols:\n"); + + for (const IRExecutionUnit::JittedGlobalVariable &global_var : + execution_unit_sp->GetJittedGlobalVariables()) { + if (global_var.m_remote_addr != LLDB_INVALID_ADDRESS) { + // Demangle the name before inserting it, so that lookups by the ConstStr + // of the demangled name will find the mangled one (needed for looking up + // metadata pointers.) + Mangled mangler(global_var.m_name); + mangler.GetDemangledName(); + m_symbol_map[global_var.m_name.GetCString()] = global_var.m_remote_addr; + LLDB_LOGF(log, " Symbol: %s at 0x%" PRIx64 ".", + global_var.m_name.GetCString(), global_var.m_remote_addr); + } + } +} diff --git a/contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp b/contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp new file mode 100644 index 000000000000..5ac2b0681ebb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp @@ -0,0 +1,418 @@ +//===-- FunctionCaller.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +using namespace lldb_private; + +char FunctionCaller::ID; + +// FunctionCaller constructor +FunctionCaller::FunctionCaller(ExecutionContextScope &exe_scope, + const CompilerType &return_type, + const Address &functionAddress, + const ValueList &arg_value_list, + const char *name) + : Expression(exe_scope), m_execution_unit_sp(), m_parser(), + m_jit_module_wp(), m_name(name ? name : "<unknown>"), + m_function_ptr(nullptr), m_function_addr(functionAddress), + m_function_return_type(return_type), + m_wrapper_function_name("__lldb_caller_function"), + m_wrapper_struct_name("__lldb_caller_struct"), m_wrapper_args_addrs(), + m_struct_valid(false), m_struct_size(0), m_return_size(0), + m_return_offset(0), m_arg_values(arg_value_list), m_compiled(false), + m_JITted(false) { + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a FunctionCaller without a process. + assert(m_jit_process_wp.lock()); +} + +// Destructor +FunctionCaller::~FunctionCaller() { + lldb::ProcessSP process_sp(m_jit_process_wp.lock()); + if (process_sp) { + lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock()); + if (jit_module_sp) + process_sp->GetTarget().GetImages().Remove(jit_module_sp); + } +} + +bool FunctionCaller::WriteFunctionWrapper( + ExecutionContext &exe_ctx, DiagnosticManager &diagnostic_manager) { + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) { + diagnostic_manager.Printf(lldb::eSeverityError, "no process."); + return false; + } + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) { + diagnostic_manager.Printf(lldb::eSeverityError, + "process does not match the stored process."); + return false; + } + + if (process->GetState() != lldb::eStateStopped) { + diagnostic_manager.Printf(lldb::eSeverityError, "process is not stopped"); + return false; + } + + if (!m_compiled) { + diagnostic_manager.Printf(lldb::eSeverityError, "function not compiled"); + return false; + } + + if (m_JITted) + return true; + + bool can_interpret = false; // should stay that way + + Status jit_error(m_parser->PrepareForExecution( + m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx, + can_interpret, eExecutionPolicyAlways)); + + if (!jit_error.Success()) { + diagnostic_manager.Printf(lldb::eSeverityError, + "Error in PrepareForExecution: %s.", + jit_error.AsCString()); + return false; + } + + if (m_parser->GetGenerateDebugInfo()) { + lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule()); + + if (jit_module_sp) { + ConstString const_func_name(FunctionName()); + FileSpec jit_file; + jit_file.SetFilename(const_func_name); + jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString()); + m_jit_module_wp = jit_module_sp; + process->GetTarget().GetImages().Append(jit_module_sp, + true /* notify */); + } + } + if (process && m_jit_start_addr) + m_jit_process_wp = process->shared_from_this(); + + m_JITted = true; + + return true; +} + +bool FunctionCaller::WriteFunctionArguments( + ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, + DiagnosticManager &diagnostic_manager) { + return WriteFunctionArguments(exe_ctx, args_addr_ref, m_arg_values, + diagnostic_manager); +} + +// FIXME: Assure that the ValueList we were passed in is consistent with the one +// that defined this function. + +bool FunctionCaller::WriteFunctionArguments( + ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, + ValueList &arg_values, DiagnosticManager &diagnostic_manager) { + // All the information to reconstruct the struct is provided by the + // StructExtractor. + if (!m_struct_valid) { + diagnostic_manager.PutString(lldb::eSeverityError, + "Argument information was not correctly " + "parsed, so the function cannot be called."); + return false; + } + + Status error; + lldb::ExpressionResults return_value = lldb::eExpressionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == nullptr) + return return_value; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + if (args_addr_ref == LLDB_INVALID_ADDRESS) { + args_addr_ref = process->AllocateMemory( + m_struct_size, lldb::ePermissionsReadable | lldb::ePermissionsWritable, + error); + if (args_addr_ref == LLDB_INVALID_ADDRESS) + return false; + m_wrapper_args_addrs.push_back(args_addr_ref); + } else { + // Make sure this is an address that we've already handed out. + if (find(m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(), + args_addr_ref) == m_wrapper_args_addrs.end()) { + return false; + } + } + + // TODO: verify fun_addr needs to be a callable address + Scalar fun_addr( + m_function_addr.GetCallableLoadAddress(exe_ctx.GetTargetPtr())); + uint64_t first_offset = m_member_offsets[0]; + process->WriteScalarToMemory(args_addr_ref + first_offset, fun_addr, + process->GetAddressByteSize(), error); + + // FIXME: We will need to extend this for Variadic functions. + + Status value_error; + + size_t num_args = arg_values.GetSize(); + if (num_args != m_arg_values.GetSize()) { + diagnostic_manager.Printf( + lldb::eSeverityError, + "Wrong number of arguments - was: %" PRIu64 " should be: %" PRIu64 "", + (uint64_t)num_args, (uint64_t)m_arg_values.GetSize()); + return false; + } + + for (size_t i = 0; i < num_args; i++) { + // FIXME: We should sanity check sizes. + + uint64_t offset = m_member_offsets[i + 1]; // Clang sizes are in bytes. + Value *arg_value = arg_values.GetValueAtIndex(i); + + // FIXME: For now just do scalars: + + // Special case: if it's a pointer, don't do anything (the ABI supports + // passing cstrings) + + if (arg_value->GetValueType() == Value::ValueType::HostAddress && + arg_value->GetContextType() == Value::ContextType::Invalid && + arg_value->GetCompilerType().IsPointerType()) + continue; + + const Scalar &arg_scalar = arg_value->ResolveValue(&exe_ctx); + + if (!process->WriteScalarToMemory(args_addr_ref + offset, arg_scalar, + arg_scalar.GetByteSize(), error)) + return false; + } + + return true; +} + +bool FunctionCaller::InsertFunction(ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + DiagnosticManager &diagnostic_manager) { + // Since we might need to call allocate memory and maybe call code to make + // the caller, we need to be stopped. + Process *process = exe_ctx.GetProcessPtr(); + if (!process) { + diagnostic_manager.PutString(lldb::eSeverityError, "no process"); + return false; + } + if (process->GetState() != lldb::eStateStopped) { + diagnostic_manager.PutString(lldb::eSeverityError, "process running"); + return false; + } + if (CompileFunction(exe_ctx.GetThreadSP(), diagnostic_manager) != 0) + return false; + if (!WriteFunctionWrapper(exe_ctx, diagnostic_manager)) + return false; + if (!WriteFunctionArguments(exe_ctx, args_addr_ref, diagnostic_manager)) + return false; + + Log *log = GetLog(LLDBLog::Step); + LLDB_LOGF(log, "Call Address: 0x%" PRIx64 " Struct Address: 0x%" PRIx64 ".\n", + m_jit_start_addr, args_addr_ref); + + return true; +} + +lldb::ThreadPlanSP FunctionCaller::GetThreadPlanToCallFunction( + ExecutionContext &exe_ctx, lldb::addr_t args_addr, + const EvaluateExpressionOptions &options, + DiagnosticManager &diagnostic_manager) { + Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step)); + + LLDB_LOGF(log, + "-- [FunctionCaller::GetThreadPlanToCallFunction] Creating " + "thread plan to call function \"%s\" --", + m_name.c_str()); + + // FIXME: Use the errors Stream for better error reporting. + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == nullptr) { + diagnostic_manager.PutString( + lldb::eSeverityError, "Can't call a function without a valid thread."); + return nullptr; + } + + // Okay, now run the function: + + Address wrapper_address(m_jit_start_addr); + + lldb::addr_t args = {args_addr}; + + lldb::ThreadPlanSP new_plan_sp(new ThreadPlanCallFunction( + *thread, wrapper_address, CompilerType(), args, options)); + new_plan_sp->SetIsControllingPlan(true); + new_plan_sp->SetOkayToDiscard(false); + return new_plan_sp; +} + +bool FunctionCaller::FetchFunctionResults(ExecutionContext &exe_ctx, + lldb::addr_t args_addr, + Value &ret_value) { + // Read the return value - it is the last field in the struct: + // FIXME: How does clang tell us there's no return value? We need to handle + // that case. + // FIXME: Create our ThreadPlanCallFunction with the return CompilerType, and + // then use GetReturnValueObject + // to fetch the value. That way we can fetch any values we need. + + Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step)); + + LLDB_LOGF(log, + "-- [FunctionCaller::FetchFunctionResults] Fetching function " + "results for \"%s\"--", + m_name.c_str()); + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == nullptr) + return false; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + Status error; + ret_value.GetScalar() = process->ReadUnsignedIntegerFromMemory( + args_addr + m_return_offset, m_return_size, 0, error); + + if (error.Fail()) + return false; + + ret_value.SetCompilerType(m_function_return_type); + ret_value.SetValueType(Value::ValueType::Scalar); + return true; +} + +void FunctionCaller::DeallocateFunctionResults(ExecutionContext &exe_ctx, + lldb::addr_t args_addr) { + std::list<lldb::addr_t>::iterator pos; + pos = std::find(m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(), + args_addr); + if (pos != m_wrapper_args_addrs.end()) + m_wrapper_args_addrs.erase(pos); + + exe_ctx.GetProcessRef().DeallocateMemory(args_addr); +} + +lldb::ExpressionResults FunctionCaller::ExecuteFunction( + ExecutionContext &exe_ctx, lldb::addr_t *args_addr_ptr, + const EvaluateExpressionOptions &options, + DiagnosticManager &diagnostic_manager, Value &results) { + lldb::ExpressionResults return_value = lldb::eExpressionSetupError; + + // FunctionCaller::ExecuteFunction execution is always just to get the + // result. Unless explicitly asked for, ignore breakpoints and unwind on + // error. + const bool enable_debugging = + exe_ctx.GetTargetPtr() && + exe_ctx.GetTargetPtr()->GetDebugUtilityExpression(); + EvaluateExpressionOptions real_options = options; + real_options.SetDebug(false); // This halts the expression for debugging. + real_options.SetGenerateDebugInfo(enable_debugging); + real_options.SetUnwindOnError(!enable_debugging); + real_options.SetIgnoreBreakpoints(!enable_debugging); + + lldb::addr_t args_addr; + + if (args_addr_ptr != nullptr) + args_addr = *args_addr_ptr; + else + args_addr = LLDB_INVALID_ADDRESS; + + if (CompileFunction(exe_ctx.GetThreadSP(), diagnostic_manager) != 0) + return lldb::eExpressionSetupError; + + if (args_addr == LLDB_INVALID_ADDRESS) { + if (!InsertFunction(exe_ctx, args_addr, diagnostic_manager)) + return lldb::eExpressionSetupError; + } + + Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step)); + + LLDB_LOGF(log, + "== [FunctionCaller::ExecuteFunction] Executing function \"%s\" ==", + m_name.c_str()); + + lldb::ThreadPlanSP call_plan_sp = GetThreadPlanToCallFunction( + exe_ctx, args_addr, real_options, diagnostic_manager); + if (!call_plan_sp) + return lldb::eExpressionSetupError; + + // We need to make sure we record the fact that we are running an expression + // here otherwise this fact will fail to be recorded when fetching an + // Objective-C object description + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + return_value = exe_ctx.GetProcessRef().RunThreadPlan( + exe_ctx, call_plan_sp, real_options, diagnostic_manager); + + if (log) { + if (return_value != lldb::eExpressionCompleted) { + LLDB_LOGF(log, + "== [FunctionCaller::ExecuteFunction] Execution of \"%s\" " + "completed abnormally: %s ==", + m_name.c_str(), + Process::ExecutionResultAsCString(return_value)); + } else { + LLDB_LOGF(log, + "== [FunctionCaller::ExecuteFunction] Execution of \"%s\" " + "completed normally ==", + m_name.c_str()); + } + } + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + if (args_addr_ptr != nullptr) + *args_addr_ptr = args_addr; + + if (return_value != lldb::eExpressionCompleted) + return return_value; + + FetchFunctionResults(exe_ctx, args_addr, results); + + if (args_addr_ptr == nullptr) + DeallocateFunctionResults(exe_ctx, args_addr); + + return lldb::eExpressionCompleted; +} diff --git a/contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp b/contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp new file mode 100644 index 000000000000..f22070442362 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp @@ -0,0 +1,1206 @@ +//===-- IRExecutionUnit.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DiagnosticHandler.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/ObjectFileJIT.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include <optional> + +using namespace lldb_private; + +IRExecutionUnit::IRExecutionUnit(std::unique_ptr<llvm::LLVMContext> &context_up, + std::unique_ptr<llvm::Module> &module_up, + ConstString &name, + const lldb::TargetSP &target_sp, + const SymbolContext &sym_ctx, + std::vector<std::string> &cpu_features) + : IRMemoryMap(target_sp), m_context_up(context_up.release()), + m_module_up(module_up.release()), m_module(m_module_up.get()), + m_cpu_features(cpu_features), m_name(name), m_sym_ctx(sym_ctx), + m_did_jit(false), m_function_load_addr(LLDB_INVALID_ADDRESS), + m_function_end_load_addr(LLDB_INVALID_ADDRESS), + m_reported_allocations(false) {} + +lldb::addr_t IRExecutionUnit::WriteNow(const uint8_t *bytes, size_t size, + Status &error) { + const bool zero_memory = false; + lldb::addr_t allocation_process_addr = + Malloc(size, 8, lldb::ePermissionsWritable | lldb::ePermissionsReadable, + eAllocationPolicyMirror, zero_memory, error); + + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + + WriteMemory(allocation_process_addr, bytes, size, error); + + if (!error.Success()) { + Status err; + Free(allocation_process_addr, err); + + return LLDB_INVALID_ADDRESS; + } + + if (Log *log = GetLog(LLDBLog::Expressions)) { + DataBufferHeap my_buffer(size, 0); + Status err; + ReadMemory(my_buffer.GetBytes(), allocation_process_addr, size, err); + + if (err.Success()) { + DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(), + lldb::eByteOrderBig, 8); + my_extractor.PutToLog(log, 0, my_buffer.GetByteSize(), + allocation_process_addr, 16, + DataExtractor::TypeUInt8); + } + } + + return allocation_process_addr; +} + +void IRExecutionUnit::FreeNow(lldb::addr_t allocation) { + if (allocation == LLDB_INVALID_ADDRESS) + return; + + Status err; + + Free(allocation, err); +} + +Status IRExecutionUnit::DisassembleFunction(Stream &stream, + lldb::ProcessSP &process_wp) { + Log *log = GetLog(LLDBLog::Expressions); + + ExecutionContext exe_ctx(process_wp); + + Status ret; + + ret.Clear(); + + lldb::addr_t func_local_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t func_remote_addr = LLDB_INVALID_ADDRESS; + + for (JittedFunction &function : m_jitted_functions) { + if (function.m_name == m_name) { + func_local_addr = function.m_local_addr; + func_remote_addr = function.m_remote_addr; + } + } + + if (func_local_addr == LLDB_INVALID_ADDRESS) { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't find function %s for disassembly", + m_name.AsCString()); + return ret; + } + + LLDB_LOGF(log, + "Found function, has local address 0x%" PRIx64 + " and remote address 0x%" PRIx64, + (uint64_t)func_local_addr, (uint64_t)func_remote_addr); + + std::pair<lldb::addr_t, lldb::addr_t> func_range; + + func_range = GetRemoteRangeForLocal(func_local_addr); + + if (func_range.first == 0 && func_range.second == 0) { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't find code range for function %s", + m_name.AsCString()); + return ret; + } + + LLDB_LOGF(log, "Function's code range is [0x%" PRIx64 "+0x%" PRIx64 "]", + func_range.first, func_range.second); + + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + ret.SetErrorToGenericError(); + ret.SetErrorString("Couldn't find the target"); + return ret; + } + + lldb::WritableDataBufferSP buffer_sp( + new DataBufferHeap(func_range.second, 0)); + + Process *process = exe_ctx.GetProcessPtr(); + Status err; + process->ReadMemory(func_remote_addr, buffer_sp->GetBytes(), + buffer_sp->GetByteSize(), err); + + if (!err.Success()) { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't read from process: %s", + err.AsCString("unknown error")); + return ret; + } + + ArchSpec arch(target->GetArchitecture()); + + const char *plugin_name = nullptr; + const char *flavor_string = nullptr; + lldb::DisassemblerSP disassembler_sp = + Disassembler::FindPlugin(arch, flavor_string, plugin_name); + + if (!disassembler_sp) { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat( + "Unable to find disassembler plug-in for %s architecture.", + arch.GetArchitectureName()); + return ret; + } + + if (!process) { + ret.SetErrorToGenericError(); + ret.SetErrorString("Couldn't find the process"); + return ret; + } + + DataExtractor extractor(buffer_sp, process->GetByteOrder(), + target->GetArchitecture().GetAddressByteSize()); + + if (log) { + LLDB_LOGF(log, "Function data has contents:"); + extractor.PutToLog(log, 0, extractor.GetByteSize(), func_remote_addr, 16, + DataExtractor::TypeUInt8); + } + + disassembler_sp->DecodeInstructions(Address(func_remote_addr), extractor, 0, + UINT32_MAX, false, false); + + InstructionList &instruction_list = disassembler_sp->GetInstructionList(); + instruction_list.Dump(&stream, true, true, /*show_control_flow_kind=*/false, + &exe_ctx); + + return ret; +} + +namespace { +struct IRExecDiagnosticHandler : public llvm::DiagnosticHandler { + Status *err; + IRExecDiagnosticHandler(Status *err) : err(err) {} + bool handleDiagnostics(const llvm::DiagnosticInfo &DI) override { + if (DI.getSeverity() == llvm::DS_Error) { + const auto &DISM = llvm::cast<llvm::DiagnosticInfoSrcMgr>(DI); + if (err && err->Success()) { + err->SetErrorToGenericError(); + err->SetErrorStringWithFormat( + "IRExecution error: %s", + DISM.getSMDiag().getMessage().str().c_str()); + } + } + + return true; + } +}; +} // namespace + +void IRExecutionUnit::ReportSymbolLookupError(ConstString name) { + m_failed_lookups.push_back(name); +} + +void IRExecutionUnit::GetRunnableInfo(Status &error, lldb::addr_t &func_addr, + lldb::addr_t &func_end) { + lldb::ProcessSP process_sp(GetProcessWP().lock()); + + static std::recursive_mutex s_runnable_info_mutex; + + func_addr = LLDB_INVALID_ADDRESS; + func_end = LLDB_INVALID_ADDRESS; + + if (!process_sp) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write the JIT compiled code into the " + "process because the process is invalid"); + return; + } + + if (m_did_jit) { + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; + + return; + }; + + std::lock_guard<std::recursive_mutex> guard(s_runnable_info_mutex); + + m_did_jit = true; + + Log *log = GetLog(LLDBLog::Expressions); + + std::string error_string; + + if (log) { + std::string s; + llvm::raw_string_ostream oss(s); + + m_module->print(oss, nullptr); + + oss.flush(); + + LLDB_LOGF(log, "Module being sent to JIT: \n%s", s.c_str()); + } + + m_module_up->getContext().setDiagnosticHandler( + std::make_unique<IRExecDiagnosticHandler>(&error)); + + llvm::EngineBuilder builder(std::move(m_module_up)); + llvm::Triple triple(m_module->getTargetTriple()); + + builder.setEngineKind(llvm::EngineKind::JIT) + .setErrorStr(&error_string) + .setRelocationModel(triple.isOSBinFormatMachO() ? llvm::Reloc::PIC_ + : llvm::Reloc::Static) + .setMCJITMemoryManager(std::make_unique<MemoryManager>(*this)) + .setOptLevel(llvm::CodeGenOptLevel::Less); + + llvm::StringRef mArch; + llvm::StringRef mCPU; + llvm::SmallVector<std::string, 0> mAttrs; + + for (std::string &feature : m_cpu_features) + mAttrs.push_back(feature); + + llvm::TargetMachine *target_machine = + builder.selectTarget(triple, mArch, mCPU, mAttrs); + + m_execution_engine_up.reset(builder.create(target_machine)); + + if (!m_execution_engine_up) { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't JIT the function: %s", + error_string.c_str()); + return; + } + + m_strip_underscore = + (m_execution_engine_up->getDataLayout().getGlobalPrefix() == '_'); + + class ObjectDumper : public llvm::ObjectCache { + public: + ObjectDumper(FileSpec output_dir) : m_out_dir(output_dir) {} + void notifyObjectCompiled(const llvm::Module *module, + llvm::MemoryBufferRef object) override { + int fd = 0; + llvm::SmallVector<char, 256> result_path; + std::string object_name_model = + "jit-object-" + module->getModuleIdentifier() + "-%%%.o"; + FileSpec model_spec + = m_out_dir.CopyByAppendingPathComponent(object_name_model); + std::string model_path = model_spec.GetPath(); + + std::error_code result + = llvm::sys::fs::createUniqueFile(model_path, fd, result_path); + if (!result) { + llvm::raw_fd_ostream fds(fd, true); + fds.write(object.getBufferStart(), object.getBufferSize()); + } + } + std::unique_ptr<llvm::MemoryBuffer> + getObject(const llvm::Module *module) override { + // Return nothing - we're just abusing the object-cache mechanism to dump + // objects. + return nullptr; + } + private: + FileSpec m_out_dir; + }; + + FileSpec save_objects_dir = process_sp->GetTarget().GetSaveJITObjectsDir(); + if (save_objects_dir) { + m_object_cache_up = std::make_unique<ObjectDumper>(save_objects_dir); + m_execution_engine_up->setObjectCache(m_object_cache_up.get()); + } + + // Make sure we see all sections, including ones that don't have + // relocations... + m_execution_engine_up->setProcessAllSections(true); + + m_execution_engine_up->DisableLazyCompilation(); + + for (llvm::Function &function : *m_module) { + if (function.isDeclaration() || function.hasPrivateLinkage()) + continue; + + const bool external = !function.hasLocalLinkage(); + + void *fun_ptr = m_execution_engine_up->getPointerToFunction(&function); + + if (!error.Success()) { + // We got an error through our callback! + return; + } + + if (!fun_ptr) { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat( + "'%s' was in the JITted module but wasn't lowered", + function.getName().str().c_str()); + return; + } + m_jitted_functions.push_back(JittedFunction( + function.getName().str().c_str(), external, reinterpret_cast<uintptr_t>(fun_ptr))); + } + + CommitAllocations(process_sp); + ReportAllocations(*m_execution_engine_up); + + // We have to do this after calling ReportAllocations because for the MCJIT, + // getGlobalValueAddress will cause the JIT to perform all relocations. That + // can only be done once, and has to happen after we do the remapping from + // local -> remote. That means we don't know the local address of the + // Variables, but we don't need that for anything, so that's okay. + + std::function<void(llvm::GlobalValue &)> RegisterOneValue = [this]( + llvm::GlobalValue &val) { + if (val.hasExternalLinkage() && !val.isDeclaration()) { + uint64_t var_ptr_addr = + m_execution_engine_up->getGlobalValueAddress(val.getName().str()); + + lldb::addr_t remote_addr = GetRemoteAddressForLocal(var_ptr_addr); + + // This is a really unfortunae API that sometimes returns local addresses + // and sometimes returns remote addresses, based on whether the variable + // was relocated during ReportAllocations or not. + + if (remote_addr == LLDB_INVALID_ADDRESS) { + remote_addr = var_ptr_addr; + } + + if (var_ptr_addr != 0) + m_jitted_global_variables.push_back(JittedGlobalVariable( + val.getName().str().c_str(), LLDB_INVALID_ADDRESS, remote_addr)); + } + }; + + for (llvm::GlobalVariable &global_var : m_module->globals()) { + RegisterOneValue(global_var); + } + + for (llvm::GlobalAlias &global_alias : m_module->aliases()) { + RegisterOneValue(global_alias); + } + + WriteData(process_sp); + + if (m_failed_lookups.size()) { + StreamString ss; + + ss.PutCString("Couldn't look up symbols:\n"); + + bool emitNewLine = false; + + for (ConstString failed_lookup : m_failed_lookups) { + if (emitNewLine) + ss.PutCString("\n"); + emitNewLine = true; + ss.PutCString(" "); + ss.PutCString(Mangled(failed_lookup).GetDemangledName().GetStringRef()); + } + + m_failed_lookups.clear(); + ss.PutCString( + "\nHint: The expression tried to call a function that is not present " + "in the target, perhaps because it was optimized out by the compiler."); + error.SetErrorString(ss.GetString()); + + return; + } + + m_function_load_addr = LLDB_INVALID_ADDRESS; + m_function_end_load_addr = LLDB_INVALID_ADDRESS; + + for (JittedFunction &jitted_function : m_jitted_functions) { + jitted_function.m_remote_addr = + GetRemoteAddressForLocal(jitted_function.m_local_addr); + + if (!m_name.IsEmpty() && jitted_function.m_name == m_name) { + AddrRange func_range = + GetRemoteRangeForLocal(jitted_function.m_local_addr); + m_function_end_load_addr = func_range.first + func_range.second; + m_function_load_addr = jitted_function.m_remote_addr; + } + } + + if (log) { + LLDB_LOGF(log, "Code can be run in the target."); + + StreamString disassembly_stream; + + Status err = DisassembleFunction(disassembly_stream, process_sp); + + if (!err.Success()) { + LLDB_LOGF(log, "Couldn't disassemble function : %s", + err.AsCString("unknown error")); + } else { + LLDB_LOGF(log, "Function disassembly:\n%s", disassembly_stream.GetData()); + } + + LLDB_LOGF(log, "Sections: "); + for (AllocationRecord &record : m_records) { + if (record.m_process_address != LLDB_INVALID_ADDRESS) { + record.dump(log); + + DataBufferHeap my_buffer(record.m_size, 0); + Status err; + ReadMemory(my_buffer.GetBytes(), record.m_process_address, + record.m_size, err); + + if (err.Success()) { + DataExtractor my_extractor(my_buffer.GetBytes(), + my_buffer.GetByteSize(), + lldb::eByteOrderBig, 8); + my_extractor.PutToLog(log, 0, my_buffer.GetByteSize(), + record.m_process_address, 16, + DataExtractor::TypeUInt8); + } + } else { + record.dump(log); + + DataExtractor my_extractor((const void *)record.m_host_address, + record.m_size, lldb::eByteOrderBig, 8); + my_extractor.PutToLog(log, 0, record.m_size, record.m_host_address, 16, + DataExtractor::TypeUInt8); + } + } + } + + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; +} + +IRExecutionUnit::~IRExecutionUnit() { + m_module_up.reset(); + m_execution_engine_up.reset(); + m_context_up.reset(); +} + +IRExecutionUnit::MemoryManager::MemoryManager(IRExecutionUnit &parent) + : m_default_mm_up(new llvm::SectionMemoryManager()), m_parent(parent) {} + +IRExecutionUnit::MemoryManager::~MemoryManager() = default; + +lldb::SectionType IRExecutionUnit::GetSectionTypeFromSectionName( + const llvm::StringRef &name, IRExecutionUnit::AllocationKind alloc_kind) { + lldb::SectionType sect_type = lldb::eSectionTypeCode; + switch (alloc_kind) { + case AllocationKind::Stub: + sect_type = lldb::eSectionTypeCode; + break; + case AllocationKind::Code: + sect_type = lldb::eSectionTypeCode; + break; + case AllocationKind::Data: + sect_type = lldb::eSectionTypeData; + break; + case AllocationKind::Global: + sect_type = lldb::eSectionTypeData; + break; + case AllocationKind::Bytes: + sect_type = lldb::eSectionTypeOther; + break; + } + + if (!name.empty()) { + if (name == "__text" || name == ".text") + sect_type = lldb::eSectionTypeCode; + else if (name == "__data" || name == ".data") + sect_type = lldb::eSectionTypeCode; + else if (name.starts_with("__debug_") || name.starts_with(".debug_")) { + const uint32_t name_idx = name[0] == '_' ? 8 : 7; + llvm::StringRef dwarf_name(name.substr(name_idx)); + switch (dwarf_name[0]) { + case 'a': + if (dwarf_name == "abbrev") + sect_type = lldb::eSectionTypeDWARFDebugAbbrev; + else if (dwarf_name == "aranges") + sect_type = lldb::eSectionTypeDWARFDebugAranges; + else if (dwarf_name == "addr") + sect_type = lldb::eSectionTypeDWARFDebugAddr; + break; + + case 'f': + if (dwarf_name == "frame") + sect_type = lldb::eSectionTypeDWARFDebugFrame; + break; + + case 'i': + if (dwarf_name == "info") + sect_type = lldb::eSectionTypeDWARFDebugInfo; + break; + + case 'l': + if (dwarf_name == "line") + sect_type = lldb::eSectionTypeDWARFDebugLine; + else if (dwarf_name == "loc") + sect_type = lldb::eSectionTypeDWARFDebugLoc; + else if (dwarf_name == "loclists") + sect_type = lldb::eSectionTypeDWARFDebugLocLists; + break; + + case 'm': + if (dwarf_name == "macinfo") + sect_type = lldb::eSectionTypeDWARFDebugMacInfo; + break; + + case 'p': + if (dwarf_name == "pubnames") + sect_type = lldb::eSectionTypeDWARFDebugPubNames; + else if (dwarf_name == "pubtypes") + sect_type = lldb::eSectionTypeDWARFDebugPubTypes; + break; + + case 's': + if (dwarf_name == "str") + sect_type = lldb::eSectionTypeDWARFDebugStr; + else if (dwarf_name == "str_offsets") + sect_type = lldb::eSectionTypeDWARFDebugStrOffsets; + break; + + case 'r': + if (dwarf_name == "ranges") + sect_type = lldb::eSectionTypeDWARFDebugRanges; + break; + + default: + break; + } + } else if (name.starts_with("__apple_") || name.starts_with(".apple_")) + sect_type = lldb::eSectionTypeInvalid; + else if (name == "__objc_imageinfo") + sect_type = lldb::eSectionTypeOther; + } + return sect_type; +} + +uint8_t *IRExecutionUnit::MemoryManager::allocateCodeSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, + llvm::StringRef SectionName) { + Log *log = GetLog(LLDBLog::Expressions); + + uint8_t *return_value = m_default_mm_up->allocateCodeSection( + Size, Alignment, SectionID, SectionName); + + m_parent.m_records.push_back(AllocationRecord( + (uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsExecutable, + GetSectionTypeFromSectionName(SectionName, AllocationKind::Code), Size, + Alignment, SectionID, SectionName.str().c_str())); + + LLDB_LOGF(log, + "IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64 + ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, (void *)return_value); + + if (m_parent.m_reported_allocations) { + Status err; + lldb::ProcessSP process_sp = + m_parent.GetBestExecutionContextScope()->CalculateProcess(); + + m_parent.CommitOneAllocation(process_sp, err, m_parent.m_records.back()); + } + + return return_value; +} + +uint8_t *IRExecutionUnit::MemoryManager::allocateDataSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, + llvm::StringRef SectionName, bool IsReadOnly) { + Log *log = GetLog(LLDBLog::Expressions); + + uint8_t *return_value = m_default_mm_up->allocateDataSection( + Size, Alignment, SectionID, SectionName, IsReadOnly); + + uint32_t permissions = lldb::ePermissionsReadable; + if (!IsReadOnly) + permissions |= lldb::ePermissionsWritable; + m_parent.m_records.push_back(AllocationRecord( + (uintptr_t)return_value, permissions, + GetSectionTypeFromSectionName(SectionName, AllocationKind::Data), Size, + Alignment, SectionID, SectionName.str().c_str())); + LLDB_LOGF(log, + "IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64 + ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, (void *)return_value); + + if (m_parent.m_reported_allocations) { + Status err; + lldb::ProcessSP process_sp = + m_parent.GetBestExecutionContextScope()->CalculateProcess(); + + m_parent.CommitOneAllocation(process_sp, err, m_parent.m_records.back()); + } + + return return_value; +} + +void IRExecutionUnit::CollectCandidateCNames(std::vector<ConstString> &C_names, + ConstString name) { + if (m_strip_underscore && name.AsCString()[0] == '_') + C_names.insert(C_names.begin(), ConstString(&name.AsCString()[1])); + C_names.push_back(name); +} + +void IRExecutionUnit::CollectCandidateCPlusPlusNames( + std::vector<ConstString> &CPP_names, + const std::vector<ConstString> &C_names, const SymbolContext &sc) { + if (auto *cpp_lang = Language::FindPlugin(lldb::eLanguageTypeC_plus_plus)) { + for (const ConstString &name : C_names) { + Mangled mangled(name); + if (cpp_lang->SymbolNameFitsToLanguage(mangled)) { + if (ConstString best_alternate = + cpp_lang->FindBestAlternateFunctionMangledName(mangled, sc)) { + CPP_names.push_back(best_alternate); + } + } + + std::vector<ConstString> alternates = + cpp_lang->GenerateAlternateFunctionManglings(name); + CPP_names.insert(CPP_names.end(), alternates.begin(), alternates.end()); + + // As a last-ditch fallback, try the base name for C++ names. It's + // terrible, but the DWARF doesn't always encode "extern C" correctly. + ConstString basename = + cpp_lang->GetDemangledFunctionNameWithoutArguments(mangled); + CPP_names.push_back(basename); + } + } +} + +class LoadAddressResolver { +public: + LoadAddressResolver(Target *target, bool &symbol_was_missing_weak) + : m_target(target), m_symbol_was_missing_weak(symbol_was_missing_weak) {} + + std::optional<lldb::addr_t> Resolve(SymbolContextList &sc_list) { + if (sc_list.IsEmpty()) + return std::nullopt; + + lldb::addr_t load_address = LLDB_INVALID_ADDRESS; + + // Missing_weak_symbol will be true only if we found only weak undefined + // references to this symbol. + m_symbol_was_missing_weak = true; + + for (auto candidate_sc : sc_list.SymbolContexts()) { + // Only symbols can be weak undefined. + if (!candidate_sc.symbol || + candidate_sc.symbol->GetType() != lldb::eSymbolTypeUndefined || + !candidate_sc.symbol->IsWeak()) + m_symbol_was_missing_weak = false; + + // First try the symbol. + if (candidate_sc.symbol) { + load_address = candidate_sc.symbol->ResolveCallableAddress(*m_target); + if (load_address == LLDB_INVALID_ADDRESS) { + Address addr = candidate_sc.symbol->GetAddress(); + load_address = m_target->GetProcessSP() + ? addr.GetLoadAddress(m_target) + : addr.GetFileAddress(); + } + } + + // If that didn't work, try the function. + if (load_address == LLDB_INVALID_ADDRESS && candidate_sc.function) { + Address addr = + candidate_sc.function->GetAddressRange().GetBaseAddress(); + load_address = m_target->GetProcessSP() ? addr.GetLoadAddress(m_target) + : addr.GetFileAddress(); + } + + // We found a load address. + if (load_address != LLDB_INVALID_ADDRESS) { + // If the load address is external, we're done. + const bool is_external = + (candidate_sc.function) || + (candidate_sc.symbol && candidate_sc.symbol->IsExternal()); + if (is_external) + return load_address; + + // Otherwise, remember the best internal load address. + if (m_best_internal_load_address == LLDB_INVALID_ADDRESS) + m_best_internal_load_address = load_address; + } + } + + // You test the address of a weak symbol against NULL to see if it is + // present. So we should return 0 for a missing weak symbol. + if (m_symbol_was_missing_weak) + return 0; + + return std::nullopt; + } + + lldb::addr_t GetBestInternalLoadAddress() const { + return m_best_internal_load_address; + } + +private: + Target *m_target; + bool &m_symbol_was_missing_weak; + lldb::addr_t m_best_internal_load_address = LLDB_INVALID_ADDRESS; +}; + +lldb::addr_t +IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names, + const lldb_private::SymbolContext &sc, + bool &symbol_was_missing_weak) { + symbol_was_missing_weak = false; + + Target *target = sc.target_sp.get(); + if (!target) { + // We shouldn't be doing any symbol lookup at all without a target. + return LLDB_INVALID_ADDRESS; + } + + LoadAddressResolver resolver(target, symbol_was_missing_weak); + + ModuleFunctionSearchOptions function_options; + function_options.include_symbols = true; + function_options.include_inlines = false; + + for (const ConstString &name : names) { + if (sc.module_sp) { + SymbolContextList sc_list; + sc.module_sp->FindFunctions(name, CompilerDeclContext(), + lldb::eFunctionNameTypeFull, function_options, + sc_list); + if (auto load_addr = resolver.Resolve(sc_list)) + return *load_addr; + } + + if (sc.target_sp) { + SymbolContextList sc_list; + sc.target_sp->GetImages().FindFunctions(name, lldb::eFunctionNameTypeFull, + function_options, sc_list); + if (auto load_addr = resolver.Resolve(sc_list)) + return *load_addr; + } + + if (sc.target_sp) { + SymbolContextList sc_list; + sc.target_sp->GetImages().FindSymbolsWithNameAndType( + name, lldb::eSymbolTypeAny, sc_list); + if (auto load_addr = resolver.Resolve(sc_list)) + return *load_addr; + } + + lldb::addr_t best_internal_load_address = + resolver.GetBestInternalLoadAddress(); + if (best_internal_load_address != LLDB_INVALID_ADDRESS) + return best_internal_load_address; + } + + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +IRExecutionUnit::FindInRuntimes(const std::vector<ConstString> &names, + const lldb_private::SymbolContext &sc) { + lldb::TargetSP target_sp = sc.target_sp; + + if (!target_sp) { + return LLDB_INVALID_ADDRESS; + } + + lldb::ProcessSP process_sp = sc.target_sp->GetProcessSP(); + + if (!process_sp) { + return LLDB_INVALID_ADDRESS; + } + + for (const ConstString &name : names) { + for (LanguageRuntime *runtime : process_sp->GetLanguageRuntimes()) { + lldb::addr_t symbol_load_addr = runtime->LookupRuntimeSymbol(name); + + if (symbol_load_addr != LLDB_INVALID_ADDRESS) + return symbol_load_addr; + } + } + + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t IRExecutionUnit::FindInUserDefinedSymbols( + const std::vector<ConstString> &names, + const lldb_private::SymbolContext &sc) { + lldb::TargetSP target_sp = sc.target_sp; + + for (const ConstString &name : names) { + lldb::addr_t symbol_load_addr = target_sp->GetPersistentSymbol(name); + + if (symbol_load_addr != LLDB_INVALID_ADDRESS) + return symbol_load_addr; + } + + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name, + bool &missing_weak) { + std::vector<ConstString> candidate_C_names; + std::vector<ConstString> candidate_CPlusPlus_names; + + CollectCandidateCNames(candidate_C_names, name); + + lldb::addr_t ret = FindInSymbols(candidate_C_names, m_sym_ctx, missing_weak); + if (ret != LLDB_INVALID_ADDRESS) + return ret; + + // If we find the symbol in runtimes or user defined symbols it can't be + // a missing weak symbol. + missing_weak = false; + ret = FindInRuntimes(candidate_C_names, m_sym_ctx); + if (ret != LLDB_INVALID_ADDRESS) + return ret; + + ret = FindInUserDefinedSymbols(candidate_C_names, m_sym_ctx); + if (ret != LLDB_INVALID_ADDRESS) + return ret; + + CollectCandidateCPlusPlusNames(candidate_CPlusPlus_names, candidate_C_names, + m_sym_ctx); + ret = FindInSymbols(candidate_CPlusPlus_names, m_sym_ctx, missing_weak); + return ret; +} + +void IRExecutionUnit::GetStaticInitializers( + std::vector<lldb::addr_t> &static_initializers) { + Log *log = GetLog(LLDBLog::Expressions); + + llvm::GlobalVariable *global_ctors = + m_module->getNamedGlobal("llvm.global_ctors"); + if (!global_ctors) { + LLDB_LOG(log, "Couldn't find llvm.global_ctors."); + return; + } + auto *ctor_array = + llvm::dyn_cast<llvm::ConstantArray>(global_ctors->getInitializer()); + if (!ctor_array) { + LLDB_LOG(log, "llvm.global_ctors not a ConstantArray."); + return; + } + + for (llvm::Use &ctor_use : ctor_array->operands()) { + auto *ctor_struct = llvm::dyn_cast<llvm::ConstantStruct>(ctor_use); + if (!ctor_struct) + continue; + // this is standardized + lldbassert(ctor_struct->getNumOperands() == 3); + auto *ctor_function = + llvm::dyn_cast<llvm::Function>(ctor_struct->getOperand(1)); + if (!ctor_function) { + LLDB_LOG(log, "global_ctor doesn't contain an llvm::Function"); + continue; + } + + ConstString ctor_function_name(ctor_function->getName().str()); + LLDB_LOG(log, "Looking for callable jitted function with name {0}.", + ctor_function_name); + + for (JittedFunction &jitted_function : m_jitted_functions) { + if (ctor_function_name != jitted_function.m_name) + continue; + if (jitted_function.m_remote_addr == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "Found jitted function with invalid address."); + continue; + } + static_initializers.push_back(jitted_function.m_remote_addr); + LLDB_LOG(log, "Calling function at address {0:x}.", + jitted_function.m_remote_addr); + break; + } + } +} + +llvm::JITSymbol +IRExecutionUnit::MemoryManager::findSymbol(const std::string &Name) { + bool missing_weak = false; + uint64_t addr = GetSymbolAddressAndPresence(Name, missing_weak); + // This is a weak symbol: + if (missing_weak) + return llvm::JITSymbol(addr, + llvm::JITSymbolFlags::Exported | llvm::JITSymbolFlags::Weak); + else + return llvm::JITSymbol(addr, llvm::JITSymbolFlags::Exported); +} + +uint64_t +IRExecutionUnit::MemoryManager::getSymbolAddress(const std::string &Name) { + bool missing_weak = false; + return GetSymbolAddressAndPresence(Name, missing_weak); +} + +uint64_t +IRExecutionUnit::MemoryManager::GetSymbolAddressAndPresence( + const std::string &Name, bool &missing_weak) { + Log *log = GetLog(LLDBLog::Expressions); + + ConstString name_cs(Name.c_str()); + + lldb::addr_t ret = m_parent.FindSymbol(name_cs, missing_weak); + + if (ret == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "IRExecutionUnit::getSymbolAddress(Name=\"%s\") = <not found>", + Name.c_str()); + + m_parent.ReportSymbolLookupError(name_cs); + return 0; + } else { + LLDB_LOGF(log, "IRExecutionUnit::getSymbolAddress(Name=\"%s\") = %" PRIx64, + Name.c_str(), ret); + return ret; + } +} + +void *IRExecutionUnit::MemoryManager::getPointerToNamedFunction( + const std::string &Name, bool AbortOnFailure) { + return (void *)getSymbolAddress(Name); +} + +lldb::addr_t +IRExecutionUnit::GetRemoteAddressForLocal(lldb::addr_t local_address) { + Log *log = GetLog(LLDBLog::Expressions); + + for (AllocationRecord &record : m_records) { + if (local_address >= record.m_host_address && + local_address < record.m_host_address + record.m_size) { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = + record.m_process_address + (local_address - record.m_host_address); + + LLDB_LOGF(log, + "IRExecutionUnit::GetRemoteAddressForLocal() found 0x%" PRIx64 + " in [0x%" PRIx64 "..0x%" PRIx64 "], and returned 0x%" PRIx64 + " from [0x%" PRIx64 "..0x%" PRIx64 "].", + local_address, (uint64_t)record.m_host_address, + (uint64_t)record.m_host_address + (uint64_t)record.m_size, ret, + record.m_process_address, + record.m_process_address + record.m_size); + + return ret; + } + } + + return LLDB_INVALID_ADDRESS; +} + +IRExecutionUnit::AddrRange +IRExecutionUnit::GetRemoteRangeForLocal(lldb::addr_t local_address) { + for (AllocationRecord &record : m_records) { + if (local_address >= record.m_host_address && + local_address < record.m_host_address + record.m_size) { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return AddrRange(0, 0); + + return AddrRange(record.m_process_address, record.m_size); + } + } + + return AddrRange(0, 0); +} + +bool IRExecutionUnit::CommitOneAllocation(lldb::ProcessSP &process_sp, + Status &error, + AllocationRecord &record) { + if (record.m_process_address != LLDB_INVALID_ADDRESS) { + return true; + } + + switch (record.m_sect_type) { + case lldb::eSectionTypeInvalid: + case lldb::eSectionTypeDWARFDebugAbbrev: + case lldb::eSectionTypeDWARFDebugAddr: + case lldb::eSectionTypeDWARFDebugAranges: + case lldb::eSectionTypeDWARFDebugCuIndex: + case lldb::eSectionTypeDWARFDebugFrame: + case lldb::eSectionTypeDWARFDebugInfo: + case lldb::eSectionTypeDWARFDebugLine: + case lldb::eSectionTypeDWARFDebugLoc: + case lldb::eSectionTypeDWARFDebugLocLists: + case lldb::eSectionTypeDWARFDebugMacInfo: + case lldb::eSectionTypeDWARFDebugPubNames: + case lldb::eSectionTypeDWARFDebugPubTypes: + case lldb::eSectionTypeDWARFDebugRanges: + case lldb::eSectionTypeDWARFDebugStr: + case lldb::eSectionTypeDWARFDebugStrOffsets: + case lldb::eSectionTypeDWARFAppleNames: + case lldb::eSectionTypeDWARFAppleTypes: + case lldb::eSectionTypeDWARFAppleNamespaces: + case lldb::eSectionTypeDWARFAppleObjC: + case lldb::eSectionTypeDWARFGNUDebugAltLink: + error.Clear(); + break; + default: + const bool zero_memory = false; + record.m_process_address = + Malloc(record.m_size, record.m_alignment, record.m_permissions, + eAllocationPolicyProcessOnly, zero_memory, error); + break; + } + + return error.Success(); +} + +bool IRExecutionUnit::CommitAllocations(lldb::ProcessSP &process_sp) { + bool ret = true; + + lldb_private::Status err; + + for (AllocationRecord &record : m_records) { + ret = CommitOneAllocation(process_sp, err, record); + + if (!ret) { + break; + } + } + + if (!ret) { + for (AllocationRecord &record : m_records) { + if (record.m_process_address != LLDB_INVALID_ADDRESS) { + Free(record.m_process_address, err); + record.m_process_address = LLDB_INVALID_ADDRESS; + } + } + } + + return ret; +} + +void IRExecutionUnit::ReportAllocations(llvm::ExecutionEngine &engine) { + m_reported_allocations = true; + + for (AllocationRecord &record : m_records) { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + continue; + + if (record.m_section_id == eSectionIDInvalid) + continue; + + engine.mapSectionAddress((void *)record.m_host_address, + record.m_process_address); + } + + // Trigger re-application of relocations. + engine.finalizeObject(); +} + +bool IRExecutionUnit::WriteData(lldb::ProcessSP &process_sp) { + bool wrote_something = false; + for (AllocationRecord &record : m_records) { + if (record.m_process_address != LLDB_INVALID_ADDRESS) { + lldb_private::Status err; + WriteMemory(record.m_process_address, (uint8_t *)record.m_host_address, + record.m_size, err); + if (err.Success()) + wrote_something = true; + } + } + return wrote_something; +} + +void IRExecutionUnit::AllocationRecord::dump(Log *log) { + if (!log) + return; + + LLDB_LOGF(log, + "[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d, name %s)", + (unsigned long long)m_host_address, (unsigned long long)m_size, + (unsigned long long)m_process_address, (unsigned)m_alignment, + (unsigned)m_section_id, m_name.c_str()); +} + +lldb::ByteOrder IRExecutionUnit::GetByteOrder() const { + ExecutionContext exe_ctx(GetBestExecutionContextScope()); + return exe_ctx.GetByteOrder(); +} + +uint32_t IRExecutionUnit::GetAddressByteSize() const { + ExecutionContext exe_ctx(GetBestExecutionContextScope()); + return exe_ctx.GetAddressByteSize(); +} + +void IRExecutionUnit::PopulateSymtab(lldb_private::ObjectFile *obj_file, + lldb_private::Symtab &symtab) { + // No symbols yet... +} + +void IRExecutionUnit::PopulateSectionList( + lldb_private::ObjectFile *obj_file, + lldb_private::SectionList §ion_list) { + for (AllocationRecord &record : m_records) { + if (record.m_size > 0) { + lldb::SectionSP section_sp(new lldb_private::Section( + obj_file->GetModule(), obj_file, record.m_section_id, + ConstString(record.m_name), record.m_sect_type, + record.m_process_address, record.m_size, + record.m_host_address, // file_offset (which is the host address for + // the data) + record.m_size, // file_size + 0, + record.m_permissions)); // flags + section_list.AddSection(section_sp); + } + } +} + +ArchSpec IRExecutionUnit::GetArchitecture() { + ExecutionContext exe_ctx(GetBestExecutionContextScope()); + if(Target *target = exe_ctx.GetTargetPtr()) + return target->GetArchitecture(); + return ArchSpec(); +} + +lldb::ModuleSP IRExecutionUnit::GetJITModule() { + ExecutionContext exe_ctx(GetBestExecutionContextScope()); + Target *target = exe_ctx.GetTargetPtr(); + if (!target) + return nullptr; + + auto Delegate = std::static_pointer_cast<lldb_private::ObjectFileJITDelegate>( + shared_from_this()); + + lldb::ModuleSP jit_module_sp = + lldb_private::Module::CreateModuleFromObjectFile<ObjectFileJIT>(Delegate); + if (!jit_module_sp) + return nullptr; + + bool changed = false; + jit_module_sp->SetLoadAddress(*target, 0, true, changed); + return jit_module_sp; +} diff --git a/contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp b/contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp new file mode 100644 index 000000000000..5b670067b5c4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp @@ -0,0 +1,1605 @@ +//===-- IRInterpreter.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunctionUsingABI.h" + +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/Support/raw_ostream.h" + +#include <map> + +using namespace llvm; +using lldb_private::LLDBLog; + +static std::string PrintValue(const Value *value, bool truncate = false) { + std::string s; + raw_string_ostream rso(s); + value->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + + size_t offset; + while ((offset = s.find('\n')) != s.npos) + s.erase(offset, 1); + while (s[0] == ' ' || s[0] == '\t') + s.erase(0, 1); + + return s; +} + +static std::string PrintType(const Type *type, bool truncate = false) { + std::string s; + raw_string_ostream rso(s); + type->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +static bool CanIgnoreCall(const CallInst *call) { + const llvm::Function *called_function = call->getCalledFunction(); + + if (!called_function) + return false; + + if (called_function->isIntrinsic()) { + switch (called_function->getIntrinsicID()) { + default: + break; + case llvm::Intrinsic::dbg_declare: + case llvm::Intrinsic::dbg_value: + return true; + } + } + + return false; +} + +class InterpreterStackFrame { +public: + typedef std::map<const Value *, lldb::addr_t> ValueMap; + + ValueMap m_values; + DataLayout &m_target_data; + lldb_private::IRExecutionUnit &m_execution_unit; + const BasicBlock *m_bb = nullptr; + const BasicBlock *m_prev_bb = nullptr; + BasicBlock::const_iterator m_ii; + BasicBlock::const_iterator m_ie; + + lldb::addr_t m_frame_process_address; + size_t m_frame_size; + lldb::addr_t m_stack_pointer; + + lldb::ByteOrder m_byte_order; + size_t m_addr_byte_size; + + InterpreterStackFrame(DataLayout &target_data, + lldb_private::IRExecutionUnit &execution_unit, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) + : m_target_data(target_data), m_execution_unit(execution_unit) { + m_byte_order = (target_data.isLittleEndian() ? lldb::eByteOrderLittle + : lldb::eByteOrderBig); + m_addr_byte_size = (target_data.getPointerSize(0)); + + m_frame_process_address = stack_frame_bottom; + m_frame_size = stack_frame_top - stack_frame_bottom; + m_stack_pointer = stack_frame_top; + } + + ~InterpreterStackFrame() = default; + + void Jump(const BasicBlock *bb) { + m_prev_bb = m_bb; + m_bb = bb; + m_ii = m_bb->begin(); + m_ie = m_bb->end(); + } + + std::string SummarizeValue(const Value *value) { + lldb_private::StreamString ss; + + ss.Printf("%s", PrintValue(value).c_str()); + + ValueMap::iterator i = m_values.find(value); + + if (i != m_values.end()) { + lldb::addr_t addr = i->second; + + ss.Printf(" 0x%llx", (unsigned long long)addr); + } + + return std::string(ss.GetString()); + } + + bool AssignToMatchType(lldb_private::Scalar &scalar, llvm::APInt value, + Type *type) { + size_t type_size = m_target_data.getTypeStoreSize(type); + + if (type_size > 8) + return false; + + if (type_size != 1) + type_size = PowerOf2Ceil(type_size); + + scalar = value.zextOrTrunc(type_size * 8); + return true; + } + + bool EvaluateValue(lldb_private::Scalar &scalar, const Value *value, + Module &module) { + const Constant *constant = dyn_cast<Constant>(value); + + if (constant) { + if (constant->getValueID() == Value::ConstantFPVal) { + if (auto *cfp = dyn_cast<ConstantFP>(constant)) { + if (cfp->getType()->isDoubleTy()) + scalar = cfp->getValueAPF().convertToDouble(); + else if (cfp->getType()->isFloatTy()) + scalar = cfp->getValueAPF().convertToFloat(); + else + return false; + return true; + } + return false; + } + APInt value_apint; + + if (!ResolveConstantValue(value_apint, constant)) + return false; + + return AssignToMatchType(scalar, value_apint, value->getType()); + } + + lldb::addr_t process_address = ResolveValue(value, module); + size_t value_size = m_target_data.getTypeStoreSize(value->getType()); + + lldb_private::DataExtractor value_extractor; + lldb_private::Status extract_error; + + m_execution_unit.GetMemoryData(value_extractor, process_address, + value_size, extract_error); + + if (!extract_error.Success()) + return false; + + lldb::offset_t offset = 0; + if (value_size <= 8) { + Type *ty = value->getType(); + if (ty->isDoubleTy()) { + scalar = value_extractor.GetDouble(&offset); + return true; + } else if (ty->isFloatTy()) { + scalar = value_extractor.GetFloat(&offset); + return true; + } else { + uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size); + return AssignToMatchType(scalar, llvm::APInt(64, u64value), + value->getType()); + } + } + + return false; + } + + bool AssignValue(const Value *value, lldb_private::Scalar scalar, + Module &module) { + lldb::addr_t process_address = ResolveValue(value, module); + + if (process_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Scalar cast_scalar; + Type *vty = value->getType(); + if (vty->isFloatTy() || vty->isDoubleTy()) { + cast_scalar = scalar; + } else { + scalar.MakeUnsigned(); + if (!AssignToMatchType(cast_scalar, scalar.UInt128(llvm::APInt()), + value->getType())) + return false; + } + + size_t value_byte_size = m_target_data.getTypeStoreSize(value->getType()); + + lldb_private::DataBufferHeap buf(value_byte_size, 0); + + lldb_private::Status get_data_error; + + if (!cast_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), + m_byte_order, get_data_error)) + return false; + + lldb_private::Status write_error; + + m_execution_unit.WriteMemory(process_address, buf.GetBytes(), + buf.GetByteSize(), write_error); + + return write_error.Success(); + } + + bool ResolveConstantValue(APInt &value, const Constant *constant) { + switch (constant->getValueID()) { + default: + break; + case Value::FunctionVal: + if (const Function *constant_func = dyn_cast<Function>(constant)) { + lldb_private::ConstString name(constant_func->getName()); + bool missing_weak = false; + lldb::addr_t addr = m_execution_unit.FindSymbol(name, missing_weak); + if (addr == LLDB_INVALID_ADDRESS) + return false; + value = APInt(m_target_data.getPointerSizeInBits(), addr); + return true; + } + break; + case Value::ConstantIntVal: + if (const ConstantInt *constant_int = dyn_cast<ConstantInt>(constant)) { + value = constant_int->getValue(); + return true; + } + break; + case Value::ConstantFPVal: + if (const ConstantFP *constant_fp = dyn_cast<ConstantFP>(constant)) { + value = constant_fp->getValueAPF().bitcastToAPInt(); + return true; + } + break; + case Value::ConstantExprVal: + if (const ConstantExpr *constant_expr = + dyn_cast<ConstantExpr>(constant)) { + switch (constant_expr->getOpcode()) { + default: + return false; + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::BitCast: + return ResolveConstantValue(value, constant_expr->getOperand(0)); + case Instruction::GetElementPtr: { + ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin(); + ConstantExpr::const_op_iterator op_end = constant_expr->op_end(); + + Constant *base = dyn_cast<Constant>(*op_cursor); + + if (!base) + return false; + + if (!ResolveConstantValue(value, base)) + return false; + + op_cursor++; + + if (op_cursor == op_end) + return true; // no offset to apply! + + SmallVector<Value *, 8> indices(op_cursor, op_end); + Type *src_elem_ty = + cast<GEPOperator>(constant_expr)->getSourceElementType(); + + // DataLayout::getIndexedOffsetInType assumes the indices are + // instances of ConstantInt. + uint64_t offset = + m_target_data.getIndexedOffsetInType(src_elem_ty, indices); + + const bool is_signed = true; + value += APInt(value.getBitWidth(), offset, is_signed); + + return true; + } + } + } + break; + case Value::ConstantPointerNullVal: + if (isa<ConstantPointerNull>(constant)) { + value = APInt(m_target_data.getPointerSizeInBits(), 0); + return true; + } + break; + } + return false; + } + + bool MakeArgument(const Argument *value, uint64_t address) { + lldb::addr_t data_address = Malloc(value->getType()); + + if (data_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Status write_error; + + m_execution_unit.WritePointerToMemory(data_address, address, write_error); + + if (!write_error.Success()) { + lldb_private::Status free_error; + m_execution_unit.Free(data_address, free_error); + return false; + } + + m_values[value] = data_address; + + lldb_private::Log *log(GetLog(LLDBLog::Expressions)); + + if (log) { + LLDB_LOGF(log, "Made an allocation for argument %s", + PrintValue(value).c_str()); + LLDB_LOGF(log, " Data region : %llx", (unsigned long long)address); + LLDB_LOGF(log, " Ref region : %llx", + (unsigned long long)data_address); + } + + return true; + } + + bool ResolveConstant(lldb::addr_t process_address, const Constant *constant) { + APInt resolved_value; + + if (!ResolveConstantValue(resolved_value, constant)) + return false; + + size_t constant_size = m_target_data.getTypeStoreSize(constant->getType()); + lldb_private::DataBufferHeap buf(constant_size, 0); + + lldb_private::Status get_data_error; + + lldb_private::Scalar resolved_scalar( + resolved_value.zextOrTrunc(llvm::NextPowerOf2(constant_size) * 8)); + if (!resolved_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), + m_byte_order, get_data_error)) + return false; + + lldb_private::Status write_error; + + m_execution_unit.WriteMemory(process_address, buf.GetBytes(), + buf.GetByteSize(), write_error); + + return write_error.Success(); + } + + lldb::addr_t Malloc(size_t size, uint8_t byte_alignment) { + lldb::addr_t ret = m_stack_pointer; + + ret -= size; + ret -= (ret % byte_alignment); + + if (ret < m_frame_process_address) + return LLDB_INVALID_ADDRESS; + + m_stack_pointer = ret; + return ret; + } + + lldb::addr_t Malloc(llvm::Type *type) { + lldb_private::Status alloc_error; + + return Malloc(m_target_data.getTypeAllocSize(type), + m_target_data.getPrefTypeAlign(type).value()); + } + + std::string PrintData(lldb::addr_t addr, llvm::Type *type) { + size_t length = m_target_data.getTypeStoreSize(type); + + lldb_private::DataBufferHeap buf(length, 0); + + lldb_private::Status read_error; + + m_execution_unit.ReadMemory(buf.GetBytes(), addr, length, read_error); + + if (!read_error.Success()) + return std::string("<couldn't read data>"); + + lldb_private::StreamString ss; + + for (size_t i = 0; i < length; i++) { + if ((!(i & 0xf)) && i) + ss.Printf("%02hhx - ", buf.GetBytes()[i]); + else + ss.Printf("%02hhx ", buf.GetBytes()[i]); + } + + return std::string(ss.GetString()); + } + + lldb::addr_t ResolveValue(const Value *value, Module &module) { + ValueMap::iterator i = m_values.find(value); + + if (i != m_values.end()) + return i->second; + + // Fall back and allocate space [allocation type Alloca] + + lldb::addr_t data_address = Malloc(value->getType()); + + if (const Constant *constant = dyn_cast<Constant>(value)) { + if (!ResolveConstant(data_address, constant)) { + lldb_private::Status free_error; + m_execution_unit.Free(data_address, free_error); + return LLDB_INVALID_ADDRESS; + } + } + + m_values[value] = data_address; + return data_address; + } +}; + +static const char *unsupported_opcode_error = + "Interpreter doesn't handle one of the expression's opcodes"; +static const char *unsupported_operand_error = + "Interpreter doesn't handle one of the expression's operands"; +static const char *interpreter_internal_error = + "Interpreter encountered an internal error"; +static const char *interrupt_error = + "Interrupted while interpreting expression"; +static const char *bad_value_error = + "Interpreter couldn't resolve a value during execution"; +static const char *memory_allocation_error = + "Interpreter couldn't allocate memory"; +static const char *memory_write_error = "Interpreter couldn't write to memory"; +static const char *memory_read_error = "Interpreter couldn't read from memory"; +static const char *timeout_error = + "Reached timeout while interpreting expression"; +static const char *too_many_functions_error = + "Interpreter doesn't handle modules with multiple function bodies."; + +static bool CanResolveConstant(llvm::Constant *constant) { + switch (constant->getValueID()) { + default: + return false; + case Value::ConstantIntVal: + case Value::ConstantFPVal: + case Value::FunctionVal: + return true; + case Value::ConstantExprVal: + if (const ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) { + switch (constant_expr->getOpcode()) { + default: + return false; + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::BitCast: + return CanResolveConstant(constant_expr->getOperand(0)); + case Instruction::GetElementPtr: { + // Check that the base can be constant-resolved. + ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin(); + Constant *base = dyn_cast<Constant>(*op_cursor); + if (!base || !CanResolveConstant(base)) + return false; + + // Check that all other operands are just ConstantInt. + for (Value *op : make_range(constant_expr->op_begin() + 1, + constant_expr->op_end())) { + ConstantInt *constant_int = dyn_cast<ConstantInt>(op); + if (!constant_int) + return false; + } + return true; + } + } + } else { + return false; + } + case Value::ConstantPointerNullVal: + return true; + } +} + +bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function, + lldb_private::Status &error, + const bool support_function_calls) { + lldb_private::Log *log(GetLog(LLDBLog::Expressions)); + + bool saw_function_with_body = false; + for (Function &f : module) { + if (f.begin() != f.end()) { + if (saw_function_with_body) { + LLDB_LOGF(log, "More than one function in the module has a body"); + error.SetErrorToGenericError(); + error.SetErrorString(too_many_functions_error); + return false; + } + saw_function_with_body = true; + LLDB_LOGF(log, "Saw function with body: %s", f.getName().str().c_str()); + } + } + + for (BasicBlock &bb : function) { + for (Instruction &ii : bb) { + switch (ii.getOpcode()) { + default: { + LLDB_LOGF(log, "Unsupported instruction: %s", PrintValue(&ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + case Instruction::Add: + case Instruction::Alloca: + case Instruction::BitCast: + case Instruction::Br: + case Instruction::PHI: + break; + case Instruction::Call: { + CallInst *call_inst = dyn_cast<CallInst>(&ii); + + if (!call_inst) { + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + if (!CanIgnoreCall(call_inst) && !support_function_calls) { + LLDB_LOGF(log, "Unsupported instruction: %s", + PrintValue(&ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + } break; + case Instruction::GetElementPtr: + break; + case Instruction::FCmp: + case Instruction::ICmp: { + CmpInst *cmp_inst = dyn_cast<CmpInst>(&ii); + + if (!cmp_inst) { + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + switch (cmp_inst->getPredicate()) { + default: { + LLDB_LOGF(log, "Unsupported ICmp predicate: %s", + PrintValue(&ii).c_str()); + + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + case CmpInst::FCMP_OEQ: + case CmpInst::ICMP_EQ: + case CmpInst::FCMP_UNE: + case CmpInst::ICMP_NE: + case CmpInst::FCMP_OGT: + case CmpInst::ICMP_UGT: + case CmpInst::FCMP_OGE: + case CmpInst::ICMP_UGE: + case CmpInst::FCMP_OLT: + case CmpInst::ICMP_ULT: + case CmpInst::FCMP_OLE: + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_SLE: + break; + } + } break; + case Instruction::And: + case Instruction::AShr: + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::Load: + case Instruction::LShr: + case Instruction::Mul: + case Instruction::Or: + case Instruction::Ret: + case Instruction::SDiv: + case Instruction::SExt: + case Instruction::Shl: + case Instruction::SRem: + case Instruction::Store: + case Instruction::Sub: + case Instruction::Trunc: + case Instruction::UDiv: + case Instruction::URem: + case Instruction::Xor: + case Instruction::ZExt: + break; + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: + break; + } + + for (unsigned oi = 0, oe = ii.getNumOperands(); oi != oe; ++oi) { + Value *operand = ii.getOperand(oi); + Type *operand_type = operand->getType(); + + switch (operand_type->getTypeID()) { + default: + break; + case Type::FixedVectorTyID: + case Type::ScalableVectorTyID: { + LLDB_LOGF(log, "Unsupported operand type: %s", + PrintType(operand_type).c_str()); + error.SetErrorString(unsupported_operand_error); + return false; + } + } + + // The IR interpreter currently doesn't know about + // 128-bit integers. As they're not that frequent, + // we can just fall back to the JIT rather than + // choking. + if (operand_type->getPrimitiveSizeInBits() > 64) { + LLDB_LOGF(log, "Unsupported operand type: %s", + PrintType(operand_type).c_str()); + error.SetErrorString(unsupported_operand_error); + return false; + } + + if (Constant *constant = llvm::dyn_cast<Constant>(operand)) { + if (!CanResolveConstant(constant)) { + LLDB_LOGF(log, "Unsupported constant: %s", + PrintValue(constant).c_str()); + error.SetErrorString(unsupported_operand_error); + return false; + } + } + } + } + } + + return true; +} + +bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function, + llvm::ArrayRef<lldb::addr_t> args, + lldb_private::IRExecutionUnit &execution_unit, + lldb_private::Status &error, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top, + lldb_private::ExecutionContext &exe_ctx, + lldb_private::Timeout<std::micro> timeout) { + lldb_private::Log *log(GetLog(LLDBLog::Expressions)); + + if (log) { + std::string s; + raw_string_ostream oss(s); + + module.print(oss, nullptr); + + oss.flush(); + + LLDB_LOGF(log, "Module as passed in to IRInterpreter::Interpret: \n\"%s\"", + s.c_str()); + } + + DataLayout data_layout(&module); + + InterpreterStackFrame frame(data_layout, execution_unit, stack_frame_bottom, + stack_frame_top); + + if (frame.m_frame_process_address == LLDB_INVALID_ADDRESS) { + error.SetErrorString("Couldn't allocate stack frame"); + } + + int arg_index = 0; + + for (llvm::Function::arg_iterator ai = function.arg_begin(), + ae = function.arg_end(); + ai != ae; ++ai, ++arg_index) { + if (args.size() <= static_cast<size_t>(arg_index)) { + error.SetErrorString("Not enough arguments passed in to function"); + return false; + } + + lldb::addr_t ptr = args[arg_index]; + + frame.MakeArgument(&*ai, ptr); + } + + frame.Jump(&function.front()); + + lldb_private::Process *process = exe_ctx.GetProcessPtr(); + lldb_private::Target *target = exe_ctx.GetTargetPtr(); + + using clock = std::chrono::steady_clock; + + // Compute the time at which the timeout has been exceeded. + std::optional<clock::time_point> end_time; + if (timeout && timeout->count() > 0) + end_time = clock::now() + *timeout; + + while (frame.m_ii != frame.m_ie) { + // Timeout reached: stop interpreting. + if (end_time && clock::now() >= *end_time) { + error.SetErrorToGenericError(); + error.SetErrorString(timeout_error); + return false; + } + + // If we have access to the debugger we can honor an interrupt request. + if (target) { + if (INTERRUPT_REQUESTED(target->GetDebugger(), + "Interrupted in IR interpreting.")) { + error.SetErrorToGenericError(); + error.SetErrorString(interrupt_error); + return false; + } + } + + const Instruction *inst = &*frame.m_ii; + + LLDB_LOGF(log, "Interpreting %s", PrintValue(inst).c_str()); + + switch (inst->getOpcode()) { + default: + break; + + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::SDiv: + case Instruction::UDiv: + case Instruction::SRem: + case Instruction::URem: + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: { + const BinaryOperator *bin_op = dyn_cast<BinaryOperator>(inst); + + if (!bin_op) { + LLDB_LOGF( + log, + "getOpcode() returns %s, but instruction is not a BinaryOperator", + inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *lhs = inst->getOperand(0); + Value *rhs = inst->getOperand(1); + + lldb_private::Scalar L; + lldb_private::Scalar R; + + if (!frame.EvaluateValue(L, lhs, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(lhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!frame.EvaluateValue(R, rhs, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(rhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb_private::Scalar result; + + switch (inst->getOpcode()) { + default: + break; + case Instruction::Add: + case Instruction::FAdd: + result = L + R; + break; + case Instruction::Mul: + case Instruction::FMul: + result = L * R; + break; + case Instruction::Sub: + case Instruction::FSub: + result = L - R; + break; + case Instruction::SDiv: + L.MakeSigned(); + R.MakeSigned(); + result = L / R; + break; + case Instruction::UDiv: + L.MakeUnsigned(); + R.MakeUnsigned(); + result = L / R; + break; + case Instruction::FDiv: + result = L / R; + break; + case Instruction::SRem: + L.MakeSigned(); + R.MakeSigned(); + result = L % R; + break; + case Instruction::URem: + L.MakeUnsigned(); + R.MakeUnsigned(); + result = L % R; + break; + case Instruction::Shl: + result = L << R; + break; + case Instruction::AShr: + result = L >> R; + break; + case Instruction::LShr: + result = L; + result.ShiftRightLogical(R); + break; + case Instruction::And: + result = L & R; + break; + case Instruction::Or: + result = L | R; + break; + case Instruction::Xor: + result = L ^ R; + break; + } + + frame.AssignValue(inst, result, module); + + if (log) { + LLDB_LOGF(log, "Interpreted a %s", inst->getOpcodeName()); + LLDB_LOGF(log, " L : %s", frame.SummarizeValue(lhs).c_str()); + LLDB_LOGF(log, " R : %s", frame.SummarizeValue(rhs).c_str()); + LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str()); + } + } break; + case Instruction::Alloca: { + const AllocaInst *alloca_inst = cast<AllocaInst>(inst); + + if (alloca_inst->isArrayAllocation()) { + LLDB_LOGF(log, + "AllocaInsts are not handled if isArrayAllocation() is true"); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + + // The semantics of Alloca are: + // Create a region R of virtual memory of type T, backed by a data + // buffer + // Create a region P of virtual memory of type T*, backed by a data + // buffer + // Write the virtual address of R into P + + Type *T = alloca_inst->getAllocatedType(); + Type *Tptr = alloca_inst->getType(); + + lldb::addr_t R = frame.Malloc(T); + + if (R == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "Couldn't allocate memory for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_allocation_error); + return false; + } + + lldb::addr_t P = frame.Malloc(Tptr); + + if (P == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "Couldn't allocate the result pointer for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_allocation_error); + return false; + } + + lldb_private::Status write_error; + + execution_unit.WritePointerToMemory(P, R, write_error); + + if (!write_error.Success()) { + LLDB_LOGF(log, "Couldn't write the result pointer for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + lldb_private::Status free_error; + execution_unit.Free(P, free_error); + execution_unit.Free(R, free_error); + return false; + } + + frame.m_values[alloca_inst] = P; + + if (log) { + LLDB_LOGF(log, "Interpreted an AllocaInst"); + LLDB_LOGF(log, " R : 0x%" PRIx64, R); + LLDB_LOGF(log, " P : 0x%" PRIx64, P); + } + } break; + case Instruction::BitCast: + case Instruction::ZExt: { + const CastInst *cast_inst = cast<CastInst>(inst); + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, S, module); + } break; + case Instruction::SExt: { + const CastInst *cast_inst = cast<CastInst>(inst); + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + S.MakeSigned(); + + lldb_private::Scalar S_signextend(S.SLongLong()); + + frame.AssignValue(inst, S_signextend, module); + } break; + case Instruction::Br: { + const BranchInst *br_inst = cast<BranchInst>(inst); + + if (br_inst->isConditional()) { + Value *condition = br_inst->getCondition(); + + lldb_private::Scalar C; + + if (!frame.EvaluateValue(C, condition, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(condition).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!C.IsZero()) + frame.Jump(br_inst->getSuccessor(0)); + else + frame.Jump(br_inst->getSuccessor(1)); + + if (log) { + LLDB_LOGF(log, "Interpreted a BrInst with a condition"); + LLDB_LOGF(log, " cond : %s", + frame.SummarizeValue(condition).c_str()); + } + } else { + frame.Jump(br_inst->getSuccessor(0)); + + if (log) { + LLDB_LOGF(log, "Interpreted a BrInst with no condition"); + } + } + } + continue; + case Instruction::PHI: { + const PHINode *phi_inst = cast<PHINode>(inst); + if (!frame.m_prev_bb) { + LLDB_LOGF(log, + "Encountered PHI node without having jumped from another " + "basic block"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *value = phi_inst->getIncomingValueForBlock(frame.m_prev_bb); + lldb_private::Scalar result; + if (!frame.EvaluateValue(result, value, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(value).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + frame.AssignValue(inst, result, module); + + if (log) { + LLDB_LOGF(log, "Interpreted a %s", inst->getOpcodeName()); + LLDB_LOGF(log, " Incoming value : %s", + frame.SummarizeValue(value).c_str()); + } + } break; + case Instruction::GetElementPtr: { + const GetElementPtrInst *gep_inst = cast<GetElementPtrInst>(inst); + + const Value *pointer_operand = gep_inst->getPointerOperand(); + Type *src_elem_ty = gep_inst->getSourceElementType(); + + lldb_private::Scalar P; + + if (!frame.EvaluateValue(P, pointer_operand, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", + PrintValue(pointer_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + typedef SmallVector<Value *, 8> IndexVector; + typedef IndexVector::iterator IndexIterator; + + SmallVector<Value *, 8> indices(gep_inst->idx_begin(), + gep_inst->idx_end()); + + SmallVector<Value *, 8> const_indices; + + for (IndexIterator ii = indices.begin(), ie = indices.end(); ii != ie; + ++ii) { + ConstantInt *constant_index = dyn_cast<ConstantInt>(*ii); + + if (!constant_index) { + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, *ii, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(*ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + LLDB_LOGF(log, "Evaluated constant index %s as %llu", + PrintValue(*ii).c_str(), I.ULongLong(LLDB_INVALID_ADDRESS)); + + constant_index = cast<ConstantInt>(ConstantInt::get( + (*ii)->getType(), I.ULongLong(LLDB_INVALID_ADDRESS))); + } + + const_indices.push_back(constant_index); + } + + uint64_t offset = + data_layout.getIndexedOffsetInType(src_elem_ty, const_indices); + + lldb_private::Scalar Poffset = P + offset; + + frame.AssignValue(inst, Poffset, module); + + if (log) { + LLDB_LOGF(log, "Interpreted a GetElementPtrInst"); + LLDB_LOGF(log, " P : %s", + frame.SummarizeValue(pointer_operand).c_str()); + LLDB_LOGF(log, " Poffset : %s", frame.SummarizeValue(inst).c_str()); + } + } break; + case Instruction::FCmp: + case Instruction::ICmp: { + const CmpInst *icmp_inst = cast<CmpInst>(inst); + + CmpInst::Predicate predicate = icmp_inst->getPredicate(); + + Value *lhs = inst->getOperand(0); + Value *rhs = inst->getOperand(1); + + lldb_private::Scalar L; + lldb_private::Scalar R; + + if (!frame.EvaluateValue(L, lhs, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(lhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!frame.EvaluateValue(R, rhs, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(rhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb_private::Scalar result; + + switch (predicate) { + default: + return false; + case CmpInst::ICMP_EQ: + case CmpInst::FCMP_OEQ: + result = (L == R); + break; + case CmpInst::ICMP_NE: + case CmpInst::FCMP_UNE: + result = (L != R); + break; + case CmpInst::ICMP_UGT: + L.MakeUnsigned(); + R.MakeUnsigned(); + result = (L > R); + break; + case CmpInst::ICMP_UGE: + L.MakeUnsigned(); + R.MakeUnsigned(); + result = (L >= R); + break; + case CmpInst::FCMP_OGE: + result = (L >= R); + break; + case CmpInst::FCMP_OGT: + result = (L > R); + break; + case CmpInst::ICMP_ULT: + L.MakeUnsigned(); + R.MakeUnsigned(); + result = (L < R); + break; + case CmpInst::FCMP_OLT: + result = (L < R); + break; + case CmpInst::ICMP_ULE: + L.MakeUnsigned(); + R.MakeUnsigned(); + result = (L <= R); + break; + case CmpInst::FCMP_OLE: + result = (L <= R); + break; + case CmpInst::ICMP_SGT: + L.MakeSigned(); + R.MakeSigned(); + result = (L > R); + break; + case CmpInst::ICMP_SGE: + L.MakeSigned(); + R.MakeSigned(); + result = (L >= R); + break; + case CmpInst::ICMP_SLT: + L.MakeSigned(); + R.MakeSigned(); + result = (L < R); + break; + case CmpInst::ICMP_SLE: + L.MakeSigned(); + R.MakeSigned(); + result = (L <= R); + break; + } + + frame.AssignValue(inst, result, module); + + if (log) { + LLDB_LOGF(log, "Interpreted an ICmpInst"); + LLDB_LOGF(log, " L : %s", frame.SummarizeValue(lhs).c_str()); + LLDB_LOGF(log, " R : %s", frame.SummarizeValue(rhs).c_str()); + LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str()); + } + } break; + case Instruction::IntToPtr: { + const IntToPtrInst *int_to_ptr_inst = cast<IntToPtrInst>(inst); + + Value *src_operand = int_to_ptr_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) { + LLDB_LOGF(log, "Interpreted an IntToPtr"); + LLDB_LOGF(log, " Src : %s", frame.SummarizeValue(src_operand).c_str()); + LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str()); + } + } break; + case Instruction::PtrToInt: { + const PtrToIntInst *ptr_to_int_inst = cast<PtrToIntInst>(inst); + + Value *src_operand = ptr_to_int_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) { + LLDB_LOGF(log, "Interpreted a PtrToInt"); + LLDB_LOGF(log, " Src : %s", frame.SummarizeValue(src_operand).c_str()); + LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str()); + } + } break; + case Instruction::Trunc: { + const TruncInst *trunc_inst = cast<TruncInst>(inst); + + Value *src_operand = trunc_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) { + LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) { + LLDB_LOGF(log, "Interpreted a Trunc"); + LLDB_LOGF(log, " Src : %s", frame.SummarizeValue(src_operand).c_str()); + LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str()); + } + } break; + case Instruction::Load: { + const LoadInst *load_inst = cast<LoadInst>(inst); + + // The semantics of Load are: + // Create a region D that will contain the loaded data + // Resolve the region P containing a pointer + // Dereference P to get the region R that the data should be loaded from + // Transfer a unit of type type(D) from R to D + + const Value *pointer_operand = load_inst->getPointerOperand(); + + lldb::addr_t D = frame.ResolveValue(load_inst, module); + lldb::addr_t P = frame.ResolveValue(pointer_operand, module); + + if (D == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "LoadInst's value doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (P == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "LoadInst's pointer doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb::addr_t R; + lldb_private::Status read_error; + execution_unit.ReadPointerFromMemory(&R, P, read_error); + + if (!read_error.Success()) { + LLDB_LOGF(log, "Couldn't read the address to be loaded for a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + Type *target_ty = load_inst->getType(); + size_t target_size = data_layout.getTypeStoreSize(target_ty); + lldb_private::DataBufferHeap buffer(target_size, 0); + + read_error.Clear(); + execution_unit.ReadMemory(buffer.GetBytes(), R, buffer.GetByteSize(), + read_error); + if (!read_error.Success()) { + LLDB_LOGF(log, "Couldn't read from a region on behalf of a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + lldb_private::Status write_error; + execution_unit.WriteMemory(D, buffer.GetBytes(), buffer.GetByteSize(), + write_error); + if (!write_error.Success()) { + LLDB_LOGF(log, "Couldn't write to a region on behalf of a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + return false; + } + + if (log) { + LLDB_LOGF(log, "Interpreted a LoadInst"); + LLDB_LOGF(log, " P : 0x%" PRIx64, P); + LLDB_LOGF(log, " R : 0x%" PRIx64, R); + LLDB_LOGF(log, " D : 0x%" PRIx64, D); + } + } break; + case Instruction::Ret: { + return true; + } + case Instruction::Store: { + const StoreInst *store_inst = cast<StoreInst>(inst); + + // The semantics of Store are: + // Resolve the region D containing the data to be stored + // Resolve the region P containing a pointer + // Dereference P to get the region R that the data should be stored in + // Transfer a unit of type type(D) from D to R + + const Value *value_operand = store_inst->getValueOperand(); + const Value *pointer_operand = store_inst->getPointerOperand(); + + lldb::addr_t D = frame.ResolveValue(value_operand, module); + lldb::addr_t P = frame.ResolveValue(pointer_operand, module); + + if (D == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "StoreInst's value doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (P == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "StoreInst's pointer doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb::addr_t R; + lldb_private::Status read_error; + execution_unit.ReadPointerFromMemory(&R, P, read_error); + + if (!read_error.Success()) { + LLDB_LOGF(log, "Couldn't read the address to be loaded for a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + Type *target_ty = value_operand->getType(); + size_t target_size = data_layout.getTypeStoreSize(target_ty); + lldb_private::DataBufferHeap buffer(target_size, 0); + + read_error.Clear(); + execution_unit.ReadMemory(buffer.GetBytes(), D, buffer.GetByteSize(), + read_error); + if (!read_error.Success()) { + LLDB_LOGF(log, "Couldn't read from a region on behalf of a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + lldb_private::Status write_error; + execution_unit.WriteMemory(R, buffer.GetBytes(), buffer.GetByteSize(), + write_error); + if (!write_error.Success()) { + LLDB_LOGF(log, "Couldn't write to a region on behalf of a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + return false; + } + + if (log) { + LLDB_LOGF(log, "Interpreted a StoreInst"); + LLDB_LOGF(log, " D : 0x%" PRIx64, D); + LLDB_LOGF(log, " P : 0x%" PRIx64, P); + LLDB_LOGF(log, " R : 0x%" PRIx64, R); + } + } break; + case Instruction::Call: { + const CallInst *call_inst = cast<CallInst>(inst); + + if (CanIgnoreCall(call_inst)) + break; + + // Get the return type + llvm::Type *returnType = call_inst->getType(); + if (returnType == nullptr) { + error.SetErrorToGenericError(); + error.SetErrorString("unable to access return type"); + return false; + } + + // Work with void, integer and pointer return types + if (!returnType->isVoidTy() && !returnType->isIntegerTy() && + !returnType->isPointerTy()) { + error.SetErrorToGenericError(); + error.SetErrorString("return type is not supported"); + return false; + } + + // Check we can actually get a thread + if (exe_ctx.GetThreadPtr() == nullptr) { + error.SetErrorToGenericError(); + error.SetErrorString("unable to acquire thread"); + return false; + } + + // Make sure we have a valid process + if (!process) { + error.SetErrorToGenericError(); + error.SetErrorString("unable to get the process"); + return false; + } + + // Find the address of the callee function + lldb_private::Scalar I; + const llvm::Value *val = call_inst->getCalledOperand(); + + if (!frame.EvaluateValue(I, val, module)) { + error.SetErrorToGenericError(); + error.SetErrorString("unable to get address of function"); + return false; + } + lldb_private::Address funcAddr(I.ULongLong(LLDB_INVALID_ADDRESS)); + + lldb_private::DiagnosticManager diagnostics; + lldb_private::EvaluateExpressionOptions options; + + llvm::FunctionType *prototype = call_inst->getFunctionType(); + + // Find number of arguments + const int numArgs = call_inst->arg_size(); + + // We work with a fixed array of 16 arguments which is our upper limit + static lldb_private::ABI::CallArgument rawArgs[16]; + if (numArgs >= 16) { + error.SetErrorToGenericError(); + error.SetErrorString("function takes too many arguments"); + return false; + } + + // Push all function arguments to the argument list that will be passed + // to the call function thread plan + for (int i = 0; i < numArgs; i++) { + // Get details of this argument + llvm::Value *arg_op = call_inst->getArgOperand(i); + llvm::Type *arg_ty = arg_op->getType(); + + // Ensure that this argument is an supported type + if (!arg_ty->isIntegerTy() && !arg_ty->isPointerTy()) { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("argument %d must be integer type", i); + return false; + } + + // Extract the arguments value + lldb_private::Scalar tmp_op = 0; + if (!frame.EvaluateValue(tmp_op, arg_op, module)) { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("unable to evaluate argument %d", i); + return false; + } + + // Check if this is a string literal or constant string pointer + if (arg_ty->isPointerTy()) { + lldb::addr_t addr = tmp_op.ULongLong(); + size_t dataSize = 0; + + bool Success = execution_unit.GetAllocSize(addr, dataSize); + UNUSED_IF_ASSERT_DISABLED(Success); + assert(Success && + "unable to locate host data for transfer to device"); + // Create the required buffer + rawArgs[i].size = dataSize; + rawArgs[i].data_up.reset(new uint8_t[dataSize + 1]); + + // Read string from host memory + execution_unit.ReadMemory(rawArgs[i].data_up.get(), addr, dataSize, + error); + assert(!error.Fail() && + "we have failed to read the string from memory"); + + // Add null terminator + rawArgs[i].data_up[dataSize] = '\0'; + rawArgs[i].type = lldb_private::ABI::CallArgument::HostPointer; + } else /* if ( arg_ty->isPointerTy() ) */ + { + rawArgs[i].type = lldb_private::ABI::CallArgument::TargetValue; + // Get argument size in bytes + rawArgs[i].size = arg_ty->getIntegerBitWidth() / 8; + // Push value into argument list for thread plan + rawArgs[i].value = tmp_op.ULongLong(); + } + } + + // Pack the arguments into an llvm::array + llvm::ArrayRef<lldb_private::ABI::CallArgument> args(rawArgs, numArgs); + + // Setup a thread plan to call the target function + lldb::ThreadPlanSP call_plan_sp( + new lldb_private::ThreadPlanCallFunctionUsingABI( + exe_ctx.GetThreadRef(), funcAddr, *prototype, *returnType, args, + options)); + + // Check if the plan is valid + lldb_private::StreamString ss; + if (!call_plan_sp || !call_plan_sp->ValidatePlan(&ss)) { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat( + "unable to make ThreadPlanCallFunctionUsingABI for 0x%llx", + I.ULongLong()); + return false; + } + + process->SetRunningUserExpression(true); + + // Execute the actual function call thread plan + lldb::ExpressionResults res = + process->RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics); + + // Check that the thread plan completed successfully + if (res != lldb::ExpressionResults::eExpressionCompleted) { + error.SetErrorToGenericError(); + error.SetErrorString("ThreadPlanCallFunctionUsingABI failed"); + return false; + } + + process->SetRunningUserExpression(false); + + // Void return type + if (returnType->isVoidTy()) { + // Cant assign to void types, so we leave the frame untouched + } else + // Integer or pointer return type + if (returnType->isIntegerTy() || returnType->isPointerTy()) { + // Get the encapsulated return value + lldb::ValueObjectSP retVal = call_plan_sp.get()->GetReturnValueObject(); + + lldb_private::Scalar returnVal = -1; + lldb_private::ValueObject *vobj = retVal.get(); + + // Check if the return value is valid + if (vobj == nullptr || !retVal) { + error.SetErrorToGenericError(); + error.SetErrorString("unable to get the return value"); + return false; + } + + // Extract the return value as a integer + lldb_private::Value &value = vobj->GetValue(); + returnVal = value.GetScalar(); + + // Push the return value as the result + frame.AssignValue(inst, returnVal, module); + } + } break; + } + + ++frame.m_ii; + } + + return false; +} diff --git a/contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp b/contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp new file mode 100644 index 000000000000..de631370bb04 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp @@ -0,0 +1,864 @@ +//===-- IRMemoryMap.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/Status.h" + +using namespace lldb_private; + +IRMemoryMap::IRMemoryMap(lldb::TargetSP target_sp) : m_target_wp(target_sp) { + if (target_sp) + m_process_wp = target_sp->GetProcessSP(); +} + +IRMemoryMap::~IRMemoryMap() { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) { + AllocationMap::iterator iter; + + Status err; + + while ((iter = m_allocations.begin()) != m_allocations.end()) { + err.Clear(); + if (iter->second.m_leak) + m_allocations.erase(iter); + else + Free(iter->first, err); + } + } +} + +lldb::addr_t IRMemoryMap::FindSpace(size_t size) { + // The FindSpace algorithm's job is to find a region of memory that the + // underlying process is unlikely to be using. + // + // The memory returned by this function will never be written to. The only + // point is that it should not shadow process memory if possible, so that + // expressions processing real values from the process do not use the wrong + // data. + // + // If the process can in fact allocate memory (CanJIT() lets us know this) + // then this can be accomplished just be allocating memory in the inferior. + // Then no guessing is required. + + lldb::TargetSP target_sp = m_target_wp.lock(); + lldb::ProcessSP process_sp = m_process_wp.lock(); + + const bool process_is_alive = process_sp && process_sp->IsAlive(); + + lldb::addr_t ret = LLDB_INVALID_ADDRESS; + if (size == 0) + return ret; + + if (process_is_alive && process_sp->CanJIT()) { + Status alloc_error; + + ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable | + lldb::ePermissionsWritable, + alloc_error); + + if (!alloc_error.Success()) + return LLDB_INVALID_ADDRESS; + else + return ret; + } + + // At this point we know that we need to hunt. + // + // First, go to the end of the existing allocations we've made if there are + // any allocations. Otherwise start at the beginning of memory. + + if (m_allocations.empty()) { + ret = 0x0; + } else { + auto back = m_allocations.rbegin(); + lldb::addr_t addr = back->first; + size_t alloc_size = back->second.m_size; + ret = llvm::alignTo(addr + alloc_size, 4096); + } + + uint64_t end_of_memory; + switch (GetAddressByteSize()) { + case 2: + end_of_memory = 0xffffull; + break; + case 4: + end_of_memory = 0xffffffffull; + break; + case 8: + end_of_memory = 0xffffffffffffffffull; + break; + default: + lldbassert(false && "Invalid address size."); + return LLDB_INVALID_ADDRESS; + } + + // Now, if it's possible to use the GetMemoryRegionInfo API to detect mapped + // regions, walk forward through memory until a region is found that has + // adequate space for our allocation. + if (process_is_alive) { + MemoryRegionInfo region_info; + Status err = process_sp->GetMemoryRegionInfo(ret, region_info); + if (err.Success()) { + while (true) { + if (region_info.GetReadable() != MemoryRegionInfo::OptionalBool::eNo || + region_info.GetWritable() != MemoryRegionInfo::OptionalBool::eNo || + region_info.GetExecutable() != + MemoryRegionInfo::OptionalBool::eNo) { + if (region_info.GetRange().GetRangeEnd() - 1 >= end_of_memory) { + ret = LLDB_INVALID_ADDRESS; + break; + } else { + ret = region_info.GetRange().GetRangeEnd(); + } + } else if (ret + size < region_info.GetRange().GetRangeEnd()) { + return ret; + } else { + // ret stays the same. We just need to walk a bit further. + } + + err = process_sp->GetMemoryRegionInfo( + region_info.GetRange().GetRangeEnd(), region_info); + if (err.Fail()) { + lldbassert(0 && "GetMemoryRegionInfo() succeeded, then failed"); + ret = LLDB_INVALID_ADDRESS; + break; + } + } + } + } + + // We've tried our algorithm, and it didn't work. Now we have to reset back + // to the end of the allocations we've already reported, or use a 'sensible' + // default if this is our first allocation. + if (m_allocations.empty()) { + uint64_t alloc_address = target_sp->GetExprAllocAddress(); + if (alloc_address > 0) { + if (alloc_address >= end_of_memory) { + lldbassert(0 && "The allocation address for expression evaluation must " + "be within process address space"); + return LLDB_INVALID_ADDRESS; + } + ret = alloc_address; + } else { + uint32_t address_byte_size = GetAddressByteSize(); + if (address_byte_size != UINT32_MAX) { + switch (address_byte_size) { + case 2: + ret = 0x8000ull; + break; + case 4: + ret = 0xee000000ull; + break; + case 8: + ret = 0xdead0fff00000000ull; + break; + default: + lldbassert(false && "Invalid address size."); + return LLDB_INVALID_ADDRESS; + } + } + } + } else { + auto back = m_allocations.rbegin(); + lldb::addr_t addr = back->first; + size_t alloc_size = back->second.m_size; + uint64_t align = target_sp->GetExprAllocAlign(); + if (align == 0) + align = 4096; + ret = llvm::alignTo(addr + alloc_size, align); + } + + return ret; +} + +IRMemoryMap::AllocationMap::iterator +IRMemoryMap::FindAllocation(lldb::addr_t addr, size_t size) { + if (addr == LLDB_INVALID_ADDRESS) + return m_allocations.end(); + + AllocationMap::iterator iter = m_allocations.lower_bound(addr); + + if (iter == m_allocations.end() || iter->first > addr) { + if (iter == m_allocations.begin()) + return m_allocations.end(); + iter--; + } + + if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size) + return iter; + + return m_allocations.end(); +} + +bool IRMemoryMap::IntersectsAllocation(lldb::addr_t addr, size_t size) const { + if (addr == LLDB_INVALID_ADDRESS) + return false; + + AllocationMap::const_iterator iter = m_allocations.lower_bound(addr); + + // Since we only know that the returned interval begins at a location greater + // than or equal to where the given interval begins, it's possible that the + // given interval intersects either the returned interval or the previous + // interval. Thus, we need to check both. Note that we only need to check + // these two intervals. Since all intervals are disjoint it is not possible + // that an adjacent interval does not intersect, but a non-adjacent interval + // does intersect. + if (iter != m_allocations.end()) { + if (AllocationsIntersect(addr, size, iter->second.m_process_start, + iter->second.m_size)) + return true; + } + + if (iter != m_allocations.begin()) { + --iter; + if (AllocationsIntersect(addr, size, iter->second.m_process_start, + iter->second.m_size)) + return true; + } + + return false; +} + +bool IRMemoryMap::AllocationsIntersect(lldb::addr_t addr1, size_t size1, + lldb::addr_t addr2, size_t size2) { + // Given two half open intervals [A, B) and [X, Y), the only 6 permutations + // that satisfy A<B and X<Y are the following: + // A B X Y + // A X B Y (intersects) + // A X Y B (intersects) + // X A B Y (intersects) + // X A Y B (intersects) + // X Y A B + // The first is B <= X, and the last is Y <= A. So the condition is !(B <= X + // || Y <= A)), or (X < B && A < Y) + return (addr2 < (addr1 + size1)) && (addr1 < (addr2 + size2)); +} + +lldb::ByteOrder IRMemoryMap::GetByteOrder() { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp->GetByteOrder(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp->GetArchitecture().GetByteOrder(); + + return lldb::eByteOrderInvalid; +} + +uint32_t IRMemoryMap::GetAddressByteSize() { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp->GetAddressByteSize(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp->GetArchitecture().GetAddressByteSize(); + + return UINT32_MAX; +} + +ExecutionContextScope *IRMemoryMap::GetBestExecutionContextScope() const { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp.get(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp.get(); + + return nullptr; +} + +IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc, + lldb::addr_t process_start, size_t size, + uint32_t permissions, uint8_t alignment, + AllocationPolicy policy) + : m_process_alloc(process_alloc), m_process_start(process_start), + m_size(size), m_policy(policy), m_leak(false), m_permissions(permissions), + m_alignment(alignment) { + switch (policy) { + default: + llvm_unreachable("Invalid AllocationPolicy"); + case eAllocationPolicyHostOnly: + case eAllocationPolicyMirror: + m_data.SetByteSize(size); + break; + case eAllocationPolicyProcessOnly: + break; + } +} + +lldb::addr_t IRMemoryMap::Malloc(size_t size, uint8_t alignment, + uint32_t permissions, AllocationPolicy policy, + bool zero_memory, Status &error) { + lldb_private::Log *log(GetLog(LLDBLog::Expressions)); + error.Clear(); + + lldb::ProcessSP process_sp; + lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS; + lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS; + + size_t allocation_size; + + if (size == 0) { + // FIXME: Malloc(0) should either return an invalid address or assert, in + // order to cut down on unnecessary allocations. + allocation_size = alignment; + } else { + // Round up the requested size to an aligned value. + allocation_size = llvm::alignTo(size, alignment); + + // The process page cache does not see the requested alignment. We can't + // assume its result will be any more than 1-byte aligned. To work around + // this, request `alignment - 1` additional bytes. + allocation_size += alignment - 1; + } + + switch (policy) { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: invalid allocation policy"); + return LLDB_INVALID_ADDRESS; + case eAllocationPolicyHostOnly: + allocation_address = FindSpace(allocation_size); + if (allocation_address == LLDB_INVALID_ADDRESS) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: address space is full"); + return LLDB_INVALID_ADDRESS; + } + break; + case eAllocationPolicyMirror: + process_sp = m_process_wp.lock(); + LLDB_LOGF(log, + "IRMemoryMap::%s process_sp=0x%" PRIxPTR + ", process_sp->CanJIT()=%s, process_sp->IsAlive()=%s", + __FUNCTION__, reinterpret_cast<uintptr_t>(process_sp.get()), + process_sp && process_sp->CanJIT() ? "true" : "false", + process_sp && process_sp->IsAlive() ? "true" : "false"); + if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) { + if (!zero_memory) + allocation_address = + process_sp->AllocateMemory(allocation_size, permissions, error); + else + allocation_address = + process_sp->CallocateMemory(allocation_size, permissions, error); + + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } else { + LLDB_LOGF(log, + "IRMemoryMap::%s switching to eAllocationPolicyHostOnly " + "due to failed condition (see previous expr log message)", + __FUNCTION__); + policy = eAllocationPolicyHostOnly; + allocation_address = FindSpace(allocation_size); + if (allocation_address == LLDB_INVALID_ADDRESS) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: address space is full"); + return LLDB_INVALID_ADDRESS; + } + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) { + if (process_sp->CanJIT() && process_sp->IsAlive()) { + if (!zero_memory) + allocation_address = + process_sp->AllocateMemory(allocation_size, permissions, error); + else + allocation_address = + process_sp->CallocateMemory(allocation_size, permissions, error); + + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } else { + error.SetErrorToGenericError(); + error.SetErrorString( + "Couldn't malloc: process doesn't support allocating memory"); + return LLDB_INVALID_ADDRESS; + } + } else { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: process doesn't exist, and this " + "memory must be in the process"); + return LLDB_INVALID_ADDRESS; + } + break; + } + + lldb::addr_t mask = alignment - 1; + aligned_address = (allocation_address + mask) & (~mask); + + m_allocations.emplace( + std::piecewise_construct, std::forward_as_tuple(aligned_address), + std::forward_as_tuple(allocation_address, aligned_address, + allocation_size, permissions, alignment, policy)); + + if (zero_memory) { + Status write_error; + std::vector<uint8_t> zero_buf(size, 0); + WriteMemory(aligned_address, zero_buf.data(), size, write_error); + } + + if (log) { + const char *policy_string; + + switch (policy) { + default: + policy_string = "<invalid policy>"; + break; + case eAllocationPolicyHostOnly: + policy_string = "eAllocationPolicyHostOnly"; + break; + case eAllocationPolicyProcessOnly: + policy_string = "eAllocationPolicyProcessOnly"; + break; + case eAllocationPolicyMirror: + policy_string = "eAllocationPolicyMirror"; + break; + } + + LLDB_LOGF(log, + "IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 + ", %s) -> 0x%" PRIx64, + (uint64_t)allocation_size, (uint64_t)alignment, + (uint64_t)permissions, policy_string, aligned_address); + } + + return aligned_address; +} + +void IRMemoryMap::Leak(lldb::addr_t process_address, Status &error) { + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't leak: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + allocation.m_leak = true; +} + +void IRMemoryMap::Free(lldb::addr_t process_address, Status &error) { + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't free: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + switch (allocation.m_policy) { + default: + case eAllocationPolicyHostOnly: { + lldb::ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) { + if (process_sp->CanJIT() && process_sp->IsAlive()) + process_sp->DeallocateMemory( + allocation.m_process_alloc); // FindSpace allocated this for real + } + + break; + } + case eAllocationPolicyMirror: + case eAllocationPolicyProcessOnly: { + lldb::ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) + process_sp->DeallocateMemory(allocation.m_process_alloc); + } + } + + if (lldb_private::Log *log = GetLog(LLDBLog::Expressions)) { + LLDB_LOGF(log, + "IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 + "..0x%" PRIx64 ")", + (uint64_t)process_address, iter->second.m_process_start, + iter->second.m_process_start + iter->second.m_size); + } + + m_allocations.erase(iter); +} + +bool IRMemoryMap::GetAllocSize(lldb::addr_t address, size_t &size) { + AllocationMap::iterator iter = FindAllocation(address, size); + if (iter == m_allocations.end()) + return false; + + Allocation &al = iter->second; + + if (address > (al.m_process_start + al.m_size)) { + size = 0; + return false; + } + + if (address > al.m_process_start) { + int dif = address - al.m_process_start; + size = al.m_size - dif; + return true; + } + + size = al.m_size; + return true; +} + +void IRMemoryMap::WriteMemory(lldb::addr_t process_address, + const uint8_t *bytes, size_t size, + Status &error) { + error.Clear(); + + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) { + process_sp->WriteMemory(process_address, bytes, size, error); + return; + } + + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: no allocation contains the target " + "range and the process doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + uint64_t offset = process_address - allocation.m_process_start; + + lldb::ProcessSP process_sp; + + switch (allocation.m_policy) { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: invalid allocation policy"); + return; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: data buffer is empty"); + return; + } + ::memcpy(allocation.m_data.GetBytes() + offset, bytes, size); + break; + case eAllocationPolicyMirror: + if (!allocation.m_data.GetByteSize()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: data buffer is empty"); + return; + } + ::memcpy(allocation.m_data.GetBytes() + offset, bytes, size); + process_sp = m_process_wp.lock(); + if (process_sp) { + process_sp->WriteMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) { + process_sp->WriteMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + } + + if (lldb_private::Log *log = GetLog(LLDBLog::Expressions)) { + LLDB_LOGF(log, + "IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIxPTR + ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, reinterpret_cast<uintptr_t>(bytes), (uint64_t)size, + (uint64_t)allocation.m_process_start, + (uint64_t)allocation.m_process_start + + (uint64_t)allocation.m_size); + } +} + +void IRMemoryMap::WriteScalarToMemory(lldb::addr_t process_address, + Scalar &scalar, size_t size, + Status &error) { + error.Clear(); + + if (size == UINT32_MAX) + size = scalar.GetByteSize(); + + if (size > 0) { + uint8_t buf[32]; + const size_t mem_size = + scalar.GetAsMemoryData(buf, size, GetByteOrder(), error); + if (mem_size > 0) { + return WriteMemory(process_address, buf, mem_size, error); + } else { + error.SetErrorToGenericError(); + error.SetErrorString( + "Couldn't write scalar: failed to get scalar as memory data"); + } + } else { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write scalar: its size was zero"); + } +} + +void IRMemoryMap::WritePointerToMemory(lldb::addr_t process_address, + lldb::addr_t address, Status &error) { + error.Clear(); + + Scalar scalar(address); + + WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error); +} + +void IRMemoryMap::ReadMemory(uint8_t *bytes, lldb::addr_t process_address, + size_t size, Status &error) { + error.Clear(); + + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) { + process_sp->ReadMemory(process_address, bytes, size, error); + return; + } + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) { + Address absolute_address(process_address); + target_sp->ReadMemory(absolute_address, bytes, size, error, true); + return; + } + + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: no allocation contains the target " + "range, and neither the process nor the target exist"); + return; + } + + Allocation &allocation = iter->second; + + uint64_t offset = process_address - allocation.m_process_start; + + if (offset > allocation.m_size) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data is not in the allocation"); + return; + } + + lldb::ProcessSP process_sp; + + switch (allocation.m_policy) { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: invalid allocation policy"); + return; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data buffer is empty"); + return; + } + if (allocation.m_data.GetByteSize() < offset + size) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: not enough underlying data"); + return; + } + + ::memcpy(bytes, allocation.m_data.GetBytes() + offset, size); + break; + case eAllocationPolicyMirror: + process_sp = m_process_wp.lock(); + if (process_sp) { + process_sp->ReadMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } else { + if (!allocation.m_data.GetByteSize()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data buffer is empty"); + return; + } + ::memcpy(bytes, allocation.m_data.GetBytes() + offset, size); + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) { + process_sp->ReadMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + } + + if (lldb_private::Log *log = GetLog(LLDBLog::Expressions)) { + LLDB_LOGF(log, + "IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIxPTR + ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, reinterpret_cast<uintptr_t>(bytes), (uint64_t)size, + (uint64_t)allocation.m_process_start, + (uint64_t)allocation.m_process_start + + (uint64_t)allocation.m_size); + } +} + +void IRMemoryMap::ReadScalarFromMemory(Scalar &scalar, + lldb::addr_t process_address, + size_t size, Status &error) { + error.Clear(); + + if (size > 0) { + DataBufferHeap buf(size, 0); + ReadMemory(buf.GetBytes(), process_address, size, error); + + if (!error.Success()) + return; + + DataExtractor extractor(buf.GetBytes(), buf.GetByteSize(), GetByteOrder(), + GetAddressByteSize()); + + lldb::offset_t offset = 0; + + switch (size) { + default: + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat( + "Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size); + return; + case 1: + scalar = extractor.GetU8(&offset); + break; + case 2: + scalar = extractor.GetU16(&offset); + break; + case 4: + scalar = extractor.GetU32(&offset); + break; + case 8: + scalar = extractor.GetU64(&offset); + break; + } + } else { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read scalar: its size was zero"); + } +} + +void IRMemoryMap::ReadPointerFromMemory(lldb::addr_t *address, + lldb::addr_t process_address, + Status &error) { + error.Clear(); + + Scalar pointer_scalar; + ReadScalarFromMemory(pointer_scalar, process_address, GetAddressByteSize(), + error); + + if (!error.Success()) + return; + + *address = pointer_scalar.ULongLong(); +} + +void IRMemoryMap::GetMemoryData(DataExtractor &extractor, + lldb::addr_t process_address, size_t size, + Status &error) { + error.Clear(); + + if (size > 0) { + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat( + "Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 + ")", + process_address, process_address + size); + return; + } + + Allocation &allocation = iter->second; + + switch (allocation.m_policy) { + default: + error.SetErrorToGenericError(); + error.SetErrorString( + "Couldn't get memory data: invalid allocation policy"); + return; + case eAllocationPolicyProcessOnly: + error.SetErrorToGenericError(); + error.SetErrorString( + "Couldn't get memory data: memory is only in the target"); + return; + case eAllocationPolicyMirror: { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (!allocation.m_data.GetByteSize()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: data buffer is empty"); + return; + } + if (process_sp) { + process_sp->ReadMemory(allocation.m_process_start, + allocation.m_data.GetBytes(), + allocation.m_data.GetByteSize(), error); + if (!error.Success()) + return; + uint64_t offset = process_address - allocation.m_process_start; + extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, + GetByteOrder(), GetAddressByteSize()); + return; + } + } break; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: data buffer is empty"); + return; + } + uint64_t offset = process_address - allocation.m_process_start; + extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, + GetByteOrder(), GetAddressByteSize()); + return; + } + } else { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: its size was zero"); + return; + } +} diff --git a/contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp b/contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp new file mode 100644 index 000000000000..b4fdfc4d1fa8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp @@ -0,0 +1,377 @@ +//===-- LLVMUserExpression.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Expression/LLVMUserExpression.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallUserExpression.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +char LLVMUserExpression::ID; + +LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope, + llvm::StringRef expr, + llvm::StringRef prefix, + SourceLanguage language, + ResultType desired_type, + const EvaluateExpressionOptions &options) + : UserExpression(exe_scope, expr, prefix, language, desired_type, options), + m_stack_frame_bottom(LLDB_INVALID_ADDRESS), + m_stack_frame_top(LLDB_INVALID_ADDRESS), m_allow_cxx(false), + m_allow_objc(false), m_transformed_text(), m_execution_unit_sp(), + m_materializer_up(), m_jit_module_wp(), m_target(nullptr), + m_can_interpret(false), m_materialized_address(LLDB_INVALID_ADDRESS) {} + +LLVMUserExpression::~LLVMUserExpression() { + if (m_target) { + lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock()); + if (jit_module_sp) + m_target->GetImages().Remove(jit_module_sp); + } +} + +lldb::ExpressionResults +LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + const EvaluateExpressionOptions &options, + lldb::UserExpressionSP &shared_ptr_to_me, + lldb::ExpressionVariableSP &result) { + // The expression log is quite verbose, and if you're just tracking the + // execution of the expression, it's quite convenient to have these logs come + // out with the STEP log as well. + Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step)); + + if (m_jit_start_addr == LLDB_INVALID_ADDRESS && !m_can_interpret) { + diagnostic_manager.PutString( + lldb::eSeverityError, + "Expression can't be run, because there is no JIT compiled function"); + return lldb::eExpressionSetupError; + } + + lldb::addr_t struct_address = LLDB_INVALID_ADDRESS; + + if (!PrepareToExecuteJITExpression(diagnostic_manager, exe_ctx, + struct_address)) { + diagnostic_manager.Printf( + lldb::eSeverityError, + "errored out in %s, couldn't PrepareToExecuteJITExpression", + __FUNCTION__); + return lldb::eExpressionSetupError; + } + + lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS; + lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS; + + if (m_can_interpret) { + llvm::Module *module = m_execution_unit_sp->GetModule(); + llvm::Function *function = m_execution_unit_sp->GetFunction(); + + if (!module || !function) { + diagnostic_manager.PutString( + lldb::eSeverityError, "supposed to interpret, but nothing is there"); + return lldb::eExpressionSetupError; + } + + Status interpreter_error; + + std::vector<lldb::addr_t> args; + + if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) { + diagnostic_manager.Printf(lldb::eSeverityError, + "errored out in %s, couldn't AddArguments", + __FUNCTION__); + return lldb::eExpressionSetupError; + } + + function_stack_bottom = m_stack_frame_bottom; + function_stack_top = m_stack_frame_top; + + IRInterpreter::Interpret(*module, *function, args, *m_execution_unit_sp, + interpreter_error, function_stack_bottom, + function_stack_top, exe_ctx, options.GetTimeout()); + + if (!interpreter_error.Success()) { + diagnostic_manager.Printf(lldb::eSeverityError, + "supposed to interpret, but failed: %s", + interpreter_error.AsCString()); + return lldb::eExpressionDiscarded; + } + } else { + if (!exe_ctx.HasThreadScope()) { + diagnostic_manager.Printf(lldb::eSeverityError, + "%s called with no thread selected", + __FUNCTION__); + return lldb::eExpressionSetupError; + } + + // Store away the thread ID for error reporting, in case it exits + // during execution: + lldb::tid_t expr_thread_id = exe_ctx.GetThreadRef().GetID(); + + Address wrapper_address(m_jit_start_addr); + + std::vector<lldb::addr_t> args; + + if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) { + diagnostic_manager.Printf(lldb::eSeverityError, + "errored out in %s, couldn't AddArguments", + __FUNCTION__); + return lldb::eExpressionSetupError; + } + + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression( + exe_ctx.GetThreadRef(), wrapper_address, args, options, + shared_ptr_to_me)); + + StreamString ss; + if (!call_plan_sp || !call_plan_sp->ValidatePlan(&ss)) { + diagnostic_manager.PutString(lldb::eSeverityError, ss.GetString()); + return lldb::eExpressionSetupError; + } + + ThreadPlanCallUserExpression *user_expression_plan = + static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get()); + + lldb::addr_t function_stack_pointer = + user_expression_plan->GetFunctionStackPointer(); + + function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize(); + function_stack_top = function_stack_pointer; + + LLDB_LOGF(log, + "-- [UserExpression::Execute] Execution of expression begins --"); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + lldb::ExpressionResults execution_result = + exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options, + diagnostic_manager); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + LLDB_LOGF(log, "-- [UserExpression::Execute] Execution of expression " + "completed --"); + + if (execution_result == lldb::eExpressionInterrupted || + execution_result == lldb::eExpressionHitBreakpoint) { + const char *error_desc = nullptr; + + if (user_expression_plan) { + if (auto real_stop_info_sp = user_expression_plan->GetRealStopInfo()) + error_desc = real_stop_info_sp->GetDescription(); + } + if (error_desc) + diagnostic_manager.Printf(lldb::eSeverityError, + "Execution was interrupted, reason: %s.", + error_desc); + else + diagnostic_manager.PutString(lldb::eSeverityError, + "Execution was interrupted."); + + if ((execution_result == lldb::eExpressionInterrupted && + options.DoesUnwindOnError()) || + (execution_result == lldb::eExpressionHitBreakpoint && + options.DoesIgnoreBreakpoints())) + diagnostic_manager.AppendMessageToDiagnostic( + "The process has been returned to the state before expression " + "evaluation."); + else { + if (execution_result == lldb::eExpressionHitBreakpoint) + user_expression_plan->TransferExpressionOwnership(); + diagnostic_manager.AppendMessageToDiagnostic( + "The process has been left at the point where it was " + "interrupted, " + "use \"thread return -x\" to return to the state before " + "expression evaluation."); + } + + return execution_result; + } else if (execution_result == lldb::eExpressionStoppedForDebug) { + diagnostic_manager.PutString( + lldb::eSeverityInfo, + "Execution was halted at the first instruction of the expression " + "function because \"debug\" was requested.\n" + "Use \"thread return -x\" to return to the state before expression " + "evaluation."); + return execution_result; + } else if (execution_result == lldb::eExpressionThreadVanished) { + diagnostic_manager.Printf( + lldb::eSeverityError, + "Couldn't complete execution; the thread " + "on which the expression was being run: 0x%" PRIx64 + " exited during its execution.", + expr_thread_id); + return execution_result; + } else if (execution_result != lldb::eExpressionCompleted) { + diagnostic_manager.Printf( + lldb::eSeverityError, "Couldn't execute function; result was %s", + Process::ExecutionResultAsCString(execution_result)); + return execution_result; + } + } + + if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result, + function_stack_bottom, function_stack_top)) { + return lldb::eExpressionCompleted; + } else { + return lldb::eExpressionResultUnavailable; + } +} + +bool LLVMUserExpression::FinalizeJITExecution( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + lldb::ExpressionVariableSP &result, lldb::addr_t function_stack_bottom, + lldb::addr_t function_stack_top) { + Log *log = GetLog(LLDBLog::Expressions); + + LLDB_LOGF(log, "-- [UserExpression::FinalizeJITExecution] Dematerializing " + "after execution --"); + + if (!m_dematerializer_sp) { + diagnostic_manager.Printf(lldb::eSeverityError, + "Couldn't apply expression side effects : no " + "dematerializer is present"); + return false; + } + + Status dematerialize_error; + + m_dematerializer_sp->Dematerialize(dematerialize_error, function_stack_bottom, + function_stack_top); + + if (!dematerialize_error.Success()) { + diagnostic_manager.Printf(lldb::eSeverityError, + "Couldn't apply expression side effects : %s", + dematerialize_error.AsCString("unknown error")); + return false; + } + + result = + GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope()); + + if (result) + result->TransferAddress(); + + m_dematerializer_sp.reset(); + + return true; +} + +bool LLVMUserExpression::PrepareToExecuteJITExpression( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + lldb::addr_t &struct_address) { + lldb::TargetSP target; + lldb::ProcessSP process; + lldb::StackFrameSP frame; + + if (!LockAndCheckContext(exe_ctx, target, process, frame)) { + diagnostic_manager.PutString( + lldb::eSeverityError, + "The context has changed before we could JIT the expression!"); + return false; + } + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) { + if (m_materialized_address == LLDB_INVALID_ADDRESS) { + Status alloc_error; + + IRMemoryMap::AllocationPolicy policy = + m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly + : IRMemoryMap::eAllocationPolicyMirror; + + const bool zero_memory = false; + + m_materialized_address = m_execution_unit_sp->Malloc( + m_materializer_up->GetStructByteSize(), + m_materializer_up->GetStructAlignment(), + lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy, + zero_memory, alloc_error); + + if (!alloc_error.Success()) { + diagnostic_manager.Printf( + lldb::eSeverityError, + "Couldn't allocate space for materialized struct: %s", + alloc_error.AsCString()); + return false; + } + } + + struct_address = m_materialized_address; + + if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) { + Status alloc_error; + + size_t stack_frame_size = target->GetExprAllocSize(); + if (stack_frame_size == 0) { + ABISP abi_sp; + if (process && (abi_sp = process->GetABI())) + stack_frame_size = abi_sp->GetStackFrameSize(); + else + stack_frame_size = 512 * 1024; + } + + const bool zero_memory = false; + + m_stack_frame_bottom = m_execution_unit_sp->Malloc( + stack_frame_size, 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyHostOnly, zero_memory, alloc_error); + + m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; + + if (!alloc_error.Success()) { + diagnostic_manager.Printf( + lldb::eSeverityError, + "Couldn't allocate space for the stack frame: %s", + alloc_error.AsCString()); + return false; + } + } + + Status materialize_error; + + m_dematerializer_sp = m_materializer_up->Materialize( + frame, *m_execution_unit_sp, struct_address, materialize_error); + + if (!materialize_error.Success()) { + diagnostic_manager.Printf(lldb::eSeverityError, + "Couldn't materialize: %s", + materialize_error.AsCString()); + return false; + } + } + return true; +} + diff --git a/contrib/llvm-project/lldb/source/Expression/Materializer.cpp b/contrib/llvm-project/lldb/source/Expression/Materializer.cpp new file mode 100644 index 000000000000..6e344dfd57c4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/Materializer.cpp @@ -0,0 +1,1604 @@ +//===-- Materializer.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/Materializer.h" +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/lldb-forward.h" + +#include <memory> +#include <optional> + +using namespace lldb_private; + +// FIXME: these should be retrieved from the target +// instead of being hard-coded. Currently we +// assume that persistent vars are materialized +// as references, and thus pick the size of a +// 64-bit pointer. +static constexpr uint32_t g_default_var_alignment = 8; +static constexpr uint32_t g_default_var_byte_size = 8; + +uint32_t Materializer::AddStructMember(Entity &entity) { + uint32_t size = entity.GetSize(); + uint32_t alignment = entity.GetAlignment(); + + uint32_t ret; + + if (m_current_offset == 0) + m_struct_alignment = alignment; + + if (m_current_offset % alignment) + m_current_offset += (alignment - (m_current_offset % alignment)); + + ret = m_current_offset; + + m_current_offset += size; + + return ret; +} + +class EntityPersistentVariable : public Materializer::Entity { +public: + EntityPersistentVariable(lldb::ExpressionVariableSP &persistent_variable_sp, + Materializer::PersistentVariableDelegate *delegate) + : Entity(), m_persistent_variable_sp(persistent_variable_sp), + m_delegate(delegate) { + // Hard-coding to maximum size of a pointer since persistent variables are + // materialized by reference + m_size = g_default_var_byte_size; + m_alignment = g_default_var_alignment; + } + + void MakeAllocation(IRMemoryMap &map, Status &err) { + Log *log = GetLog(LLDBLog::Expressions); + + // Allocate a spare memory area to store the persistent variable's + // contents. + + Status allocate_error; + const bool zero_memory = false; + + lldb::addr_t mem = map.Malloc( + m_persistent_variable_sp->GetByteSize().value_or(0), 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, zero_memory, allocate_error); + + if (!allocate_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't allocate a memory area to store %s: %s", + m_persistent_variable_sp->GetName().GetCString(), + allocate_error.AsCString()); + return; + } + + LLDB_LOGF(log, "Allocated %s (0x%" PRIx64 ") successfully", + m_persistent_variable_sp->GetName().GetCString(), mem); + + // Put the location of the spare memory into the live data of the + // ValueObject. + + m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create( + map.GetBestExecutionContextScope(), + m_persistent_variable_sp->GetCompilerType(), + m_persistent_variable_sp->GetName(), mem, eAddressTypeLoad, + map.GetAddressByteSize()); + + // Clear the flag if the variable will never be deallocated. + + if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVKeepInTarget) { + Status leak_error; + map.Leak(mem, leak_error); + m_persistent_variable_sp->m_flags &= + ~ExpressionVariable::EVNeedsAllocation; + } + + // Write the contents of the variable to the area. + + Status write_error; + + map.WriteMemory(mem, m_persistent_variable_sp->GetValueBytes(), + m_persistent_variable_sp->GetByteSize().value_or(0), + write_error); + + if (!write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write %s to the target: %s", + m_persistent_variable_sp->GetName().AsCString(), + write_error.AsCString()); + return; + } + } + + void DestroyAllocation(IRMemoryMap &map, Status &err) { + Status deallocate_error; + + map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue() + .GetScalar() + .ULongLong(), + deallocate_error); + + m_persistent_variable_sp->m_live_sp.reset(); + + if (!deallocate_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't deallocate memory for %s: %s", + m_persistent_variable_sp->GetName().GetCString(), + deallocate_error.AsCString()); + } + } + + void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) { + LLDB_LOGF(log, + "EntityPersistentVariable::Materialize [address = 0x%" PRIx64 + ", m_name = %s, m_flags = 0x%hx]", + (uint64_t)load_addr, + m_persistent_variable_sp->GetName().AsCString(), + m_persistent_variable_sp->m_flags); + } + + if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVNeedsAllocation) { + MakeAllocation(map, err); + m_persistent_variable_sp->m_flags |= + ExpressionVariable::EVIsLLDBAllocated; + + if (!err.Success()) + return; + } + + if ((m_persistent_variable_sp->m_flags & + ExpressionVariable::EVIsProgramReference && + m_persistent_variable_sp->m_live_sp) || + m_persistent_variable_sp->m_flags & + ExpressionVariable::EVIsLLDBAllocated) { + Status write_error; + + map.WriteScalarToMemory( + load_addr, + m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(), + map.GetAddressByteSize(), write_error); + + if (!write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write the location of %s to memory: %s", + m_persistent_variable_sp->GetName().AsCString(), + write_error.AsCString()); + } + } else { + err.SetErrorStringWithFormat( + "no materialization happened for persistent variable %s", + m_persistent_variable_sp->GetName().AsCString()); + return; + } + } + + void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, lldb::addr_t frame_top, + lldb::addr_t frame_bottom, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) { + LLDB_LOGF(log, + "EntityPersistentVariable::Dematerialize [address = 0x%" PRIx64 + ", m_name = %s, m_flags = 0x%hx]", + (uint64_t)process_address + m_offset, + m_persistent_variable_sp->GetName().AsCString(), + m_persistent_variable_sp->m_flags); + } + + if (m_delegate) { + m_delegate->DidDematerialize(m_persistent_variable_sp); + } + + if ((m_persistent_variable_sp->m_flags & + ExpressionVariable::EVIsLLDBAllocated) || + (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVIsProgramReference)) { + if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVIsProgramReference && + !m_persistent_variable_sp->m_live_sp) { + // If the reference comes from the program, then the + // ClangExpressionVariable's live variable data hasn't been set up yet. + // Do this now. + + lldb::addr_t location; + Status read_error; + + map.ReadPointerFromMemory(&location, load_addr, read_error); + + if (!read_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't read the address of program-allocated variable %s: %s", + m_persistent_variable_sp->GetName().GetCString(), + read_error.AsCString()); + return; + } + + m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create( + map.GetBestExecutionContextScope(), + m_persistent_variable_sp.get()->GetCompilerType(), + m_persistent_variable_sp->GetName(), location, eAddressTypeLoad, + m_persistent_variable_sp->GetByteSize().value_or(0)); + + if (frame_top != LLDB_INVALID_ADDRESS && + frame_bottom != LLDB_INVALID_ADDRESS && location >= frame_bottom && + location <= frame_top) { + // If the variable is resident in the stack frame created by the + // expression, then it cannot be relied upon to stay around. We + // treat it as needing reallocation. + m_persistent_variable_sp->m_flags |= + ExpressionVariable::EVIsLLDBAllocated; + m_persistent_variable_sp->m_flags |= + ExpressionVariable::EVNeedsAllocation; + m_persistent_variable_sp->m_flags |= + ExpressionVariable::EVNeedsFreezeDry; + m_persistent_variable_sp->m_flags &= + ~ExpressionVariable::EVIsProgramReference; + } + } + + lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue() + .GetScalar() + .ULongLong(); + + if (!m_persistent_variable_sp->m_live_sp) { + err.SetErrorStringWithFormat( + "couldn't find the memory area used to store %s", + m_persistent_variable_sp->GetName().GetCString()); + return; + } + + if (m_persistent_variable_sp->m_live_sp->GetValue() + .GetValueAddressType() != eAddressTypeLoad) { + err.SetErrorStringWithFormat( + "the address of the memory area for %s is in an incorrect format", + m_persistent_variable_sp->GetName().GetCString()); + return; + } + + if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVNeedsFreezeDry || + m_persistent_variable_sp->m_flags & + ExpressionVariable::EVKeepInTarget) { + LLDB_LOGF(log, "Dematerializing %s from 0x%" PRIx64 " (size = %llu)", + m_persistent_variable_sp->GetName().GetCString(), + (uint64_t)mem, + (unsigned long long)m_persistent_variable_sp->GetByteSize() + .value_or(0)); + + // Read the contents of the spare memory area + + m_persistent_variable_sp->ValueUpdated(); + + Status read_error; + + map.ReadMemory(m_persistent_variable_sp->GetValueBytes(), mem, + m_persistent_variable_sp->GetByteSize().value_or(0), + read_error); + + if (!read_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't read the contents of %s from memory: %s", + m_persistent_variable_sp->GetName().GetCString(), + read_error.AsCString()); + return; + } + + m_persistent_variable_sp->m_flags &= + ~ExpressionVariable::EVNeedsFreezeDry; + } + } else { + err.SetErrorStringWithFormat( + "no dematerialization happened for persistent variable %s", + m_persistent_variable_sp->GetName().AsCString()); + return; + } + + lldb::ProcessSP process_sp = + map.GetBestExecutionContextScope()->CalculateProcess(); + if (!process_sp || !process_sp->CanJIT()) { + // Allocations are not persistent so persistent variables cannot stay + // materialized. + + m_persistent_variable_sp->m_flags |= + ExpressionVariable::EVNeedsAllocation; + + DestroyAllocation(map, err); + if (!err.Success()) + return; + } else if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVNeedsAllocation && + !(m_persistent_variable_sp->m_flags & + ExpressionVariable::EVKeepInTarget)) { + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + } + + void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, + Log *log) override { + StreamString dump_stream; + + Status err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n", + load_addr, + m_persistent_variable_sp->GetName().AsCString()); + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data(m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + dump_stream.PutChar('\n'); + } + } + + { + dump_stream.Printf("Target:\n"); + + lldb::addr_t target_address; + + map.ReadPointerFromMemory(&target_address, load_addr, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DataBufferHeap data(m_persistent_variable_sp->GetByteSize().value_or(0), + 0); + + map.ReadMemory(data.GetBytes(), target_address, + m_persistent_variable_sp->GetByteSize().value_or(0), + err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + target_address); + + dump_stream.PutChar('\n'); + } + } + } + + log->PutString(dump_stream.GetString()); + } + + void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {} + +private: + lldb::ExpressionVariableSP m_persistent_variable_sp; + Materializer::PersistentVariableDelegate *m_delegate; +}; + +uint32_t Materializer::AddPersistentVariable( + lldb::ExpressionVariableSP &persistent_variable_sp, + PersistentVariableDelegate *delegate, Status &err) { + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + *iter = std::make_unique<EntityPersistentVariable>(persistent_variable_sp, + delegate); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +/// Base class for materialization of Variables and ValueObjects. +/// +/// Subclasses specify how to obtain the Value which is to be +/// materialized. +class EntityVariableBase : public Materializer::Entity { +public: + virtual ~EntityVariableBase() = default; + + EntityVariableBase() { + // Hard-coding to maximum size of a pointer since all variables are + // materialized by reference + m_size = g_default_var_byte_size; + m_alignment = g_default_var_alignment; + } + + void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + if (log) { + LLDB_LOGF(log, + "EntityVariable::Materialize [address = 0x%" PRIx64 + ", m_variable_sp = %s]", + (uint64_t)load_addr, GetName().GetCString()); + } + + ExecutionContextScope *scope = frame_sp.get(); + + if (!scope) + scope = map.GetBestExecutionContextScope(); + + lldb::ValueObjectSP valobj_sp = SetupValueObject(scope); + + if (!valobj_sp) { + err.SetErrorStringWithFormat( + "couldn't get a value object for variable %s", GetName().AsCString()); + return; + } + + Status valobj_error = valobj_sp->GetError(); + + if (valobj_error.Fail()) { + err.SetErrorStringWithFormat("couldn't get the value of variable %s: %s", + GetName().AsCString(), + valobj_error.AsCString()); + return; + } + + if (m_is_reference) { + DataExtractor valobj_extractor; + Status extract_error; + valobj_sp->GetData(valobj_extractor, extract_error); + + if (!extract_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't read contents of reference variable %s: %s", + GetName().AsCString(), extract_error.AsCString()); + return; + } + + lldb::offset_t offset = 0; + lldb::addr_t reference_addr = valobj_extractor.GetAddress(&offset); + + Status write_error; + map.WritePointerToMemory(load_addr, reference_addr, write_error); + + if (!write_error.Success()) { + err.SetErrorStringWithFormat("couldn't write the contents of reference " + "variable %s to memory: %s", + GetName().AsCString(), + write_error.AsCString()); + return; + } + } else { + AddressType address_type = eAddressTypeInvalid; + const bool scalar_is_load_address = false; + lldb::addr_t addr_of_valobj = + valobj_sp->GetAddressOf(scalar_is_load_address, &address_type); + if (addr_of_valobj != LLDB_INVALID_ADDRESS) { + Status write_error; + map.WritePointerToMemory(load_addr, addr_of_valobj, write_error); + + if (!write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write the address of variable %s to memory: %s", + GetName().AsCString(), write_error.AsCString()); + return; + } + } else { + DataExtractor data; + Status extract_error; + valobj_sp->GetData(data, extract_error); + if (!extract_error.Success()) { + err.SetErrorStringWithFormat("couldn't get the value of %s: %s", + GetName().AsCString(), + extract_error.AsCString()); + return; + } + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { + err.SetErrorStringWithFormat( + "trying to create a temporary region for %s but one exists", + GetName().AsCString()); + return; + } + + if (data.GetByteSize() < GetByteSize(scope)) { + if (data.GetByteSize() == 0 && !LocationExpressionIsValid()) { + err.SetErrorStringWithFormat("the variable '%s' has no location, " + "it may have been optimized out", + GetName().AsCString()); + } else { + err.SetErrorStringWithFormat( + "size of variable %s (%" PRIu64 + ") is larger than the ValueObject's size (%" PRIu64 ")", + GetName().AsCString(), GetByteSize(scope).value_or(0), + data.GetByteSize()); + } + return; + } + + std::optional<size_t> opt_bit_align = GetTypeBitAlign(scope); + if (!opt_bit_align) { + err.SetErrorStringWithFormat("can't get the type alignment for %s", + GetName().AsCString()); + return; + } + + size_t byte_align = (*opt_bit_align + 7) / 8; + + Status alloc_error; + const bool zero_memory = false; + + m_temporary_allocation = map.Malloc( + data.GetByteSize(), byte_align, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, zero_memory, alloc_error); + + m_temporary_allocation_size = data.GetByteSize(); + + m_original_data = std::make_shared<DataBufferHeap>(data.GetDataStart(), + data.GetByteSize()); + + if (!alloc_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't allocate a temporary region for %s: %s", + GetName().AsCString(), alloc_error.AsCString()); + return; + } + + Status write_error; + + map.WriteMemory(m_temporary_allocation, data.GetDataStart(), + data.GetByteSize(), write_error); + + if (!write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write to the temporary region for %s: %s", + GetName().AsCString(), write_error.AsCString()); + return; + } + + Status pointer_write_error; + + map.WritePointerToMemory(load_addr, m_temporary_allocation, + pointer_write_error); + + if (!pointer_write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write the address of the temporary region for %s: %s", + GetName().AsCString(), pointer_write_error.AsCString()); + } + } + } + } + + void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, lldb::addr_t frame_top, + lldb::addr_t frame_bottom, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + if (log) { + LLDB_LOGF(log, + "EntityVariable::Dematerialize [address = 0x%" PRIx64 + ", m_variable_sp = %s]", + (uint64_t)load_addr, GetName().AsCString()); + } + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { + ExecutionContextScope *scope = frame_sp.get(); + + if (!scope) + scope = map.GetBestExecutionContextScope(); + + lldb::ValueObjectSP valobj_sp = SetupValueObject(scope); + + if (!valobj_sp) { + err.SetErrorStringWithFormat( + "couldn't get a value object for variable %s", + GetName().AsCString()); + return; + } + + lldb_private::DataExtractor data; + + Status extract_error; + + map.GetMemoryData(data, m_temporary_allocation, + valobj_sp->GetByteSize().value_or(0), extract_error); + + if (!extract_error.Success()) { + err.SetErrorStringWithFormat("couldn't get the data for variable %s", + GetName().AsCString()); + return; + } + + bool actually_write = true; + + if (m_original_data) { + if ((data.GetByteSize() == m_original_data->GetByteSize()) && + !memcmp(m_original_data->GetBytes(), data.GetDataStart(), + data.GetByteSize())) { + actually_write = false; + } + } + + Status set_error; + + if (actually_write) { + valobj_sp->SetData(data, set_error); + + if (!set_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write the new contents of %s back into the variable", + GetName().AsCString()); + return; + } + } + + Status free_error; + + map.Free(m_temporary_allocation, free_error); + + if (!free_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't free the temporary region for %s: %s", + GetName().AsCString(), free_error.AsCString()); + return; + } + + m_original_data.reset(); + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + } + + void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, + Log *log) override { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", load_addr); + + Status err; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data(m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DataExtractor extractor(data.GetBytes(), data.GetByteSize(), + map.GetByteOrder(), map.GetAddressByteSize()); + + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + lldb::offset_t offset = 0; + + ptr = extractor.GetAddress(&offset); + + dump_stream.PutChar('\n'); + } + } + + if (m_temporary_allocation == LLDB_INVALID_ADDRESS) { + dump_stream.Printf("Points to process memory:\n"); + } else { + dump_stream.Printf("Temporary allocation:\n"); + } + + if (ptr == LLDB_INVALID_ADDRESS) { + dump_stream.Printf(" <could not be be found>\n"); + } else { + DataBufferHeap data(m_temporary_allocation_size, 0); + + map.ReadMemory(data.GetBytes(), m_temporary_allocation, + m_temporary_allocation_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutString(dump_stream.GetString()); + } + + void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { + Status free_error; + + map.Free(m_temporary_allocation, free_error); + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + } + +private: + virtual ConstString GetName() const = 0; + + /// Creates and returns ValueObject tied to this variable + /// and prepares Entity for materialization. + /// + /// Called each time the Materializer (de)materializes a + /// variable. We re-create the ValueObject based on the + /// current ExecutionContextScope since clients such as + /// conditional breakpoints may materialize the same + /// EntityVariable multiple times with different frames. + /// + /// Each subsequent use of the EntityVariableBase interface + /// will query the newly created ValueObject until this + /// function is called again. + virtual lldb::ValueObjectSP + SetupValueObject(ExecutionContextScope *scope) = 0; + + /// Returns size in bytes of the type associated with this variable + /// + /// \returns On success, returns byte size of the type associated + /// with this variable. Returns std::nullopt otherwise. + virtual std::optional<uint64_t> + GetByteSize(ExecutionContextScope *scope) const = 0; + + /// Returns 'true' if the location expression associated with this variable + /// is valid. + virtual bool LocationExpressionIsValid() const = 0; + + /// Returns alignment of the type associated with this variable in bits. + /// + /// \returns On success, returns alignment in bits for the type associated + /// with this variable. Returns std::nullopt otherwise. + virtual std::optional<size_t> + GetTypeBitAlign(ExecutionContextScope *scope) const = 0; + +protected: + bool m_is_reference = false; + lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS; + size_t m_temporary_allocation_size = 0; + lldb::DataBufferSP m_original_data; +}; + +/// Represents an Entity constructed from a VariableSP. +/// +/// This class is used for materialization of variables for which +/// the user has a VariableSP on hand. The ValueObject is then +/// derived from the associated DWARF location expression when needed +/// by the Materializer. +class EntityVariable : public EntityVariableBase { +public: + EntityVariable(lldb::VariableSP &variable_sp) : m_variable_sp(variable_sp) { + m_is_reference = + m_variable_sp->GetType()->GetForwardCompilerType().IsReferenceType(); + } + + ConstString GetName() const override { return m_variable_sp->GetName(); } + + lldb::ValueObjectSP SetupValueObject(ExecutionContextScope *scope) override { + assert(m_variable_sp != nullptr); + return ValueObjectVariable::Create(scope, m_variable_sp); + } + + std::optional<uint64_t> + GetByteSize(ExecutionContextScope *scope) const override { + return m_variable_sp->GetType()->GetByteSize(scope); + } + + bool LocationExpressionIsValid() const override { + return m_variable_sp->LocationExpressionList().IsValid(); + } + + std::optional<size_t> + GetTypeBitAlign(ExecutionContextScope *scope) const override { + return m_variable_sp->GetType()->GetLayoutCompilerType().GetTypeBitAlign( + scope); + } + +private: + lldb::VariableSP m_variable_sp; ///< Variable that this entity is based on. +}; + +/// Represents an Entity constructed from a VariableSP. +/// +/// This class is used for materialization of variables for +/// which the user does not have a VariableSP available (e.g., +/// when materializing ivars). +class EntityValueObject : public EntityVariableBase { +public: + EntityValueObject(ConstString name, ValueObjectProviderTy provider) + : m_name(name), m_valobj_provider(std::move(provider)) { + assert(m_valobj_provider); + } + + ConstString GetName() const override { return m_name; } + + lldb::ValueObjectSP SetupValueObject(ExecutionContextScope *scope) override { + m_valobj_sp = + m_valobj_provider(GetName(), scope->CalculateStackFrame().get()); + + if (m_valobj_sp) + m_is_reference = m_valobj_sp->GetCompilerType().IsReferenceType(); + + return m_valobj_sp; + } + + std::optional<uint64_t> + GetByteSize(ExecutionContextScope *scope) const override { + if (m_valobj_sp) + return m_valobj_sp->GetCompilerType().GetByteSize(scope); + + return {}; + } + + bool LocationExpressionIsValid() const override { + if (m_valobj_sp) + return m_valobj_sp->GetError().Success(); + + return false; + } + + std::optional<size_t> + GetTypeBitAlign(ExecutionContextScope *scope) const override { + if (m_valobj_sp) + return m_valobj_sp->GetCompilerType().GetTypeBitAlign(scope); + + return {}; + } + +private: + ConstString m_name; + lldb::ValueObjectSP m_valobj_sp; + ValueObjectProviderTy m_valobj_provider; +}; + +uint32_t Materializer::AddVariable(lldb::VariableSP &variable_sp, Status &err) { + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + *iter = std::make_unique<EntityVariable>(variable_sp); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +uint32_t Materializer::AddValueObject(ConstString name, + ValueObjectProviderTy valobj_provider, + Status &err) { + assert(valobj_provider); + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + *iter = std::make_unique<EntityValueObject>(name, std::move(valobj_provider)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityResultVariable : public Materializer::Entity { +public: + EntityResultVariable(const CompilerType &type, bool is_program_reference, + bool keep_in_memory, + Materializer::PersistentVariableDelegate *delegate) + : Entity(), m_type(type), m_is_program_reference(is_program_reference), + m_keep_in_memory(keep_in_memory), m_delegate(delegate) { + // Hard-coding to maximum size of a pointer since all results are + // materialized by reference + m_size = g_default_var_byte_size; + m_alignment = g_default_var_alignment; + } + + void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, Status &err) override { + if (!m_is_program_reference) { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { + err.SetErrorString("Trying to create a temporary region for the result " + "but one exists"); + return; + } + + const lldb::addr_t load_addr = process_address + m_offset; + + ExecutionContextScope *exe_scope = frame_sp.get(); + if (!exe_scope) + exe_scope = map.GetBestExecutionContextScope(); + + std::optional<uint64_t> byte_size = m_type.GetByteSize(exe_scope); + if (!byte_size) { + err.SetErrorStringWithFormat("can't get size of type \"%s\"", + m_type.GetTypeName().AsCString()); + return; + } + + std::optional<size_t> opt_bit_align = m_type.GetTypeBitAlign(exe_scope); + if (!opt_bit_align) { + err.SetErrorStringWithFormat("can't get the alignment of type \"%s\"", + m_type.GetTypeName().AsCString()); + return; + } + + size_t byte_align = (*opt_bit_align + 7) / 8; + + Status alloc_error; + const bool zero_memory = true; + + m_temporary_allocation = map.Malloc( + *byte_size, byte_align, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, zero_memory, alloc_error); + m_temporary_allocation_size = *byte_size; + + if (!alloc_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't allocate a temporary region for the result: %s", + alloc_error.AsCString()); + return; + } + + Status pointer_write_error; + + map.WritePointerToMemory(load_addr, m_temporary_allocation, + pointer_write_error); + + if (!pointer_write_error.Success()) { + err.SetErrorStringWithFormat("couldn't write the address of the " + "temporary region for the result: %s", + pointer_write_error.AsCString()); + } + } + } + + void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, lldb::addr_t frame_top, + lldb::addr_t frame_bottom, Status &err) override { + err.Clear(); + + ExecutionContextScope *exe_scope = frame_sp.get(); + if (!exe_scope) + exe_scope = map.GetBestExecutionContextScope(); + + if (!exe_scope) { + err.SetErrorString("Couldn't dematerialize a result variable: invalid " + "execution context scope"); + return; + } + + lldb::addr_t address; + Status read_error; + const lldb::addr_t load_addr = process_address + m_offset; + + map.ReadPointerFromMemory(&address, load_addr, read_error); + + if (!read_error.Success()) { + err.SetErrorString("Couldn't dematerialize a result variable: couldn't " + "read its address"); + return; + } + + lldb::TargetSP target_sp = exe_scope->CalculateTarget(); + + if (!target_sp) { + err.SetErrorString("Couldn't dematerialize a result variable: no target"); + return; + } + + auto type_system_or_err = + target_sp->GetScratchTypeSystemForLanguage(m_type.GetMinimumLanguage()); + + if (auto error = type_system_or_err.takeError()) { + err.SetErrorStringWithFormat("Couldn't dematerialize a result variable: " + "couldn't get the corresponding type " + "system: %s", + llvm::toString(std::move(error)).c_str()); + return; + } + auto ts = *type_system_or_err; + if (!ts) { + err.SetErrorStringWithFormat("Couldn't dematerialize a result variable: " + "couldn't corresponding type system is " + "no longer live."); + return; + } + PersistentExpressionState *persistent_state = + ts->GetPersistentExpressionState(); + + if (!persistent_state) { + err.SetErrorString("Couldn't dematerialize a result variable: " + "corresponding type system doesn't handle persistent " + "variables"); + return; + } + + ConstString name = m_delegate + ? m_delegate->GetName() + : persistent_state->GetNextPersistentVariableName(); + + lldb::ExpressionVariableSP ret = persistent_state->CreatePersistentVariable( + exe_scope, name, m_type, map.GetByteOrder(), map.GetAddressByteSize()); + + if (!ret) { + err.SetErrorStringWithFormat("couldn't dematerialize a result variable: " + "failed to make persistent variable %s", + name.AsCString()); + return; + } + + lldb::ProcessSP process_sp = + map.GetBestExecutionContextScope()->CalculateProcess(); + + if (m_delegate) { + m_delegate->DidDematerialize(ret); + } + + bool can_persist = + (m_is_program_reference && process_sp && process_sp->CanJIT() && + !(address >= frame_bottom && address < frame_top)); + + if (can_persist && m_keep_in_memory) { + ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, m_type, name, + address, eAddressTypeLoad, + map.GetAddressByteSize()); + } + + ret->ValueUpdated(); + + const size_t pvar_byte_size = ret->GetByteSize().value_or(0); + uint8_t *pvar_data = ret->GetValueBytes(); + + map.ReadMemory(pvar_data, address, pvar_byte_size, read_error); + + if (!read_error.Success()) { + err.SetErrorString( + "Couldn't dematerialize a result variable: couldn't read its memory"); + return; + } + + if (!can_persist || !m_keep_in_memory) { + ret->m_flags |= ExpressionVariable::EVNeedsAllocation; + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { + Status free_error; + map.Free(m_temporary_allocation, free_error); + } + } else { + ret->m_flags |= ExpressionVariable::EVIsLLDBAllocated; + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + + void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, + Log *log) override { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr); + + Status err; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data(m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DataExtractor extractor(data.GetBytes(), data.GetByteSize(), + map.GetByteOrder(), map.GetAddressByteSize()); + + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + lldb::offset_t offset = 0; + + ptr = extractor.GetAddress(&offset); + + dump_stream.PutChar('\n'); + } + } + + if (m_temporary_allocation == LLDB_INVALID_ADDRESS) { + dump_stream.Printf("Points to process memory:\n"); + } else { + dump_stream.Printf("Temporary allocation:\n"); + } + + if (ptr == LLDB_INVALID_ADDRESS) { + dump_stream.Printf(" <could not be be found>\n"); + } else { + DataBufferHeap data(m_temporary_allocation_size, 0); + + map.ReadMemory(data.GetBytes(), m_temporary_allocation, + m_temporary_allocation_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutString(dump_stream.GetString()); + } + + void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override { + if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) { + Status free_error; + + map.Free(m_temporary_allocation, free_error); + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + +private: + CompilerType m_type; + bool m_is_program_reference; + bool m_keep_in_memory; + + lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS; + size_t m_temporary_allocation_size = 0; + Materializer::PersistentVariableDelegate *m_delegate; +}; + +uint32_t Materializer::AddResultVariable(const CompilerType &type, + bool is_program_reference, + bool keep_in_memory, + PersistentVariableDelegate *delegate, + Status &err) { + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + *iter = std::make_unique<EntityResultVariable>(type, is_program_reference, + keep_in_memory, delegate); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntitySymbol : public Materializer::Entity { +public: + EntitySymbol(const Symbol &symbol) : Entity(), m_symbol(symbol) { + // Hard-coding to maximum size of a symbol + m_size = g_default_var_byte_size; + m_alignment = g_default_var_alignment; + } + + void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) { + LLDB_LOGF(log, + "EntitySymbol::Materialize [address = 0x%" PRIx64 + ", m_symbol = %s]", + (uint64_t)load_addr, m_symbol.GetName().AsCString()); + } + + const Address sym_address = m_symbol.GetAddress(); + + ExecutionContextScope *exe_scope = frame_sp.get(); + if (!exe_scope) + exe_scope = map.GetBestExecutionContextScope(); + + lldb::TargetSP target_sp; + + if (exe_scope) + target_sp = map.GetBestExecutionContextScope()->CalculateTarget(); + + if (!target_sp) { + err.SetErrorStringWithFormat( + "couldn't resolve symbol %s because there is no target", + m_symbol.GetName().AsCString()); + return; + } + + lldb::addr_t resolved_address = sym_address.GetLoadAddress(target_sp.get()); + + if (resolved_address == LLDB_INVALID_ADDRESS) + resolved_address = sym_address.GetFileAddress(); + + Status pointer_write_error; + + map.WritePointerToMemory(load_addr, resolved_address, pointer_write_error); + + if (!pointer_write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write the address of symbol %s: %s", + m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); + return; + } + } + + void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, lldb::addr_t frame_top, + lldb::addr_t frame_bottom, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) { + LLDB_LOGF(log, + "EntitySymbol::Dematerialize [address = 0x%" PRIx64 + ", m_symbol = %s]", + (uint64_t)load_addr, m_symbol.GetName().AsCString()); + } + + // no work needs to be done + } + + void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, + Log *log) override { + StreamString dump_stream; + + Status err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntitySymbol (%s)\n", load_addr, + m_symbol.GetName().AsCString()); + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data(m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutString(dump_stream.GetString()); + } + + void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {} + +private: + Symbol m_symbol; +}; + +uint32_t Materializer::AddSymbol(const Symbol &symbol_sp, Status &err) { + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + *iter = std::make_unique<EntitySymbol>(symbol_sp); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityRegister : public Materializer::Entity { +public: + EntityRegister(const RegisterInfo ®ister_info) + : Entity(), m_register_info(register_info) { + // Hard-coding alignment conservatively + m_size = m_register_info.byte_size; + m_alignment = m_register_info.byte_size; + } + + void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) { + LLDB_LOGF(log, + "EntityRegister::Materialize [address = 0x%" PRIx64 + ", m_register_info = %s]", + (uint64_t)load_addr, m_register_info.name); + } + + RegisterValue reg_value; + + if (!frame_sp.get()) { + err.SetErrorStringWithFormat( + "couldn't materialize register %s without a stack frame", + m_register_info.name); + return; + } + + lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); + + if (!reg_context_sp->ReadRegister(&m_register_info, reg_value)) { + err.SetErrorStringWithFormat("couldn't read the value of register %s", + m_register_info.name); + return; + } + + DataExtractor register_data; + + if (!reg_value.GetData(register_data)) { + err.SetErrorStringWithFormat("couldn't get the data for register %s", + m_register_info.name); + return; + } + + if (register_data.GetByteSize() != m_register_info.byte_size) { + err.SetErrorStringWithFormat( + "data for register %s had size %llu but we expected %llu", + m_register_info.name, (unsigned long long)register_data.GetByteSize(), + (unsigned long long)m_register_info.byte_size); + return; + } + + m_register_contents = std::make_shared<DataBufferHeap>( + register_data.GetDataStart(), register_data.GetByteSize()); + + Status write_error; + + map.WriteMemory(load_addr, register_data.GetDataStart(), + register_data.GetByteSize(), write_error); + + if (!write_error.Success()) { + err.SetErrorStringWithFormat( + "couldn't write the contents of register %s: %s", + m_register_info.name, write_error.AsCString()); + return; + } + } + + void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, lldb::addr_t frame_top, + lldb::addr_t frame_bottom, Status &err) override { + Log *log = GetLog(LLDBLog::Expressions); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) { + LLDB_LOGF(log, + "EntityRegister::Dematerialize [address = 0x%" PRIx64 + ", m_register_info = %s]", + (uint64_t)load_addr, m_register_info.name); + } + + Status extract_error; + + DataExtractor register_data; + + if (!frame_sp.get()) { + err.SetErrorStringWithFormat( + "couldn't dematerialize register %s without a stack frame", + m_register_info.name); + return; + } + + lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); + + map.GetMemoryData(register_data, load_addr, m_register_info.byte_size, + extract_error); + + if (!extract_error.Success()) { + err.SetErrorStringWithFormat("couldn't get the data for register %s: %s", + m_register_info.name, + extract_error.AsCString()); + return; + } + + if (!memcmp(register_data.GetDataStart(), m_register_contents->GetBytes(), + register_data.GetByteSize())) { + // No write required, and in particular we avoid errors if the register + // wasn't writable + + m_register_contents.reset(); + return; + } + + m_register_contents.reset(); + + RegisterValue register_value(register_data.GetData(), + register_data.GetByteOrder()); + + if (!reg_context_sp->WriteRegister(&m_register_info, register_value)) { + err.SetErrorStringWithFormat("couldn't write the value of register %s", + m_register_info.name); + return; + } + } + + void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, + Log *log) override { + StreamString dump_stream; + + Status err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityRegister (%s)\n", load_addr, + m_register_info.name); + + { + dump_stream.Printf("Value:\n"); + + DataBufferHeap data(m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) { + dump_stream.Printf(" <could not be read>\n"); + } else { + DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, + load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutString(dump_stream.GetString()); + } + + void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {} + +private: + RegisterInfo m_register_info; + lldb::DataBufferSP m_register_contents; +}; + +uint32_t Materializer::AddRegister(const RegisterInfo ®ister_info, + Status &err) { + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + *iter = std::make_unique<EntityRegister>(register_info); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +Materializer::~Materializer() { + DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); + + if (dematerializer_sp) + dematerializer_sp->Wipe(); +} + +Materializer::DematerializerSP +Materializer::Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, + lldb::addr_t process_address, Status &error) { + ExecutionContextScope *exe_scope = frame_sp.get(); + if (!exe_scope) + exe_scope = map.GetBestExecutionContextScope(); + + DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); + + if (dematerializer_sp) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't materialize: already materialized"); + } + + DematerializerSP ret( + new Dematerializer(*this, frame_sp, map, process_address)); + + if (!exe_scope) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't materialize: target doesn't exist"); + } + + for (EntityUP &entity_up : m_entities) { + entity_up->Materialize(frame_sp, map, process_address, error); + + if (!error.Success()) + return DematerializerSP(); + } + + if (Log *log = GetLog(LLDBLog::Expressions)) { + LLDB_LOGF( + log, + "Materializer::Materialize (frame_sp = %p, process_address = 0x%" PRIx64 + ") materialized:", + static_cast<void *>(frame_sp.get()), process_address); + for (EntityUP &entity_up : m_entities) + entity_up->DumpToLog(map, process_address, log); + } + + m_dematerializer_wp = ret; + + return ret; +} + +void Materializer::Dematerializer::Dematerialize(Status &error, + lldb::addr_t frame_bottom, + lldb::addr_t frame_top) { + lldb::StackFrameSP frame_sp; + + lldb::ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + frame_sp = thread_sp->GetFrameWithStackID(m_stack_id); + + ExecutionContextScope *exe_scope = frame_sp.get(); + if (!exe_scope) + exe_scope = m_map->GetBestExecutionContextScope(); + + if (!IsValid()) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't dematerialize: invalid dematerializer"); + } + + if (!exe_scope) { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't dematerialize: target is gone"); + } else { + if (Log *log = GetLog(LLDBLog::Expressions)) { + LLDB_LOGF(log, + "Materializer::Dematerialize (frame_sp = %p, process_address " + "= 0x%" PRIx64 ") about to dematerialize:", + static_cast<void *>(frame_sp.get()), m_process_address); + for (EntityUP &entity_up : m_materializer->m_entities) + entity_up->DumpToLog(*m_map, m_process_address, log); + } + + for (EntityUP &entity_up : m_materializer->m_entities) { + entity_up->Dematerialize(frame_sp, *m_map, m_process_address, frame_top, + frame_bottom, error); + + if (!error.Success()) + break; + } + } + + Wipe(); +} + +void Materializer::Dematerializer::Wipe() { + if (!IsValid()) + return; + + for (EntityUP &entity_up : m_materializer->m_entities) { + entity_up->Wipe(*m_map, m_process_address); + } + + m_materializer = nullptr; + m_map = nullptr; + m_process_address = LLDB_INVALID_ADDRESS; +} + +Materializer::PersistentVariableDelegate::PersistentVariableDelegate() = + default; +Materializer::PersistentVariableDelegate::~PersistentVariableDelegate() = + default; diff --git a/contrib/llvm-project/lldb/source/Expression/ObjectFileJIT.cpp b/contrib/llvm-project/lldb/source/Expression/ObjectFileJIT.cpp new file mode 100644 index 000000000000..9a839866096b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/ObjectFileJIT.cpp @@ -0,0 +1,222 @@ +//===-- ObjectFileJIT.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Expression/ObjectFileJIT.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Timer.h" +#include "lldb/Utility/UUID.h" + +using namespace lldb; +using namespace lldb_private; + +char ObjectFileJIT::ID; + +void ObjectFileJIT::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileJIT::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectFile *ObjectFileJIT::CreateInstance(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { + // JIT'ed object file is backed by the ObjectFileJITDelegate, never read from + // a file + return nullptr; +} + +ObjectFile *ObjectFileJIT::CreateMemoryInstance(const lldb::ModuleSP &module_sp, + WritableDataBufferSP data_sp, + const ProcessSP &process_sp, + lldb::addr_t header_addr) { + // JIT'ed object file is backed by the ObjectFileJITDelegate, never read from + // memory + return nullptr; +} + +size_t ObjectFileJIT::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + // JIT'ed object file can't be read from a file on disk + return 0; +} + +ObjectFileJIT::ObjectFileJIT(const lldb::ModuleSP &module_sp, + const ObjectFileJITDelegateSP &delegate_sp) + : ObjectFile(module_sp, nullptr, 0, 0, DataBufferSP(), 0), m_delegate_wp() { + if (delegate_sp) { + m_delegate_wp = delegate_sp; + m_data.SetByteOrder(delegate_sp->GetByteOrder()); + m_data.SetAddressByteSize(delegate_sp->GetAddressByteSize()); + } +} + +ObjectFileJIT::~ObjectFileJIT() = default; + +bool ObjectFileJIT::ParseHeader() { + // JIT code is never in a file, nor is it required to have any header + return false; +} + +ByteOrder ObjectFileJIT::GetByteOrder() const { return m_data.GetByteOrder(); } + +bool ObjectFileJIT::IsExecutable() const { return false; } + +uint32_t ObjectFileJIT::GetAddressByteSize() const { + return m_data.GetAddressByteSize(); +} + +void ObjectFileJIT::ParseSymtab(Symtab &symtab) { + ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock()); + if (delegate_sp) + delegate_sp->PopulateSymtab(this, symtab); +} + +bool ObjectFileJIT::IsStripped() { + return false; // JIT code that is in a module is never stripped +} + +void ObjectFileJIT::CreateSections(SectionList &unified_section_list) { + if (!m_sections_up) { + m_sections_up = std::make_unique<SectionList>(); + ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock()); + if (delegate_sp) { + delegate_sp->PopulateSectionList(this, *m_sections_up); + unified_section_list = *m_sections_up; + } + } +} + +void ObjectFileJIT::Dump(Stream *s) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); + s->Printf("%p: ", static_cast<void *>(this)); + s->Indent(); + s->PutCString("ObjectFileJIT"); + + if (ArchSpec arch = GetArchitecture()) + *s << ", arch = " << arch.GetArchitectureName(); + + s->EOL(); + + SectionList *sections = GetSectionList(); + if (sections) + sections->Dump(s->AsRawOstream(), s->GetIndentLevel(), nullptr, true, + UINT32_MAX); + + if (m_symtab_up) + m_symtab_up->Dump(s, nullptr, eSortOrderNone); + } +} + +UUID ObjectFileJIT::GetUUID() { + // TODO: maybe get from delegate, not needed for first pass + return UUID(); +} + +uint32_t ObjectFileJIT::GetDependentModules(FileSpecList &files) { + // JIT modules don't have dependencies, but they could + // if external functions are called and we know where they are + files.Clear(); + return 0; +} + +lldb_private::Address ObjectFileJIT::GetEntryPointAddress() { + return Address(); +} + +lldb_private::Address ObjectFileJIT::GetBaseAddress() { return Address(); } + +ObjectFile::Type ObjectFileJIT::CalculateType() { return eTypeJIT; } + +ObjectFile::Strata ObjectFileJIT::CalculateStrata() { return eStrataJIT; } + +ArchSpec ObjectFileJIT::GetArchitecture() { + if (ObjectFileJITDelegateSP delegate_sp = m_delegate_wp.lock()) + return delegate_sp->GetArchitecture(); + return ArchSpec(); +} + +bool ObjectFileJIT::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + // "value" is an offset to apply to each top level segment + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that size on disk (to avoid __PAGEZERO) and load them + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && section_sp->GetFileSize() > 0 && + !section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + return num_loaded_sections > 0; +} + +size_t ObjectFileJIT::ReadSectionData(lldb_private::Section *section, + lldb::offset_t section_offset, void *dst, + size_t dst_len) { + lldb::offset_t file_size = section->GetFileSize(); + if (section_offset < file_size) { + size_t src_len = file_size - section_offset; + if (src_len > dst_len) + src_len = dst_len; + const uint8_t *src = + ((uint8_t *)(uintptr_t)section->GetFileOffset()) + section_offset; + + memcpy(dst, src, src_len); + return src_len; + } + return 0; +} + +size_t +ObjectFileJIT::ReadSectionData(lldb_private::Section *section, + lldb_private::DataExtractor §ion_data) { + if (section->GetFileSize()) { + const void *src = (void *)(uintptr_t)section->GetFileOffset(); + + DataBufferSP data_sp = + std::make_shared<DataBufferHeap>(src, section->GetFileSize()); + section_data.SetData(data_sp, 0, data_sp->GetByteSize()); + section_data.SetByteOrder(GetByteOrder()); + section_data.SetAddressByteSize(GetAddressByteSize()); + return section_data.GetByteSize(); + } + section_data.Clear(); + return 0; +} diff --git a/contrib/llvm-project/lldb/source/Expression/REPL.cpp b/contrib/llvm-project/lldb/source/Expression/REPL.cpp new file mode 100644 index 000000000000..a6a4ffb5e0af --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/REPL.cpp @@ -0,0 +1,622 @@ +//===-- REPL.cpp ----------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/REPL.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StreamFile.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/AnsiTerminal.h" + +#include <memory> + +using namespace lldb_private; + +char REPL::ID; + +REPL::REPL(Target &target) : m_target(target) { + // Make sure all option values have sane defaults + Debugger &debugger = m_target.GetDebugger(); + debugger.SetShowProgress(false); + auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext(); + m_format_options.OptionParsingStarting(&exe_ctx); + m_varobj_options.OptionParsingStarting(&exe_ctx); +} + +REPL::~REPL() = default; + +lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language, + Debugger *debugger, Target *target, + const char *repl_options) { + uint32_t idx = 0; + lldb::REPLSP ret; + + while (REPLCreateInstance create_instance = + PluginManager::GetREPLCreateCallbackAtIndex(idx)) { + LanguageSet supported_languages = + PluginManager::GetREPLSupportedLanguagesAtIndex(idx++); + if (!supported_languages[language]) + continue; + ret = (*create_instance)(err, language, debugger, target, repl_options); + if (ret) { + break; + } + } + + return ret; +} + +std::string REPL::GetSourcePath() { + llvm::StringRef file_basename = GetSourceFileBasename(); + FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); + if (tmpdir_file_spec) { + tmpdir_file_spec.SetFilename(file_basename); + m_repl_source_path = tmpdir_file_spec.GetPath(); + } else { + tmpdir_file_spec = FileSpec("/tmp"); + tmpdir_file_spec.AppendPathComponent(file_basename); + } + + return tmpdir_file_spec.GetPath(); +} + +lldb::IOHandlerSP REPL::GetIOHandler() { + if (!m_io_handler_sp) { + Debugger &debugger = m_target.GetDebugger(); + m_io_handler_sp = std::make_shared<IOHandlerEditline>( + debugger, IOHandler::Type::REPL, + "lldb-repl", // Name of input reader for history + llvm::StringRef("> "), // prompt + llvm::StringRef(". "), // Continuation prompt + true, // Multi-line + true, // The REPL prompt is always colored + 1, // Line number + *this); + + // Don't exit if CTRL+C is pressed + static_cast<IOHandlerEditline *>(m_io_handler_sp.get()) + ->SetInterruptExits(false); + + if (m_io_handler_sp->GetIsInteractive() && + m_io_handler_sp->GetIsRealTerminal()) { + m_indent_str.assign(debugger.GetTabSize(), ' '); + m_enable_auto_indent = debugger.GetAutoIndent(); + } else { + m_indent_str.clear(); + m_enable_auto_indent = false; + } + } + return m_io_handler_sp; +} + +void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) { + lldb::ProcessSP process_sp = m_target.GetProcessSP(); + if (process_sp && process_sp->IsAlive()) + return; + lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP()); + error_sp->Printf("REPL requires a running target process.\n"); + io_handler.SetIsDone(true); +} + +bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; } + +void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) { +} + +const char *REPL::IOHandlerGetFixIndentationCharacters() { + return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr); +} + +llvm::StringRef REPL::IOHandlerGetControlSequence(char ch) { + static constexpr llvm::StringLiteral control_sequence(":quit\n"); + if (ch == 'd') + return control_sequence; + return {}; +} + +const char *REPL::IOHandlerGetCommandPrefix() { return ":"; } + +const char *REPL::IOHandlerGetHelpPrologue() { + return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. " + "Valid statements, expressions, and declarations are immediately " + "compiled and executed.\n\n" + "The complete set of LLDB debugging commands are also available as " + "described below.\n\nCommands " + "must be prefixed with a colon at the REPL prompt (:quit for " + "example.) Typing just a colon " + "followed by return will switch to the LLDB prompt.\n\n" + "Type “< path” to read in code from a text file “path”.\n\n"; +} + +bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) { + // Check for meta command + const size_t num_lines = lines.GetSize(); + if (num_lines == 1) { + const char *first_line = lines.GetStringAtIndex(0); + if (first_line[0] == ':') + return true; // Meta command is a single line where that starts with ':' + } + + // Check if REPL input is done + std::string source_string(lines.CopyList()); + return SourceIsComplete(source_string); +} + +int REPL::CalculateActualIndentation(const StringList &lines) { + std::string last_line = lines[lines.GetSize() - 1]; + + int actual_indent = 0; + for (char &ch : last_line) { + if (ch != ' ') + break; + ++actual_indent; + } + + return actual_indent; +} + +int REPL::IOHandlerFixIndentation(IOHandler &io_handler, + const StringList &lines, + int cursor_position) { + if (!m_enable_auto_indent) + return 0; + + if (!lines.GetSize()) { + return 0; + } + + int tab_size = io_handler.GetDebugger().GetTabSize(); + + lldb::offset_t desired_indent = + GetDesiredIndentation(lines, cursor_position, tab_size); + + int actual_indent = REPL::CalculateActualIndentation(lines); + + if (desired_indent == LLDB_INVALID_OFFSET) + return 0; + + return (int)desired_indent - actual_indent; +} + +static bool ReadCode(const std::string &path, std::string &code, + lldb::StreamFileSP &error_sp) { + auto &fs = FileSystem::Instance(); + llvm::Twine pathTwine(path); + if (!fs.Exists(pathTwine)) { + error_sp->Printf("no such file at path '%s'\n", path.c_str()); + return false; + } + if (!fs.Readable(pathTwine)) { + error_sp->Printf("could not read file at path '%s'\n", path.c_str()); + return false; + } + const size_t file_size = fs.GetByteSize(pathTwine); + const size_t max_size = code.max_size(); + if (file_size > max_size) { + error_sp->Printf("file at path '%s' too large: " + "file_size = %zu, max_size = %zu\n", + path.c_str(), file_size, max_size); + return false; + } + auto data_sp = fs.CreateDataBuffer(pathTwine); + if (data_sp == nullptr) { + error_sp->Printf("could not create buffer for file at path '%s'\n", + path.c_str()); + return false; + } + code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize()); + return true; +} + +void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) { + lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); + lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP()); + bool extra_line = false; + bool did_quit = false; + + if (code.empty()) { + m_code.AppendString(""); + static_cast<IOHandlerEditline &>(io_handler) + .SetBaseLineNumber(m_code.GetSize() + 1); + } else { + Debugger &debugger = m_target.GetDebugger(); + CommandInterpreter &ci = debugger.GetCommandInterpreter(); + extra_line = ci.GetSpaceReplPrompts(); + + ExecutionContext exe_ctx(m_target.GetProcessSP() + ->GetThreadList() + .GetSelectedThread() + ->GetSelectedFrame(DoNoSelectMostRelevantFrame) + .get()); + + lldb::ProcessSP process_sp(exe_ctx.GetProcessSP()); + + if (code[0] == ':') { + // Meta command + // Strip the ':' + code.erase(0, 1); + if (!llvm::StringRef(code).trim().empty()) { + // "lldb" was followed by arguments, so just execute the command dump + // the results + + // Turn off prompt on quit in case the user types ":quit" + const bool saved_prompt_on_quit = ci.GetPromptOnQuit(); + if (saved_prompt_on_quit) + ci.SetPromptOnQuit(false); + + // Execute the command + CommandReturnObject result(debugger.GetUseColor()); + result.SetImmediateOutputStream(output_sp); + result.SetImmediateErrorStream(error_sp); + ci.HandleCommand(code.c_str(), eLazyBoolNo, result); + + if (saved_prompt_on_quit) + ci.SetPromptOnQuit(true); + + if (result.GetStatus() == lldb::eReturnStatusQuit) { + did_quit = true; + io_handler.SetIsDone(true); + if (debugger.CheckTopIOHandlerTypes( + IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { + // We typed "quit" or an alias to quit so we need to check if the + // command interpreter is above us and tell it that it is done as + // well so we don't drop back into the command interpreter if we + // have already quit + lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); + if (io_handler_sp) + io_handler_sp->SetIsDone(true); + } + } + } else { + // ":" was followed by no arguments, so push the LLDB command prompt + if (debugger.CheckTopIOHandlerTypes( + IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { + // If the user wants to get back to the command interpreter and the + // command interpreter is what launched the REPL, then just let the + // REPL exit and fall back to the command interpreter. + io_handler.SetIsDone(true); + } else { + // The REPL wasn't launched the by the command interpreter, it is the + // base IOHandler, so we need to get the command interpreter and + lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); + if (io_handler_sp) { + io_handler_sp->SetIsDone(false); + debugger.RunIOHandlerAsync(ci.GetIOHandler()); + } + } + } + } else { + if (code[0] == '<') { + // User wants to read code from a file. + // Interpret rest of line as a literal path. + auto path = llvm::StringRef(code.substr(1)).trim().str(); + if (!ReadCode(path, code, error_sp)) { + return; + } + } + + // Unwind any expression we might have been running in case our REPL + // expression crashed and the user was looking around + if (m_dedicated_repl_mode) { + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread && thread->UnwindInnermostExpression().Success()) { + thread->SetSelectedFrameByIndex(0, false); + exe_ctx.SetFrameSP( + thread->GetSelectedFrame(DoNoSelectMostRelevantFrame)); + } + } + + const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors(); + + EvaluateExpressionOptions expr_options = m_expr_options; + expr_options.SetCoerceToId(m_varobj_options.use_objc); + expr_options.SetKeepInMemory(true); + expr_options.SetUseDynamic(m_varobj_options.use_dynamic); + expr_options.SetGenerateDebugInfo(true); + expr_options.SetREPLEnabled(true); + expr_options.SetColorizeErrors(colorize_err); + expr_options.SetPoundLine(m_repl_source_path.c_str(), + m_code.GetSize() + 1); + + expr_options.SetLanguage(GetLanguage()); + + PersistentExpressionState *persistent_state = + m_target.GetPersistentExpressionStateForLanguage(GetLanguage()); + if (!persistent_state) + return; + + const size_t var_count_before = persistent_state->GetSize(); + + const char *expr_prefix = nullptr; + lldb::ValueObjectSP result_valobj_sp; + Status error; + lldb::ExpressionResults execution_results = + UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(), + expr_prefix, result_valobj_sp, error, + nullptr); // fixed expression + + if (llvm::Error err = OnExpressionEvaluated(exe_ctx, code, expr_options, + execution_results, + result_valobj_sp, error)) { + *error_sp << llvm::toString(std::move(err)) << "\n"; + } else if (process_sp && process_sp->IsAlive()) { + bool add_to_code = true; + bool handled = false; + if (result_valobj_sp) { + lldb::Format format = m_format_options.GetFormat(); + + if (result_valobj_sp->GetError().Success()) { + handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp); + } else if (result_valobj_sp->GetError().GetError() == + UserExpression::kNoResult) { + if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) { + error_sp->PutCString("(void)\n"); + handled = true; + } + } + } + + if (debugger.GetPrintDecls()) { + for (size_t vi = var_count_before, ve = persistent_state->GetSize(); + vi != ve; ++vi) { + lldb::ExpressionVariableSP persistent_var_sp = + persistent_state->GetVariableAtIndex(vi); + lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject(); + + PrintOneVariable(debugger, output_sp, valobj_sp, + persistent_var_sp.get()); + } + } + + if (!handled) { + bool useColors = error_sp->GetFile().GetIsTerminalWithColors(); + switch (execution_results) { + case lldb::eExpressionSetupError: + case lldb::eExpressionParseError: + add_to_code = false; + [[fallthrough]]; + case lldb::eExpressionDiscarded: + error_sp->Printf("%s\n", error.AsCString()); + break; + + case lldb::eExpressionCompleted: + break; + case lldb::eExpressionInterrupted: + if (useColors) { + error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); + error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); + } + error_sp->Printf("Execution interrupted. "); + if (useColors) + error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); + error_sp->Printf("Enter code to recover and continue.\nEnter LLDB " + "commands to investigate (type :help for " + "assistance.)\n"); + break; + + case lldb::eExpressionHitBreakpoint: + // Breakpoint was hit, drop into LLDB command interpreter + if (useColors) { + error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); + error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); + } + output_sp->Printf("Execution stopped at breakpoint. "); + if (useColors) + error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); + output_sp->Printf("Enter LLDB commands to investigate (type help " + "for assistance.)\n"); + { + lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); + if (io_handler_sp) { + io_handler_sp->SetIsDone(false); + debugger.RunIOHandlerAsync(ci.GetIOHandler()); + } + } + break; + + case lldb::eExpressionTimedOut: + error_sp->Printf("error: timeout\n"); + if (error.AsCString()) + error_sp->Printf("error: %s\n", error.AsCString()); + break; + case lldb::eExpressionResultUnavailable: + // Shoulnd't happen??? + error_sp->Printf("error: could not fetch result -- %s\n", + error.AsCString()); + break; + case lldb::eExpressionStoppedForDebug: + // Shoulnd't happen??? + error_sp->Printf("error: stopped for debug -- %s\n", + error.AsCString()); + break; + case lldb::eExpressionThreadVanished: + // Shoulnd't happen??? + error_sp->Printf("error: expression thread vanished -- %s\n", + error.AsCString()); + break; + } + } + + if (add_to_code) { + const uint32_t new_default_line = m_code.GetSize() + 1; + + m_code.SplitIntoLines(code); + + // Update our code on disk + if (!m_repl_source_path.empty()) { + auto file = FileSystem::Instance().Open( + FileSpec(m_repl_source_path), + File::eOpenOptionWriteOnly | File::eOpenOptionTruncate | + File::eOpenOptionCanCreate, + lldb::eFilePermissionsFileDefault); + if (file) { + std::string code(m_code.CopyList()); + code.append(1, '\n'); + size_t bytes_written = code.size(); + file.get()->Write(code.c_str(), bytes_written); + file.get()->Close(); + } else { + std::string message = llvm::toString(file.takeError()); + error_sp->Printf("error: couldn't open %s: %s\n", + m_repl_source_path.c_str(), message.c_str()); + } + + // Now set the default file and line to the REPL source file + m_target.GetSourceManager().SetDefaultFileAndLine( + FileSpec(m_repl_source_path), new_default_line); + } + static_cast<IOHandlerEditline &>(io_handler) + .SetBaseLineNumber(m_code.GetSize() + 1); + } + if (extra_line) { + output_sp->Printf("\n"); + } + } + } + + // Don't complain about the REPL process going away if we are in the + // process of quitting. + if (!did_quit && (!process_sp || !process_sp->IsAlive())) { + error_sp->Printf( + "error: REPL process is no longer alive, exiting REPL\n"); + io_handler.SetIsDone(true); + } + } +} + +void REPL::IOHandlerComplete(IOHandler &io_handler, + CompletionRequest &request) { + // Complete an LLDB command if the first character is a colon... + if (request.GetRawLine().starts_with(":")) { + Debugger &debugger = m_target.GetDebugger(); + + // auto complete LLDB commands + llvm::StringRef new_line = request.GetRawLine().drop_front(); + CompletionResult sub_result; + CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1, + sub_result); + debugger.GetCommandInterpreter().HandleCompletion(sub_request); + StringList matches, descriptions; + sub_result.GetMatches(matches); + // Prepend command prefix that was excluded in the completion request. + if (request.GetCursorIndex() == 0) + for (auto &match : matches) + match.insert(0, 1, ':'); + sub_result.GetDescriptions(descriptions); + request.AddCompletions(matches, descriptions); + return; + } + + // Strip spaces from the line and see if we had only spaces + if (request.GetRawLine().trim().empty()) { + // Only spaces on this line, so just indent + request.AddCompletion(m_indent_str); + return; + } + + std::string current_code; + current_code.append(m_code.CopyList()); + + IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler); + StringList current_lines = editline.GetCurrentLines(); + const uint32_t current_line_idx = editline.GetCurrentLineIndex(); + + if (current_line_idx < current_lines.GetSize()) { + for (uint32_t i = 0; i < current_line_idx; ++i) { + const char *line_cstr = current_lines.GetStringAtIndex(i); + if (line_cstr) { + current_code.append("\n"); + current_code.append(line_cstr); + } + } + } + + current_code.append("\n"); + current_code += request.GetRawLine(); + + CompleteCode(current_code, request); +} + +bool QuitCommandOverrideCallback(void *baton, const char **argv) { + Target *target = (Target *)baton; + lldb::ProcessSP process_sp(target->GetProcessSP()); + if (process_sp) { + process_sp->Destroy(false); + process_sp->GetTarget().GetDebugger().ClearIOHandlers(); + } + return false; +} + +Status REPL::RunLoop() { + Status error; + + error = DoInitialization(); + m_repl_source_path = GetSourcePath(); + + if (!error.Success()) + return error; + + Debugger &debugger = m_target.GetDebugger(); + + lldb::IOHandlerSP io_handler_sp(GetIOHandler()); + + FileSpec save_default_file; + uint32_t save_default_line = 0; + + if (!m_repl_source_path.empty()) { + // Save the current default file and line + m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file, + save_default_line); + } + + debugger.RunIOHandlerAsync(io_handler_sp); + + // Check if we are in dedicated REPL mode where LLDB was start with the "-- + // repl" option from the command line. Currently we know this by checking if + // the debugger already has a IOHandler thread. + if (!debugger.HasIOHandlerThread()) { + // The debugger doesn't have an existing IOHandler thread, so this must be + // dedicated REPL mode... + m_dedicated_repl_mode = true; + debugger.StartIOHandlerThread(); + llvm::StringRef command_name_str("quit"); + CommandObject *cmd_obj = + debugger.GetCommandInterpreter().GetCommandObjectForCommand( + command_name_str); + if (cmd_obj) { + assert(command_name_str.empty()); + cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target); + } + } + + // Wait for the REPL command interpreter to get popped + io_handler_sp->WaitForPop(); + + if (m_dedicated_repl_mode) { + // If we were in dedicated REPL mode we would have started the IOHandler + // thread, and we should kill our process + lldb::ProcessSP process_sp = m_target.GetProcessSP(); + if (process_sp && process_sp->IsAlive()) + process_sp->Destroy(false); + + // Wait for the IO handler thread to exit (TODO: don't do this if the IO + // handler thread already exists...) + debugger.JoinIOHandlerThread(); + } + + // Restore the default file and line + if (save_default_file && save_default_line != 0) + m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file, + save_default_line); + return error; +} diff --git a/contrib/llvm-project/lldb/source/Expression/UserExpression.cpp b/contrib/llvm-project/lldb/source/Expression/UserExpression.cpp new file mode 100644 index 000000000000..b78f43995767 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/UserExpression.cpp @@ -0,0 +1,436 @@ +//===-- UserExpression.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <cstdio> +#include <sys/types.h> + +#include <cstdlib> +#include <map> +#include <string> + +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallUserExpression.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/BinaryFormat/Dwarf.h" + +using namespace lldb_private; + +char UserExpression::ID; + +UserExpression::UserExpression(ExecutionContextScope &exe_scope, + llvm::StringRef expr, llvm::StringRef prefix, + SourceLanguage language, ResultType desired_type, + const EvaluateExpressionOptions &options) + : Expression(exe_scope), m_expr_text(std::string(expr)), + m_expr_prefix(std::string(prefix)), m_language(language), + m_desired_type(desired_type), m_options(options) {} + +UserExpression::~UserExpression() = default; + +void UserExpression::InstallContext(ExecutionContext &exe_ctx) { + m_jit_process_wp = exe_ctx.GetProcessSP(); + + lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP(); + + if (frame_sp) + m_address = frame_sp->GetFrameCodeAddress(); +} + +bool UserExpression::LockAndCheckContext(ExecutionContext &exe_ctx, + lldb::TargetSP &target_sp, + lldb::ProcessSP &process_sp, + lldb::StackFrameSP &frame_sp) { + lldb::ProcessSP expected_process_sp = m_jit_process_wp.lock(); + process_sp = exe_ctx.GetProcessSP(); + + if (process_sp != expected_process_sp) + return false; + + process_sp = exe_ctx.GetProcessSP(); + target_sp = exe_ctx.GetTargetSP(); + frame_sp = exe_ctx.GetFrameSP(); + + if (m_address.IsValid()) { + if (!frame_sp) + return false; + return (Address::CompareLoadAddress(m_address, + frame_sp->GetFrameCodeAddress(), + target_sp.get()) == 0); + } + + return true; +} + +bool UserExpression::MatchesContext(ExecutionContext &exe_ctx) { + lldb::TargetSP target_sp; + lldb::ProcessSP process_sp; + lldb::StackFrameSP frame_sp; + + return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp); +} + +lldb::ValueObjectSP UserExpression::GetObjectPointerValueObject( + lldb::StackFrameSP frame_sp, llvm::StringRef object_name, Status &err) { + err.Clear(); + + if (!frame_sp) { + err.SetErrorStringWithFormatv( + "Couldn't load '{0}' because the context is incomplete", object_name); + return {}; + } + + lldb::VariableSP var_sp; + lldb::ValueObjectSP valobj_sp; + + return frame_sp->GetValueForVariableExpressionPath( + object_name, lldb::eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsNoFragileObjcIvar | + StackFrame::eExpressionPathOptionsNoSyntheticChildren | + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var_sp, err); +} + +lldb::addr_t UserExpression::GetObjectPointer(lldb::StackFrameSP frame_sp, + llvm::StringRef object_name, + Status &err) { + auto valobj_sp = + GetObjectPointerValueObject(std::move(frame_sp), object_name, err); + + if (!err.Success() || !valobj_sp.get()) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + if (ret == LLDB_INVALID_ADDRESS) { + err.SetErrorStringWithFormatv( + "Couldn't load '{0}' because its value couldn't be evaluated", + object_name); + return LLDB_INVALID_ADDRESS; + } + + return ret; +} + +lldb::ExpressionResults +UserExpression::Evaluate(ExecutionContext &exe_ctx, + const EvaluateExpressionOptions &options, + llvm::StringRef expr, llvm::StringRef prefix, + lldb::ValueObjectSP &result_valobj_sp, Status &error, + std::string *fixed_expression, ValueObject *ctx_obj) { + Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step)); + + if (ctx_obj) { + static unsigned const ctx_type_mask = lldb::TypeFlags::eTypeIsClass | + lldb::TypeFlags::eTypeIsStructUnion | + lldb::TypeFlags::eTypeIsReference; + if (!(ctx_obj->GetTypeInfo() & ctx_type_mask)) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of " + "an invalid type, can't run expressions."); + error.SetErrorString("a context object of an invalid type passed"); + return lldb::eExpressionSetupError; + } + } + + if (ctx_obj && ctx_obj->GetTypeInfo() & lldb::TypeFlags::eTypeIsReference) { + Status error; + lldb::ValueObjectSP deref_ctx_sp = ctx_obj->Dereference(error); + if (!error.Success()) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of " + "a reference type that can't be dereferenced, can't run " + "expressions."); + error.SetErrorString( + "passed context object of an reference type cannot be deferenced"); + return lldb::eExpressionSetupError; + } + + ctx_obj = deref_ctx_sp.get(); + } + + lldb_private::ExecutionPolicy execution_policy = options.GetExecutionPolicy(); + SourceLanguage language = options.GetLanguage(); + const ResultType desired_type = options.DoesCoerceToId() + ? UserExpression::eResultTypeId + : UserExpression::eResultTypeAny; + lldb::ExpressionResults execution_results = lldb::eExpressionSetupError; + + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a NULL target, can't " + "run expressions."); + error.SetErrorString("expression passed a null target"); + return lldb::eExpressionSetupError; + } + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == nullptr && execution_policy == eExecutionPolicyAlways) { + LLDB_LOG(log, "== [UserExpression::Evaluate] No process, but the policy is " + "eExecutionPolicyAlways"); + + error.SetErrorString("expression needed to run but couldn't: no process"); + + return execution_results; + } + + // Since we might need to allocate memory, we need to be stopped to run + // an expression. + if (process != nullptr && process->GetState() != lldb::eStateStopped) { + error.SetErrorStringWithFormatv( + "unable to evaluate expression while the process is {0}: the process " + "must be stopped because the expression might require allocating " + "memory.", + StateAsCString(process->GetState())); + return execution_results; + } + + // Explicitly force the IR interpreter to evaluate the expression when the + // there is no process that supports running the expression for us. Don't + // change the execution policy if we have the special top-level policy that + // doesn't contain any expression and there is nothing to interpret. + if (execution_policy != eExecutionPolicyTopLevel && + (process == nullptr || !process->CanJIT())) + execution_policy = eExecutionPolicyNever; + + // We need to set the expression execution thread here, turns out parse can + // call functions in the process of looking up symbols, which will escape the + // context set by exe_ctx passed to Execute. + lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP(); + ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher( + thread_sp); + + llvm::StringRef full_prefix; + llvm::StringRef option_prefix(options.GetPrefix()); + std::string full_prefix_storage; + if (!prefix.empty() && !option_prefix.empty()) { + full_prefix_storage = std::string(prefix); + full_prefix_storage.append(std::string(option_prefix)); + full_prefix = full_prefix_storage; + } else if (!prefix.empty()) + full_prefix = prefix; + else + full_prefix = option_prefix; + + // If the language was not specified in the expression command, set it to the + // language in the target's properties if specified, else default to the + // langage for the frame. + if (!language) { + if (target->GetLanguage() != lldb::eLanguageTypeUnknown) + language = target->GetLanguage(); + else if (StackFrame *frame = exe_ctx.GetFramePtr()) + language = frame->GetLanguage(); + } + + lldb::UserExpressionSP user_expression_sp( + target->GetUserExpressionForLanguage(expr, full_prefix, language, + desired_type, options, ctx_obj, + error)); + if (error.Fail() || !user_expression_sp) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Getting expression: {0} ==", + error.AsCString()); + return lldb::eExpressionSetupError; + } + + LLDB_LOG(log, "== [UserExpression::Evaluate] Parsing expression {0} ==", + expr.str()); + + const bool keep_expression_in_memory = true; + const bool generate_debug_info = options.GetGenerateDebugInfo(); + + if (options.InvokeCancelCallback(lldb::eExpressionEvaluationParse)) { + error.SetErrorString("expression interrupted by callback before parse"); + result_valobj_sp = ValueObjectConstResult::Create( + exe_ctx.GetBestExecutionContextScope(), error); + return lldb::eExpressionInterrupted; + } + + DiagnosticManager diagnostic_manager; + + bool parse_success = + user_expression_sp->Parse(diagnostic_manager, exe_ctx, execution_policy, + keep_expression_in_memory, generate_debug_info); + + // Calculate the fixed expression always, since we need it for errors. + std::string tmp_fixed_expression; + if (fixed_expression == nullptr) + fixed_expression = &tmp_fixed_expression; + + *fixed_expression = user_expression_sp->GetFixedText().str(); + + // If there is a fixed expression, try to parse it: + if (!parse_success) { + // Delete the expression that failed to parse before attempting to parse + // the next expression. + user_expression_sp.reset(); + + execution_results = lldb::eExpressionParseError; + if (!fixed_expression->empty() && options.GetAutoApplyFixIts()) { + const uint64_t max_fix_retries = options.GetRetriesWithFixIts(); + for (uint64_t i = 0; i < max_fix_retries; ++i) { + // Try parsing the fixed expression. + lldb::UserExpressionSP fixed_expression_sp( + target->GetUserExpressionForLanguage( + fixed_expression->c_str(), full_prefix, language, desired_type, + options, ctx_obj, error)); + if (!fixed_expression_sp) + break; + DiagnosticManager fixed_diagnostic_manager; + parse_success = fixed_expression_sp->Parse( + fixed_diagnostic_manager, exe_ctx, execution_policy, + keep_expression_in_memory, generate_debug_info); + if (parse_success) { + diagnostic_manager.Clear(); + user_expression_sp = fixed_expression_sp; + break; + } + // The fixed expression also didn't parse. Let's check for any new + // fixits we could try. + if (!fixed_expression_sp->GetFixedText().empty()) { + *fixed_expression = fixed_expression_sp->GetFixedText().str(); + } else { + // Fixed expression didn't compile without a fixit, don't retry and + // don't tell the user about it. + fixed_expression->clear(); + break; + } + } + } + + if (!parse_success) { + std::string msg; + { + llvm::raw_string_ostream os(msg); + if (!diagnostic_manager.Diagnostics().empty()) + os << diagnostic_manager.GetString(); + else + os << "expression failed to parse (no further compiler diagnostics)"; + if (target->GetEnableNotifyAboutFixIts() && fixed_expression && + !fixed_expression->empty()) + os << "\nfixed expression suggested:\n " << *fixed_expression; + } + error.SetExpressionError(execution_results, msg.c_str()); + } + } + + if (parse_success) { + lldb::ExpressionVariableSP expr_result; + + if (execution_policy == eExecutionPolicyNever && + !user_expression_sp->CanInterpret()) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Expression may not run, but " + "is not constant =="); + + if (!diagnostic_manager.Diagnostics().size()) + error.SetExpressionError(lldb::eExpressionSetupError, + "expression needed to run but couldn't"); + } else if (execution_policy == eExecutionPolicyTopLevel) { + error.SetError(UserExpression::kNoResult, lldb::eErrorTypeGeneric); + return lldb::eExpressionCompleted; + } else { + if (options.InvokeCancelCallback(lldb::eExpressionEvaluationExecution)) { + error.SetExpressionError( + lldb::eExpressionInterrupted, + "expression interrupted by callback before execution"); + result_valobj_sp = ValueObjectConstResult::Create( + exe_ctx.GetBestExecutionContextScope(), error); + return lldb::eExpressionInterrupted; + } + + diagnostic_manager.Clear(); + + LLDB_LOG(log, "== [UserExpression::Evaluate] Executing expression =="); + + execution_results = + user_expression_sp->Execute(diagnostic_manager, exe_ctx, options, + user_expression_sp, expr_result); + + if (execution_results != lldb::eExpressionCompleted) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Execution completed " + "abnormally =="); + + if (!diagnostic_manager.Diagnostics().size()) + error.SetExpressionError( + execution_results, "expression failed to execute, unknown error"); + else + error.SetExpressionError(execution_results, + diagnostic_manager.GetString().c_str()); + } else { + if (expr_result) { + result_valobj_sp = expr_result->GetValueObject(); + result_valobj_sp->SetPreferredDisplayLanguage( + language.AsLanguageType()); + + LLDB_LOG(log, + "== [UserExpression::Evaluate] Execution completed " + "normally with result {0} ==", + result_valobj_sp->GetValueAsCString()); + } else { + LLDB_LOG(log, "== [UserExpression::Evaluate] Execution completed " + "normally with no result =="); + + error.SetError(UserExpression::kNoResult, lldb::eErrorTypeGeneric); + } + } + } + } + + if (options.InvokeCancelCallback(lldb::eExpressionEvaluationComplete)) { + error.SetExpressionError( + lldb::eExpressionInterrupted, + "expression interrupted by callback after complete"); + return lldb::eExpressionInterrupted; + } + + if (result_valobj_sp.get() == nullptr) { + result_valobj_sp = ValueObjectConstResult::Create( + exe_ctx.GetBestExecutionContextScope(), error); + } + + return execution_results; +} + +lldb::ExpressionResults +UserExpression::Execute(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + const EvaluateExpressionOptions &options, + lldb::UserExpressionSP &shared_ptr_to_me, + lldb::ExpressionVariableSP &result_var) { + lldb::ExpressionResults expr_result = DoExecute( + diagnostic_manager, exe_ctx, options, shared_ptr_to_me, result_var); + Target *target = exe_ctx.GetTargetPtr(); + if (options.GetSuppressPersistentResult() && result_var && target) { + if (auto *persistent_state = + target->GetPersistentExpressionStateForLanguage( + m_language.AsLanguageType())) + persistent_state->RemovePersistentVariable(result_var); + } + return expr_result; +} diff --git a/contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp b/contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp new file mode 100644 index 000000000000..7b34c2c2ff76 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp @@ -0,0 +1,114 @@ +//===-- UtilityFunction.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <cstdio> +#include <sys/types.h> + +#include "lldb/Core/Module.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb_private; +using namespace lldb; + +char UtilityFunction::ID; + +/// Constructor +/// +/// \param[in] text +/// The text of the function. Must be a full translation unit. +/// +/// \param[in] name +/// The name of the function, as used in the text. +UtilityFunction::UtilityFunction(ExecutionContextScope &exe_scope, + std::string text, std::string name, + bool enable_debugging) + : Expression(exe_scope), m_execution_unit_sp(), m_jit_module_wp(), + m_function_text(std::move(text)), m_function_name(std::move(name)) {} + +UtilityFunction::~UtilityFunction() { + lldb::ProcessSP process_sp(m_jit_process_wp.lock()); + if (process_sp) { + lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock()); + if (jit_module_sp) + process_sp->GetTarget().GetImages().Remove(jit_module_sp); + } +} + +// FIXME: We should check that every time this is called it is called with the +// same return type & arguments... + +FunctionCaller *UtilityFunction::MakeFunctionCaller( + const CompilerType &return_type, const ValueList &arg_value_list, + lldb::ThreadSP thread_to_use_sp, Status &error) { + if (m_caller_up) + return m_caller_up.get(); + + ProcessSP process_sp = m_jit_process_wp.lock(); + if (!process_sp) { + error.SetErrorString("Can't make a function caller without a process."); + return nullptr; + } + // Since we might need to allocate memory and maybe call code to make + // the caller, we need to be stopped. + if (process_sp->GetState() != lldb::eStateStopped) { + error.SetErrorStringWithFormatv( + "Can't make a function caller while the process is {0}: the process " + "must be stopped to allocate memory.", + StateAsCString(process_sp->GetState())); + return nullptr; + } + + Address impl_code_address; + impl_code_address.SetOffset(StartAddress()); + std::string name(m_function_name); + name.append("-caller"); + + m_caller_up.reset(process_sp->GetTarget().GetFunctionCallerForLanguage( + Language().AsLanguageType(), return_type, impl_code_address, + arg_value_list, name.c_str(), error)); + if (error.Fail()) { + + return nullptr; + } + if (m_caller_up) { + DiagnosticManager diagnostics; + + unsigned num_errors = + m_caller_up->CompileFunction(thread_to_use_sp, diagnostics); + if (num_errors) { + error.SetErrorStringWithFormat( + "Error compiling %s caller function: \"%s\".", + m_function_name.c_str(), diagnostics.GetString().c_str()); + m_caller_up.reset(); + return nullptr; + } + + diagnostics.Clear(); + ExecutionContext exe_ctx(process_sp); + + if (!m_caller_up->WriteFunctionWrapper(exe_ctx, diagnostics)) { + error.SetErrorStringWithFormat( + "Error inserting caller function for %s: \"%s\".", + m_function_name.c_str(), diagnostics.GetString().c_str()); + m_caller_up.reset(); + return nullptr; + } + } + return m_caller_up.get(); +} |