aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Expression
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Expression')
-rw-r--r--contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp2403
-rw-r--r--contrib/llvm-project/lldb/source/Expression/DWARFExpressionList.cpp240
-rw-r--r--contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp87
-rw-r--r--contrib/llvm-project/lldb/source/Expression/Expression.cpp28
-rw-r--r--contrib/llvm-project/lldb/source/Expression/ExpressionParser.cpp72
-rw-r--r--contrib/llvm-project/lldb/source/Expression/ExpressionTypeSystemHelper.cpp13
-rw-r--r--contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp86
-rw-r--r--contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp418
-rw-r--r--contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp1206
-rw-r--r--contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp1605
-rw-r--r--contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp864
-rw-r--r--contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp377
-rw-r--r--contrib/llvm-project/lldb/source/Expression/Materializer.cpp1604
-rw-r--r--contrib/llvm-project/lldb/source/Expression/ObjectFileJIT.cpp222
-rw-r--r--contrib/llvm-project/lldb/source/Expression/REPL.cpp622
-rw-r--r--contrib/llvm-project/lldb/source/Expression/UserExpression.cpp436
-rw-r--r--contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp114
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 &param : 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(&param_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 = &param;
+ 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 &param_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 &section_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 &register_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 &register_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 &section_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();
+}