Originally we created our 64-bit UID scheme by using the first byte as sort of a "tag" to represent what kind of symbol this was, and we re-used the PDB_SymType enumeration for this. For native pdb support, this is not really the right abstraction layer, because what we really want is something that tells us *how* to find the symbol. This means, specifically, is in the globals stream / public stream / module stream / TPI stream / etc, and for whichever one it is in, where is it within that stream? A good example of why the old namespacing scheme was insufficient is that it is more or less impossible to create a uid for a field list member of a class/struction/union/enum that tells you how to locate the original record. With this new scheme, the first byte is no longer a PDB_SymType enum but a new enum created specifically to identify where in the PDB this record lives. This gives us much better flexibility in what kinds of symbols the uids can identify. llvm-svn: 347018
428 lines
11 KiB
C++
428 lines
11 KiB
C++
//===-- PdbUtil.cpp ---------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "PdbUtil.h"
|
|
#include "PdbSymUid.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.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;
|
|
|
|
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::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) {
|
|
// Not all PDB names can be parsed with CPlusPlusNameParser.
|
|
// E.g. it fails on names containing `anonymous namespace'.
|
|
// So we simply drop everything before '::'
|
|
|
|
auto offset = name.rfind("::");
|
|
if (offset == llvm::StringRef::npos)
|
|
return name;
|
|
assert(offset + 2 <= name.size());
|
|
|
|
return name.substr(offset + 2);
|
|
}
|