//===-- PdbUtil.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "PdbUtil.h" #include "DWARFLocationExpression.h" #include "PdbIndex.h" #include "PdbSymUid.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.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 "Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h" #include "lldb/Symbol/Block.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/lldb-enumerations.h" using namespace lldb_private; using namespace lldb_private::npdb; using namespace llvm::codeview; using namespace llvm::pdb; // The returned range list is guaranteed to be sorted and no overlaps between // adjacent ranges because fields in LocalVariableAddrGap are unsigned integers. static Variable::RangeList MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range, llvm::ArrayRef gaps) { lldb::addr_t start = index.MakeVirtualAddress(range.ISectStart, range.OffsetStart); if (start == LLDB_INVALID_ADDRESS) return {}; lldb::addr_t end = start + range.Range; Variable::RangeList result; while (!gaps.empty()) { const LocalVariableAddrGap &gap = gaps.front(); lldb::addr_t gap_start = start + gap.GapStartOffset; result.Append(start, gap_start - start); start = gap_start + gap.Range; gaps = gaps.drop_front(); } result.Append(start, end - start); return result; } namespace { struct MemberLocations { std::map offset_to_location; DWARFExpression expr; bool is_dwarf = false; MemberLocations() = default; MemberLocations(const DWARFExpression &expr) : expr(expr), is_dwarf(true) {} MemberLocations(uint64_t offset, const MemberValLocation &member_loc) { insert(offset, member_loc); } void insert(uint64_t offset, const MemberValLocation &member_loc) { offset_to_location[offset] = member_loc; } struct Comparator { public: bool operator()(const MemberLocations &, const MemberLocations &) const { return false; } }; }; // A range map with address ranges to a map of pair of offset and locaitons. typedef RangeDataVector RangeMap; void AddMemberLocationRanges(RangeMap &location_map, uint64_t offset, MemberValLocation member_loc, const Variable::RangeList &ranges) { RangeMap new_location_map; auto add_overlap_region = [&](lldb::addr_t base, lldb::addr_t end, RangeMap::Entry *entry) { RangeMap::Entry overlap_region = {base, end - base, entry->data}; overlap_region.data.insert(offset, member_loc); new_location_map.Append(overlap_region); }; for (const auto &range : ranges) { lldb::addr_t base = range.GetRangeBase(); lldb::addr_t end = range.GetRangeEnd(); uint32_t base_idx = location_map.FindEntryIndexThatContainsOrFollows(base); while (auto *entry = location_map.GetMutableEntryAtIndex(base_idx)) { if (base >= end || entry->base >= end) break; if (entry->data.is_dwarf) base = entry->GetRangeEnd(); else { lldb::addr_t entry_end = entry->GetRangeEnd(); if (base > entry->base) { if (end < entry_end) new_location_map.Append({end, entry_end - end, entry->data}); add_overlap_region(base, end < entry_end ? end : entry_end, entry); entry->SetRangeEnd(base); } else if (base < entry->base) { new_location_map.Append( {base, entry->base - base, {offset, member_loc}}); if (entry_end == end) entry->data.insert(offset, member_loc); else { add_overlap_region(entry->base, end, entry); entry->ShrinkFront(end - entry->base); } } else { if (end < entry_end) { new_location_map.Append({end, entry_end, entry->data}); entry->SetRangeEnd(end); } entry->data.insert(offset, member_loc); } base = entry_end; } ++base_idx; } if (base >= end) continue; new_location_map.Append({base, end - base, {offset, member_loc}}); } for (const auto &entry : new_location_map) location_map.Append(entry); if (!new_location_map.IsEmpty()) location_map.Sort(); } void AddDwarfRange(RangeMap &location_map, const DWARFExpression &expr, const Variable::RangeList &ranges) { if (!expr.IsValid()) return; RangeMap new_location_map; for (const auto &range : ranges) { lldb::addr_t base = range.GetRangeBase(); lldb::addr_t end = range.GetRangeEnd(); uint32_t base_idx = location_map.FindEntryIndexThatContains(base); uint32_t end_idx = location_map.FindEntryIndexThatContains(end - 1); // range is within an entry. if (base_idx == end_idx && base_idx != UINT32_MAX) { auto *entry = location_map.GetMutableEntryAtIndex(base_idx); if (base > entry->base) { new_location_map.Append({entry->base, base - entry->base, entry->data}); entry->ShrinkFront(base - entry->base); } if (end == entry->GetRangeEnd()) entry->data = expr; else { entry->ShrinkFront(end - base); new_location_map.Append({base, end - base, expr}); } continue; } base_idx = location_map.FindEntryIndexThatContainsOrFollows(base); if (auto *entry = location_map.GetMutableEntryAtIndex(base_idx)) { if (entry->Contains(base) && entry->base != base) { entry->SetRangeEnd(base); ++base_idx; } } end_idx = location_map.FindEntryIndexThatContainsOrFollows(end - 1); if (auto *entry = location_map.GetMutableEntryAtIndex(end_idx)) { if (entry->Contains(end - 1)) { if (entry->GetRangeEnd() == end) ++end_idx; else entry->ShrinkFront(end - entry->base); } } if (end_idx == UINT32_MAX) end_idx = location_map.GetSize(); // Erase existing ranges covered by new range. location_map.Erase(base_idx, end_idx); new_location_map.Append({base, end - base, expr}); } for (const auto &entry : new_location_map) location_map.Append(entry); location_map.Sort(); } } // namespace 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(type, cr)); return CVTagRecord(std::move(cr)); } case LF_UNION: { UnionRecord ur; llvm::cantFail(TypeDeserializer::deserializeAs(type, ur)); return CVTagRecord(std::move(ur)); } case LF_ENUM: { EnumRecord er; llvm::cantFail(TypeDeserializer::deserializeAs(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 RecordT createRecord(const CVSymbol &sym) { RecordT record(static_cast(sym.kind())); cantFail(SymbolDeserializer::deserializeAs(sym, record)); return record; } template static SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { RecordT record = createRecord(sym); return {record.Segment, record.CodeOffset}; } template <> SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { TrampolineSym record = createRecord(sym); return {record.ThunkSection, record.ThunkOffset}; } template <> SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { Thunk32Sym record = createRecord(sym); return {record.Segment, record.Offset}; } template <> SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { CoffGroupSym record = createRecord(sym); return {record.Segment, record.Offset}; } template <> SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { DataSym record = createRecord(sym); return {record.Segment, record.DataOffset}; } template <> SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { ThreadLocalDataSym record = createRecord(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(sym); case S_THUNK32: return ::GetSegmentAndOffset(sym); break; case S_TRAMPOLINE: return ::GetSegmentAndOffset(sym); break; case S_COFFGROUP: return ::GetSegmentAndOffset(sym); break; case S_BLOCK32: return ::GetSegmentAndOffset(sym); break; case S_LABEL32: return ::GetSegmentAndOffset(sym); break; case S_CALLSITEINFO: return ::GetSegmentAndOffset(sym); break; case S_HEAPALLOCSITE: return ::GetSegmentAndOffset(sym); break; case S_LDATA32: case S_GDATA32: case S_LMANDATA: case S_GMANDATA: return ::GetSegmentAndOffset(sym); break; case S_LTHREAD32: case S_GTHREAD32: return ::GetSegmentAndOffset(sym); break; default: lldbassert(false && "Record does not have a segment/offset!"); } return {0, 0}; } template SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) { RecordT record = createRecord(sym); return {record.Segment, record.CodeOffset, record.CodeSize}; } template <> SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) { TrampolineSym record = createRecord(sym); return {record.ThunkSection, record.ThunkOffset, record.Size}; } template <> SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) { Thunk32Sym record = createRecord(sym); return SegmentOffsetLength{record.Segment, record.Offset, record.Length}; } template <> SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) { CoffGroupSym record = createRecord(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(sym); case S_THUNK32: return ::GetSegmentOffsetAndLength(sym); break; case S_TRAMPOLINE: return ::GetSegmentOffsetAndLength(sym); break; case S_COFFGROUP: return ::GetSegmentOffsetAndLength(sym); break; case S_BLOCK32: return ::GetSegmentOffsetAndLength(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(cvt, cr)); return cr.isForwardRef(); case LF_UNION: llvm::cantFail(TypeDeserializer::deserializeAs(cvt, ur)); return ur.isForwardRef(); case LF_ENUM: llvm::cantFail(TypeDeserializer::deserializeAs(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(cvt, cr)); return cr.FieldList; } case LF_UNION: { UnionRecord ur; cantFail(TypeDeserializer::deserializeAs(cvt, ur)); return ur.FieldList; } case LF_ENUM: { EnumRecord er; cantFail(TypeDeserializer::deserializeAs(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(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(sym, reg)); result.type = reg.Type; result.name = reg.Name; return result; } if (sym.kind() == S_REGISTER) { RegisterSym reg(SymbolRecordKind::RegisterSym); cantFail(SymbolDeserializer::deserializeAs(sym, reg)); result.type = reg.Index; result.name = reg.Name; return result; } if (sym.kind() == S_LOCAL) { LocalSym local(SymbolRecordKind::LocalSym); cantFail(SymbolDeserializer::deserializeAs(sym, local)); result.type = local.Type; result.name = local.Name; result.is_param = ((local.Flags & LocalSymFlags::IsParameter) != LocalSymFlags::None); return result; } if (sym.kind() == S_GDATA32 || sym.kind() == S_LDATA32) { DataSym data(SymbolRecordKind::DataSym); cantFail(SymbolDeserializer::deserializeAs(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(sym, data)); result.type = data.Type; result.name = data.Name; return result; } if (sym.kind() == S_CONSTANT) { ConstantSym constant(SymbolRecordKind::ConstantSym); cantFail(SymbolDeserializer::deserializeAs(sym, constant)); result.type = constant.Type; result.name = constant.Name; return result; } lldbassert(false && "Invalid variable record kind!"); return {}; } static llvm::FixedStreamArray::Iterator 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; auto strings = index.pdb().getStringTable(); if (!strings) { consumeError(strings.takeError()); return false; } 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); if (frame_proc_cvs.kind() != S_FRAMEPROC) return RegisterId::NONE; FrameProcSym frame_proc(SymbolRecordKind::FrameProcSym); cantFail(SymbolDeserializer::deserializeAs(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, Block &func_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(sym, reg)); result.location = DWARFExpressionList( module, MakeRegRelLocationExpression(reg.Register, reg.Offset, module), nullptr); return result; } if (sym.kind() == S_REGISTER) { RegisterSym reg(SymbolRecordKind::RegisterSym); cantFail(SymbolDeserializer::deserializeAs(sym, reg)); result.location = DWARFExpressionList( module, MakeEnregisteredLocationExpression(reg.Register, module), nullptr); return result; } if (sym.kind() == S_LOCAL) { LocalSym local(SymbolRecordKind::LocalSym); if (llvm::Error error = SymbolDeserializer::deserializeAs(sym, local)) { llvm::consumeError(std::move(error)); return result; } PdbCompilandSymId loc_specifier_id(var_id.modi, var_id.offset + sym.RecordData.size()); CVSymbol loc_specifier_cvs; // Only used for S_DEFRANGE_FRAMEPOINTER_REL. RegisterId base_reg = RegisterId::NONE; size_t type_size = GetSizeOfType(result.type, index.tpi()); // A map from offset of a field in parent to size of the field. std::map offset_to_size; // When overlaps happens, always prefer the one that doesn't split the value // into multiple locations and the location parsed first is perfered. RangeMap location_map; // Iterate through all location records after S_LOCAL. They describe the // value of this variable at different locations. bool finished = false; while (!finished) { loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id); switch (loc_specifier_cvs.kind()) { case S_DEFRANGE_FRAMEPOINTER_REL: { DefRangeFramePointerRelSym loc( SymbolRecordKind::DefRangeFramePointerRelSym); if (llvm::Error error = SymbolDeserializer::deserializeAs( loc_specifier_cvs, loc)) { llvm::consumeError(std::move(error)); return result; } Variable::RangeList raw_ranges = MakeRangeList(index, loc.Range, loc.Gaps); if (base_reg == RegisterId::NONE) { PdbCompilandSymId func_scope_id = PdbSymUid(func_block.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()); base_reg = GetBaseFrameRegister(index, frame_proc_id, result.is_param); if (base_reg == RegisterId::NONE) break; } DWARFExpression expr; if (base_reg == RegisterId::VFRAME) { llvm::StringRef program; if (GetFrameDataProgram(index, raw_ranges, program)) expr = MakeVFrameRelLocationExpression(program, loc.Hdr.Offset, module); else { // invalid variable } } else expr = MakeRegRelLocationExpression(base_reg, loc.Hdr.Offset, module); AddDwarfRange(location_map, expr, raw_ranges); break; } case S_DEFRANGE_REGISTER: { DefRangeRegisterSym loc(SymbolRecordKind::DefRangeRegisterSym); if (llvm::Error error = SymbolDeserializer::deserializeAs( loc_specifier_cvs, loc)) { llvm::consumeError(std::move(error)); return result; } RegisterId reg_id = (RegisterId)(uint16_t)loc.Hdr.Register; Variable::RangeList raw_ranges = MakeRangeList(index, loc.Range, loc.Gaps); DWARFExpression expr = MakeEnregisteredLocationExpression(reg_id, module); AddDwarfRange(location_map, expr, raw_ranges); break; } case S_DEFRANGE_REGISTER_REL: { DefRangeRegisterRelSym loc(SymbolRecordKind::DefRangeRegisterRelSym); if (llvm::Error error = SymbolDeserializer::deserializeAs( loc_specifier_cvs, loc)) { llvm::consumeError(std::move(error)); return result; } Variable::RangeList raw_ranges = MakeRangeList(index, loc.Range, loc.Gaps); RegisterId reg_id = (RegisterId)(uint16_t)loc.Hdr.Register; DWARFExpression expr; if (reg_id == RegisterId::VFRAME) { llvm::StringRef program; if (GetFrameDataProgram(index, raw_ranges, program)) expr = MakeVFrameRelLocationExpression( program, loc.Hdr.BasePointerOffset, module); else { // invalid variable } } else { expr = MakeRegRelLocationExpression(reg_id, loc.Hdr.BasePointerOffset, module); } // FIXME: If it's UDT, we need to know the size of the value in byte. if (!loc.hasSpilledUDTMember()) AddDwarfRange(location_map, expr, raw_ranges); break; } case S_DEFRANGE_SUBFIELD_REGISTER: { DefRangeSubfieldRegisterSym loc( SymbolRecordKind::DefRangeSubfieldRegisterSym); if (llvm::Error error = SymbolDeserializer::deserializeAs( loc_specifier_cvs, loc)) { llvm::consumeError(std::move(error)); return result; } Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps); uint32_t reg_size = GetRegisterSize((RegisterId)(uint16_t)loc.Hdr.Register); if (reg_size == 0) break; offset_to_size[loc.Hdr.OffsetInParent] = reg_size; AddMemberLocationRanges(location_map, loc.Hdr.OffsetInParent, {loc.Hdr.Register, 0, true}, ranges); break; } // FIXME: Handle other kinds. LLVM only generates the 4 types of records // above. MSVC generates other location types. case S_DEFRANGE: case S_DEFRANGE_SUBFIELD: case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: break; default: finished = true; break; } loc_specifier_id = PdbCompilandSymId( loc_specifier_id.modi, loc_specifier_id.offset + loc_specifier_cvs.RecordData.size()); } for (const auto &entry : location_map) { DWARFExpression dwarf_expr = entry.data.is_dwarf ? entry.data.expr : MakeEnregisteredLocationExpressionForComposite( entry.data.offset_to_location, offset_to_size, type_size, module); result.location.AddExpression(entry.GetRangeBase(), entry.GetRangeEnd(), dwarf_expr); } 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::Character8: return lldb::eBasicTypeChar8; 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: case SimpleTypeKind::Character8: 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 static size_t GetSizeOfTypeInternal(CVType cvt) { RecordType record; llvm::cantFail(TypeDeserializer::deserializeAs(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(cvt, record)); return GetSizeOfType({record.UnderlyingType}, tpi); } case LF_POINTER: return GetSizeOfTypeInternal(cvt); case LF_ARRAY: return GetSizeOfTypeInternal(cvt); case LF_CLASS: case LF_STRUCTURE: case LF_INTERFACE: return GetSizeOfTypeInternal(cvt); case LF_UNION: return GetSizeOfTypeInternal(cvt); case LF_BITFIELD: { BitFieldRecord record; llvm::cantFail(TypeDeserializer::deserializeAs(cvt, record)); return GetSizeOfType({record.Type}, tpi); } default: break; } return 0; }