Page MenuHomePhabricator

trunk
No OneTemporary

File Metadata

Created
Mon, May 25, 8:46 AM
This file is larger than 256 KB, so syntax highlighting was skipped.
Index: lit/SymbolFile/PDB/variables-locations.test
===================================================================
--- lit/SymbolFile/PDB/variables-locations.test (revision 352844)
+++ lit/SymbolFile/PDB/variables-locations.test (revision 352845)
@@ -1,14 +1,20 @@
REQUIRES: system-windows, lld
RUN: %build --compiler=clang-cl --output=%t.exe %S/Inputs/VariablesLocationsTest.cpp
+RUN: env LLDB_USE_NATIVE_PDB_READER=0 %lldb -b -s %S/Inputs/VariablesLocationsTest.script -- %t.exe | FileCheck %s
RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -b -s %S/Inputs/VariablesLocationsTest.script -- %t.exe | FileCheck %s
CHECK: g_var = 2222
CHECK: arg_0 = 1111
CHECK: arg_1 = 0.123
CHECK: loc_0 = 'x'
CHECK: loc_1 = 0.567
CHECK: loc_0 = true
CHECK: loc_1 = 3333
+
+CHECK: arg_0 = 22
+
+CHECK: loc_0 = (a = 1234)
+CHECK: loc_1 = 5678
Index: lit/SymbolFile/PDB/Inputs/VariablesLocationsTest.script
===================================================================
--- lit/SymbolFile/PDB/Inputs/VariablesLocationsTest.script (revision 352844)
+++ lit/SymbolFile/PDB/Inputs/VariablesLocationsTest.script (revision 352845)
@@ -1,16 +1,25 @@
breakpoint set --file VariablesLocationsTest.cpp --line 6
+breakpoint set --file VariablesLocationsTest.cpp --line 15
run
target variable g_var
frame variable arg_0
frame variable arg_1
frame variable loc_0
frame variable loc_1
frame select 1
frame variable loc_0
frame variable loc_1
+
+continue
+
+frame variable arg_0
+
+frame variable loc_0
+frame variable loc_1
+
Index: lit/SymbolFile/PDB/Inputs/VariablesLocationsTest.cpp
===================================================================
--- lit/SymbolFile/PDB/Inputs/VariablesLocationsTest.cpp (revision 352844)
+++ lit/SymbolFile/PDB/Inputs/VariablesLocationsTest.cpp (revision 352845)
@@ -1,15 +1,26 @@
int g_var = 2222;
void __fastcall foo(short arg_0, float arg_1) {
char loc_0 = 'x';
double loc_1 = 0.5678;
}
+__declspec(align(128)) struct S {
+ int a = 1234;
+};
+
+void bar(int arg_0) {
+ S loc_0;
+ int loc_1 = 5678;
+}
+
+
int main(int argc, char *argv[]) {
bool loc_0 = true;
int loc_1 = 3333;
foo(1111, 0.1234);
+ bar(22);
return 0;
}
Index: source/Expression/DWARFExpression.cpp
===================================================================
--- source/Expression/DWARFExpression.cpp (revision 352844)
+++ source/Expression/DWARFExpression.cpp (revision 352845)
@@ -1,3416 +1,3416 @@
//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Expression/DWARFExpression.h"
#include <inttypes.h>
#include <vector>
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Utility/DataEncoder.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/VMRange.h"
#include "lldb/Host/Host.h"
#include "lldb/Utility/Endian.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackID.h"
#include "lldb/Target/Thread.h"
#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
using namespace lldb;
using namespace lldb_private;
static lldb::addr_t
ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
uint32_t index) {
uint32_t index_size = dwarf_cu->GetAddressByteSize();
dw_offset_t addr_base = dwarf_cu->GetAddrBase();
lldb::offset_t offset = addr_base + index * index_size;
return dwarf_cu->GetSymbolFileDWARF()->get_debug_addr_data().GetMaxU64(
&offset, index_size);
}
//----------------------------------------------------------------------
// DWARFExpression constructor
//----------------------------------------------------------------------
DWARFExpression::DWARFExpression(DWARFUnit *dwarf_cu)
: m_module_wp(), m_data(), m_dwarf_cu(dwarf_cu),
m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {}
DWARFExpression::DWARFExpression(const DWARFExpression &rhs)
: m_module_wp(rhs.m_module_wp), m_data(rhs.m_data),
m_dwarf_cu(rhs.m_dwarf_cu), m_reg_kind(rhs.m_reg_kind),
m_loclist_slide(rhs.m_loclist_slide) {}
DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp,
const DataExtractor &data,
DWARFUnit *dwarf_cu,
lldb::offset_t data_offset,
lldb::offset_t data_length)
: m_module_wp(), m_data(data, data_offset, data_length),
m_dwarf_cu(dwarf_cu), m_reg_kind(eRegisterKindDWARF),
m_loclist_slide(LLDB_INVALID_ADDRESS) {
if (module_sp)
m_module_wp = module_sp;
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
DWARFExpression::~DWARFExpression() {}
bool DWARFExpression::IsValid() const { return m_data.GetByteSize() > 0; }
void DWARFExpression::SetOpcodeData(const DataExtractor &data) {
m_data = data;
}
void DWARFExpression::CopyOpcodeData(lldb::ModuleSP module_sp,
const DataExtractor &data,
lldb::offset_t data_offset,
lldb::offset_t data_length) {
const uint8_t *bytes = data.PeekData(data_offset, data_length);
if (bytes) {
m_module_wp = module_sp;
m_data.SetData(DataBufferSP(new DataBufferHeap(bytes, data_length)));
m_data.SetByteOrder(data.GetByteOrder());
m_data.SetAddressByteSize(data.GetAddressByteSize());
}
}
void DWARFExpression::CopyOpcodeData(const void *data,
lldb::offset_t data_length,
ByteOrder byte_order,
uint8_t addr_byte_size) {
if (data && data_length) {
m_data.SetData(DataBufferSP(new DataBufferHeap(data, data_length)));
m_data.SetByteOrder(byte_order);
m_data.SetAddressByteSize(addr_byte_size);
}
}
void DWARFExpression::CopyOpcodeData(uint64_t const_value,
lldb::offset_t const_value_byte_size,
uint8_t addr_byte_size) {
if (const_value_byte_size) {
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::SetOpcodeData(lldb::ModuleSP module_sp,
const DataExtractor &data,
lldb::offset_t data_offset,
lldb::offset_t data_length) {
m_module_wp = module_sp;
m_data.SetData(data, data_offset, data_length);
}
void DWARFExpression::DumpLocation(Stream *s, lldb::offset_t offset,
lldb::offset_t length,
lldb::DescriptionLevel level,
ABI *abi) const {
if (!m_data.ValidOffsetForDataOfSize(offset, length))
return;
const lldb::offset_t start_offset = offset;
const lldb::offset_t end_offset = offset + length;
while (m_data.ValidOffset(offset) && offset < end_offset) {
const lldb::offset_t op_offset = offset;
const uint8_t op = m_data.GetU8(&offset);
switch (level) {
default:
break;
case lldb::eDescriptionLevelBrief:
if (op_offset > start_offset)
s->PutChar(' ');
break;
case lldb::eDescriptionLevelFull:
case lldb::eDescriptionLevelVerbose:
if (op_offset > start_offset)
s->EOL();
s->Indent();
if (level == lldb::eDescriptionLevelFull)
break;
// Fall through for verbose and print offset and DW_OP prefix..
s->Printf("0x%8.8" PRIx64 ": %s", op_offset,
op >= DW_OP_APPLE_uninit ? "DW_OP_APPLE_" : "DW_OP_");
break;
}
switch (op) {
case DW_OP_addr:
*s << "DW_OP_addr(" << m_data.GetAddress(&offset) << ") ";
break; // 0x03 1 address
case DW_OP_deref:
*s << "DW_OP_deref";
break; // 0x06
case DW_OP_const1u:
s->Printf("DW_OP_const1u(0x%2.2x)", m_data.GetU8(&offset));
break; // 0x08 1 1-byte constant
case DW_OP_const1s:
s->Printf("DW_OP_const1s(0x%2.2x)", m_data.GetU8(&offset));
break; // 0x09 1 1-byte constant
case DW_OP_const2u:
s->Printf("DW_OP_const2u(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x0a 1 2-byte constant
case DW_OP_const2s:
s->Printf("DW_OP_const2s(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x0b 1 2-byte constant
case DW_OP_const4u:
s->Printf("DW_OP_const4u(0x%8.8x)", m_data.GetU32(&offset));
break; // 0x0c 1 4-byte constant
case DW_OP_const4s:
s->Printf("DW_OP_const4s(0x%8.8x)", m_data.GetU32(&offset));
break; // 0x0d 1 4-byte constant
case DW_OP_const8u:
s->Printf("DW_OP_const8u(0x%16.16" PRIx64 ")", m_data.GetU64(&offset));
break; // 0x0e 1 8-byte constant
case DW_OP_const8s:
s->Printf("DW_OP_const8s(0x%16.16" PRIx64 ")", m_data.GetU64(&offset));
break; // 0x0f 1 8-byte constant
case DW_OP_constu:
s->Printf("DW_OP_constu(0x%" PRIx64 ")", m_data.GetULEB128(&offset));
break; // 0x10 1 ULEB128 constant
case DW_OP_consts:
s->Printf("DW_OP_consts(0x%" PRId64 ")", m_data.GetSLEB128(&offset));
break; // 0x11 1 SLEB128 constant
case DW_OP_dup:
s->PutCString("DW_OP_dup");
break; // 0x12
case DW_OP_drop:
s->PutCString("DW_OP_drop");
break; // 0x13
case DW_OP_over:
s->PutCString("DW_OP_over");
break; // 0x14
case DW_OP_pick:
s->Printf("DW_OP_pick(0x%2.2x)", m_data.GetU8(&offset));
break; // 0x15 1 1-byte stack index
case DW_OP_swap:
s->PutCString("DW_OP_swap");
break; // 0x16
case DW_OP_rot:
s->PutCString("DW_OP_rot");
break; // 0x17
case DW_OP_xderef:
s->PutCString("DW_OP_xderef");
break; // 0x18
case DW_OP_abs:
s->PutCString("DW_OP_abs");
break; // 0x19
case DW_OP_and:
s->PutCString("DW_OP_and");
break; // 0x1a
case DW_OP_div:
s->PutCString("DW_OP_div");
break; // 0x1b
case DW_OP_minus:
s->PutCString("DW_OP_minus");
break; // 0x1c
case DW_OP_mod:
s->PutCString("DW_OP_mod");
break; // 0x1d
case DW_OP_mul:
s->PutCString("DW_OP_mul");
break; // 0x1e
case DW_OP_neg:
s->PutCString("DW_OP_neg");
break; // 0x1f
case DW_OP_not:
s->PutCString("DW_OP_not");
break; // 0x20
case DW_OP_or:
s->PutCString("DW_OP_or");
break; // 0x21
case DW_OP_plus:
s->PutCString("DW_OP_plus");
break; // 0x22
case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend
s->Printf("DW_OP_plus_uconst(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_shl:
s->PutCString("DW_OP_shl");
break; // 0x24
case DW_OP_shr:
s->PutCString("DW_OP_shr");
break; // 0x25
case DW_OP_shra:
s->PutCString("DW_OP_shra");
break; // 0x26
case DW_OP_xor:
s->PutCString("DW_OP_xor");
break; // 0x27
case DW_OP_skip:
s->Printf("DW_OP_skip(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x2f 1 signed 2-byte constant
case DW_OP_bra:
s->Printf("DW_OP_bra(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x28 1 signed 2-byte constant
case DW_OP_eq:
s->PutCString("DW_OP_eq");
break; // 0x29
case DW_OP_ge:
s->PutCString("DW_OP_ge");
break; // 0x2a
case DW_OP_gt:
s->PutCString("DW_OP_gt");
break; // 0x2b
case DW_OP_le:
s->PutCString("DW_OP_le");
break; // 0x2c
case DW_OP_lt:
s->PutCString("DW_OP_lt");
break; // 0x2d
case DW_OP_ne:
s->PutCString("DW_OP_ne");
break; // 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:
s->Printf("DW_OP_lit%i", op - DW_OP_lit0);
break; // 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
{
uint32_t reg_num = op - DW_OP_reg0;
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->PutCString(reg_info.name);
break;
} else if (reg_info.alt_name) {
s->PutCString(reg_info.alt_name);
break;
}
}
}
s->Printf("DW_OP_reg%u", reg_num);
break;
} break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31: {
uint32_t reg_num = op - DW_OP_breg0;
int64_t reg_offset = m_data.GetSLEB128(&offset);
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset);
break;
} else if (reg_info.alt_name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset);
break;
}
}
}
s->Printf("DW_OP_breg%i(0x%" PRIx64 ")", reg_num, reg_offset);
} break;
case DW_OP_regx: // 0x90 1 ULEB128 register
{
uint32_t reg_num = m_data.GetULEB128(&offset);
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->PutCString(reg_info.name);
break;
} else if (reg_info.alt_name) {
s->PutCString(reg_info.alt_name);
break;
}
}
}
s->Printf("DW_OP_regx(%" PRIu32 ")", reg_num);
break;
} break;
case DW_OP_fbreg: // 0x91 1 SLEB128 offset
s->Printf("DW_OP_fbreg(%" PRIi64 ")", m_data.GetSLEB128(&offset));
break;
case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset
{
uint32_t reg_num = m_data.GetULEB128(&offset);
int64_t reg_offset = m_data.GetSLEB128(&offset);
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset);
break;
} else if (reg_info.alt_name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset);
break;
}
}
}
s->Printf("DW_OP_bregx(reg=%" PRIu32 ",offset=%" PRIi64 ")", reg_num,
reg_offset);
} break;
case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed
s->Printf("DW_OP_piece(0x%" PRIx64 ")", m_data.GetULEB128(&offset));
break;
case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved
s->Printf("DW_OP_deref_size(0x%2.2x)", m_data.GetU8(&offset));
break;
case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved
s->Printf("DW_OP_xderef_size(0x%2.2x)", m_data.GetU8(&offset));
break;
case DW_OP_nop:
s->PutCString("DW_OP_nop");
break; // 0x96
case DW_OP_push_object_address:
s->PutCString("DW_OP_push_object_address");
break; // 0x97 DWARF3
case DW_OP_call2: // 0x98 DWARF3 1 2-byte offset of DIE
s->Printf("DW_OP_call2(0x%4.4x)", m_data.GetU16(&offset));
break;
case DW_OP_call4: // 0x99 DWARF3 1 4-byte offset of DIE
s->Printf("DW_OP_call4(0x%8.8x)", m_data.GetU32(&offset));
break;
case DW_OP_call_ref: // 0x9a DWARF3 1 4- or 8-byte offset of DIE
s->Printf("DW_OP_call_ref(0x%8.8" PRIx64 ")", m_data.GetAddress(&offset));
break;
// case DW_OP_call_frame_cfa: s << "call_frame_cfa"; break;
// // 0x9c DWARF3
// case DW_OP_bit_piece: // 0x9d DWARF3 2
// s->Printf("DW_OP_bit_piece(0x%x, 0x%x)",
// m_data.GetULEB128(&offset), m_data.GetULEB128(&offset));
// break;
// case DW_OP_lo_user: s->PutCString("DW_OP_lo_user"); break;
// // 0xe0
// case DW_OP_hi_user: s->PutCString("DW_OP_hi_user"); break;
// // 0xff
// case DW_OP_APPLE_extern:
// s->Printf("DW_OP_APPLE_extern(%" PRIu64 ")",
// m_data.GetULEB128(&offset));
// break;
// case DW_OP_APPLE_array_ref:
// s->PutCString("DW_OP_APPLE_array_ref");
// break;
case DW_OP_form_tls_address:
s->PutCString("DW_OP_form_tls_address"); // 0x9b
break;
case DW_OP_GNU_addr_index: // 0xfb
s->Printf("DW_OP_GNU_addr_index(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_GNU_const_index: // 0xfc
s->Printf("DW_OP_GNU_const_index(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_GNU_push_tls_address:
s->PutCString("DW_OP_GNU_push_tls_address"); // 0xe0
break;
case DW_OP_APPLE_uninit:
s->PutCString("DW_OP_APPLE_uninit"); // 0xF0
break;
// case DW_OP_APPLE_assign: // 0xF1 - pops value off and
// assigns it to second item on stack (2nd item must have
// assignable context)
// s->PutCString("DW_OP_APPLE_assign");
// break;
// case DW_OP_APPLE_address_of: // 0xF2 - gets the address of
// the top stack item (top item must be a variable, or have
// value_type that is an address already)
// s->PutCString("DW_OP_APPLE_address_of");
// break;
// case DW_OP_APPLE_value_of: // 0xF3 - pops the value off the
// stack and pushes the value of that object (top item must be a
// variable, or expression local)
// s->PutCString("DW_OP_APPLE_value_of");
// break;
// case DW_OP_APPLE_deref_type: // 0xF4 - gets the address of
// the top stack item (top item must be a variable, or a clang
// type)
// s->PutCString("DW_OP_APPLE_deref_type");
// break;
// case DW_OP_APPLE_expr_local: // 0xF5 - ULEB128 expression
// local index
// s->Printf("DW_OP_APPLE_expr_local(%" PRIu64 ")",
// m_data.GetULEB128(&offset));
// break;
// case DW_OP_APPLE_constf: // 0xF6 - 1 byte float size,
// followed by constant float data
// {
// uint8_t float_length = m_data.GetU8(&offset);
// s->Printf("DW_OP_APPLE_constf(<%u> ", float_length);
// m_data.Dump(s, offset, eFormatHex, float_length, 1,
// UINT32_MAX, DW_INVALID_ADDRESS, 0, 0);
// s->PutChar(')');
// // Consume the float data
// m_data.GetData(&offset, float_length);
// }
// break;
// case DW_OP_APPLE_scalar_cast:
// s->Printf("DW_OP_APPLE_scalar_cast(%s)",
// Scalar::GetValueTypeAsCString
// ((Scalar::Type)m_data.GetU8(&offset)));
// break;
// case DW_OP_APPLE_clang_cast:
// {
// clang::Type *clang_type = (clang::Type
// *)m_data.GetMaxU64(&offset, sizeof(void*));
// s->Printf("DW_OP_APPLE_clang_cast(%p)", clang_type);
// }
// break;
// case DW_OP_APPLE_clear:
// s->PutCString("DW_OP_APPLE_clear");
// break;
// case DW_OP_APPLE_error: // 0xFF - Stops expression
// evaluation and returns an error (no args)
// s->PutCString("DW_OP_APPLE_error");
// break;
}
}
}
void DWARFExpression::SetLocationListSlide(addr_t slide) {
m_loclist_slide = slide;
}
int DWARFExpression::GetRegisterKind() { return m_reg_kind; }
void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
m_reg_kind = reg_kind;
}
bool DWARFExpression::IsLocationList() const {
return m_loclist_slide != LLDB_INVALID_ADDRESS;
}
void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level,
addr_t location_list_base_addr,
ABI *abi) const {
if (IsLocationList()) {
// We have a location list
lldb::offset_t offset = 0;
uint32_t count = 0;
addr_t curr_base_addr = location_list_base_addr;
while (m_data.ValidOffset(offset)) {
addr_t begin_addr_offset = LLDB_INVALID_ADDRESS;
addr_t end_addr_offset = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset,
begin_addr_offset, end_addr_offset))
break;
if (begin_addr_offset == 0 && end_addr_offset == 0)
break;
if (begin_addr_offset < end_addr_offset) {
if (count > 0)
s->PutCString(", ");
VMRange addr_range(curr_base_addr + begin_addr_offset,
curr_base_addr + end_addr_offset);
addr_range.Dump(s, 0, 8);
s->PutChar('{');
lldb::offset_t location_length = m_data.GetU16(&offset);
DumpLocation(s, offset, location_length, level, abi);
s->PutChar('}');
offset += location_length;
} else {
if ((m_data.GetAddressByteSize() == 4 &&
(begin_addr_offset == UINT32_MAX)) ||
(m_data.GetAddressByteSize() == 8 &&
(begin_addr_offset == UINT64_MAX))) {
curr_base_addr = end_addr_offset + location_list_base_addr;
// We have a new base address
if (count > 0)
s->PutCString(", ");
*s << "base_addr = " << end_addr_offset;
}
}
count++;
}
} else {
// We have a normal location that contains DW_OP location opcodes
DumpLocation(s, 0, m_data.GetByteSize(), level, abi);
}
}
static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
lldb::RegisterKind reg_kind,
uint32_t reg_num, Status *error_ptr,
Value &value) {
if (reg_ctx == NULL) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat("No register context in frame.\n");
} else {
uint32_t native_reg =
reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
if (native_reg == LLDB_INVALID_REGNUM) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat("Unable to convert register "
"kind=%u reg_num=%u to a native "
"register number.\n",
reg_kind, reg_num);
} else {
const RegisterInfo *reg_info =
reg_ctx->GetRegisterInfoAtIndex(native_reg);
RegisterValue reg_value;
if (reg_ctx->ReadRegister(reg_info, reg_value)) {
if (reg_value.GetScalarValue(value.GetScalar())) {
value.SetValueType(Value::eValueTypeScalar);
value.SetContext(Value::eContextTypeRegisterInfo,
const_cast<RegisterInfo *>(reg_info));
if (error_ptr)
error_ptr->Clear();
return true;
} else {
// If we get this error, then we need to implement a value buffer in
// the dwarf expression evaluation function...
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"register %s can't be converted to a scalar value",
reg_info->name);
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat("register %s is not available",
reg_info->name);
}
}
}
return false;
}
// bool
// DWARFExpression::LocationListContainsLoadAddress (Process* process, const
// Address &addr) const
//{
// return LocationListContainsLoadAddress(process,
// addr.GetLoadAddress(process));
//}
//
// bool
// DWARFExpression::LocationListContainsLoadAddress (Process* process, addr_t
// load_addr) const
//{
// if (load_addr == LLDB_INVALID_ADDRESS)
// return false;
//
// if (IsLocationList())
// {
// lldb::offset_t offset = 0;
//
// addr_t loc_list_base_addr = m_loclist_slide.GetLoadAddress(process);
//
// if (loc_list_base_addr == LLDB_INVALID_ADDRESS)
// return false;
//
// while (m_data.ValidOffset(offset))
// {
// // We need to figure out what the value is for the location.
// addr_t lo_pc = m_data.GetAddress(&offset);
// addr_t hi_pc = m_data.GetAddress(&offset);
// if (lo_pc == 0 && hi_pc == 0)
// break;
// else
// {
// lo_pc += loc_list_base_addr;
// hi_pc += loc_list_base_addr;
//
// if (lo_pc <= load_addr && load_addr < hi_pc)
// return true;
//
// offset += m_data.GetU16(&offset);
// }
// }
// }
// return false;
//}
static offset_t GetOpcodeDataSize(const DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) {
lldb::offset_t offset = data_offset;
switch (op) {
case DW_OP_addr:
case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3)
return data.GetAddressByteSize();
// Opcodes with no arguments
case DW_OP_deref: // 0x06
case DW_OP_dup: // 0x12
case DW_OP_drop: // 0x13
case DW_OP_over: // 0x14
case DW_OP_swap: // 0x16
case DW_OP_rot: // 0x17
case DW_OP_xderef: // 0x18
case DW_OP_abs: // 0x19
case DW_OP_and: // 0x1a
case DW_OP_div: // 0x1b
case DW_OP_minus: // 0x1c
case DW_OP_mod: // 0x1d
case DW_OP_mul: // 0x1e
case DW_OP_neg: // 0x1f
case DW_OP_not: // 0x20
case DW_OP_or: // 0x21
case DW_OP_plus: // 0x22
case DW_OP_shl: // 0x24
case DW_OP_shr: // 0x25
case DW_OP_shra: // 0x26
case DW_OP_xor: // 0x27
case DW_OP_eq: // 0x29
case DW_OP_ge: // 0x2a
case DW_OP_gt: // 0x2b
case DW_OP_le: // 0x2c
case DW_OP_lt: // 0x2d
case DW_OP_ne: // 0x2e
case DW_OP_lit0: // 0x30
case DW_OP_lit1: // 0x31
case DW_OP_lit2: // 0x32
case DW_OP_lit3: // 0x33
case DW_OP_lit4: // 0x34
case DW_OP_lit5: // 0x35
case DW_OP_lit6: // 0x36
case DW_OP_lit7: // 0x37
case DW_OP_lit8: // 0x38
case DW_OP_lit9: // 0x39
case DW_OP_lit10: // 0x3A
case DW_OP_lit11: // 0x3B
case DW_OP_lit12: // 0x3C
case DW_OP_lit13: // 0x3D
case DW_OP_lit14: // 0x3E
case DW_OP_lit15: // 0x3F
case DW_OP_lit16: // 0x40
case DW_OP_lit17: // 0x41
case DW_OP_lit18: // 0x42
case DW_OP_lit19: // 0x43
case DW_OP_lit20: // 0x44
case DW_OP_lit21: // 0x45
case DW_OP_lit22: // 0x46
case DW_OP_lit23: // 0x47
case DW_OP_lit24: // 0x48
case DW_OP_lit25: // 0x49
case DW_OP_lit26: // 0x4A
case DW_OP_lit27: // 0x4B
case DW_OP_lit28: // 0x4C
case DW_OP_lit29: // 0x4D
case DW_OP_lit30: // 0x4E
case DW_OP_lit31: // 0x4f
case DW_OP_reg0: // 0x50
case DW_OP_reg1: // 0x51
case DW_OP_reg2: // 0x52
case DW_OP_reg3: // 0x53
case DW_OP_reg4: // 0x54
case DW_OP_reg5: // 0x55
case DW_OP_reg6: // 0x56
case DW_OP_reg7: // 0x57
case DW_OP_reg8: // 0x58
case DW_OP_reg9: // 0x59
case DW_OP_reg10: // 0x5A
case DW_OP_reg11: // 0x5B
case DW_OP_reg12: // 0x5C
case DW_OP_reg13: // 0x5D
case DW_OP_reg14: // 0x5E
case DW_OP_reg15: // 0x5F
case DW_OP_reg16: // 0x60
case DW_OP_reg17: // 0x61
case DW_OP_reg18: // 0x62
case DW_OP_reg19: // 0x63
case DW_OP_reg20: // 0x64
case DW_OP_reg21: // 0x65
case DW_OP_reg22: // 0x66
case DW_OP_reg23: // 0x67
case DW_OP_reg24: // 0x68
case DW_OP_reg25: // 0x69
case DW_OP_reg26: // 0x6A
case DW_OP_reg27: // 0x6B
case DW_OP_reg28: // 0x6C
case DW_OP_reg29: // 0x6D
case DW_OP_reg30: // 0x6E
case DW_OP_reg31: // 0x6F
case DW_OP_nop: // 0x96
case DW_OP_push_object_address: // 0x97 DWARF3
case DW_OP_form_tls_address: // 0x9b DWARF3
case DW_OP_call_frame_cfa: // 0x9c DWARF3
case DW_OP_stack_value: // 0x9f DWARF4
case DW_OP_GNU_push_tls_address: // 0xe0 GNU extension
return 0;
// Opcodes with a single 1 byte arguments
case DW_OP_const1u: // 0x08 1 1-byte constant
case DW_OP_const1s: // 0x09 1 1-byte constant
case DW_OP_pick: // 0x15 1 1-byte stack index
case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved
case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved
return 1;
// Opcodes with a single 2 byte arguments
case DW_OP_const2u: // 0x0a 1 2-byte constant
case DW_OP_const2s: // 0x0b 1 2-byte constant
case DW_OP_skip: // 0x2f 1 signed 2-byte constant
case DW_OP_bra: // 0x28 1 signed 2-byte constant
case DW_OP_call2: // 0x98 1 2-byte offset of DIE (DWARF3)
return 2;
// Opcodes with a single 4 byte arguments
case DW_OP_const4u: // 0x0c 1 4-byte constant
case DW_OP_const4s: // 0x0d 1 4-byte constant
case DW_OP_call4: // 0x99 1 4-byte offset of DIE (DWARF3)
return 4;
// Opcodes with a single 8 byte arguments
case DW_OP_const8u: // 0x0e 1 8-byte constant
case DW_OP_const8s: // 0x0f 1 8-byte constant
return 8;
// All opcodes that have a single ULEB (signed or unsigned) argument
case DW_OP_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;
}
default:
break;
}
return LLDB_INVALID_OFFSET;
}
lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx,
bool &error) const {
error = false;
if (IsLocationList())
return LLDB_INVALID_ADDRESS;
lldb::offset_t offset = 0;
uint32_t curr_op_addr_idx = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_addr) {
const lldb::addr_t op_file_addr = m_data.GetAddress(&offset);
if (curr_op_addr_idx == op_addr_idx)
return op_file_addr;
else
++curr_op_addr_idx;
} else if (op == DW_OP_GNU_addr_index) {
uint64_t index = m_data.GetULEB128(&offset);
if (curr_op_addr_idx == op_addr_idx) {
if (!m_dwarf_cu) {
error = true;
break;
}
return ReadAddressFromDebugAddrSection(m_dwarf_cu, index);
} else
++curr_op_addr_idx;
} else {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET) {
error = true;
break;
}
offset += op_arg_size;
}
}
return LLDB_INVALID_ADDRESS;
}
bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
if (IsLocationList())
return false;
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_addr) {
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
// We have to make a copy of the data as we don't know if this data is
// from a read only memory mapped buffer, so we duplicate all of the data
// first, then modify it, and if all goes well, we then replace the data
// for this expression
// So first we copy the data into a heap buffer
std::unique_ptr<DataBufferHeap> head_data_ap(
new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize()));
// Make en encoder so we can write the address into the buffer using the
// correct byte order (endianness)
DataEncoder encoder(head_data_ap->GetBytes(), head_data_ap->GetByteSize(),
m_data.GetByteOrder(), addr_byte_size);
// Replace the address in the new buffer
if (encoder.PutMaxU64(offset, addr_byte_size, file_addr) == UINT32_MAX)
return false;
// All went well, so now we can reset the data using a shared pointer to
// the heap data so "m_data" will now correctly manage the heap data.
m_data.SetData(DataBufferSP(head_data_ap.release()));
return true;
} else {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET)
break;
offset += op_arg_size;
}
}
return false;
}
bool DWARFExpression::ContainsThreadLocalStorage() const {
// We are assuming for now that any thread local variable will not have a
// location list. This has been true for all thread local variables we have
// seen so far produced by any compiler.
if (IsLocationList())
return false;
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address)
return true;
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
else
offset += op_arg_size;
}
return false;
}
bool DWARFExpression::LinkThreadLocalStorage(
lldb::ModuleSP new_module_sp,
std::function<lldb::addr_t(lldb::addr_t file_addr)> const
&link_address_callback) {
// We are assuming for now that any thread local variable will not have a
// location list. This has been true for all thread local variables we have
// seen so far produced by any compiler.
if (IsLocationList())
return false;
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
// We have to make a copy of the data as we don't know if this data is from a
// read only memory mapped buffer, so we duplicate all of the data first,
// then modify it, and if all goes well, we then replace the data for this
// expression
// So first we copy the data into a heap buffer
std::shared_ptr<DataBufferHeap> heap_data_sp(
new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize()));
// Make en encoder so we can write the address into the buffer using the
// correct byte order (endianness)
DataEncoder encoder(heap_data_sp->GetBytes(), heap_data_sp->GetByteSize(),
m_data.GetByteOrder(), addr_byte_size);
lldb::offset_t offset = 0;
lldb::offset_t const_offset = 0;
lldb::addr_t const_value = 0;
size_t const_byte_size = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
bool decoded_data = false;
switch (op) {
case DW_OP_const4u:
// Remember the const offset in case we later have a
// DW_OP_form_tls_address or DW_OP_GNU_push_tls_address
const_offset = offset;
const_value = m_data.GetU32(&offset);
decoded_data = true;
const_byte_size = 4;
break;
case DW_OP_const8u:
// Remember the const offset in case we later have a
// DW_OP_form_tls_address or DW_OP_GNU_push_tls_address
const_offset = offset;
const_value = m_data.GetU64(&offset);
decoded_data = true;
const_byte_size = 8;
break;
case DW_OP_form_tls_address:
case DW_OP_GNU_push_tls_address:
// DW_OP_form_tls_address and DW_OP_GNU_push_tls_address must be preceded
// by a file address on the stack. We assume that DW_OP_const4u or
// DW_OP_const8u is used for these values, and we check that the last
// opcode we got before either of these was DW_OP_const4u or
// DW_OP_const8u. If so, then we can link the value accodingly. For
// Darwin, the value in the DW_OP_const4u or DW_OP_const8u is the file
// address of a structure that contains a function pointer, the pthread
// key and the offset into the data pointed to by the pthread key. So we
// must link this address and also set the module of this expression to
// the new_module_sp so we can resolve the file address correctly
if (const_byte_size > 0) {
lldb::addr_t linked_file_addr = link_address_callback(const_value);
if (linked_file_addr == LLDB_INVALID_ADDRESS)
return false;
// Replace the address in the new buffer
if (encoder.PutMaxU64(const_offset, const_byte_size,
linked_file_addr) == UINT32_MAX)
return false;
}
break;
default:
const_offset = 0;
const_value = 0;
const_byte_size = 0;
break;
}
if (!decoded_data) {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
else
offset += op_arg_size;
}
}
// If we linked the TLS address correctly, update the module so that when the
// expression is evaluated it can resolve the file address to a load address
// and read the
// TLS data
m_module_wp = new_module_sp;
m_data.SetData(heap_data_sp);
return true;
}
bool DWARFExpression::LocationListContainsAddress(
lldb::addr_t loclist_base_addr, lldb::addr_t addr) const {
if (addr == LLDB_INVALID_ADDRESS)
return false;
if (IsLocationList()) {
lldb::offset_t offset = 0;
if (loclist_base_addr == LLDB_INVALID_ADDRESS)
return false;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
addr_t hi_pc = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
hi_pc))
break;
if (lo_pc == 0 && hi_pc == 0)
break;
lo_pc += loclist_base_addr - m_loclist_slide;
hi_pc += loclist_base_addr - m_loclist_slide;
if (lo_pc <= addr && addr < hi_pc)
return true;
offset += m_data.GetU16(&offset);
}
}
return false;
}
bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
lldb::offset_t &offset,
lldb::offset_t &length) {
offset = 0;
if (!IsLocationList()) {
length = m_data.GetByteSize();
return true;
}
if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) {
addr_t curr_base_addr = base_addr;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
addr_t hi_pc = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
hi_pc))
break;
if (lo_pc == 0 && hi_pc == 0)
break;
lo_pc += curr_base_addr - m_loclist_slide;
hi_pc += curr_base_addr - m_loclist_slide;
length = m_data.GetU16(&offset);
if (length > 0 && lo_pc <= pc && pc < hi_pc)
return true;
offset += length;
}
}
offset = LLDB_INVALID_OFFSET;
length = 0;
return false;
}
bool DWARFExpression::DumpLocationForAddress(Stream *s,
lldb::DescriptionLevel level,
addr_t base_addr, addr_t address,
ABI *abi) {
lldb::offset_t offset = 0;
lldb::offset_t length = 0;
if (GetLocation(base_addr, address, offset, length)) {
if (length > 0) {
DumpLocation(s, offset, length, level, abi);
return true;
}
}
return false;
}
bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope,
lldb::addr_t loclist_base_load_addr,
const Value *initial_value_ptr,
const Value *object_address_ptr, Value &result,
Status *error_ptr) const {
ExecutionContext exe_ctx(exe_scope);
return Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, initial_value_ptr,
object_address_ptr, result, error_ptr);
}
bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
RegisterContext *reg_ctx,
lldb::addr_t loclist_base_load_addr,
const Value *initial_value_ptr,
const Value *object_address_ptr, Value &result,
Status *error_ptr) const {
ModuleSP module_sp = m_module_wp.lock();
if (IsLocationList()) {
lldb::offset_t offset = 0;
addr_t pc;
StackFrame *frame = NULL;
if (reg_ctx)
pc = reg_ctx->GetPC();
else {
frame = exe_ctx->GetFramePtr();
if (!frame)
return false;
RegisterContextSP reg_ctx_sp = frame->GetRegisterContext();
if (!reg_ctx_sp)
return false;
pc = reg_ctx_sp->GetPC();
}
if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) {
if (pc == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorString("Invalid PC in frame.");
return false;
}
addr_t curr_loclist_base_load_addr = loclist_base_load_addr;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
addr_t hi_pc = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset,
lo_pc, hi_pc))
break;
if (lo_pc == 0 && hi_pc == 0)
break;
lo_pc += curr_loclist_base_load_addr - m_loclist_slide;
hi_pc += curr_loclist_base_load_addr - m_loclist_slide;
uint16_t length = m_data.GetU16(&offset);
if (length > 0 && lo_pc <= pc && pc < hi_pc) {
return DWARFExpression::Evaluate(
exe_ctx, reg_ctx, module_sp, m_data, m_dwarf_cu, offset, length,
m_reg_kind, initial_value_ptr, object_address_ptr, result,
error_ptr);
}
offset += length;
}
}
if (error_ptr)
error_ptr->SetErrorString("variable not available");
return false;
}
// Not a location list, just a single expression.
return DWARFExpression::Evaluate(
exe_ctx, reg_ctx, module_sp, m_data, m_dwarf_cu, 0, m_data.GetByteSize(),
m_reg_kind, initial_value_ptr, object_address_ptr, result, error_ptr);
}
bool DWARFExpression::Evaluate(
ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::ModuleSP module_sp, const DataExtractor &opcodes,
DWARFUnit *dwarf_cu, const lldb::offset_t opcodes_offset,
const lldb::offset_t opcodes_length, const lldb::RegisterKind reg_kind,
const Value *initial_value_ptr, const Value *object_address_ptr,
Value &result, Status *error_ptr) {
if (opcodes_length == 0) {
if (error_ptr)
error_ptr->SetErrorString(
"no location, value may have been optimized out");
return false;
}
std::vector<Value> stack;
Process *process = NULL;
StackFrame *frame = NULL;
if (exe_ctx) {
process = exe_ctx->GetProcessPtr();
frame = exe_ctx->GetFramePtr();
}
if (reg_ctx == NULL && frame)
reg_ctx = frame->GetRegisterContext().get();
if (initial_value_ptr)
stack.push_back(*initial_value_ptr);
lldb::offset_t offset = opcodes_offset;
const lldb::offset_t end_offset = opcodes_offset + opcodes_length;
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
// Make sure all of the data is available in opcodes.
if (!opcodes.ValidOffsetForDataOfSize(opcodes_offset, opcodes_length)) {
if (error_ptr)
error_ptr->SetErrorString(
"invalid offset and/or length for opcodes buffer.");
return false;
}
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
while (opcodes.ValidOffset(offset) && offset < end_offset) {
const lldb::offset_t op_offset = offset;
const uint8_t op = opcodes.GetU8(&offset);
if (log && log->GetVerbose()) {
size_t count = stack.size();
log->Printf("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);
log->Printf(" %s", new_value.GetData());
}
log->Printf("0x%8.8" PRIx64 ": %s", op_offset, DW_OP_value_to_name(op));
}
switch (op) {
//----------------------------------------------------------------------
// The DW_OP_addr operation has a single operand that encodes a machine
// address and whose size is the size of an address on the target machine.
//----------------------------------------------------------------------
case DW_OP_addr:
stack.push_back(Scalar(opcodes.GetAddress(&offset)));
stack.back().SetValueType(Value::eValueTypeFileAddress);
// Convert the file address to a load address, so subsequent
// DWARF operators can operate on it.
if (frame)
stack.back().ConvertToLoadAddress(module_sp.get(),
frame->CalculateTarget().get());
break;
//----------------------------------------------------------------------
// The DW_OP_addr_sect_offset4 is used for any location expressions in
// shared libraries that have a location like:
// DW_OP_addr(0x1000)
// If this address resides in a shared library, then this virtual address
// won't make sense when it is evaluated in the context of a running
// process where shared libraries have been slid. To account for this, this
// new address type where we can store the section pointer and a 4 byte
// offset.
//----------------------------------------------------------------------
// case DW_OP_addr_sect_offset4:
// {
// result_type = eResultTypeFileAddress;
// lldb::Section *sect = (lldb::Section
// *)opcodes.GetMaxU64(&offset, sizeof(void *));
// lldb::addr_t sect_offset = opcodes.GetU32(&offset);
//
// Address so_addr (sect, sect_offset);
// lldb::addr_t load_addr = so_addr.GetLoadAddress();
// if (load_addr != LLDB_INVALID_ADDRESS)
// {
// // We successfully resolve a file address to a load
// // address.
// stack.push_back(load_addr);
// break;
// }
// else
// {
// // We were able
// if (error_ptr)
// error_ptr->SetErrorStringWithFormat ("Section %s in
// %s is not currently loaded.\n",
// sect->GetName().AsCString(),
// sect->GetModule()->GetFileSpec().GetFilename().AsCString());
// return false;
// }
// }
// break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_deref
// OPERANDS: none
// DESCRIPTION: Pops the top stack entry and treats it as an address.
// The value retrieved from that address is pushed. The size of the data
// retrieved from the dereferenced address is the size of an address on the
// target machine.
//----------------------------------------------------------------------
case DW_OP_deref: {
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString("Expression stack empty for DW_OP_deref.");
return false;
}
Value::ValueType value_type = stack.back().GetValueType();
switch (value_type) {
case Value::eValueTypeHostAddress: {
void *src = (void *)stack.back().GetScalar().ULongLong();
intptr_t ptr;
::memcpy(&ptr, src, sizeof(void *));
stack.back().GetScalar() = ptr;
stack.back().ClearContext();
} break;
case Value::eValueTypeFileAddress: {
auto file_addr = stack.back().GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
if (!module_sp) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"need module to resolve file address for DW_OP_deref");
return false;
}
Address so_addr;
if (!module_sp->ResolveFileAddress(file_addr, so_addr)) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to resolve file address in module");
return false;
}
addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
if (load_Addr == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to resolve load address");
return false;
}
stack.back().GetScalar() = load_Addr;
stack.back().SetValueType(Value::eValueTypeLoadAddress);
// Fall through to load address code below...
} LLVM_FALLTHROUGH;
case Value::eValueTypeLoadAddress:
if (exe_ctx) {
if (process) {
lldb::addr_t pointer_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
Status error;
lldb::addr_t pointer_value =
process->ReadPointerFromMemory(pointer_addr, error);
if (pointer_value != LLDB_INVALID_ADDRESS) {
stack.back().GetScalar() = pointer_value;
stack.back().ClearContext();
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"Failed to dereference pointer from 0x%" PRIx64
" for DW_OP_deref: %s\n",
pointer_addr, error.AsCString());
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL process for DW_OP_deref.\n");
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL execution context for DW_OP_deref.\n");
return false;
}
break;
default:
break;
}
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_deref_size
// OPERANDS: 1
// 1 - uint8_t that specifies the size of the data to dereference.
// DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top
// stack entry and treats it as an address. The value retrieved from that
// address is pushed. In the DW_OP_deref_size operation, however, the size
// in bytes of the data retrieved from the dereferenced address is
// specified by the single operand. This operand is a 1-byte unsigned
// integral constant whose value may not be larger than the size of an
// address on the target machine. The data retrieved is zero extended to
// the size of an address on the target machine before being pushed on the
// expression stack.
//----------------------------------------------------------------------
case DW_OP_deref_size: {
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack empty for DW_OP_deref_size.");
return false;
}
uint8_t size = opcodes.GetU8(&offset);
Value::ValueType value_type = stack.back().GetValueType();
switch (value_type) {
case Value::eValueTypeHostAddress: {
void *src = (void *)stack.back().GetScalar().ULongLong();
intptr_t ptr;
::memcpy(&ptr, src, sizeof(void *));
// I can't decide whether the size operand should apply to the bytes in
// their
// lldb-host endianness or the target endianness.. I doubt this'll ever
// come up but I'll opt for assuming big endian regardless.
switch (size) {
case 1:
ptr = ptr & 0xff;
break;
case 2:
ptr = ptr & 0xffff;
break;
case 3:
ptr = ptr & 0xffffff;
break;
case 4:
ptr = ptr & 0xffffffff;
break;
// the casts are added to work around the case where intptr_t is a 32
// bit quantity;
// presumably we won't hit the 5..7 cases if (void*) is 32-bits in this
// program.
case 5:
ptr = (intptr_t)ptr & 0xffffffffffULL;
break;
case 6:
ptr = (intptr_t)ptr & 0xffffffffffffULL;
break;
case 7:
ptr = (intptr_t)ptr & 0xffffffffffffffULL;
break;
default:
break;
}
stack.back().GetScalar() = ptr;
stack.back().ClearContext();
} break;
case Value::eValueTypeLoadAddress:
if (exe_ctx) {
if (process) {
lldb::addr_t pointer_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
uint8_t addr_bytes[sizeof(lldb::addr_t)];
Status error;
if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) ==
size) {
DataExtractor addr_data(addr_bytes, sizeof(addr_bytes),
process->GetByteOrder(), size);
lldb::offset_t addr_data_offset = 0;
switch (size) {
case 1:
stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset);
break;
case 2:
stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset);
break;
case 4:
stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset);
break;
case 8:
stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset);
break;
default:
stack.back().GetScalar() =
addr_data.GetPointer(&addr_data_offset);
}
stack.back().ClearContext();
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"Failed to dereference pointer from 0x%" PRIx64
" for DW_OP_deref: %s\n",
pointer_addr, error.AsCString());
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL process for DW_OP_deref.\n");
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL execution context for DW_OP_deref.\n");
return false;
}
break;
default:
break;
}
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_xderef_size
// OPERANDS: 1
// 1 - uint8_t that specifies the size of the data to dereference.
// DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at
// the top of the stack is treated as an address. The second stack entry is
// treated as an "address space identifier" for those architectures that
// support multiple address spaces. The top two stack elements are popped,
// a data item is retrieved through an implementation-defined address
// calculation and pushed as the new stack top. In the DW_OP_xderef_size
// operation, however, the size in bytes of the data retrieved from the
// dereferenced address is specified by the single operand. This operand is
// a 1-byte unsigned integral constant whose value may not be larger than
// the size of an address on the target machine. The data retrieved is zero
// extended to the size of an address on the target machine before being
// pushed on the expression stack.
//----------------------------------------------------------------------
case DW_OP_xderef_size:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size.");
return false;
//----------------------------------------------------------------------
// OPCODE: DW_OP_xderef
// OPERANDS: none
// DESCRIPTION: Provides an extended dereference mechanism. The entry at
// the top of the stack is treated as an address. The second stack entry is
// treated as an "address space identifier" for those architectures that
// support multiple address spaces. The top two stack elements are popped,
// a data item is retrieved through an implementation-defined address
// calculation and pushed as the new stack top. The size of the data
// retrieved from the dereferenced address is the size of an address on the
// target machine.
//----------------------------------------------------------------------
case DW_OP_xderef:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef.");
return false;
//----------------------------------------------------------------------
// All DW_OP_constXXX opcodes have a single operand as noted below:
//
// Opcode Operand 1
// --------------- ----------------------------------------------------
// DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s
// 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer
// constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u
// 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer
// constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s
// 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer
// constant DW_OP_consts signed LEB128 integer constant
//----------------------------------------------------------------------
case DW_OP_const1u:
stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset)));
break;
case DW_OP_const1s:
stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset)));
break;
case DW_OP_const2u:
stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset)));
break;
case DW_OP_const2s:
stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset)));
break;
case DW_OP_const4u:
stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset)));
break;
case DW_OP_const4s:
stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset)));
break;
case DW_OP_const8u:
stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset)));
break;
case DW_OP_const8s:
stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset)));
break;
case DW_OP_constu:
stack.push_back(Scalar(opcodes.GetULEB128(&offset)));
break;
case DW_OP_consts:
stack.push_back(Scalar(opcodes.GetSLEB128(&offset)));
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_dup
// OPERANDS: none
// DESCRIPTION: duplicates the value at the top of the stack
//----------------------------------------------------------------------
case DW_OP_dup:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString("Expression stack empty for DW_OP_dup.");
return false;
} else
stack.push_back(stack.back());
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_drop
// OPERANDS: none
// DESCRIPTION: pops the value at the top of the stack
//----------------------------------------------------------------------
case DW_OP_drop:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString("Expression stack empty for DW_OP_drop.");
return false;
} else
stack.pop_back();
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_over
// OPERANDS: none
// DESCRIPTION: Duplicates the entry currently second in the stack at
// the top of the stack.
//----------------------------------------------------------------------
case DW_OP_over:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_over.");
return false;
} else
stack.push_back(stack[stack.size() - 2]);
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_pick
// OPERANDS: uint8_t index into the current stack
// DESCRIPTION: The stack entry with the specified index (0 through 255,
// inclusive) is pushed on the stack
//----------------------------------------------------------------------
case DW_OP_pick: {
uint8_t pick_idx = opcodes.GetU8(&offset);
if (pick_idx < stack.size())
stack.push_back(stack[pick_idx]);
else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"Index %u out of range for DW_OP_pick.\n", pick_idx);
return false;
}
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_swap
// OPERANDS: none
// DESCRIPTION: swaps the top two stack entries. The entry at the top
// of the stack becomes the second stack entry, and the second entry
// becomes the top of the stack
//----------------------------------------------------------------------
case DW_OP_swap:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_swap.");
return false;
} else {
tmp = stack.back();
stack.back() = stack[stack.size() - 2];
stack[stack.size() - 2] = tmp;
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_rot
// OPERANDS: none
// DESCRIPTION: Rotates the first three stack entries. The entry at
// the top of the stack becomes the third stack entry, the second entry
// becomes the top of the stack, and the third entry becomes the second
// entry.
//----------------------------------------------------------------------
case DW_OP_rot:
if (stack.size() < 3) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 3 items for DW_OP_rot.");
return false;
} else {
size_t last_idx = stack.size() - 1;
Value old_top = stack[last_idx];
stack[last_idx] = stack[last_idx - 1];
stack[last_idx - 1] = stack[last_idx - 2];
stack[last_idx - 2] = old_top;
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_abs
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, interprets it as a signed
// value and pushes its absolute value. If the absolute value can not be
// represented, the result is undefined.
//----------------------------------------------------------------------
case DW_OP_abs:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_abs.");
return false;
} else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) {
if (error_ptr)
error_ptr->SetErrorString(
"Failed to take the absolute value of the first stack item.");
return false;
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_and
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, performs a bitwise and
// operation on the two, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_and:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_and.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_div
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, divides the former second
// entry by the former top of the stack using signed division, and pushes
// the result.
//----------------------------------------------------------------------
case DW_OP_div:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_div.");
return false;
} else {
tmp = stack.back();
if (tmp.ResolveValue(exe_ctx).IsZero()) {
if (error_ptr)
error_ptr->SetErrorString("Divide by zero.");
return false;
} else {
stack.pop_back();
stack.back() =
stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx);
if (!stack.back().ResolveValue(exe_ctx).IsValid()) {
if (error_ptr)
error_ptr->SetErrorString("Divide failed.");
return false;
}
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_minus
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, subtracts the former top
// of the stack from the former second entry, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_minus:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_minus.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_mod
// OPERANDS: none
// DESCRIPTION: pops the top two stack values and pushes the result of
// the calculation: former second stack entry modulo the former top of the
// stack.
//----------------------------------------------------------------------
case DW_OP_mod:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_mod.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_mul
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, multiplies them
// together, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_mul:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_mul.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_neg
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, and pushes its negation.
//----------------------------------------------------------------------
case DW_OP_neg:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_neg.");
return false;
} else {
if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) {
if (error_ptr)
error_ptr->SetErrorString("Unary negate failed.");
return false;
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_not
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, and pushes its bitwise
// complement
//----------------------------------------------------------------------
case DW_OP_not:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_not.");
return false;
} else {
if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) {
if (error_ptr)
error_ptr->SetErrorString("Logical NOT failed.");
return false;
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_or
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, performs a bitwise or
// operation on the two, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_or:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_or.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_plus
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, adds them together, and
// pushes the result.
//----------------------------------------------------------------------
case DW_OP_plus:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_plus.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().GetScalar() += tmp.GetScalar();
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_plus_uconst
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128
// constant operand and pushes the result.
//----------------------------------------------------------------------
case DW_OP_plus_uconst:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_plus_uconst.");
return false;
} else {
const uint64_t uconst_value = opcodes.GetULEB128(&offset);
// Implicit conversion from a UINT to a Scalar...
stack.back().GetScalar() += uconst_value;
if (!stack.back().GetScalar().IsValid()) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_plus_uconst failed.");
return false;
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_shl
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, shifts the former
// second entry left by the number of bits specified by the former top of
// the stack, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_shl:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_shl.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_shr
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, shifts the former second
// entry right logically (filling with zero bits) by the number of bits
// specified by the former top of the stack, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_shr:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_shr.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical(
tmp.ResolveValue(exe_ctx))) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_shr failed.");
return false;
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_shra
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, shifts the former second
// entry right arithmetically (divide the magnitude by 2, keep the same
// sign for the result) by the number of bits specified by the former top
// of the stack, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_shra:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_shra.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_xor
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, performs the bitwise
// exclusive-or operation on the two, and pushes the result.
//----------------------------------------------------------------------
case DW_OP_xor:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_xor.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_skip
// OPERANDS: int16_t
// DESCRIPTION: An unconditional branch. Its single operand is a 2-byte
// signed integer constant. The 2-byte constant is the number of bytes of
// the DWARF expression to skip forward or backward from the current
// operation, beginning after the 2-byte constant.
//----------------------------------------------------------------------
case DW_OP_skip: {
int16_t skip_offset = (int16_t)opcodes.GetU16(&offset);
lldb::offset_t new_offset = offset + skip_offset;
if (new_offset >= opcodes_offset && new_offset < end_offset)
offset = new_offset;
else {
if (error_ptr)
error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip.");
return false;
}
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_bra
// OPERANDS: int16_t
// DESCRIPTION: A conditional branch. Its single operand is a 2-byte
// signed integer constant. This operation pops the top of stack. If the
// value popped is not the constant 0, the 2-byte constant operand is the
// number of bytes of the DWARF expression to skip forward or backward from
// the current operation, beginning after the 2-byte constant.
//----------------------------------------------------------------------
case DW_OP_bra:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_bra.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
int16_t bra_offset = (int16_t)opcodes.GetU16(&offset);
Scalar zero(0);
if (tmp.ResolveValue(exe_ctx) != zero) {
lldb::offset_t new_offset = offset + bra_offset;
if (new_offset >= opcodes_offset && new_offset < end_offset)
offset = new_offset;
else {
if (error_ptr)
error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra.");
return false;
}
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_eq
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// equals (==) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
//----------------------------------------------------------------------
case DW_OP_eq:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_eq.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_ge
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// greater than or equal to (>=) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
//----------------------------------------------------------------------
case DW_OP_ge:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_ge.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_gt
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// greater than (>) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
//----------------------------------------------------------------------
case DW_OP_gt:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_gt.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_le
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// less than or equal to (<=) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
//----------------------------------------------------------------------
case DW_OP_le:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_le.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_lt
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// less than (<) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
//----------------------------------------------------------------------
case DW_OP_lt:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_lt.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_ne
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// not equal (!=) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
//----------------------------------------------------------------------
case DW_OP_ne:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_ne.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx);
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_litn
// OPERANDS: none
// DESCRIPTION: encode the unsigned literal values from 0 through 31.
// STACK RESULT: push the unsigned literal constant value onto the top
// of the stack.
//----------------------------------------------------------------------
case DW_OP_lit0:
case DW_OP_lit1:
case DW_OP_lit2:
case DW_OP_lit3:
case DW_OP_lit4:
case DW_OP_lit5:
case DW_OP_lit6:
case DW_OP_lit7:
case DW_OP_lit8:
case DW_OP_lit9:
case DW_OP_lit10:
case DW_OP_lit11:
case DW_OP_lit12:
case DW_OP_lit13:
case DW_OP_lit14:
case DW_OP_lit15:
case DW_OP_lit16:
case DW_OP_lit17:
case DW_OP_lit18:
case DW_OP_lit19:
case DW_OP_lit20:
case DW_OP_lit21:
case DW_OP_lit22:
case DW_OP_lit23:
case DW_OP_lit24:
case DW_OP_lit25:
case DW_OP_lit26:
case DW_OP_lit27:
case DW_OP_lit28:
case DW_OP_lit29:
case DW_OP_lit30:
case DW_OP_lit31:
stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0)));
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_regN
// OPERANDS: none
// DESCRIPTION: Push the value in register n on the top of the stack.
//----------------------------------------------------------------------
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31: {
reg_num = op - DW_OP_reg0;
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
stack.push_back(tmp);
else
return false;
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_regx
// OPERANDS:
// ULEB128 literal operand that encodes the register.
// DESCRIPTION: Push the value in register on the top of the stack.
//----------------------------------------------------------------------
case DW_OP_regx: {
reg_num = opcodes.GetULEB128(&offset);
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
stack.push_back(tmp);
else
return false;
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_bregN
// OPERANDS:
// SLEB128 offset from register N
// DESCRIPTION: Value is in memory at the address specified by register
// N plus an offset.
//----------------------------------------------------------------------
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31: {
reg_num = op - DW_OP_breg0;
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
tmp)) {
int64_t breg_offset = opcodes.GetSLEB128(&offset);
tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
tmp.ClearContext();
stack.push_back(tmp);
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else
return false;
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_bregx
// OPERANDS: 2
// ULEB128 literal operand that encodes the register.
// SLEB128 offset from register N
// DESCRIPTION: Value is in memory at the address specified by register
// N plus an offset.
//----------------------------------------------------------------------
case DW_OP_bregx: {
reg_num = opcodes.GetULEB128(&offset);
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
tmp)) {
int64_t breg_offset = opcodes.GetSLEB128(&offset);
tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
tmp.ClearContext();
stack.push_back(tmp);
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else
return false;
} break;
case DW_OP_fbreg:
if (exe_ctx) {
if (frame) {
Scalar value;
if (frame->GetFrameBaseValue(value, error_ptr)) {
int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
value += fbreg_offset;
stack.push_back(value);
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else
return false;
} else {
if (error_ptr)
error_ptr->SetErrorString(
"Invalid stack frame in context for DW_OP_fbreg opcode.");
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL execution context for DW_OP_fbreg.\n");
return false;
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_nop
// OPERANDS: none
// DESCRIPTION: A place holder. It has no effect on the location stack
// or any of its values.
//----------------------------------------------------------------------
case DW_OP_nop:
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_piece
// OPERANDS: 1
// ULEB128: byte size of the piece
// DESCRIPTION: The operand describes the size in bytes of the piece of
// the object referenced by the DWARF expression whose result is at the top
// of the stack. If the piece is located in a register, but does not occupy
// the entire register, the placement of the piece within that register is
// defined by the ABI.
//
// Many compilers store a single variable in sets of registers, or store a
// variable partially in memory and partially in registers. DW_OP_piece
// provides a way of describing how large a part of a variable a particular
// DWARF expression refers to.
//----------------------------------------------------------------------
case DW_OP_piece: {
const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
if (piece_byte_size > 0) {
Value curr_piece;
if (stack.empty()) {
// In a multi-piece expression, this means that the current piece is
// not available. Fill with zeros for now by resizing the data and
// appending it
curr_piece.ResizeData(piece_byte_size);
::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size);
pieces.AppendDataToHostBuffer(curr_piece);
} else {
Status error;
// Extract the current piece into "curr_piece"
Value curr_piece_source_value(stack.back());
stack.pop_back();
const Value::ValueType curr_piece_source_value_type =
curr_piece_source_value.GetValueType();
switch (curr_piece_source_value_type) {
case Value::eValueTypeLoadAddress:
if (process) {
if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
lldb::addr_t load_addr =
curr_piece_source_value.GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
if (process->ReadMemory(
load_addr, curr_piece.GetBuffer().GetBytes(),
piece_byte_size, error) != piece_byte_size) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to read memory DW_OP_piece(%" PRIu64
") from 0x%" PRIx64,
piece_byte_size, load_addr);
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to resize the piece memory buffer for "
"DW_OP_piece(%" PRIu64 ")",
piece_byte_size);
return false;
}
}
break;
case Value::eValueTypeFileAddress:
case Value::eValueTypeHostAddress:
if (error_ptr) {
lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
error_ptr->SetErrorStringWithFormat(
"failed to read memory DW_OP_piece(%" PRIu64
") from %s address 0x%" PRIx64,
piece_byte_size, curr_piece_source_value.GetValueType() ==
Value::eValueTypeFileAddress
? "file"
: "host",
addr);
}
return false;
case Value::eValueTypeScalar: {
uint32_t bit_size = piece_byte_size * 8;
uint32_t bit_offset = 0;
if (!curr_piece_source_value.GetScalar().ExtractBitfield(
bit_size, bit_offset)) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"unable to extract %" PRIu64 " bytes from a %" PRIu64
" byte scalar value.",
piece_byte_size,
(uint64_t)curr_piece_source_value.GetScalar()
.GetByteSize());
return false;
}
curr_piece = curr_piece_source_value;
} break;
case Value::eValueTypeVector: {
if (curr_piece_source_value.GetVector().length >= piece_byte_size)
curr_piece_source_value.GetVector().length = piece_byte_size;
else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"unable to extract %" PRIu64 " bytes from a %" PRIu64
" byte vector value.",
piece_byte_size,
(uint64_t)curr_piece_source_value.GetVector().length);
return false;
}
} break;
}
// Check if this is the first piece?
if (op_piece_offset == 0) {
// This is the first piece, we should push it back onto the stack
// so subsequent pieces will be able to access this piece and add
// to it
if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
if (error_ptr)
error_ptr->SetErrorString("failed to append piece data");
return false;
}
} else {
// If this is the second or later piece there should be a value on
// the stack
if (pieces.GetBuffer().GetByteSize() != op_piece_offset) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"DW_OP_piece for offset %" PRIu64
" but top of stack is of size %" PRIu64,
op_piece_offset, pieces.GetBuffer().GetByteSize());
return false;
}
if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
if (error_ptr)
error_ptr->SetErrorString("failed to append piece data");
return false;
}
}
op_piece_offset += piece_byte_size;
}
}
} break;
case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
if (stack.size() < 1) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_bit_piece.");
return false;
} else {
const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
switch (stack.back().GetValueType()) {
case Value::eValueTypeScalar: {
if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
piece_bit_offset)) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"unable to extract %" PRIu64 " bit value with %" PRIu64
" bit offset from a %" PRIu64 " bit scalar value.",
piece_bit_size, piece_bit_offset,
(uint64_t)(stack.back().GetScalar().GetByteSize() * 8));
return false;
}
} break;
case Value::eValueTypeFileAddress:
case Value::eValueTypeLoadAddress:
case Value::eValueTypeHostAddress:
if (error_ptr) {
error_ptr->SetErrorStringWithFormat(
"unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
", bit_offset = %" PRIu64 ") from an address value.",
piece_bit_size, piece_bit_offset);
}
return false;
case Value::eValueTypeVector:
if (error_ptr) {
error_ptr->SetErrorStringWithFormat(
"unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
", bit_offset = %" PRIu64 ") from a vector value.",
piece_bit_size, piece_bit_offset);
}
return false;
}
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_push_object_address
// OPERANDS: none
// DESCRIPTION: Pushes the address of the object currently being
// evaluated as part of evaluation of a user presented expression. This
// object may correspond to an independent variable described by its own
// DIE or it may be a component of an array, structure, or class whose
// address has been dynamically determined by an earlier step during user
// expression evaluation.
//----------------------------------------------------------------------
case DW_OP_push_object_address:
if (object_address_ptr)
stack.push_back(*object_address_ptr);
else {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_push_object_address used without "
"specifying an object address");
return false;
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_call2
// OPERANDS:
// uint16_t compile unit relative offset of a DIE
// DESCRIPTION: Performs subroutine calls during evaluation
// of a DWARF expression. The operand is the 2-byte unsigned offset of a
// debugging information entry in the current compilation unit.
//
// Operand interpretation is exactly like that for DW_FORM_ref2.
//
// This operation transfers control of DWARF expression evaluation to the
// DW_AT_location attribute of the referenced DIE. If there is no such
// attribute, then there is no effect. Execution of the DWARF expression of
// a DW_AT_location attribute may add to and/or remove from values on the
// stack. Execution returns to the point following the call when the end of
// the attribute is reached. Values on the stack at the time of the call
// may be used as parameters by the called expression and values left on
// the stack by the called expression may be used as return values by prior
// agreement between the calling and called expressions.
//----------------------------------------------------------------------
case DW_OP_call2:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2.");
return false;
//----------------------------------------------------------------------
// OPCODE: DW_OP_call4
// OPERANDS: 1
// uint32_t compile unit relative offset of a DIE
// DESCRIPTION: Performs a subroutine call during evaluation of a DWARF
// expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of
// a debugging information entry in the current compilation unit.
//
// Operand interpretation DW_OP_call4 is exactly like that for
// DW_FORM_ref4.
//
// This operation transfers control of DWARF expression evaluation to the
// DW_AT_location attribute of the referenced DIE. If there is no such
// attribute, then there is no effect. Execution of the DWARF expression of
// a DW_AT_location attribute may add to and/or remove from values on the
// stack. Execution returns to the point following the call when the end of
// the attribute is reached. Values on the stack at the time of the call
// may be used as parameters by the called expression and values left on
// the stack by the called expression may be used as return values by prior
// agreement between the calling and called expressions.
//----------------------------------------------------------------------
case DW_OP_call4:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4.");
return false;
//----------------------------------------------------------------------
// OPCODE: DW_OP_stack_value
// OPERANDS: None
// DESCRIPTION: Specifies that the object does not exist in memory but
// rather is a constant value. The value from the top of the stack is the
// value to be used. This is the actual object value and not the location.
//----------------------------------------------------------------------
case DW_OP_stack_value:
stack.back().SetValueType(Value::eValueTypeScalar);
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_call_frame_cfa
// OPERANDS: None
// DESCRIPTION: Specifies a DWARF expression that pushes the value of
// the canonical frame address consistent with the call frame information
// located in .debug_frame (or in the FDEs of the eh_frame section).
//----------------------------------------------------------------------
case DW_OP_call_frame_cfa:
if (frame) {
// Note that we don't have to parse FDEs because this DWARF expression
// is commonly evaluated with a valid stack frame.
StackID id = frame->GetStackID();
addr_t cfa = id.GetCallFrameAddress();
if (cfa != LLDB_INVALID_ADDRESS) {
stack.push_back(Scalar(cfa));
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else if (error_ptr)
error_ptr->SetErrorString("Stack frame does not include a canonical "
"frame address for DW_OP_call_frame_cfa "
"opcode.");
} else {
if (error_ptr)
error_ptr->SetErrorString("Invalid stack frame in context for "
"DW_OP_call_frame_cfa opcode.");
return false;
}
break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension
// opcode, DW_OP_GNU_push_tls_address)
// OPERANDS: none
// DESCRIPTION: Pops a TLS offset from the stack, converts it to
// an address in the current thread's thread-local storage block, and
// pushes it on the stack.
//----------------------------------------------------------------------
case DW_OP_form_tls_address:
case DW_OP_GNU_push_tls_address: {
if (stack.size() < 1) {
if (error_ptr) {
if (op == DW_OP_form_tls_address)
error_ptr->SetErrorString(
"DW_OP_form_tls_address needs an argument.");
else
error_ptr->SetErrorString(
"DW_OP_GNU_push_tls_address needs an argument.");
}
return false;
}
if (!exe_ctx || !module_sp) {
if (error_ptr)
error_ptr->SetErrorString("No context to evaluate TLS within.");
return false;
}
Thread *thread = exe_ctx->GetThreadPtr();
if (!thread) {
if (error_ptr)
error_ptr->SetErrorString("No thread to evaluate TLS within.");
return false;
}
// Lookup the TLS block address for this thread and module.
const addr_t tls_file_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
const addr_t tls_load_addr =
thread->GetThreadLocalData(module_sp, tls_file_addr);
if (tls_load_addr == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorString(
"No TLS data currently exists for this thread.");
return false;
}
stack.back().GetScalar() = tls_load_addr;
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_GNU_addr_index
// 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_GNU_addr_index: {
if (!dwarf_cu) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a "
"compile unit being specified");
return false;
}
uint64_t index = opcodes.GetULEB128(&offset);
uint32_t index_size = dwarf_cu->GetAddressByteSize();
dw_offset_t addr_base = dwarf_cu->GetAddrBase();
lldb::offset_t offset = addr_base + index * index_size;
uint64_t value =
dwarf_cu->GetSymbolFileDWARF()->get_debug_addr_data().GetMaxU64(
&offset, index_size);
stack.push_back(Scalar(value));
stack.back().SetValueType(Value::eValueTypeFileAddress);
} break;
//----------------------------------------------------------------------
// OPCODE: DW_OP_GNU_const_index
// OPERANDS: 1
// ULEB128: index to the .debug_addr section
// DESCRIPTION: Pushes an constant with the size of a machine address to
// the stack from the .debug_addr section with the base address specified
// by the DW_AT_addr_base attribute and the 0 based index is the ULEB128
// encoded index.
//----------------------------------------------------------------------
case DW_OP_GNU_const_index: {
if (!dwarf_cu) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_GNU_const_index found without a "
"compile unit being specified");
return false;
}
uint64_t index = opcodes.GetULEB128(&offset);
uint32_t index_size = dwarf_cu->GetAddressByteSize();
dw_offset_t addr_base = dwarf_cu->GetAddrBase();
lldb::offset_t offset = addr_base + index * index_size;
const DWARFDataExtractor &debug_addr =
dwarf_cu->GetSymbolFileDWARF()->get_debug_addr_data();
switch (index_size) {
case 4:
stack.push_back(Scalar(debug_addr.GetU32(&offset)));
break;
case 8:
stack.push_back(Scalar(debug_addr.GetU64(&offset)));
break;
default:
assert(false && "Unhandled index size");
return false;
}
} break;
default:
if (log)
log->Printf("Unhandled opcode %s in DWARFExpression.",
DW_OP_value_to_name(op));
break;
}
}
if (stack.empty()) {
// Nothing on the stack, check if we created a piece value from DW_OP_piece
// or DW_OP_bit_piece opcodes
if (pieces.GetBuffer().GetByteSize()) {
result = pieces;
} else {
if (error_ptr)
error_ptr->SetErrorString("Stack empty after evaluation.");
return false;
}
} else {
if (log && log->GetVerbose()) {
size_t count = stack.size();
log->Printf("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);
log->Printf(" %s", new_value.GetData());
}
}
result = stack.back();
}
return true; // Return true on success
}
size_t DWARFExpression::LocationListSize(const DWARFUnit *dwarf_cu,
const DataExtractor &debug_loc_data,
lldb::offset_t offset) {
const lldb::offset_t debug_loc_offset = offset;
while (debug_loc_data.ValidOffset(offset)) {
lldb::addr_t start_addr = LLDB_INVALID_ADDRESS;
lldb::addr_t end_addr = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(dwarf_cu, debug_loc_data, &offset,
start_addr, end_addr))
break;
if (start_addr == 0 && end_addr == 0)
break;
uint16_t loc_length = debug_loc_data.GetU16(&offset);
offset += loc_length;
}
if (offset > debug_loc_offset)
return offset - debug_loc_offset;
return 0;
}
bool DWARFExpression::AddressRangeForLocationListEntry(
const DWARFUnit *dwarf_cu, const DataExtractor &debug_loc_data,
lldb::offset_t *offset_ptr, lldb::addr_t &low_pc, lldb::addr_t &high_pc) {
if (!debug_loc_data.ValidOffset(*offset_ptr))
return false;
DWARFExpression::LocationListFormat format =
dwarf_cu->GetSymbolFileDWARF()->GetLocationListFormat();
switch (format) {
case NonLocationList:
return false;
case RegularLocationList:
low_pc = debug_loc_data.GetAddress(offset_ptr);
high_pc = debug_loc_data.GetAddress(offset_ptr);
return true;
case SplitDwarfLocationList:
case LocLists:
switch (debug_loc_data.GetU8(offset_ptr)) {
case DW_LLE_end_of_list:
return false;
case DW_LLE_startx_endx: {
uint64_t index = debug_loc_data.GetULEB128(offset_ptr);
low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
index = debug_loc_data.GetULEB128(offset_ptr);
high_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
return true;
}
case DW_LLE_startx_length: {
uint64_t index = debug_loc_data.GetULEB128(offset_ptr);
low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
uint64_t length = (format == LocLists)
? debug_loc_data.GetULEB128(offset_ptr)
: debug_loc_data.GetU32(offset_ptr);
high_pc = low_pc + length;
return true;
}
case DW_LLE_start_length: {
low_pc = debug_loc_data.GetAddress(offset_ptr);
high_pc = low_pc + debug_loc_data.GetULEB128(offset_ptr);
return true;
}
case DW_LLE_start_end: {
low_pc = debug_loc_data.GetAddress(offset_ptr);
high_pc = debug_loc_data.GetAddress(offset_ptr);
return true;
}
default:
// Not supported entry type
lldbassert(false && "Not supported location list type");
return false;
}
}
assert(false && "Not supported location list type");
return false;
}
static bool print_dwarf_exp_op(Stream &s, const DataExtractor &data,
lldb::offset_t *offset_ptr, int address_size,
int dwarf_ref_size) {
uint8_t opcode = data.GetU8(offset_ptr);
DRC_class opcode_class;
uint64_t uint;
int64_t sint;
int size;
opcode_class = DW_OP_value_to_class(opcode) & (~DRC_DWARFv3);
s.Printf("%s ", DW_OP_value_to_name(opcode));
/* Does this take zero parameters? If so we can shortcut this function. */
if (opcode_class == DRC_ZEROOPERANDS)
return true;
if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) {
uint = data.GetULEB128(offset_ptr);
sint = data.GetSLEB128(offset_ptr);
s.Printf("%" PRIu64 " %" PRIi64, uint, sint);
return true;
}
if (opcode_class != DRC_ONEOPERAND) {
s.Printf("UNKNOWN OP %u", opcode);
return false;
}
switch (opcode) {
case DW_OP_addr:
size = address_size;
break;
case DW_OP_const1u:
size = 1;
break;
case DW_OP_const1s:
size = -1;
break;
case DW_OP_const2u:
size = 2;
break;
case DW_OP_const2s:
size = -2;
break;
case DW_OP_const4u:
size = 4;
break;
case DW_OP_const4s:
size = -4;
break;
case DW_OP_const8u:
size = 8;
break;
case DW_OP_const8s:
size = -8;
break;
case DW_OP_constu:
size = 128;
break;
case DW_OP_consts:
size = -128;
break;
case DW_OP_fbreg:
size = -128;
break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31:
size = -128;
break;
case DW_OP_pick:
case DW_OP_deref_size:
case DW_OP_xderef_size:
size = 1;
break;
case DW_OP_skip:
case DW_OP_bra:
size = -2;
break;
case DW_OP_call2:
size = 2;
break;
case DW_OP_call4:
size = 4;
break;
case DW_OP_call_ref:
size = dwarf_ref_size;
break;
case DW_OP_piece:
case DW_OP_plus_uconst:
case DW_OP_regx:
case DW_OP_GNU_addr_index:
case DW_OP_GNU_const_index:
size = 128;
break;
default:
s.Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode);
- return true;
+ return false;
}
switch (size) {
case -1:
sint = (int8_t)data.GetU8(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -2:
sint = (int16_t)data.GetU16(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -4:
sint = (int32_t)data.GetU32(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -8:
sint = (int64_t)data.GetU64(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -128:
sint = data.GetSLEB128(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case 1:
uint = data.GetU8(offset_ptr);
s.Printf("0x%2.2" PRIx64, uint);
break;
case 2:
uint = data.GetU16(offset_ptr);
s.Printf("0x%4.4" PRIx64, uint);
break;
case 4:
uint = data.GetU32(offset_ptr);
s.Printf("0x%8.8" PRIx64, uint);
break;
case 8:
uint = data.GetU64(offset_ptr);
s.Printf("0x%16.16" PRIx64, uint);
break;
case 128:
uint = data.GetULEB128(offset_ptr);
s.Printf("0x%" PRIx64, uint);
break;
}
- return false;
+ return true;
}
bool DWARFExpression::PrintDWARFExpression(Stream &s, const DataExtractor &data,
int address_size, int dwarf_ref_size,
bool location_expression) {
int op_count = 0;
lldb::offset_t offset = 0;
while (data.ValidOffset(offset)) {
if (location_expression && op_count > 0)
return false;
if (op_count > 0)
s.PutCString(", ");
if (!print_dwarf_exp_op(s, data, &offset, address_size, dwarf_ref_size))
return false;
op_count++;
}
return true;
}
void DWARFExpression::PrintDWARFLocationList(
Stream &s, const DWARFUnit *cu, const DataExtractor &debug_loc_data,
lldb::offset_t offset) {
uint64_t start_addr, end_addr;
uint32_t addr_size = DWARFUnit::GetAddressByteSize(cu);
s.SetAddressByteSize(DWARFUnit::GetAddressByteSize(cu));
dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0;
while (debug_loc_data.ValidOffset(offset)) {
start_addr = debug_loc_data.GetMaxU64(&offset, addr_size);
end_addr = debug_loc_data.GetMaxU64(&offset, addr_size);
if (start_addr == 0 && end_addr == 0)
break;
s.PutCString("\n ");
s.Indent();
if (cu)
s.AddressRange(start_addr + base_addr, end_addr + base_addr,
cu->GetAddressByteSize(), NULL, ": ");
uint32_t loc_length = debug_loc_data.GetU16(&offset);
DataExtractor locationData(debug_loc_data, offset, loc_length);
PrintDWARFExpression(s, locationData, addr_size, 4, false);
offset += loc_length;
}
}
bool DWARFExpression::GetOpAndEndOffsets(StackFrame &frame,
lldb::offset_t &op_offset,
lldb::offset_t &end_offset) {
SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction);
if (!sc.function) {
return false;
}
addr_t loclist_base_file_addr =
sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) {
return false;
}
addr_t pc_file_addr = frame.GetFrameCodeAddress().GetFileAddress();
lldb::offset_t opcodes_offset, opcodes_length;
if (!GetLocation(loclist_base_file_addr, pc_file_addr, opcodes_offset,
opcodes_length)) {
return false;
}
if (opcodes_length == 0) {
return false;
}
op_offset = opcodes_offset;
end_offset = opcodes_offset + opcodes_length;
return true;
}
bool DWARFExpression::MatchesOperand(StackFrame &frame,
const Instruction::Operand &operand) {
using namespace OperandMatchers;
lldb::offset_t op_offset;
lldb::offset_t end_offset;
if (!GetOpAndEndOffsets(frame, op_offset, end_offset)) {
return false;
}
if (!m_data.ValidOffset(op_offset) || op_offset >= end_offset) {
return false;
}
RegisterContextSP reg_ctx_sp = frame.GetRegisterContext();
if (!reg_ctx_sp) {
return false;
}
DataExtractor opcodes = m_data;
uint8_t opcode = opcodes.GetU8(&op_offset);
if (opcode == DW_OP_fbreg) {
int64_t offset = opcodes.GetSLEB128(&op_offset);
DWARFExpression *fb_expr = frame.GetFrameBaseExpression(nullptr);
if (!fb_expr) {
return false;
}
auto recurse = [&frame, fb_expr](const Instruction::Operand &child) {
return fb_expr->MatchesOperand(frame, child);
};
if (!offset &&
MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
recurse)(operand)) {
return true;
}
return MatchUnaryOp(
MatchOpType(Instruction::Operand::Type::Dereference),
MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
MatchImmOp(offset), recurse))(operand);
}
bool dereference = false;
const RegisterInfo *reg = nullptr;
int64_t offset = 0;
if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) {
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0);
} else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) {
offset = opcodes.GetSLEB128(&op_offset);
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_breg0);
} else if (opcode == DW_OP_regx) {
uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
} else if (opcode == DW_OP_bregx) {
uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
offset = opcodes.GetSLEB128(&op_offset);
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
} else {
return false;
}
if (!reg) {
return false;
}
if (dereference) {
if (!offset &&
MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
MatchRegOp(*reg))(operand)) {
return true;
}
return MatchUnaryOp(
MatchOpType(Instruction::Operand::Type::Dereference),
MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
MatchRegOp(*reg),
MatchImmOp(offset)))(operand);
} else {
return MatchRegOp(*reg)(operand);
}
}
Index: source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp (revision 352844)
+++ source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp (revision 352845)
@@ -1,1570 +1,1572 @@
//===-- SymbolFileNativePDB.cpp ---------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "SymbolFileNativePDB.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamBuffer.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/ClangASTImporter.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/ClangUtil.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Symbol/VariableList.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/RecordName.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "llvm/Demangle/MicrosoftDemangle.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "DWARFLocationExpression.h"
#include "PdbAstBuilder.h"
#include "PdbSymUid.h"
#include "PdbUtil.h"
#include "UdtRecordCompleter.h"
using namespace lldb;
using namespace lldb_private;
using namespace npdb;
using namespace llvm::codeview;
using namespace llvm::pdb;
static lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
switch (lang) {
case PDB_Lang::Cpp:
return lldb::LanguageType::eLanguageTypeC_plus_plus;
case PDB_Lang::C:
return lldb::LanguageType::eLanguageTypeC;
default:
return lldb::LanguageType::eLanguageTypeUnknown;
}
}
static std::unique_ptr<PDBFile> loadPDBFile(std::string PdbPath,
llvm::BumpPtrAllocator &Allocator) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ErrorOrBuffer =
llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false);
if (!ErrorOrBuffer)
return nullptr;
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
llvm::StringRef Path = Buffer->getBufferIdentifier();
auto Stream = llvm::make_unique<llvm::MemoryBufferByteStream>(
std::move(Buffer), llvm::support::little);
auto File = llvm::make_unique<PDBFile>(Path, std::move(Stream), Allocator);
if (auto EC = File->parseFileHeaders()) {
llvm::consumeError(std::move(EC));
return nullptr;
}
if (auto EC = File->parseStreamData()) {
llvm::consumeError(std::move(EC));
return nullptr;
}
return File;
}
static std::unique_ptr<PDBFile>
loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
// Try to find a matching PDB for an EXE.
using namespace llvm::object;
auto expected_binary = createBinary(exe_path);
// If the file isn't a PE/COFF executable, fail.
if (!expected_binary) {
llvm::consumeError(expected_binary.takeError());
return nullptr;
}
OwningBinary<Binary> binary = std::move(*expected_binary);
auto *obj = llvm::dyn_cast<llvm::object::COFFObjectFile>(binary.getBinary());
if (!obj)
return nullptr;
const llvm::codeview::DebugInfo *pdb_info = nullptr;
// If it doesn't have a debug directory, fail.
llvm::StringRef pdb_file;
auto ec = obj->getDebugPDBInfo(pdb_info, pdb_file);
if (ec)
return nullptr;
// if the file doesn't exist, is not a pdb, or doesn't have a matching guid,
// fail.
llvm::file_magic magic;
ec = llvm::identify_magic(pdb_file, magic);
if (ec || magic != llvm::file_magic::pdb)
return nullptr;
std::unique_ptr<PDBFile> pdb = loadPDBFile(pdb_file, allocator);
if (!pdb)
return nullptr;
auto expected_info = pdb->getPDBInfoStream();
if (!expected_info) {
llvm::consumeError(expected_info.takeError());
return nullptr;
}
llvm::codeview::GUID guid;
memcpy(&guid, pdb_info->PDB70.Signature, 16);
if (expected_info->getGuid() != guid)
return nullptr;
return pdb;
}
static bool IsFunctionPrologue(const CompilandIndexItem &cci,
lldb::addr_t addr) {
// FIXME: Implement this.
return false;
}
static bool IsFunctionEpilogue(const CompilandIndexItem &cci,
lldb::addr_t addr) {
// FIXME: Implement this.
return false;
}
static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
switch (kind) {
case SimpleTypeKind::Boolean128:
case SimpleTypeKind::Boolean16:
case SimpleTypeKind::Boolean32:
case SimpleTypeKind::Boolean64:
case SimpleTypeKind::Boolean8:
return "bool";
case SimpleTypeKind::Byte:
case SimpleTypeKind::UnsignedCharacter:
return "unsigned char";
case SimpleTypeKind::NarrowCharacter:
return "char";
case SimpleTypeKind::SignedCharacter:
case SimpleTypeKind::SByte:
return "signed char";
case SimpleTypeKind::Character16:
return "char16_t";
case SimpleTypeKind::Character32:
return "char32_t";
case SimpleTypeKind::Complex80:
case SimpleTypeKind::Complex64:
case SimpleTypeKind::Complex32:
return "complex";
case SimpleTypeKind::Float128:
case SimpleTypeKind::Float80:
return "long double";
case SimpleTypeKind::Float64:
return "double";
case SimpleTypeKind::Float32:
return "float";
case SimpleTypeKind::Float16:
return "single";
case SimpleTypeKind::Int128:
return "__int128";
case SimpleTypeKind::Int64:
case SimpleTypeKind::Int64Quad:
return "int64_t";
case SimpleTypeKind::Int32:
return "int";
case SimpleTypeKind::Int16:
return "short";
case SimpleTypeKind::UInt128:
return "unsigned __int128";
case SimpleTypeKind::UInt64:
case SimpleTypeKind::UInt64Quad:
return "uint64_t";
case SimpleTypeKind::HResult:
return "HRESULT";
case SimpleTypeKind::UInt32:
return "unsigned";
case SimpleTypeKind::UInt16:
case SimpleTypeKind::UInt16Short:
return "unsigned short";
case SimpleTypeKind::Int32Long:
return "long";
case SimpleTypeKind::UInt32Long:
return "unsigned long";
case SimpleTypeKind::Void:
return "void";
case SimpleTypeKind::WideCharacter:
return "wchar_t";
default:
return "";
}
}
static bool IsClassRecord(TypeLeafKind kind) {
switch (kind) {
case LF_STRUCTURE:
case LF_CLASS:
case LF_INTERFACE:
return true;
default:
return false;
}
}
void SymbolFileNativePDB::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
DebuggerInitialize);
}
void SymbolFileNativePDB::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}
void SymbolFileNativePDB::DebuggerInitialize(Debugger &debugger) {}
ConstString SymbolFileNativePDB::GetPluginNameStatic() {
static ConstString g_name("native-pdb");
return g_name;
}
const char *SymbolFileNativePDB::GetPluginDescriptionStatic() {
return "Microsoft PDB debug symbol cross-platform file reader.";
}
SymbolFile *SymbolFileNativePDB::CreateInstance(ObjectFile *obj_file) {
return new SymbolFileNativePDB(obj_file);
}
SymbolFileNativePDB::SymbolFileNativePDB(ObjectFile *object_file)
: SymbolFile(object_file) {}
SymbolFileNativePDB::~SymbolFileNativePDB() {}
uint32_t SymbolFileNativePDB::CalculateAbilities() {
uint32_t abilities = 0;
if (!m_obj_file)
return 0;
if (!m_index) {
// Lazily load and match the PDB file, but only do this once.
std::unique_ptr<PDBFile> file_up =
loadMatchingPDBFile(m_obj_file->GetFileSpec().GetPath(), m_allocator);
if (!file_up) {
auto module_sp = m_obj_file->GetModule();
if (!module_sp)
return 0;
// See if any symbol file is specified through `--symfile` option.
FileSpec symfile = module_sp->GetSymbolFileFileSpec();
if (!symfile)
return 0;
file_up = loadPDBFile(symfile.GetPath(), m_allocator);
}
if (!file_up)
return 0;
auto expected_index = PdbIndex::create(std::move(file_up));
if (!expected_index) {
llvm::consumeError(expected_index.takeError());
return 0;
}
m_index = std::move(*expected_index);
}
if (!m_index)
return 0;
// We don't especially have to be precise here. We only distinguish between
// stripped and not stripped.
abilities = kAllAbilities;
if (m_index->dbi().isStripped())
abilities &= ~(Blocks | LocalVariables);
return abilities;
}
void SymbolFileNativePDB::InitializeObject() {
m_obj_load_address = m_obj_file->GetFileOffset();
m_index->SetLoadAddress(m_obj_load_address);
m_index->ParseSectionContribs();
TypeSystem *ts = m_obj_file->GetModule()->GetTypeSystemForLanguage(
lldb::eLanguageTypeC_plus_plus);
if (ts)
ts->SetSymbolFile(this);
m_ast = llvm::make_unique<PdbAstBuilder>(*m_obj_file, *m_index);
}
uint32_t SymbolFileNativePDB::GetNumCompileUnits() {
const DbiModuleList &modules = m_index->dbi().modules();
uint32_t count = modules.getModuleCount();
if (count == 0)
return count;
// The linker can inject an additional "dummy" compilation unit into the
// PDB. Ignore this special compile unit for our purposes, if it is there.
// It is always the last one.
DbiModuleDescriptor last = modules.getModuleDescriptor(count - 1);
if (last.getModuleName() == "* Linker *")
--count;
return count;
}
Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) {
CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) {
// This is a function. It must be global. Creating the Function entry for
// it automatically creates a block for it.
CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii);
return GetOrCreateFunction(block_id, *comp_unit)->GetBlock(false);
}
lldbassert(sym.kind() == S_BLOCK32);
// This is a block. Its parent is either a function or another block. In
// either case, its parent can be viewed as a block (e.g. a function contains
// 1 big block. So just get the parent block and add this block to it.
BlockSym block(static_cast<SymbolRecordKind>(sym.kind()));
cantFail(SymbolDeserializer::deserializeAs<BlockSym>(sym, block));
lldbassert(block.Parent != 0);
PdbCompilandSymId parent_id(block_id.modi, block.Parent);
Block &parent_block = GetOrCreateBlock(parent_id);
lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id);
BlockSP child_block = std::make_shared<Block>(opaque_block_uid);
parent_block.AddChild(child_block);
m_ast->GetOrCreateBlockDecl(block_id);
m_blocks.insert({opaque_block_uid, child_block});
return *child_block;
}
lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id,
CompileUnit &comp_unit) {
const CompilandIndexItem *cci =
m_index->compilands().GetCompiland(func_id.modi);
lldbassert(cci);
CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(func_id.offset);
lldbassert(sym_record.kind() == S_LPROC32 || sym_record.kind() == S_GPROC32);
SegmentOffsetLength sol = GetSegmentOffsetAndLength(sym_record);
auto file_vm_addr = m_index->MakeVirtualAddress(sol.so);
if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
return nullptr;
AddressRange func_range(file_vm_addr, sol.length,
comp_unit.GetModule()->GetSectionList());
if (!func_range.GetBaseAddress().IsValid())
return nullptr;
ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind()));
cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc));
if (proc.FunctionType == TypeIndex::None())
return nullptr;
TypeSP func_type = GetOrCreateType(proc.FunctionType);
if (!func_type)
return nullptr;
PdbTypeSymId sig_id(proc.FunctionType, false);
Mangled mangled(proc.Name);
FunctionSP func_sp = std::make_shared<Function>(
&comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled,
func_type.get(), func_range);
comp_unit.AddFunction(func_sp);
m_ast->GetOrCreateFunctionDecl(func_id);
return func_sp;
}
CompUnitSP
SymbolFileNativePDB::CreateCompileUnit(const CompilandIndexItem &cci) {
lldb::LanguageType lang =
cci.m_compile_opts ? TranslateLanguage(cci.m_compile_opts->getLanguage())
: lldb::eLanguageTypeUnknown;
LazyBool optimized = eLazyBoolNo;
if (cci.m_compile_opts && cci.m_compile_opts->hasOptimizations())
optimized = eLazyBoolYes;
llvm::SmallString<64> source_file_name =
m_index->compilands().GetMainSourceFile(cci);
FileSpec fs(source_file_name);
CompUnitSP cu_sp =
std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr, fs,
toOpaqueUid(cci.m_id), lang, optimized);
m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
cci.m_id.modi, cu_sp);
return cu_sp;
}
lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbTypeSymId type_id,
const ModifierRecord &mr,
CompilerType ct) {
TpiStream &stream = m_index->tpi();
std::string name;
if (mr.ModifiedType.isSimple())
name = GetSimpleTypeName(mr.ModifiedType.getSimpleKind());
else
name = computeTypeName(stream.typeCollection(), mr.ModifiedType);
Declaration decl;
lldb::TypeSP modified_type = GetOrCreateType(mr.ModifiedType);
return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(name),
modified_type->GetByteSize(), nullptr,
LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
ct, Type::eResolveStateFull);
}
lldb::TypeSP
SymbolFileNativePDB::CreatePointerType(PdbTypeSymId type_id,
const llvm::codeview::PointerRecord &pr,
CompilerType ct) {
TypeSP pointee = GetOrCreateType(pr.ReferentType);
if (!pointee)
return nullptr;
if (pr.isPointerToMember()) {
MemberPointerInfo mpi = pr.getMemberInfo();
GetOrCreateType(mpi.ContainingType);
}
Declaration decl;
return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(),
pr.getSize(), nullptr, LLDB_INVALID_UID,
Type::eEncodingIsUID, decl, ct,
Type::eResolveStateFull);
}
lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti,
CompilerType ct) {
uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false));
if (ti == TypeIndex::NullptrT()) {
Declaration decl;
return std::make_shared<Type>(
uid, this, ConstString("std::nullptr_t"), 0, nullptr, LLDB_INVALID_UID,
Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull);
}
if (ti.getSimpleMode() != SimpleTypeMode::Direct) {
TypeSP direct_sp = GetOrCreateType(ti.makeDirect());
uint32_t pointer_size = 0;
switch (ti.getSimpleMode()) {
case SimpleTypeMode::FarPointer32:
case SimpleTypeMode::NearPointer32:
pointer_size = 4;
break;
case SimpleTypeMode::NearPointer64:
pointer_size = 8;
break;
default:
// 128-bit and 16-bit pointers unsupported.
return nullptr;
}
Declaration decl;
return std::make_shared<Type>(
uid, this, ConstString(), pointer_size, nullptr, LLDB_INVALID_UID,
Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull);
}
if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated)
return nullptr;
size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind());
llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind());
Declaration decl;
return std::make_shared<Type>(uid, this, ConstString(type_name), size,
nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID,
decl, ct, Type::eResolveStateFull);
}
static std::string GetUnqualifiedTypeName(const TagRecord &record) {
if (!record.hasUniqueName()) {
MSVCUndecoratedNameParser parser(record.Name);
llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
return specs.back().GetBaseName();
}
llvm::ms_demangle::Demangler demangler;
StringView sv(record.UniqueName.begin(), record.UniqueName.size());
llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv);
if (demangler.Error)
return record.Name;
llvm::ms_demangle::IdentifierNode *idn =
ttn->QualifiedName->getUnqualifiedIdentifier();
return idn->toString();
}
lldb::TypeSP
SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id,
const TagRecord &record,
size_t size, CompilerType ct) {
std::string uname = GetUnqualifiedTypeName(record);
// FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
Declaration decl;
return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(uname),
size, nullptr, LLDB_INVALID_UID,
Type::eEncodingIsUID, decl, ct,
Type::eResolveStateForward);
}
lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
const ClassRecord &cr,
CompilerType ct) {
return CreateClassStructUnion(type_id, cr, cr.getSize(), ct);
}
lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
const UnionRecord &ur,
CompilerType ct) {
return CreateClassStructUnion(type_id, ur, ur.getSize(), ct);
}
lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
const EnumRecord &er,
CompilerType ct) {
std::string uname = GetUnqualifiedTypeName(er);
Declaration decl;
TypeSP underlying_type = GetOrCreateType(er.UnderlyingType);
return std::make_shared<lldb_private::Type>(
toOpaqueUid(type_id), this, ConstString(uname),
underlying_type->GetByteSize(), nullptr, LLDB_INVALID_UID,
lldb_private::Type::eEncodingIsUID, decl, ct,
lldb_private::Type::eResolveStateForward);
}
TypeSP SymbolFileNativePDB::CreateArrayType(PdbTypeSymId type_id,
const ArrayRecord &ar,
CompilerType ct) {
TypeSP element_type = GetOrCreateType(ar.ElementType);
Declaration decl;
TypeSP array_sp = std::make_shared<lldb_private::Type>(
toOpaqueUid(type_id), this, ConstString(), ar.Size, nullptr,
LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ct,
lldb_private::Type::eResolveStateFull);
array_sp->SetEncodingType(element_type.get());
return array_sp;
}
TypeSP SymbolFileNativePDB::CreateProcedureType(PdbTypeSymId type_id,
const ProcedureRecord &pr,
CompilerType ct) {
Declaration decl;
return std::make_shared<lldb_private::Type>(
toOpaqueUid(type_id), this, ConstString(), 0, nullptr, LLDB_INVALID_UID,
lldb_private::Type::eEncodingIsUID, decl, ct,
lldb_private::Type::eResolveStateFull);
}
TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) {
if (type_id.index.isSimple())
return CreateSimpleType(type_id.index, ct);
TpiStream &stream = type_id.is_ipi ? m_index->ipi() : m_index->tpi();
CVType cvt = stream.getType(type_id.index);
if (cvt.kind() == LF_MODIFIER) {
ModifierRecord modifier;
llvm::cantFail(
TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier));
return CreateModifierType(type_id, modifier, ct);
}
if (cvt.kind() == LF_POINTER) {
PointerRecord pointer;
llvm::cantFail(
TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer));
return CreatePointerType(type_id, pointer, ct);
}
if (IsClassRecord(cvt.kind())) {
ClassRecord cr;
llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
return CreateTagType(type_id, cr, ct);
}
if (cvt.kind() == LF_ENUM) {
EnumRecord er;
llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
return CreateTagType(type_id, er, ct);
}
if (cvt.kind() == LF_UNION) {
UnionRecord ur;
llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
return CreateTagType(type_id, ur, ct);
}
if (cvt.kind() == LF_ARRAY) {
ArrayRecord ar;
llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar));
return CreateArrayType(type_id, ar, ct);
}
if (cvt.kind() == LF_PROCEDURE) {
ProcedureRecord pr;
llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr));
return CreateProcedureType(type_id, pr, ct);
}
return nullptr;
}
TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbTypeSymId type_id) {
// If they search for a UDT which is a forward ref, try and resolve the full
// decl and just map the forward ref uid to the full decl record.
llvm::Optional<PdbTypeSymId> full_decl_uid;
if (IsForwardRefUdt(type_id, m_index->tpi())) {
auto expected_full_ti =
m_index->tpi().findFullDeclForForwardRef(type_id.index);
if (!expected_full_ti)
llvm::consumeError(expected_full_ti.takeError());
else if (*expected_full_ti != type_id.index) {
full_decl_uid = PdbTypeSymId(*expected_full_ti, false);
// It's possible that a lookup would occur for the full decl causing it
// to be cached, then a second lookup would occur for the forward decl.
// We don't want to create a second full decl, so make sure the full
// decl hasn't already been cached.
auto full_iter = m_types.find(toOpaqueUid(*full_decl_uid));
if (full_iter != m_types.end()) {
TypeSP result = full_iter->second;
// Map the forward decl to the TypeSP for the full decl so we can take
// the fast path next time.
m_types[toOpaqueUid(type_id)] = result;
return result;
}
}
}
PdbTypeSymId best_decl_id = full_decl_uid ? *full_decl_uid : type_id;
clang::QualType qt = m_ast->GetOrCreateType(best_decl_id);
TypeSP result = CreateType(best_decl_id, m_ast->ToCompilerType(qt));
if (!result)
return nullptr;
uint64_t best_uid = toOpaqueUid(best_decl_id);
m_types[best_uid] = result;
// If we had both a forward decl and a full decl, make both point to the new
// type.
if (full_decl_uid)
m_types[toOpaqueUid(type_id)] = result;
return result;
}
TypeSP SymbolFileNativePDB::GetOrCreateType(PdbTypeSymId type_id) {
// We can't use try_emplace / overwrite here because the process of creating
// a type could create nested types, which could invalidate iterators. So
// we have to do a 2-phase lookup / insert.
auto iter = m_types.find(toOpaqueUid(type_id));
if (iter != m_types.end())
return iter->second;
TypeSP type = CreateAndCacheType(type_id);
if (type)
m_obj_file->GetModule()->GetTypeList()->Insert(type);
return type;
}
VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
CVSymbol sym = m_index->symrecords().readRecord(var_id.offset);
if (sym.kind() == S_CONSTANT)
return CreateConstantSymbol(var_id, sym);
lldb::ValueType scope = eValueTypeInvalid;
TypeIndex ti;
llvm::StringRef name;
lldb::addr_t addr = 0;
uint16_t section = 0;
uint32_t offset = 0;
bool is_external = false;
switch (sym.kind()) {
case S_GDATA32:
is_external = true;
LLVM_FALLTHROUGH;
case S_LDATA32: {
DataSym ds(sym.kind());
llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, ds));
ti = ds.Type;
scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal
: eValueTypeVariableStatic;
name = ds.Name;
section = ds.Segment;
offset = ds.DataOffset;
addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset);
break;
}
case S_GTHREAD32:
is_external = true;
LLVM_FALLTHROUGH;
case S_LTHREAD32: {
ThreadLocalDataSym tlds(sym.kind());
llvm::cantFail(
SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, tlds));
ti = tlds.Type;
name = tlds.Name;
section = tlds.Segment;
offset = tlds.DataOffset;
addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset);
scope = eValueTypeVariableThreadLocal;
break;
}
default:
llvm_unreachable("unreachable!");
}
CompUnitSP comp_unit;
llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr);
if (modi) {
CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(*modi);
comp_unit = GetOrCreateCompileUnit(cci);
}
Declaration decl;
PdbTypeSymId tid(ti, false);
SymbolFileTypeSP type_sp =
std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid));
Variable::RangeList ranges;
m_ast->GetOrCreateVariableDecl(var_id);
DWARFExpression location = MakeGlobalLocationExpression(
section, offset, GetObjectFile()->GetModule());
std::string global_name("::");
global_name += name;
VariableSP var_sp = std::make_shared<Variable>(
toOpaqueUid(var_id), name.str().c_str(), global_name.c_str(), type_sp,
scope, comp_unit.get(), ranges, &decl, location, is_external, false,
false);
var_sp->SetLocationIsConstantValueData(false);
return var_sp;
}
lldb::VariableSP
SymbolFileNativePDB::CreateConstantSymbol(PdbGlobalSymId var_id,
const CVSymbol &cvs) {
TpiStream &tpi = m_index->tpi();
ConstantSym constant(cvs.kind());
llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(cvs, constant));
std::string global_name("::");
global_name += constant.Name;
PdbTypeSymId tid(constant.Type, false);
SymbolFileTypeSP type_sp =
std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid));
Declaration decl;
Variable::RangeList ranges;
ModuleSP module = GetObjectFile()->GetModule();
DWARFExpression location = MakeConstantLocationExpression(
constant.Type, tpi, constant.Value, module);
VariableSP var_sp = std::make_shared<Variable>(
toOpaqueUid(var_id), constant.Name.str().c_str(), global_name.c_str(),
type_sp, eValueTypeVariableGlobal, module.get(), ranges, &decl, location,
false, false, false);
var_sp->SetLocationIsConstantValueData(true);
return var_sp;
}
VariableSP
SymbolFileNativePDB::GetOrCreateGlobalVariable(PdbGlobalSymId var_id) {
auto emplace_result = m_global_vars.try_emplace(toOpaqueUid(var_id), nullptr);
if (emplace_result.second)
emplace_result.first->second = CreateGlobalVariable(var_id);
return emplace_result.first->second;
}
lldb::TypeSP SymbolFileNativePDB::GetOrCreateType(TypeIndex ti) {
return GetOrCreateType(PdbTypeSymId(ti, false));
}
FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbCompilandSymId func_id,
CompileUnit &comp_unit) {
auto emplace_result = m_functions.try_emplace(toOpaqueUid(func_id), nullptr);
if (emplace_result.second)
emplace_result.first->second = CreateFunction(func_id, comp_unit);
return emplace_result.first->second;
}
CompUnitSP
SymbolFileNativePDB::GetOrCreateCompileUnit(const CompilandIndexItem &cci) {
auto emplace_result =
m_compilands.try_emplace(toOpaqueUid(cci.m_id), nullptr);
if (emplace_result.second)
emplace_result.first->second = CreateCompileUnit(cci);
lldbassert(emplace_result.first->second);
return emplace_result.first->second;
}
Block &SymbolFileNativePDB::GetOrCreateBlock(PdbCompilandSymId block_id) {
auto iter = m_blocks.find(toOpaqueUid(block_id));
if (iter != m_blocks.end())
return *iter->second;
return CreateBlock(block_id);
}
void SymbolFileNativePDB::ParseDeclsForContext(
lldb_private::CompilerDeclContext decl_ctx) {
clang::DeclContext *context = m_ast->FromCompilerDeclContext(decl_ctx);
if (!context)
return;
m_ast->ParseDeclsForContext(*context);
}
lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) {
if (index >= GetNumCompileUnits())
return CompUnitSP();
lldbassert(index < UINT16_MAX);
if (index >= UINT16_MAX)
return nullptr;
CompilandIndexItem &item = m_index->compilands().GetOrCreateCompiland(index);
return GetOrCreateCompileUnit(item);
}
lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) {
PdbSymUid uid(comp_unit.GetID());
lldbassert(uid.kind() == PdbSymUidKind::Compiland);
CompilandIndexItem *item =
m_index->compilands().GetCompiland(uid.asCompiland().modi);
lldbassert(item);
if (!item->m_compile_opts)
return lldb::eLanguageTypeUnknown;
return TranslateLanguage(item->m_compile_opts->getLanguage());
}
void SymbolFileNativePDB::AddSymbols(Symtab &symtab) { return; }
size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) {
PdbSymUid uid{comp_unit.GetID()};
lldbassert(uid.kind() == PdbSymUidKind::Compiland);
uint16_t modi = uid.asCompiland().modi;
CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(modi);
size_t count = comp_unit.GetNumFunctions();
const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray();
for (auto iter = syms.begin(); iter != syms.end(); ++iter) {
if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32)
continue;
PdbCompilandSymId sym_id{modi, iter.offset()};
FunctionSP func = GetOrCreateFunction(sym_id, comp_unit);
}
size_t new_count = comp_unit.GetNumFunctions();
lldbassert(new_count >= count);
return new_count - count;
}
static bool NeedsResolvedCompileUnit(uint32_t resolve_scope) {
// If any of these flags are set, we need to resolve the compile unit.
uint32_t flags = eSymbolContextCompUnit;
flags |= eSymbolContextVariable;
flags |= eSymbolContextFunction;
flags |= eSymbolContextBlock;
flags |= eSymbolContextLineEntry;
return (resolve_scope & flags) != 0;
}
uint32_t SymbolFileNativePDB::ResolveSymbolContext(
const Address &addr, SymbolContextItem resolve_scope, SymbolContext &sc) {
uint32_t resolved_flags = 0;
lldb::addr_t file_addr = addr.GetFileAddress();
if (NeedsResolvedCompileUnit(resolve_scope)) {
llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr);
if (!modi)
return 0;
CompilandIndexItem *cci = m_index->compilands().GetCompiland(*modi);
if (!cci)
return 0;
sc.comp_unit = GetOrCreateCompileUnit(*cci).get();
resolved_flags |= eSymbolContextCompUnit;
}
if (resolve_scope & eSymbolContextFunction ||
resolve_scope & eSymbolContextBlock) {
lldbassert(sc.comp_unit);
std::vector<SymbolAndUid> matches = m_index->FindSymbolsByVa(file_addr);
// Search the matches in reverse. This way if there are multiple matches
// (for example we are 3 levels deep in a nested scope) it will find the
// innermost one first.
for (const auto &match : llvm::reverse(matches)) {
if (match.uid.kind() != PdbSymUidKind::CompilandSym)
continue;
PdbCompilandSymId csid = match.uid.asCompilandSym();
CVSymbol cvs = m_index->ReadSymbolRecord(csid);
PDB_SymType type = CVSymToPDBSym(cvs.kind());
if (type != PDB_SymType::Function && type != PDB_SymType::Block)
continue;
if (type == PDB_SymType::Function) {
sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get();
sc.block = sc.GetFunctionBlock();
}
if (type == PDB_SymType::Block) {
sc.block = &GetOrCreateBlock(csid);
sc.function = sc.block->CalculateSymbolContextFunction();
}
resolved_flags |= eSymbolContextFunction;
resolved_flags |= eSymbolContextBlock;
break;
}
}
if (resolve_scope & eSymbolContextLineEntry) {
lldbassert(sc.comp_unit);
if (auto *line_table = sc.comp_unit->GetLineTable()) {
if (line_table->FindLineEntryByAddress(addr, sc.line_entry))
resolved_flags |= eSymbolContextLineEntry;
}
}
return resolved_flags;
}
uint32_t SymbolFileNativePDB::ResolveSymbolContext(
const FileSpec &file_spec, uint32_t line, bool check_inlines,
lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
return 0;
}
static void AppendLineEntryToSequence(LineTable &table, LineSequence &sequence,
const CompilandIndexItem &cci,
lldb::addr_t base_addr,
uint32_t file_number,
const LineFragmentHeader &block,
const LineNumberEntry &cur) {
LineInfo cur_info(cur.Flags);
if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto())
return;
uint64_t addr = base_addr + cur.Offset;
bool is_statement = cur_info.isStatement();
bool is_prologue = IsFunctionPrologue(cci, addr);
bool is_epilogue = IsFunctionEpilogue(cci, addr);
uint32_t lno = cur_info.getStartLine();
table.AppendLineEntryToSequence(&sequence, addr, lno, 0, file_number,
is_statement, false, is_prologue, is_epilogue,
false);
}
static void TerminateLineSequence(LineTable &table,
const LineFragmentHeader &block,
lldb::addr_t base_addr, uint32_t file_number,
uint32_t last_line,
std::unique_ptr<LineSequence> seq) {
// The end is always a terminal entry, so insert it regardless.
table.AppendLineEntryToSequence(seq.get(), base_addr + block.CodeSize,
last_line, 0, file_number, false, false,
false, false, true);
table.InsertSequence(seq.release());
}
bool SymbolFileNativePDB::ParseLineTable(CompileUnit &comp_unit) {
// Unfortunately LLDB is set up to parse the entire compile unit line table
// all at once, even if all it really needs is line info for a specific
// function. In the future it would be nice if it could set the sc.m_function
// member, and we could only get the line info for the function in question.
PdbSymUid cu_id(comp_unit.GetID());
lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
CompilandIndexItem *cci =
m_index->compilands().GetCompiland(cu_id.asCompiland().modi);
lldbassert(cci);
auto line_table = llvm::make_unique<LineTable>(&comp_unit);
// This is basically a copy of the .debug$S subsections from all original COFF
// object files merged together with address relocations applied. We are
// looking for all DEBUG_S_LINES subsections.
for (const DebugSubsectionRecord &dssr :
cci->m_debug_stream.getSubsectionsArray()) {
if (dssr.kind() != DebugSubsectionKind::Lines)
continue;
DebugLinesSubsectionRef lines;
llvm::BinaryStreamReader reader(dssr.getRecordData());
if (auto EC = lines.initialize(reader)) {
llvm::consumeError(std::move(EC));
return false;
}
const LineFragmentHeader *lfh = lines.header();
uint64_t virtual_addr =
m_index->MakeVirtualAddress(lfh->RelocSegment, lfh->RelocOffset);
const auto &checksums = cci->m_strings.checksums().getArray();
const auto &strings = cci->m_strings.strings();
for (const LineColumnEntry &group : lines) {
// Indices in this structure are actually offsets of records in the
// DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index
// into the global PDB string table.
auto iter = checksums.at(group.NameIndex);
if (iter == checksums.end())
continue;
llvm::Expected<llvm::StringRef> efn =
strings.getString(iter->FileNameOffset);
if (!efn) {
llvm::consumeError(efn.takeError());
continue;
}
// LLDB wants the index of the file in the list of support files.
auto fn_iter = llvm::find(cci->m_file_list, *efn);
lldbassert(fn_iter != cci->m_file_list.end());
// LLDB support file indices are 1-based.
uint32_t file_index =
1 + std::distance(cci->m_file_list.begin(), fn_iter);
std::unique_ptr<LineSequence> sequence(
line_table->CreateLineSequenceContainer());
lldbassert(!group.LineNumbers.empty());
for (const LineNumberEntry &entry : group.LineNumbers) {
AppendLineEntryToSequence(*line_table, *sequence, *cci, virtual_addr,
file_index, *lfh, entry);
}
LineInfo last_line(group.LineNumbers.back().Flags);
TerminateLineSequence(*line_table, *lfh, virtual_addr, file_index,
last_line.getEndLine(), std::move(sequence));
}
}
if (line_table->GetSize() == 0)
return false;
comp_unit.SetLineTable(line_table.release());
return true;
}
bool SymbolFileNativePDB::ParseDebugMacros(CompileUnit &comp_unit) {
// PDB doesn't contain information about macros
return false;
}
bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit,
FileSpecList &support_files) {
PdbSymUid cu_id(comp_unit.GetID());
lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
CompilandIndexItem *cci =
m_index->compilands().GetCompiland(cu_id.asCompiland().modi);
lldbassert(cci);
for (llvm::StringRef f : cci->m_file_list) {
FileSpec::Style style =
f.startswith("/") ? FileSpec::Style::posix : FileSpec::Style::windows;
FileSpec spec(f, style);
support_files.Append(spec);
}
llvm::SmallString<64> main_source_file =
m_index->compilands().GetMainSourceFile(*cci);
FileSpec::Style style = main_source_file.startswith("/")
? FileSpec::Style::posix
: FileSpec::Style::windows;
FileSpec spec(main_source_file, style);
support_files.Insert(0, spec);
return true;
}
bool SymbolFileNativePDB::ParseImportedModules(
const SymbolContext &sc, std::vector<ConstString> &imported_modules) {
// PDB does not yet support module debug info
return false;
}
size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) {
GetOrCreateBlock(PdbSymUid(func.GetID()).asCompilandSym());
// FIXME: Parse child blocks
return 1;
}
void SymbolFileNativePDB::DumpClangAST(Stream &s) { m_ast->Dump(s); }
uint32_t SymbolFileNativePDB::FindGlobalVariables(
const ConstString &name, const CompilerDeclContext *parent_decl_ctx,
uint32_t max_matches, VariableList &variables) {
using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName(
name.GetStringRef(), m_index->symrecords());
for (const SymbolAndOffset &result : results) {
VariableSP var;
switch (result.second.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_GTHREAD32:
case SymbolKind::S_LTHREAD32:
case SymbolKind::S_CONSTANT: {
PdbGlobalSymId global(result.first, false);
var = GetOrCreateGlobalVariable(global);
variables.AddVariable(var);
break;
}
default:
continue;
}
}
return variables.GetSize();
}
uint32_t SymbolFileNativePDB::FindFunctions(
const ConstString &name, const CompilerDeclContext *parent_decl_ctx,
FunctionNameType name_type_mask, bool include_inlines, bool append,
SymbolContextList &sc_list) {
// For now we only support lookup by method name.
if (!(name_type_mask & eFunctionNameTypeMethod))
return 0;
using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
name.GetStringRef(), m_index->symrecords());
for (const SymbolAndOffset &match : matches) {
if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
continue;
ProcRefSym proc(match.second.kind());
cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc));
if (!IsValidRecord(proc))
continue;
CompilandIndexItem &cci =
m_index->compilands().GetOrCreateCompiland(proc.modi());
SymbolContext sc;
sc.comp_unit = GetOrCreateCompileUnit(cci).get();
PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
sc_list.Append(sc);
}
return sc_list.GetSize();
}
uint32_t SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,
bool include_inlines, bool append,
SymbolContextList &sc_list) {
return 0;
}
uint32_t SymbolFileNativePDB::FindTypes(
const ConstString &name, const CompilerDeclContext *parent_decl_ctx,
bool append, uint32_t max_matches,
llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) {
if (!append)
types.Clear();
if (!name)
return 0;
searched_symbol_files.clear();
searched_symbol_files.insert(this);
// There is an assumption 'name' is not a regex
size_t match_count = FindTypesByName(name.GetStringRef(), max_matches, types);
return match_count;
}
size_t
SymbolFileNativePDB::FindTypes(const std::vector<CompilerContext> &context,
bool append, TypeMap &types) {
return 0;
}
size_t SymbolFileNativePDB::FindTypesByName(llvm::StringRef name,
uint32_t max_matches,
TypeMap &types) {
size_t match_count = 0;
std::vector<TypeIndex> matches = m_index->tpi().findRecordsByName(name);
if (max_matches > 0 && max_matches < matches.size())
matches.resize(max_matches);
for (TypeIndex ti : matches) {
TypeSP type = GetOrCreateType(ti);
if (!type)
continue;
types.Insert(type);
++match_count;
}
return match_count;
}
size_t SymbolFileNativePDB::ParseTypes(CompileUnit &comp_unit) {
// Only do the full type scan the first time.
if (m_done_full_type_scan)
return 0;
size_t old_count = m_obj_file->GetModule()->GetTypeList()->GetSize();
LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
// First process the entire TPI stream.
for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) {
TypeSP type = GetOrCreateType(*ti);
if (type)
(void)type->GetFullCompilerType();
}
// Next look for S_UDT records in the globals stream.
for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
PdbGlobalSymId global{gid, false};
CVSymbol sym = m_index->ReadSymbolRecord(global);
if (sym.kind() != S_UDT)
continue;
UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
bool is_typedef = true;
if (IsTagRecord(PdbTypeSymId{udt.Type, false}, m_index->tpi())) {
CVType cvt = m_index->tpi().getType(udt.Type);
llvm::StringRef name = CVTagRecord::create(cvt).name();
if (name == udt.Name)
is_typedef = false;
}
if (is_typedef)
GetOrCreateTypedef(global);
}
size_t new_count = m_obj_file->GetModule()->GetTypeList()->GetSize();
m_done_full_type_scan = true;
return new_count - old_count;
}
size_t
SymbolFileNativePDB::ParseVariablesForCompileUnit(CompileUnit &comp_unit,
VariableList &variables) {
PdbSymUid sym_uid(comp_unit.GetID());
lldbassert(sym_uid.kind() == PdbSymUidKind::Compiland);
return 0;
}
VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id,
PdbCompilandSymId var_id,
bool is_param) {
ModuleSP module = GetObjectFile()->GetModule();
- VariableInfo var_info = GetVariableLocationInfo(*m_index, var_id, module);
+ Block &block = GetOrCreateBlock(scope_id);
+ VariableInfo var_info =
+ GetVariableLocationInfo(*m_index, var_id, block, module);
if (!var_info.location || !var_info.ranges)
return nullptr;
CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi);
CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii);
TypeSP type_sp = GetOrCreateType(var_info.type);
std::string name = var_info.name.str();
Declaration decl;
SymbolFileTypeSP sftype =
std::make_shared<SymbolFileType>(*this, type_sp->GetID());
ValueType var_scope =
is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal;
VariableSP var_sp = std::make_shared<Variable>(
toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope,
comp_unit_sp.get(), *var_info.ranges, &decl, *var_info.location, false,
false, false);
if (!is_param)
m_ast->GetOrCreateVariableDecl(scope_id, var_id);
m_local_variables[toOpaqueUid(var_id)] = var_sp;
return var_sp;
}
VariableSP SymbolFileNativePDB::GetOrCreateLocalVariable(
PdbCompilandSymId scope_id, PdbCompilandSymId var_id, bool is_param) {
auto iter = m_local_variables.find(toOpaqueUid(var_id));
if (iter != m_local_variables.end())
return iter->second;
return CreateLocalVariable(scope_id, var_id, is_param);
}
TypeSP SymbolFileNativePDB::CreateTypedef(PdbGlobalSymId id) {
CVSymbol sym = m_index->ReadSymbolRecord(id);
lldbassert(sym.kind() == SymbolKind::S_UDT);
UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
TypeSP target_type = GetOrCreateType(udt.Type);
(void)m_ast->GetOrCreateTypedefDecl(id);
Declaration decl;
return std::make_shared<lldb_private::Type>(
toOpaqueUid(id), this, ConstString(udt.Name), target_type->GetByteSize(),
nullptr, target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID,
decl, target_type->GetForwardCompilerType(),
lldb_private::Type::eResolveStateForward);
}
TypeSP SymbolFileNativePDB::GetOrCreateTypedef(PdbGlobalSymId id) {
auto iter = m_types.find(toOpaqueUid(id));
if (iter != m_types.end())
return iter->second;
return CreateTypedef(id);
}
size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) {
Block &block = GetOrCreateBlock(block_id);
size_t count = 0;
CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
uint32_t params_remaining = 0;
switch (sym.kind()) {
case S_GPROC32:
case S_LPROC32: {
ProcSym proc(static_cast<SymbolRecordKind>(sym.kind()));
cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym, proc));
CVType signature = m_index->tpi().getType(proc.FunctionType);
ProcedureRecord sig;
cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(signature, sig));
params_remaining = sig.getParameterCount();
break;
}
case S_BLOCK32:
break;
default:
lldbassert(false && "Symbol is not a block!");
return 0;
}
VariableListSP variables = block.GetBlockVariableList(false);
if (!variables) {
variables = std::make_shared<VariableList>();
block.SetVariableList(variables);
}
CVSymbolArray syms = limitSymbolArrayToScope(
cii->m_debug_stream.getSymbolArray(), block_id.offset);
// Skip the first record since it's a PROC32 or BLOCK32, and there's
// no point examining it since we know it's not a local variable.
syms.drop_front();
auto iter = syms.begin();
auto end = syms.end();
while (iter != end) {
uint32_t record_offset = iter.offset();
CVSymbol variable_cvs = *iter;
PdbCompilandSymId child_sym_id(block_id.modi, record_offset);
++iter;
// If this is a block, recurse into its children and then skip it.
if (variable_cvs.kind() == S_BLOCK32) {
uint32_t block_end = getScopeEndOffset(variable_cvs);
count += ParseVariablesForBlock(child_sym_id);
iter = syms.at(block_end);
continue;
}
bool is_param = params_remaining > 0;
VariableSP variable;
switch (variable_cvs.kind()) {
case S_REGREL32:
case S_REGISTER:
case S_LOCAL:
variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param);
if (is_param)
--params_remaining;
if (variable)
variables->AddVariableIfUnique(variable);
break;
default:
break;
}
}
// Pass false for set_children, since we call this recursively so that the
// children will call this for themselves.
block.SetDidParseVariables(true, false);
return count;
}
size_t SymbolFileNativePDB::ParseVariablesForContext(const SymbolContext &sc) {
lldbassert(sc.function || sc.comp_unit);
VariableListSP variables;
if (sc.block) {
PdbSymUid block_id(sc.block->GetID());
size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
return count;
}
if (sc.function) {
PdbSymUid block_id(sc.function->GetID());
size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
return count;
}
if (sc.comp_unit) {
variables = sc.comp_unit->GetVariableList(false);
if (!variables) {
variables = std::make_shared<VariableList>();
sc.comp_unit->SetVariableList(variables);
}
return ParseVariablesForCompileUnit(*sc.comp_unit, *variables);
}
llvm_unreachable("Unreachable!");
}
CompilerDecl SymbolFileNativePDB::GetDeclForUID(lldb::user_id_t uid) {
clang::Decl *decl = m_ast->GetOrCreateDeclForUid(PdbSymUid(uid));
return m_ast->ToCompilerDecl(*decl);
}
CompilerDeclContext
SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) {
clang::DeclContext *context =
m_ast->GetOrCreateDeclContextForUid(PdbSymUid(uid));
if (!context)
return {};
return m_ast->ToCompilerDeclContext(*context);
}
CompilerDeclContext
SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) {
clang::DeclContext *context = m_ast->GetParentDeclContext(PdbSymUid(uid));
return m_ast->ToCompilerDeclContext(*context);
}
Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) {
auto iter = m_types.find(type_uid);
// lldb should not be passing us non-sensical type uids. the only way it
// could have a type uid in the first place is if we handed it out, in which
// case we should know about the type. However, that doesn't mean we've
// instantiated it yet. We can vend out a UID for a future type. So if the
// type doesn't exist, let's instantiate it now.
if (iter != m_types.end())
return &*iter->second;
PdbSymUid uid(type_uid);
lldbassert(uid.kind() == PdbSymUidKind::Type);
PdbTypeSymId type_id = uid.asTypeSym();
if (type_id.index.isNoneType())
return nullptr;
TypeSP type_sp = CreateAndCacheType(type_id);
return &*type_sp;
}
llvm::Optional<SymbolFile::ArrayInfo>
SymbolFileNativePDB::GetDynamicArrayInfoForUID(
lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
return llvm::None;
}
bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) {
clang::QualType qt =
clang::QualType::getFromOpaquePtr(compiler_type.GetOpaqueQualType());
return m_ast->CompleteType(qt);
}
size_t SymbolFileNativePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope,
TypeClass type_mask,
lldb_private::TypeList &type_list) {
return 0;
}
CompilerDeclContext
SymbolFileNativePDB::FindNamespace(const ConstString &name,
const CompilerDeclContext *parent_decl_ctx) {
return {};
}
TypeSystem *
SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) {
auto type_system =
m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
if (type_system)
type_system->SetSymbolFile(this);
return type_system;
}
ConstString SymbolFileNativePDB::GetPluginName() {
static ConstString g_name("pdb");
return g_name;
}
uint32_t SymbolFileNativePDB::GetPluginVersion() { return 1; }
Index: source/Plugins/SymbolFile/NativePDB/PdbIndex.h
===================================================================
--- source/Plugins/SymbolFile/NativePDB/PdbIndex.h (revision 352844)
+++ source/Plugins/SymbolFile/NativePDB/PdbIndex.h (revision 352845)
@@ -1,161 +1,162 @@
//===-- PdbIndex.h ----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H
#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H
#include "lldb/lldb-types.h"
#include "llvm/ADT/IntervalMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "CompileUnitIndex.h"
#include "PdbSymUid.h"
#include <map>
#include <memory>
namespace llvm {
namespace pdb {
class DbiStream;
class TpiStream;
class TpiStream;
class InfoStream;
class PublicsStream;
class GlobalsStream;
class SymbolStream;
} // namespace pdb
} // namespace llvm
namespace lldb_private {
namespace npdb {
struct SegmentOffset;
/// PdbIndex - Lazy access to the important parts of a PDB file.
///
/// This is a layer on top of LLVM's native PDB support libraries which cache
/// certain data when it is accessed the first time. The entire PDB file is
/// mapped into memory, and the underlying support libraries vend out memory
/// that is always backed by the file, so it is safe to hold StringRefs and
/// ArrayRefs into the backing memory as long as the PdbIndex instance is
/// alive.
class PdbIndex {
/// The underlying PDB file.
std::unique_ptr<llvm::pdb::PDBFile> m_file;
/// The DBI stream. This contains general high level information about the
/// features present in the PDB file, compile units (such as the information
/// necessary to locate full symbol information for each compile unit),
/// section contributions, and other data which is not specifically symbol or
/// type records.
llvm::pdb::DbiStream *m_dbi = nullptr;
/// TPI (types) and IPI (indices) streams. These are both in the exact same
/// format with different data. Most type records are stored in the TPI
/// stream but certain specific types of records are stored in the IPI stream.
/// The IPI stream records can refer to the records in the TPI stream, but not
/// the other way around.
llvm::pdb::TpiStream *m_tpi = nullptr;
llvm::pdb::TpiStream *m_ipi = nullptr;
/// This is called the "PDB Stream" in the Microsoft reference implementation.
/// It contains information about the structure of the file, as well as fields
/// used to match EXE and PDB.
llvm::pdb::InfoStream *m_info = nullptr;
/// Publics stream. Is actually a serialized hash table where the keys are
/// addresses of symbols in the executable, and values are a record containing
/// mangled names and an index which can be used to locate more detailed info
/// about the symbol in the Symbol Records stream. The publics stream only
/// contains info about externally visible symbols.
llvm::pdb::PublicsStream *m_publics = nullptr;
/// Globals stream. Contrary to its name, this does not contain information
/// about all "global variables" or "global functions". Rather, it is the
/// "global symbol table", i.e. it contains information about *every* symbol
/// in the executable. It is a hash table keyed on name, whose values are
/// indices into the symbol records stream to find the full record.
llvm::pdb::GlobalsStream *m_globals = nullptr;
/// Symbol records stream. The publics and globals stream refer to records
/// in this stream. For some records, like constants and typedefs, the
/// complete record lives in this stream. For other symbol types, such as
/// functions, data, and other things that have been materialied into a
/// specific compile unit, the records here simply provide a reference
/// necessary to locate the full information.
llvm::pdb::SymbolStream *m_symrecords = nullptr;
/// Index of all compile units, mapping identifier to |CompilandIndexItem|
/// instance.
CompileUnitIndex m_cus;
/// An allocator for the interval maps
llvm::IntervalMap<lldb::addr_t, uint32_t>::Allocator m_allocator;
/// Maps virtual address to module index
llvm::IntervalMap<lldb::addr_t, uint16_t> m_va_to_modi;
/// The address at which the program has been loaded into memory.
lldb::addr_t m_load_address = 0;
PdbIndex();
void BuildAddrToSymbolMap(CompilandIndexItem &cci);
public:
static llvm::Expected<std::unique_ptr<PdbIndex>>
create(std::unique_ptr<llvm::pdb::PDBFile>);
void SetLoadAddress(lldb::addr_t addr) { m_load_address = addr; }
+ lldb::addr_t GetLoadAddress() const { return m_load_address; }
void ParseSectionContribs();
llvm::pdb::PDBFile &pdb() { return *m_file; }
const llvm::pdb::PDBFile &pdb() const { return *m_file; }
llvm::pdb::DbiStream &dbi() { return *m_dbi; }
const llvm::pdb::DbiStream &dbi() const { return *m_dbi; }
llvm::pdb::TpiStream &tpi() { return *m_tpi; }
const llvm::pdb::TpiStream &tpi() const { return *m_tpi; }
llvm::pdb::TpiStream &ipi() { return *m_ipi; }
const llvm::pdb::TpiStream &ipi() const { return *m_ipi; }
llvm::pdb::InfoStream &info() { return *m_info; }
const llvm::pdb::InfoStream &info() const { return *m_info; }
llvm::pdb::PublicsStream &publics() { return *m_publics; }
const llvm::pdb::PublicsStream &publics() const { return *m_publics; }
llvm::pdb::GlobalsStream &globals() { return *m_globals; }
const llvm::pdb::GlobalsStream &globals() const { return *m_globals; }
llvm::pdb::SymbolStream &symrecords() { return *m_symrecords; }
const llvm::pdb::SymbolStream &symrecords() const { return *m_symrecords; }
CompileUnitIndex &compilands() { return m_cus; }
const CompileUnitIndex &compilands() const { return m_cus; }
lldb::addr_t MakeVirtualAddress(uint16_t segment, uint32_t offset) const;
lldb::addr_t MakeVirtualAddress(const SegmentOffset &so) const;
std::vector<SymbolAndUid> FindSymbolsByVa(lldb::addr_t va);
llvm::codeview::CVSymbol ReadSymbolRecord(PdbCompilandSymId cu_sym) const;
llvm::codeview::CVSymbol ReadSymbolRecord(PdbGlobalSymId global) const;
llvm::Optional<uint16_t> GetModuleIndexForAddr(uint16_t segment,
uint32_t offset) const;
llvm::Optional<uint16_t> GetModuleIndexForVa(lldb::addr_t va) const;
};
} // namespace npdb
} // namespace lldb_private
#endif
Index: source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp (revision 352844)
+++ source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp (revision 352845)
@@ -1,749 +1,877 @@
//===-- PdbUtil.cpp ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "PdbUtil.h"
#include "DWARFLocationExpression.h"
#include "PdbIndex.h"
#include "PdbSymUid.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Symbol/Block.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/lldb-enumerations.h"
using namespace lldb_private;
using namespace lldb_private::npdb;
using namespace llvm::codeview;
using namespace llvm::pdb;
static Variable::RangeList
MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range,
llvm::ArrayRef<LocalVariableAddrGap> gaps) {
lldb::addr_t start =
index.MakeVirtualAddress(range.ISectStart, range.OffsetStart);
lldb::addr_t end = start + range.Range;
Variable::RangeList result;
while (!gaps.empty()) {
const LocalVariableAddrGap &gap = gaps.front();
lldb::addr_t size = gap.GapStartOffset - start;
result.Append(start, size);
start += gap.Range;
gaps = gaps.drop_front();
}
- result.Append(start, end);
+ result.Append(start, end - start);
return result;
}
CVTagRecord CVTagRecord::create(CVType type) {
assert(IsTagRecord(type) && "type is not a tag record!");
switch (type.kind()) {
case LF_CLASS:
case LF_STRUCTURE:
case LF_INTERFACE: {
ClassRecord cr;
llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr));
return CVTagRecord(std::move(cr));
}
case LF_UNION: {
UnionRecord ur;
llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur));
return CVTagRecord(std::move(ur));
}
case LF_ENUM: {
EnumRecord er;
llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(type, er));
return CVTagRecord(std::move(er));
}
default:
llvm_unreachable("Unreachable!");
}
}
CVTagRecord::CVTagRecord(ClassRecord &&c)
: cvclass(std::move(c)),
m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {}
CVTagRecord::CVTagRecord(UnionRecord &&u)
: cvunion(std::move(u)), m_kind(Union) {}
CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {}
PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) {
switch (kind) {
case S_COMPILE3:
case S_OBJNAME:
return PDB_SymType::CompilandDetails;
case S_ENVBLOCK:
return PDB_SymType::CompilandEnv;
case S_THUNK32:
case S_TRAMPOLINE:
return PDB_SymType::Thunk;
case S_COFFGROUP:
return PDB_SymType::CoffGroup;
case S_EXPORT:
return PDB_SymType::Export;
case S_LPROC32:
case S_GPROC32:
case S_LPROC32_DPC:
return PDB_SymType::Function;
case S_PUB32:
return PDB_SymType::PublicSymbol;
case S_INLINESITE:
return PDB_SymType::InlineSite;
case S_LOCAL:
case S_BPREL32:
case S_REGREL32:
case S_MANCONSTANT:
case S_CONSTANT:
case S_LDATA32:
case S_GDATA32:
case S_LMANDATA:
case S_GMANDATA:
case S_LTHREAD32:
case S_GTHREAD32:
return PDB_SymType::Data;
case S_BLOCK32:
return PDB_SymType::Block;
case S_LABEL32:
return PDB_SymType::Label;
case S_CALLSITEINFO:
return PDB_SymType::CallSite;
case S_HEAPALLOCSITE:
return PDB_SymType::HeapAllocationSite;
case S_CALLEES:
return PDB_SymType::Callee;
case S_CALLERS:
return PDB_SymType::Caller;
default:
lldbassert(false && "Invalid symbol record kind!");
}
return PDB_SymType::None;
}
PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) {
switch (kind) {
case LF_ARRAY:
return PDB_SymType::ArrayType;
case LF_ARGLIST:
return PDB_SymType::FunctionSig;
case LF_BCLASS:
return PDB_SymType::BaseClass;
case LF_BINTERFACE:
return PDB_SymType::BaseInterface;
case LF_CLASS:
case LF_STRUCTURE:
case LF_INTERFACE:
case LF_UNION:
return PDB_SymType::UDT;
case LF_POINTER:
return PDB_SymType::PointerType;
case LF_ENUM:
return PDB_SymType::Enum;
case LF_PROCEDURE:
return PDB_SymType::FunctionSig;
case LF_BITFIELD:
return PDB_SymType::BuiltinType;
default:
lldbassert(false && "Invalid type record kind!");
}
return PDB_SymType::None;
}
bool lldb_private::npdb::SymbolHasAddress(const CVSymbol &sym) {
switch (sym.kind()) {
case S_GPROC32:
case S_LPROC32:
case S_GPROC32_ID:
case S_LPROC32_ID:
case S_LPROC32_DPC:
case S_LPROC32_DPC_ID:
case S_THUNK32:
case S_TRAMPOLINE:
case S_COFFGROUP:
case S_BLOCK32:
case S_LABEL32:
case S_CALLSITEINFO:
case S_HEAPALLOCSITE:
case S_LDATA32:
case S_GDATA32:
case S_LMANDATA:
case S_GMANDATA:
case S_LTHREAD32:
case S_GTHREAD32:
return true;
default:
return false;
}
}
bool lldb_private::npdb::SymbolIsCode(const CVSymbol &sym) {
switch (sym.kind()) {
case S_GPROC32:
case S_LPROC32:
case S_GPROC32_ID:
case S_LPROC32_ID:
case S_LPROC32_DPC:
case S_LPROC32_DPC_ID:
case S_THUNK32:
case S_TRAMPOLINE:
case S_COFFGROUP:
case S_BLOCK32:
return true;
default:
return false;
}
}
template <typename RecordT> RecordT createRecord(const CVSymbol &sym) {
RecordT record(static_cast<SymbolRecordKind>(sym.kind()));
cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record));
return record;
}
template <typename RecordT>
static SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) {
RecordT record = createRecord<RecordT>(sym);
return {record.Segment, record.CodeOffset};
}
template <>
SegmentOffset GetSegmentAndOffset<TrampolineSym>(const CVSymbol &sym) {
TrampolineSym record = createRecord<TrampolineSym>(sym);
return {record.ThunkSection, record.ThunkOffset};
}
template <> SegmentOffset GetSegmentAndOffset<Thunk32Sym>(const CVSymbol &sym) {
Thunk32Sym record = createRecord<Thunk32Sym>(sym);
return {record.Segment, record.Offset};
}
template <>
SegmentOffset GetSegmentAndOffset<CoffGroupSym>(const CVSymbol &sym) {
CoffGroupSym record = createRecord<CoffGroupSym>(sym);
return {record.Segment, record.Offset};
}
template <> SegmentOffset GetSegmentAndOffset<DataSym>(const CVSymbol &sym) {
DataSym record = createRecord<DataSym>(sym);
return {record.Segment, record.DataOffset};
}
template <>
SegmentOffset GetSegmentAndOffset<ThreadLocalDataSym>(const CVSymbol &sym) {
ThreadLocalDataSym record = createRecord<ThreadLocalDataSym>(sym);
return {record.Segment, record.DataOffset};
}
SegmentOffset lldb_private::npdb::GetSegmentAndOffset(const CVSymbol &sym) {
switch (sym.kind()) {
case S_GPROC32:
case S_LPROC32:
case S_GPROC32_ID:
case S_LPROC32_ID:
case S_LPROC32_DPC:
case S_LPROC32_DPC_ID:
return ::GetSegmentAndOffset<ProcSym>(sym);
case S_THUNK32:
return ::GetSegmentAndOffset<Thunk32Sym>(sym);
break;
case S_TRAMPOLINE:
return ::GetSegmentAndOffset<TrampolineSym>(sym);
break;
case S_COFFGROUP:
return ::GetSegmentAndOffset<CoffGroupSym>(sym);
break;
case S_BLOCK32:
return ::GetSegmentAndOffset<BlockSym>(sym);
break;
case S_LABEL32:
return ::GetSegmentAndOffset<LabelSym>(sym);
break;
case S_CALLSITEINFO:
return ::GetSegmentAndOffset<CallSiteInfoSym>(sym);
break;
case S_HEAPALLOCSITE:
return ::GetSegmentAndOffset<HeapAllocationSiteSym>(sym);
break;
case S_LDATA32:
case S_GDATA32:
case S_LMANDATA:
case S_GMANDATA:
return ::GetSegmentAndOffset<DataSym>(sym);
break;
case S_LTHREAD32:
case S_GTHREAD32:
return ::GetSegmentAndOffset<ThreadLocalDataSym>(sym);
break;
default:
lldbassert(false && "Record does not have a segment/offset!");
}
return {0, 0};
}
template <typename RecordT>
SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) {
RecordT record = createRecord<RecordT>(sym);
return {record.Segment, record.CodeOffset, record.CodeSize};
}
template <>
SegmentOffsetLength
GetSegmentOffsetAndLength<TrampolineSym>(const CVSymbol &sym) {
TrampolineSym record = createRecord<TrampolineSym>(sym);
return {record.ThunkSection, record.ThunkOffset, record.Size};
}
template <>
SegmentOffsetLength GetSegmentOffsetAndLength<Thunk32Sym>(const CVSymbol &sym) {
Thunk32Sym record = createRecord<Thunk32Sym>(sym);
return SegmentOffsetLength{record.Segment, record.Offset, record.Length};
}
template <>
SegmentOffsetLength
GetSegmentOffsetAndLength<CoffGroupSym>(const CVSymbol &sym) {
CoffGroupSym record = createRecord<CoffGroupSym>(sym);
return SegmentOffsetLength{record.Segment, record.Offset, record.Size};
}
SegmentOffsetLength
lldb_private::npdb::GetSegmentOffsetAndLength(const CVSymbol &sym) {
switch (sym.kind()) {
case S_GPROC32:
case S_LPROC32:
case S_GPROC32_ID:
case S_LPROC32_ID:
case S_LPROC32_DPC:
case S_LPROC32_DPC_ID:
return ::GetSegmentOffsetAndLength<ProcSym>(sym);
case S_THUNK32:
return ::GetSegmentOffsetAndLength<Thunk32Sym>(sym);
break;
case S_TRAMPOLINE:
return ::GetSegmentOffsetAndLength<TrampolineSym>(sym);
break;
case S_COFFGROUP:
return ::GetSegmentOffsetAndLength<CoffGroupSym>(sym);
break;
case S_BLOCK32:
return ::GetSegmentOffsetAndLength<BlockSym>(sym);
break;
default:
lldbassert(false && "Record does not have a segment/offset/length triple!");
}
return {0, 0, 0};
}
bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
ClassRecord cr;
UnionRecord ur;
EnumRecord er;
switch (cvt.kind()) {
case LF_CLASS:
case LF_STRUCTURE:
case LF_INTERFACE:
llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
return cr.isForwardRef();
case LF_UNION:
llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
return ur.isForwardRef();
case LF_ENUM:
llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
return er.isForwardRef();
default:
return false;
}
}
bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
switch (cvt.kind()) {
case LF_CLASS:
case LF_STRUCTURE:
case LF_UNION:
case LF_ENUM:
return true;
default:
return false;
}
}
bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) {
switch (cvt.kind()) {
case LF_CLASS:
case LF_STRUCTURE:
case LF_UNION:
return true;
default:
return false;
}
}
bool lldb_private::npdb::IsForwardRefUdt(const PdbTypeSymId &id,
TpiStream &tpi) {
if (id.is_ipi || id.index.isSimple())
return false;
return IsForwardRefUdt(tpi.getType(id.index));
}
bool lldb_private::npdb::IsTagRecord(const PdbTypeSymId &id, TpiStream &tpi) {
if (id.is_ipi || id.index.isSimple())
return false;
return IsTagRecord(tpi.getType(id.index));
}
lldb::AccessType
lldb_private::npdb::TranslateMemberAccess(MemberAccess access) {
switch (access) {
case MemberAccess::Private:
return lldb::eAccessPrivate;
case MemberAccess::Protected:
return lldb::eAccessProtected;
case MemberAccess::Public:
return lldb::eAccessPublic;
case MemberAccess::None:
return lldb::eAccessNone;
}
llvm_unreachable("unreachable");
}
TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) {
switch (cvt.kind()) {
case LF_CLASS:
case LF_STRUCTURE:
case LF_INTERFACE: {
ClassRecord cr;
cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
return cr.FieldList;
}
case LF_UNION: {
UnionRecord ur;
cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
return ur.FieldList;
}
case LF_ENUM: {
EnumRecord er;
cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
return er.FieldList;
}
default:
llvm_unreachable("Unreachable!");
}
}
TypeIndex lldb_private::npdb::LookThroughModifierRecord(CVType modifier) {
lldbassert(modifier.kind() == LF_MODIFIER);
ModifierRecord mr;
llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(modifier, mr));
return mr.ModifiedType;
}
llvm::StringRef lldb_private::npdb::DropNameScope(llvm::StringRef name) {
return MSVCUndecoratedNameParser::DropScope(name);
}
VariableInfo lldb_private::npdb::GetVariableNameInfo(CVSymbol sym) {
VariableInfo result;
if (sym.kind() == S_REGREL32) {
RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
result.type = reg.Type;
result.name = reg.Name;
return result;
}
if (sym.kind() == S_REGISTER) {
RegisterSym reg(SymbolRecordKind::RegisterSym);
cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
result.type = reg.Index;
result.name = reg.Name;
return result;
}
if (sym.kind() == S_LOCAL) {
LocalSym local(SymbolRecordKind::LocalSym);
cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
result.type = local.Type;
result.name = local.Name;
return result;
}
if (sym.kind() == S_GDATA32 || sym.kind() == S_LDATA32) {
DataSym data(SymbolRecordKind::DataSym);
cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, data));
result.type = data.Type;
result.name = data.Name;
return result;
}
if (sym.kind() == S_GTHREAD32 || sym.kind() == S_LTHREAD32) {
ThreadLocalDataSym data(SymbolRecordKind::ThreadLocalDataSym);
cantFail(SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, data));
result.type = data.Type;
result.name = data.Name;
return result;
}
if (sym.kind() == S_CONSTANT) {
ConstantSym constant(SymbolRecordKind::ConstantSym);
cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym, constant));
result.type = constant.Type;
result.name = constant.Name;
return result;
}
lldbassert(false && "Invalid variable record kind!");
return {};
}
+static auto
+GetCorrespondingFrameData(lldb::addr_t load_addr,
+ const DebugFrameDataSubsectionRef &fpo_data,
+ const Variable::RangeList &ranges) {
+ lldbassert(!ranges.IsEmpty());
+
+ // assume that all variable ranges correspond to one frame data
+ using RangeListEntry = Variable::RangeList::Entry;
+ const RangeListEntry &range = ranges.GetEntryRef(0);
+
+ auto it = fpo_data.begin();
+
+ // start by searching first frame data range containing variable range
+ for (; it != fpo_data.end(); ++it) {
+ RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize);
+
+ if (fd_range.Contains(range)) {
+ break;
+ }
+ }
+
+ // then first most nested entry that still contains variable range
+ auto found = it;
+ for (; it != fpo_data.end(); ++it) {
+ RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize);
+
+ if (!fd_range.Contains(range)) {
+ break;
+ }
+ found = it;
+ }
+
+ return found;
+}
+
+static bool GetFrameDataProgram(PdbIndex &index,
+ const Variable::RangeList &ranges,
+ llvm::StringRef &out_program) {
+ const DebugFrameDataSubsectionRef &new_fpo_data =
+ index.dbi().getNewFpoRecords();
+
+ auto frame_data_it =
+ GetCorrespondingFrameData(index.GetLoadAddress(), new_fpo_data, ranges);
+ if (frame_data_it == new_fpo_data.end())
+ return false;
+
+ PDBStringTable &strings = cantFail(index.pdb().getStringTable());
+ out_program = cantFail(strings.getStringForID(frame_data_it->FrameFunc));
+ return true;
+}
+
+static RegisterId GetBaseFrameRegister(PdbIndex &index,
+ PdbCompilandSymId frame_proc_id,
+ bool is_parameter) {
+ CVSymbol frame_proc_cvs = index.ReadSymbolRecord(frame_proc_id);
+ lldbassert(frame_proc_cvs.kind() == S_FRAMEPROC);
+
+ FrameProcSym frame_proc(SymbolRecordKind::FrameProcSym);
+ cantFail(SymbolDeserializer::deserializeAs<FrameProcSym>(frame_proc_cvs,
+ frame_proc));
+
+ CPUType cpu_type = index.compilands()
+ .GetCompiland(frame_proc_id.modi)
+ ->m_compile_opts->Machine;
+
+ return is_parameter ? frame_proc.getParamFramePtrReg(cpu_type)
+ : frame_proc.getLocalFramePtrReg(cpu_type);
+}
+
VariableInfo lldb_private::npdb::GetVariableLocationInfo(
- PdbIndex &index, PdbCompilandSymId var_id, lldb::ModuleSP module) {
+ PdbIndex &index, PdbCompilandSymId var_id, Block &block,
+ lldb::ModuleSP module) {
CVSymbol sym = index.ReadSymbolRecord(var_id);
VariableInfo result = GetVariableNameInfo(sym);
if (sym.kind() == S_REGREL32) {
RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
result.location =
MakeRegRelLocationExpression(reg.Register, reg.Offset, module);
result.ranges.emplace();
return result;
}
if (sym.kind() == S_REGISTER) {
RegisterSym reg(SymbolRecordKind::RegisterSym);
cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
result.location = MakeEnregisteredLocationExpression(reg.Register, module);
result.ranges.emplace();
return result;
}
if (sym.kind() == S_LOCAL) {
LocalSym local(SymbolRecordKind::LocalSym);
cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
PdbCompilandSymId loc_specifier_id(var_id.modi,
var_id.offset + sym.RecordData.size());
CVSymbol loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id);
if (loc_specifier_cvs.kind() == S_DEFRANGE_FRAMEPOINTER_REL) {
DefRangeFramePointerRelSym loc(
SymbolRecordKind::DefRangeFramePointerRelSym);
cantFail(SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>(
loc_specifier_cvs, loc));
- // FIXME: The register needs to come from the S_FRAMEPROC symbol.
- result.location =
- MakeRegRelLocationExpression(RegisterId::RSP, loc.Offset, module);
- result.ranges = MakeRangeList(index, loc.Range, loc.Gaps);
- } else {
- // FIXME: Handle other kinds
+
+ Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+
+ // TODO: may be better to pass function scope and not lookup it every
+ // time? find nearest parent function block
+ Block *cur = &block;
+ while (cur->GetParent()) {
+ cur = cur->GetParent();
+ }
+ PdbCompilandSymId func_scope_id =
+ PdbSymUid(cur->GetID()).asCompilandSym();
+ CVSymbol func_block_cvs = index.ReadSymbolRecord(func_scope_id);
+ lldbassert(func_block_cvs.kind() == S_GPROC32 ||
+ func_block_cvs.kind() == S_LPROC32);
+
+ PdbCompilandSymId frame_proc_id(
+ func_scope_id.modi, func_scope_id.offset + func_block_cvs.length());
+
+ bool is_parameter =
+ ((local.Flags & LocalSymFlags::IsParameter) != LocalSymFlags::None);
+ RegisterId base_reg =
+ GetBaseFrameRegister(index, frame_proc_id, is_parameter);
+
+ if (base_reg == RegisterId::VFRAME) {
+ llvm::StringRef program;
+ if (GetFrameDataProgram(index, ranges, program)) {
+ result.location =
+ MakeVFrameRelLocationExpression(program, loc.Offset, module);
+ result.ranges = std::move(ranges);
+ } else {
+ // invalid variable
+ }
+ } else {
+ result.location =
+ MakeRegRelLocationExpression(base_reg, loc.Offset, module);
+ result.ranges = std::move(ranges);
+ }
+ } else if (loc_specifier_cvs.kind() == S_DEFRANGE_REGISTER_REL) {
+ DefRangeRegisterRelSym loc(SymbolRecordKind::DefRangeRegisterRelSym);
+ cantFail(SymbolDeserializer::deserializeAs<DefRangeRegisterRelSym>(
+ loc_specifier_cvs, loc));
+
+ Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+
+ RegisterId base_reg = (RegisterId)(uint16_t)loc.Hdr.Register;
+
+ if (base_reg == RegisterId::VFRAME) {
+ llvm::StringRef program;
+ if (GetFrameDataProgram(index, ranges, program)) {
+ result.location = MakeVFrameRelLocationExpression(
+ program, loc.Hdr.BasePointerOffset, module);
+ result.ranges = std::move(ranges);
+ } else {
+ // invalid variable
+ }
+ } else {
+ result.location = MakeRegRelLocationExpression(
+ base_reg, loc.Hdr.BasePointerOffset, module);
+ result.ranges = std::move(ranges);
+ }
}
+
+ // FIXME: Handle other kinds
return result;
}
llvm_unreachable("Symbol is not a local variable!");
return result;
}
lldb::BasicType
lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
switch (kind) {
case SimpleTypeKind::Boolean128:
case SimpleTypeKind::Boolean16:
case SimpleTypeKind::Boolean32:
case SimpleTypeKind::Boolean64:
case SimpleTypeKind::Boolean8:
return lldb::eBasicTypeBool;
case SimpleTypeKind::Byte:
case SimpleTypeKind::UnsignedCharacter:
return lldb::eBasicTypeUnsignedChar;
case SimpleTypeKind::NarrowCharacter:
return lldb::eBasicTypeChar;
case SimpleTypeKind::SignedCharacter:
case SimpleTypeKind::SByte:
return lldb::eBasicTypeSignedChar;
case SimpleTypeKind::Character16:
return lldb::eBasicTypeChar16;
case SimpleTypeKind::Character32:
return lldb::eBasicTypeChar32;
case SimpleTypeKind::Complex80:
return lldb::eBasicTypeLongDoubleComplex;
case SimpleTypeKind::Complex64:
return lldb::eBasicTypeDoubleComplex;
case SimpleTypeKind::Complex32:
return lldb::eBasicTypeFloatComplex;
case SimpleTypeKind::Float128:
case SimpleTypeKind::Float80:
return lldb::eBasicTypeLongDouble;
case SimpleTypeKind::Float64:
return lldb::eBasicTypeDouble;
case SimpleTypeKind::Float32:
return lldb::eBasicTypeFloat;
case SimpleTypeKind::Float16:
return lldb::eBasicTypeHalf;
case SimpleTypeKind::Int128:
return lldb::eBasicTypeInt128;
case SimpleTypeKind::Int64:
case SimpleTypeKind::Int64Quad:
return lldb::eBasicTypeLongLong;
case SimpleTypeKind::Int32:
return lldb::eBasicTypeInt;
case SimpleTypeKind::Int16:
case SimpleTypeKind::Int16Short:
return lldb::eBasicTypeShort;
case SimpleTypeKind::UInt128:
return lldb::eBasicTypeUnsignedInt128;
case SimpleTypeKind::UInt64:
case SimpleTypeKind::UInt64Quad:
return lldb::eBasicTypeUnsignedLongLong;
case SimpleTypeKind::HResult:
case SimpleTypeKind::UInt32:
return lldb::eBasicTypeUnsignedInt;
case SimpleTypeKind::UInt16:
case SimpleTypeKind::UInt16Short:
return lldb::eBasicTypeUnsignedShort;
case SimpleTypeKind::Int32Long:
return lldb::eBasicTypeLong;
case SimpleTypeKind::UInt32Long:
return lldb::eBasicTypeUnsignedLong;
case SimpleTypeKind::Void:
return lldb::eBasicTypeVoid;
case SimpleTypeKind::WideCharacter:
return lldb::eBasicTypeWChar;
default:
return lldb::eBasicTypeInvalid;
}
}
size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
switch (kind) {
case SimpleTypeKind::Boolean128:
case SimpleTypeKind::Int128:
case SimpleTypeKind::UInt128:
case SimpleTypeKind::Float128:
return 16;
case SimpleTypeKind::Complex80:
case SimpleTypeKind::Float80:
return 10;
case SimpleTypeKind::Boolean64:
case SimpleTypeKind::Complex64:
case SimpleTypeKind::UInt64:
case SimpleTypeKind::UInt64Quad:
case SimpleTypeKind::Float64:
case SimpleTypeKind::Int64:
case SimpleTypeKind::Int64Quad:
return 8;
case SimpleTypeKind::Boolean32:
case SimpleTypeKind::Character32:
case SimpleTypeKind::Complex32:
case SimpleTypeKind::Float32:
case SimpleTypeKind::Int32:
case SimpleTypeKind::Int32Long:
case SimpleTypeKind::UInt32Long:
case SimpleTypeKind::HResult:
case SimpleTypeKind::UInt32:
return 4;
case SimpleTypeKind::Boolean16:
case SimpleTypeKind::Character16:
case SimpleTypeKind::Float16:
case SimpleTypeKind::Int16:
case SimpleTypeKind::Int16Short:
case SimpleTypeKind::UInt16:
case SimpleTypeKind::UInt16Short:
case SimpleTypeKind::WideCharacter:
return 2;
case SimpleTypeKind::Boolean8:
case SimpleTypeKind::Byte:
case SimpleTypeKind::UnsignedCharacter:
case SimpleTypeKind::NarrowCharacter:
case SimpleTypeKind::SignedCharacter:
case SimpleTypeKind::SByte:
return 1;
case SimpleTypeKind::Void:
default:
return 0;
}
}
PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id,
TpiStream &tpi) {
if (id.index.isSimple())
return id;
CVType cvt = tpi.getType(id.index);
// Only tag records have a best and a worst record.
if (!IsTagRecord(cvt))
return id;
// Tag records that are not forward decls are full decls, hence they are the
// best.
if (!IsForwardRefUdt(cvt))
return id;
return llvm::cantFail(tpi.findFullDeclForForwardRef(id.index));
}
template <typename RecordType> static size_t GetSizeOfTypeInternal(CVType cvt) {
RecordType record;
llvm::cantFail(TypeDeserializer::deserializeAs<RecordType>(cvt, record));
return record.getSize();
}
size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
llvm::pdb::TpiStream &tpi) {
if (id.index.isSimple()) {
switch (id.index.getSimpleMode()) {
case SimpleTypeMode::Direct:
return GetTypeSizeForSimpleKind(id.index.getSimpleKind());
case SimpleTypeMode::NearPointer32:
case SimpleTypeMode::FarPointer32:
return 4;
case SimpleTypeMode::NearPointer64:
return 8;
case SimpleTypeMode::NearPointer128:
return 16;
default:
break;
}
return 0;
}
TypeIndex index = id.index;
if (IsForwardRefUdt(index, tpi))
index = llvm::cantFail(tpi.findFullDeclForForwardRef(index));
CVType cvt = tpi.getType(index);
switch (cvt.kind()) {
case LF_MODIFIER:
return GetSizeOfType({LookThroughModifierRecord(cvt)}, tpi);
case LF_ENUM: {
EnumRecord record;
llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, record));
return GetSizeOfType({record.UnderlyingType}, tpi);
}
case LF_POINTER:
return GetSizeOfTypeInternal<PointerRecord>(cvt);
case LF_ARRAY:
return GetSizeOfTypeInternal<ArrayRecord>(cvt);
case LF_CLASS:
case LF_STRUCTURE:
case LF_INTERFACE:
return GetSizeOfTypeInternal<ClassRecord>(cvt);
case LF_UNION:
return GetSizeOfTypeInternal<UnionRecord>(cvt);
default:
break;
}
return 0;
}
Index: source/Plugins/SymbolFile/NativePDB/PdbUtil.h
===================================================================
--- source/Plugins/SymbolFile/NativePDB/PdbUtil.h (revision 352844)
+++ source/Plugins/SymbolFile/NativePDB/PdbUtil.h (revision 352845)
@@ -1,158 +1,158 @@
//===-- PdbUtil.h -----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H
#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/lldb-enumerations.h"
#include "llvm/ADT/Optional.h"
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "PdbSymUid.h"
#include <tuple>
#include <utility>
namespace llvm {
namespace pdb {
class TpiStream;
}
} // namespace llvm
namespace lldb_private {
namespace npdb {
class PdbIndex;
struct CVTagRecord {
enum Kind { Class, Struct, Union, Enum };
static CVTagRecord create(llvm::codeview::CVType type);
Kind kind() const { return m_kind; }
const llvm::codeview::TagRecord &asTag() const {
if (m_kind == Struct || m_kind == Class)
return cvclass;
if (m_kind == Enum)
return cvenum;
return cvunion;
}
const llvm::codeview::ClassRecord &asClass() const {
assert(m_kind == Struct || m_kind == Class);
return cvclass;
}
const llvm::codeview::EnumRecord &asEnum() const {
assert(m_kind == Enum);
return cvenum;
}
const llvm::codeview::UnionRecord &asUnion() const {
assert(m_kind == Union);
return cvunion;
}
llvm::StringRef name() const {
if (m_kind == Struct || m_kind == Union)
return cvclass.Name;
if (m_kind == Enum)
return cvenum.Name;
return cvunion.Name;
}
private:
CVTagRecord(llvm::codeview::ClassRecord &&c);
CVTagRecord(llvm::codeview::UnionRecord &&u);
CVTagRecord(llvm::codeview::EnumRecord &&e);
union {
llvm::codeview::ClassRecord cvclass;
llvm::codeview::EnumRecord cvenum;
llvm::codeview::UnionRecord cvunion;
};
Kind m_kind;
};
struct SegmentOffset {
SegmentOffset() = default;
SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {}
uint16_t segment = 0;
uint32_t offset = 0;
};
struct SegmentOffsetLength {
SegmentOffsetLength() = default;
SegmentOffsetLength(uint16_t s, uint32_t o, uint32_t l)
: so(s, o), length(l) {}
SegmentOffset so;
uint32_t length = 0;
};
struct VariableInfo {
llvm::StringRef name;
llvm::codeview::TypeIndex type;
llvm::Optional<DWARFExpression> location;
llvm::Optional<Variable::RangeList> ranges;
};
llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind kind);
llvm::pdb::PDB_SymType CVTypeToPDBType(llvm::codeview::TypeLeafKind kind);
bool SymbolHasAddress(const llvm::codeview::CVSymbol &sym);
bool SymbolIsCode(const llvm::codeview::CVSymbol &sym);
SegmentOffset GetSegmentAndOffset(const llvm::codeview::CVSymbol &sym);
SegmentOffsetLength
GetSegmentOffsetAndLength(const llvm::codeview::CVSymbol &sym);
template <typename RecordT> bool IsValidRecord(const RecordT &sym) {
return true;
}
inline bool IsValidRecord(const llvm::codeview::ProcRefSym &sym) {
// S_PROCREF symbols have 1-based module indices.
return sym.Module > 0;
}
bool IsForwardRefUdt(llvm::codeview::CVType cvt);
bool IsTagRecord(llvm::codeview::CVType cvt);
bool IsClassStructUnion(llvm::codeview::CVType cvt);
bool IsForwardRefUdt(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi);
bool IsTagRecord(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi);
lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access);
llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt);
llvm::codeview::TypeIndex
LookThroughModifierRecord(llvm::codeview::CVType modifier);
llvm::StringRef DropNameScope(llvm::StringRef name);
VariableInfo GetVariableNameInfo(llvm::codeview::CVSymbol symbol);
-VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id,
+VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, Block& block,
lldb::ModuleSP module);
size_t GetTypeSizeForSimpleKind(llvm::codeview::SimpleTypeKind kind);
lldb::BasicType
GetCompilerTypeForSimpleKind(llvm::codeview::SimpleTypeKind kind);
PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
} // namespace npdb
} // namespace lldb_private
#endif
Index: source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp (revision 0)
+++ source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp (revision 352845)
@@ -0,0 +1,528 @@
+//===-- PDBFPOProgramToDWARFExpression.cpp ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbFPOProgramToDWARFExpression.h"
+#include "CodeViewRegisterMapping.h"
+
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/DenseMap.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/EnumTables.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class FPOProgramNode;
+class FPOProgramASTVisitor;
+
+class FPOProgramNode {
+public:
+ enum Kind {
+ Register,
+ IntegerLiteral,
+ BinaryOp,
+ UnaryOp,
+ };
+
+protected:
+ FPOProgramNode(Kind kind) : m_token_kind(kind) {}
+
+public:
+ virtual ~FPOProgramNode() = default;
+ virtual void Accept(FPOProgramASTVisitor *visitor) = 0;
+
+ Kind GetKind() const { return m_token_kind; }
+
+private:
+ Kind m_token_kind;
+};
+
+class FPOProgramNodeRegisterRef : public FPOProgramNode {
+public:
+ FPOProgramNodeRegisterRef(llvm::StringRef name)
+ : FPOProgramNode(Register), m_name(name) {}
+
+ void Accept(FPOProgramASTVisitor *visitor) override;
+
+ llvm::StringRef GetName() const { return m_name; }
+ uint32_t GetLLDBRegNum() const { return m_lldb_reg_num; }
+
+ bool ResolveLLDBRegisterNum(llvm::Triple::ArchType arch_type);
+
+private:
+ llvm::StringRef m_name;
+ uint32_t m_lldb_reg_num = LLDB_INVALID_REGNUM;
+};
+
+bool FPOProgramNodeRegisterRef::ResolveLLDBRegisterNum(
+ llvm::Triple::ArchType arch_type) {
+
+ llvm::StringRef reg_name = m_name.slice(1, m_name.size());
+
+ // lookup register name to get lldb register number
+ llvm::ArrayRef<llvm::EnumEntry<uint16_t>> register_names =
+ llvm::codeview::getRegisterNames();
+ auto it = llvm::find_if(
+ register_names,
+ [&reg_name](const llvm::EnumEntry<uint16_t> &register_entry) {
+ return reg_name.compare_lower(register_entry.Name) == 0;
+ });
+
+ if (it == register_names.end()) {
+ return false;
+ }
+
+ auto reg_id = static_cast<llvm::codeview::RegisterId>(it->Value);
+ m_lldb_reg_num = npdb::GetLLDBRegisterNumber(arch_type, reg_id);
+
+ return m_lldb_reg_num != LLDB_INVALID_REGNUM;
+}
+
+class FPOProgramNodeIntegerLiteral : public FPOProgramNode {
+public:
+ FPOProgramNodeIntegerLiteral(uint32_t value)
+ : FPOProgramNode(IntegerLiteral), m_value(value) {}
+
+ void Accept(FPOProgramASTVisitor *visitor) override;
+
+ uint32_t GetValue() const { return m_value; }
+
+private:
+ uint32_t m_value;
+};
+
+class FPOProgramNodeBinaryOp : public FPOProgramNode {
+public:
+ enum OpType {
+ Plus,
+ Minus,
+ Align,
+ };
+
+ FPOProgramNodeBinaryOp(OpType op_type, FPOProgramNode *left,
+ FPOProgramNode *right)
+ : FPOProgramNode(BinaryOp), m_op_type(op_type), m_left(left),
+ m_right(right) {}
+
+ void Accept(FPOProgramASTVisitor *visitor) override;
+
+ OpType GetOpType() const { return m_op_type; }
+
+ const FPOProgramNode *Left() const { return m_left; }
+ FPOProgramNode *&Left() { return m_left; }
+
+ const FPOProgramNode *Right() const { return m_right; }
+ FPOProgramNode *&Right() { return m_right; }
+
+private:
+ OpType m_op_type;
+ FPOProgramNode *m_left;
+ FPOProgramNode *m_right;
+};
+
+class FPOProgramNodeUnaryOp : public FPOProgramNode {
+public:
+ enum OpType {
+ Deref,
+ };
+
+ FPOProgramNodeUnaryOp(OpType op_type, FPOProgramNode *operand)
+ : FPOProgramNode(UnaryOp), m_op_type(op_type), m_operand(operand) {}
+
+ void Accept(FPOProgramASTVisitor *visitor) override;
+
+ OpType GetOpType() const { return m_op_type; }
+
+ const FPOProgramNode *Operand() const { return m_operand; }
+ FPOProgramNode *&Operand() { return m_operand; }
+
+private:
+ OpType m_op_type;
+ FPOProgramNode *m_operand;
+};
+
+class FPOProgramASTVisitor {
+public:
+ virtual ~FPOProgramASTVisitor() = default;
+
+ virtual void Visit(FPOProgramNodeRegisterRef *node) {}
+ virtual void Visit(FPOProgramNodeIntegerLiteral *node) {}
+ virtual void Visit(FPOProgramNodeBinaryOp *node) {}
+ virtual void Visit(FPOProgramNodeUnaryOp *node) {}
+};
+
+void FPOProgramNodeRegisterRef::Accept(FPOProgramASTVisitor *visitor) {
+ visitor->Visit(this);
+}
+
+void FPOProgramNodeIntegerLiteral::Accept(FPOProgramASTVisitor *visitor) {
+ visitor->Visit(this);
+}
+
+void FPOProgramNodeBinaryOp::Accept(FPOProgramASTVisitor *visitor) {
+ visitor->Visit(this);
+}
+
+void FPOProgramNodeUnaryOp::Accept(FPOProgramASTVisitor *visitor) {
+ visitor->Visit(this);
+}
+
+class FPOProgramASTVisitorMergeDependent : public FPOProgramASTVisitor {
+public:
+ FPOProgramASTVisitorMergeDependent(
+ const llvm::DenseMap<llvm::StringRef, FPOProgramNode *>
+ &dependent_programs)
+ : m_dependent_programs(dependent_programs) {}
+
+ void Merge(FPOProgramNode *&node_ref);
+
+private:
+ void Visit(FPOProgramNodeRegisterRef *node) override {}
+ void Visit(FPOProgramNodeIntegerLiteral *node) override {}
+ void Visit(FPOProgramNodeBinaryOp *node) override;
+ void Visit(FPOProgramNodeUnaryOp *node) override;
+
+ void TryReplace(FPOProgramNode *&node_ref) const;
+
+private:
+ const llvm::DenseMap<llvm::StringRef, FPOProgramNode *> &m_dependent_programs;
+};
+
+void FPOProgramASTVisitorMergeDependent::Merge(FPOProgramNode *&node_ref) {
+ TryReplace(node_ref);
+ node_ref->Accept(this);
+}
+
+void FPOProgramASTVisitorMergeDependent::Visit(FPOProgramNodeBinaryOp *node) {
+ Merge(node->Left());
+ Merge(node->Right());
+}
+void FPOProgramASTVisitorMergeDependent::Visit(FPOProgramNodeUnaryOp *node) {
+ Merge(node->Operand());
+}
+
+void FPOProgramASTVisitorMergeDependent::TryReplace(
+ FPOProgramNode *&node_ref) const {
+
+ while (node_ref->GetKind() == FPOProgramNode::Register) {
+ auto *node_register_ref =
+ static_cast<FPOProgramNodeRegisterRef *>(node_ref);
+
+ auto it = m_dependent_programs.find(node_register_ref->GetName());
+ if (it == m_dependent_programs.end()) {
+ break;
+ }
+
+ node_ref = it->second;
+ }
+}
+
+class FPOProgramASTVisitorResolveRegisterRefs : public FPOProgramASTVisitor {
+public:
+ FPOProgramASTVisitorResolveRegisterRefs(
+ const llvm::DenseMap<llvm::StringRef, FPOProgramNode *>
+ &dependent_programs,
+ llvm::Triple::ArchType arch_type)
+ : m_dependent_programs(dependent_programs), m_arch_type(arch_type) {}
+
+ bool Resolve(FPOProgramNode *program);
+
+private:
+ void Visit(FPOProgramNodeRegisterRef *node) override;
+ void Visit(FPOProgramNodeIntegerLiteral *node) override {}
+ void Visit(FPOProgramNodeBinaryOp *node) override;
+ void Visit(FPOProgramNodeUnaryOp *node) override;
+
+private:
+ const llvm::DenseMap<llvm::StringRef, FPOProgramNode *> &m_dependent_programs;
+ llvm::Triple::ArchType m_arch_type;
+ bool m_no_error_flag = true;
+};
+
+bool FPOProgramASTVisitorResolveRegisterRefs::Resolve(FPOProgramNode *program) {
+ program->Accept(this);
+ return m_no_error_flag;
+}
+
+void FPOProgramASTVisitorResolveRegisterRefs::Visit(
+ FPOProgramNodeRegisterRef *node) {
+
+ // lookup register reference as lvalue in predecedent assignments
+ auto it = m_dependent_programs.find(node->GetName());
+ if (it != m_dependent_programs.end()) {
+ // dependent programs are already resolved and valid
+ return;
+ }
+ // try to resolve register reference as lldb register name
+ m_no_error_flag = node->ResolveLLDBRegisterNum(m_arch_type);
+}
+
+void FPOProgramASTVisitorResolveRegisterRefs::Visit(
+ FPOProgramNodeBinaryOp *node) {
+ m_no_error_flag = Resolve(node->Left()) && Resolve(node->Right());
+}
+
+void FPOProgramASTVisitorResolveRegisterRefs::Visit(
+ FPOProgramNodeUnaryOp *node) {
+ m_no_error_flag = Resolve(node->Operand());
+}
+
+class FPOProgramASTVisitorDWARFCodegen : public FPOProgramASTVisitor {
+public:
+ FPOProgramASTVisitorDWARFCodegen(Stream &stream) : m_out_stream(stream) {}
+
+ void Emit(FPOProgramNode *program);
+
+private:
+ void Visit(FPOProgramNodeRegisterRef *node) override;
+ void Visit(FPOProgramNodeIntegerLiteral *node) override;
+ void Visit(FPOProgramNodeBinaryOp *node) override;
+ void Visit(FPOProgramNodeUnaryOp *node) override;
+
+private:
+ Stream &m_out_stream;
+};
+
+void FPOProgramASTVisitorDWARFCodegen::Emit(FPOProgramNode *program) {
+ program->Accept(this);
+}
+
+void FPOProgramASTVisitorDWARFCodegen::Visit(FPOProgramNodeRegisterRef *node) {
+
+ uint32_t reg_num = node->GetLLDBRegNum();
+ lldbassert(reg_num != LLDB_INVALID_REGNUM);
+
+ if (reg_num > 31) {
+ m_out_stream.PutHex8(DW_OP_bregx);
+ m_out_stream.PutULEB128(reg_num);
+ } else
+ m_out_stream.PutHex8(DW_OP_breg0 + reg_num);
+
+ m_out_stream.PutSLEB128(0);
+}
+
+void FPOProgramASTVisitorDWARFCodegen::Visit(
+ FPOProgramNodeIntegerLiteral *node) {
+ uint32_t value = node->GetValue();
+ m_out_stream.PutHex8(DW_OP_constu);
+ m_out_stream.PutULEB128(value);
+}
+
+void FPOProgramASTVisitorDWARFCodegen::Visit(FPOProgramNodeBinaryOp *node) {
+
+ Emit(node->Left());
+ Emit(node->Right());
+
+ switch (node->GetOpType()) {
+ case FPOProgramNodeBinaryOp::Plus:
+ m_out_stream.PutHex8(DW_OP_plus);
+ // NOTE: can be optimized by using DW_OP_plus_uconst opcpode
+ // if right child node is constant value
+ break;
+ case FPOProgramNodeBinaryOp::Minus:
+ m_out_stream.PutHex8(DW_OP_minus);
+ break;
+ case FPOProgramNodeBinaryOp::Align:
+ // emit align operator a @ b as
+ // a & ~(b - 1)
+ // NOTE: implicitly assuming that b is power of 2
+ m_out_stream.PutHex8(DW_OP_lit1);
+ m_out_stream.PutHex8(DW_OP_minus);
+ m_out_stream.PutHex8(DW_OP_not);
+
+ m_out_stream.PutHex8(DW_OP_and);
+ break;
+ }
+}
+
+void FPOProgramASTVisitorDWARFCodegen::Visit(FPOProgramNodeUnaryOp *node) {
+ Emit(node->Operand());
+
+ switch (node->GetOpType()) {
+ case FPOProgramNodeUnaryOp::Deref:
+ m_out_stream.PutHex8(DW_OP_deref);
+ break;
+ }
+}
+
+class NodeAllocator {
+public:
+ template <typename T, typename... Args> T *makeNode(Args &&... args) {
+ void *new_node_mem = m_alloc.Allocate(sizeof(T), alignof(T));
+ return new (new_node_mem) T(std::forward<Args>(args)...);
+ }
+
+private:
+ llvm::BumpPtrAllocator m_alloc;
+};
+
+} // namespace
+
+static bool ParseFPOSingleAssignmentProgram(llvm::StringRef program,
+ NodeAllocator &alloc,
+ llvm::StringRef &register_name,
+ FPOProgramNode *&ast) {
+ llvm::SmallVector<llvm::StringRef, 16> tokens;
+ llvm::SplitString(program, tokens, " ");
+
+ if (tokens.empty())
+ return false;
+
+ llvm::SmallVector<FPOProgramNode *, 4> eval_stack;
+
+ llvm::DenseMap<llvm::StringRef, FPOProgramNodeBinaryOp::OpType> ops_binary = {
+ {"+", FPOProgramNodeBinaryOp::Plus},
+ {"-", FPOProgramNodeBinaryOp::Minus},
+ {"@", FPOProgramNodeBinaryOp::Align},
+ };
+
+ llvm::DenseMap<llvm::StringRef, FPOProgramNodeUnaryOp::OpType> ops_unary = {
+ {"^", FPOProgramNodeUnaryOp::Deref},
+ };
+
+ constexpr llvm::StringLiteral ra_search_keyword = ".raSearch";
+
+ // lvalue of assignment is always first token
+ // rvalue program goes next
+ for (size_t i = 1; i < tokens.size(); ++i) {
+ llvm::StringRef cur = tokens[i];
+
+ auto ops_binary_it = ops_binary.find(cur);
+ if (ops_binary_it != ops_binary.end()) {
+ // token is binary operator
+ if (eval_stack.size() < 2) {
+ return false;
+ }
+ FPOProgramNode *right = eval_stack.pop_back_val();
+ FPOProgramNode *left = eval_stack.pop_back_val();
+ FPOProgramNode *node = alloc.makeNode<FPOProgramNodeBinaryOp>(
+ ops_binary_it->second, left, right);
+ eval_stack.push_back(node);
+ continue;
+ }
+
+ auto ops_unary_it = ops_unary.find(cur);
+ if (ops_unary_it != ops_unary.end()) {
+ // token is unary operator
+ if (eval_stack.empty()) {
+ return false;
+ }
+ FPOProgramNode *operand = eval_stack.pop_back_val();
+ FPOProgramNode *node =
+ alloc.makeNode<FPOProgramNodeUnaryOp>(ops_unary_it->second, operand);
+ eval_stack.push_back(node);
+ continue;
+ }
+
+ if (cur.startswith("$")) {
+ // token is register ref
+ eval_stack.push_back(alloc.makeNode<FPOProgramNodeRegisterRef>(cur));
+ continue;
+ }
+
+ if (cur == ra_search_keyword) {
+ // TODO: .raSearch is unsupported
+ return false;
+ }
+
+ uint32_t value;
+ if (!cur.getAsInteger(10, value)) {
+ // token is integer literal
+ eval_stack.push_back(alloc.makeNode<FPOProgramNodeIntegerLiteral>(value));
+ continue;
+ }
+
+ // unexpected token
+ return false;
+ }
+
+ if (eval_stack.size() != 1) {
+ return false;
+ }
+
+ register_name = tokens[0];
+ ast = eval_stack.pop_back_val();
+
+ return true;
+}
+
+static FPOProgramNode *ParseFPOProgram(llvm::StringRef program,
+ llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type,
+ NodeAllocator &alloc) {
+ llvm::DenseMap<llvm::StringRef, FPOProgramNode *> dependent_programs;
+
+ size_t cur = 0;
+ while (true) {
+ size_t assign_index = program.find('=', cur);
+ if (assign_index == llvm::StringRef::npos) {
+ llvm::StringRef tail = program.slice(cur, llvm::StringRef::npos);
+ if (!tail.trim().empty()) {
+ // missing assign operator
+ return nullptr;
+ }
+ break;
+ }
+ llvm::StringRef assignment_program = program.slice(cur, assign_index);
+
+ llvm::StringRef lvalue_name;
+ FPOProgramNode *rvalue_ast = nullptr;
+ if (!ParseFPOSingleAssignmentProgram(assignment_program, alloc, lvalue_name,
+ rvalue_ast)) {
+ return nullptr;
+ }
+
+ lldbassert(rvalue_ast);
+
+ // check & resolve assignment program
+ FPOProgramASTVisitorResolveRegisterRefs resolver(dependent_programs,
+ arch_type);
+ if (!resolver.Resolve(rvalue_ast)) {
+ return nullptr;
+ }
+
+ if (lvalue_name == register_name) {
+ // found target assignment program - no need to parse further
+
+ // emplace valid dependent subtrees to make target assignment independent
+ // from predecessors
+ FPOProgramASTVisitorMergeDependent merger(dependent_programs);
+ merger.Merge(rvalue_ast);
+
+ return rvalue_ast;
+ }
+
+ dependent_programs[lvalue_name] = rvalue_ast;
+ cur = assign_index + 1;
+ }
+
+ return nullptr;
+}
+
+bool lldb_private::npdb::TranslateFPOProgramToDWARFExpression(
+ llvm::StringRef program, llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type, Stream &stream) {
+ NodeAllocator node_alloc;
+ FPOProgramNode *target_program =
+ ParseFPOProgram(program, register_name, arch_type, node_alloc);
+ if (target_program == nullptr) {
+ return false;
+ }
+
+ FPOProgramASTVisitorDWARFCodegen codegen(stream);
+ codegen.Emit(target_program);
+ return true;
+}
Index: source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp (revision 352844)
+++ source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp (revision 352845)
@@ -1,672 +1,258 @@
//===-- DWARFLocationExpression.cpp -----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DWARFLocationExpression.h"
-#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/StreamBuffer.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Support/Endian.h"
#include "PdbUtil.h"
+#include "CodeViewRegisterMapping.h"
+#include "PdbFPOProgramToDWARFExpression.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::npdb;
using namespace llvm::codeview;
using namespace llvm::pdb;
-static const uint32_t g_code_view_to_lldb_registers_x86[] = {
- LLDB_INVALID_REGNUM, // NONE
- lldb_al_i386, // AL
- lldb_cl_i386, // CL
- lldb_dl_i386, // DL
- lldb_bl_i386, // BL
- lldb_ah_i386, // AH
- lldb_ch_i386, // CH
- lldb_dh_i386, // DH
- lldb_bh_i386, // BH
- lldb_ax_i386, // AX
- lldb_cx_i386, // CX
- lldb_dx_i386, // DX
- lldb_bx_i386, // BX
- lldb_sp_i386, // SP
- lldb_bp_i386, // BP
- lldb_si_i386, // SI
- lldb_di_i386, // DI
- lldb_eax_i386, // EAX
- lldb_ecx_i386, // ECX
- lldb_edx_i386, // EDX
- lldb_ebx_i386, // EBX
- lldb_esp_i386, // ESP
- lldb_ebp_i386, // EBP
- lldb_esi_i386, // ESI
- lldb_edi_i386, // EDI
- lldb_es_i386, // ES
- lldb_cs_i386, // CS
- lldb_ss_i386, // SS
- lldb_ds_i386, // DS
- lldb_fs_i386, // FS
- lldb_gs_i386, // GS
- LLDB_INVALID_REGNUM, // IP
- LLDB_INVALID_REGNUM, // FLAGS
- lldb_eip_i386, // EIP
- lldb_eflags_i386, // EFLAGS
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, // TEMP
- LLDB_INVALID_REGNUM, // TEMPH
- LLDB_INVALID_REGNUM, // QUOTE
- LLDB_INVALID_REGNUM, // PCDR3
- LLDB_INVALID_REGNUM, // PCDR4
- LLDB_INVALID_REGNUM, // PCDR5
- LLDB_INVALID_REGNUM, // PCDR6
- LLDB_INVALID_REGNUM, // PCDR7
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, // CR0
- LLDB_INVALID_REGNUM, // CR1
- LLDB_INVALID_REGNUM, // CR2
- LLDB_INVALID_REGNUM, // CR3
- LLDB_INVALID_REGNUM, // CR4
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- lldb_dr0_i386, // DR0
- lldb_dr1_i386, // DR1
- lldb_dr2_i386, // DR2
- lldb_dr3_i386, // DR3
- lldb_dr4_i386, // DR4
- lldb_dr5_i386, // DR5
- lldb_dr6_i386, // DR6
- lldb_dr7_i386, // DR7
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
- LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,