mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
1305 lines
46 KiB
C++
1305 lines
46 KiB
C++
/// \file PDBImporter.cpp
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
|
|
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
|
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
|
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
|
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
|
|
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
|
|
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.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/InputFile.h"
|
|
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
|
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
|
|
#include "llvm/DebugInfo/PDB/PDB.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/Process.h"
|
|
#include "llvm/Support/Program.h"
|
|
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Model/Importer/Binary/Options.h"
|
|
#include "revng/Model/Importer/DebugInfo/PDBImporter.h"
|
|
#include "revng/Model/Pass/AllPasses.h"
|
|
#include "revng/Model/Processing.h"
|
|
#include "revng/Model/QualifiedType.h"
|
|
#include "revng/Model/Type.h"
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/Debug.h"
|
|
#include "revng/Support/MetaAddress.h"
|
|
#include "revng/Support/ProgramRunner.h"
|
|
|
|
#include "ImportDebugInfoHelper.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::codeview;
|
|
using namespace llvm::object;
|
|
using namespace llvm::pdb;
|
|
|
|
static Logger<> DILogger("pdb-importer");
|
|
|
|
// Force using a specific PDB.
|
|
static llvm::cl::opt<std::string> UsePDB("use-pdb",
|
|
llvm::cl::desc("Path to the PDB."),
|
|
llvm::cl::cat(MainCategory));
|
|
|
|
namespace {
|
|
class PDBImporterImpl {
|
|
private:
|
|
PDBImporter &Importer;
|
|
DenseMap<TypeIndex, model::TypePath> ProcessedTypes;
|
|
|
|
public:
|
|
PDBImporterImpl(PDBImporter &Importer) : Importer(Importer) {}
|
|
void run(NativeSession &Session);
|
|
|
|
private:
|
|
void populateTypes();
|
|
void populateSymbolsWithTypes(NativeSession &Session);
|
|
};
|
|
|
|
/// Visitor for CodeView type streams found in PDB files. It overrides callbacks
|
|
/// (from `TypeVisitorCallbacks`) to types of interest for the revng `Model`.
|
|
/// During the traversal of the graph from PDB that represents the type system,
|
|
/// the `Types:` field of the `Model` is being populated. Since each CodeView
|
|
/// type in the PDB has unique `TypeIndex` that will be used when a symbol from
|
|
/// PDB symbol stream uses a certain type, we also keep a map of such
|
|
/// `TypeIndex` to corresponding type generated within the `Model` (it is done
|
|
/// by using `ProcessedTypes`), so it can be used when connecting functions from
|
|
/// `Model` with corresponding prototypes.
|
|
class PDBImporterTypeVisitor : public TypeVisitorCallbacks {
|
|
private:
|
|
TupleTree<model::Binary> &Model;
|
|
LazyRandomTypeCollection &Types;
|
|
DenseMap<TypeIndex, model::TypePath> &ProcessedTypes;
|
|
|
|
TypeIndex CurrentTypeIndex = TypeIndex::None();
|
|
std::map<TypeIndex, SmallVector<DataMemberRecord, 8>> InProgressMemberTypes;
|
|
std::map<TypeIndex, SmallVector<EnumeratorRecord, 8>>
|
|
InProgressEnumeratorTypes;
|
|
std::map<TypeIndex, ArgListRecord> InProgressArgumentsTypes;
|
|
|
|
// Methods of a Class type. It references concrete MemberFunctionRecord.
|
|
std::map<TypeIndex, SmallVector<OneMethodRecord, 8>>
|
|
InProgressFunctionMemberTypes;
|
|
DenseMap<TypeIndex, MemberFunctionRecord>
|
|
InProgressConcreteFunctionMemberTypes;
|
|
|
|
public:
|
|
PDBImporterTypeVisitor(TupleTree<model::Binary> &M,
|
|
LazyRandomTypeCollection &Types,
|
|
DenseMap<TypeIndex, model::TypePath> &ProcessedTypes) :
|
|
TypeVisitorCallbacks(),
|
|
Model(M),
|
|
Types(Types),
|
|
ProcessedTypes(ProcessedTypes) {}
|
|
|
|
Error visitTypeBegin(CVType &Record) override;
|
|
Error visitTypeBegin(CVType &Record, TypeIndex TI) override;
|
|
|
|
Error visitKnownRecord(CVType &Record, ClassRecord &Class) override;
|
|
Error visitKnownMember(CVMemberRecord &Record,
|
|
EnumeratorRecord &Member) override;
|
|
Error visitKnownRecord(CVType &Record, EnumRecord &Enum) override;
|
|
Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc) override;
|
|
Error visitKnownRecord(CVType &Record, UnionRecord &Union) override;
|
|
Error visitKnownRecord(CVType &Record, ArgListRecord &Args) override;
|
|
Error visitKnownMember(CVMemberRecord &Record,
|
|
DataMemberRecord &Member) override;
|
|
Error visitKnownRecord(CVType &Record, FieldListRecord &FieldList) override;
|
|
Error visitKnownRecord(CVType &Record, PointerRecord &Ptr) override;
|
|
Error visitKnownRecord(CVType &Record, ModifierRecord &Modifier) override;
|
|
Error visitKnownRecord(CVType &Record, ArrayRecord &Array) override;
|
|
|
|
Error visitKnownMember(CVMemberRecord &Record,
|
|
OneMethodRecord &FnMember) override;
|
|
Error visitKnownRecord(CVType &CVR,
|
|
MemberFunctionRecord &MemberFnRecord) override;
|
|
|
|
std::optional<TupleTreeReference<model::Type, model::Binary>>
|
|
getModelTypeForIndex(TypeIndex Index);
|
|
void createPrimitiveType(TypeIndex SimpleType);
|
|
};
|
|
|
|
/// Visitor for CodeView symbol streams found in PDB files. It is being used for
|
|
/// connecting functions from `Model` to their prototypes. We assume the PDB
|
|
/// type stream was traversed before invoking this class.
|
|
class PDBImporterSymbolVisitor : public SymbolVisitorCallbacks {
|
|
private:
|
|
TupleTree<model::Binary> &Model;
|
|
DenseMap<TypeIndex, model::TypePath> &ProcessedTypes;
|
|
|
|
NativeSession &Session;
|
|
MetaAddress &ImageBase;
|
|
|
|
public:
|
|
PDBImporterSymbolVisitor(TupleTree<model::Binary> &M,
|
|
DenseMap<TypeIndex, model::TypePath> &ProcessedTypes,
|
|
NativeSession &Session,
|
|
MetaAddress &ImageBase) :
|
|
Model(M),
|
|
ProcessedTypes(ProcessedTypes),
|
|
Session(Session),
|
|
ImageBase(ImageBase) {}
|
|
|
|
Error visitSymbolBegin(CVSymbol &Record) override;
|
|
Error visitSymbolBegin(CVSymbol &Record, uint32_t Offset) override;
|
|
Error visitKnownRecord(CVSymbol &Record, ProcSym &Proc) override;
|
|
};
|
|
} // namespace
|
|
|
|
void PDBImporterImpl::populateTypes() {
|
|
auto InputFile = InputFile::open(Importer.getPDBFile()->getFilePath());
|
|
if (not InputFile) {
|
|
revng_log(DILogger, "Unable to open PDB file " << InputFile.takeError());
|
|
consumeError(InputFile.takeError());
|
|
return;
|
|
}
|
|
|
|
auto StreamTpiOrErr = Importer.getPDBFile()->getPDBTpiStream();
|
|
if (not StreamTpiOrErr) {
|
|
revng_log(DILogger,
|
|
"Unable to find TPI in PDB file: " << StreamTpiOrErr.takeError());
|
|
consumeError(StreamTpiOrErr.takeError());
|
|
return;
|
|
}
|
|
|
|
PDBImporterTypeVisitor TypeVisitor(Importer.getModel(),
|
|
InputFile->types(),
|
|
ProcessedTypes);
|
|
if (auto Err = visitTypeStream(InputFile->types(), TypeVisitor)) {
|
|
revng_log(DILogger, "Error during visiting types: " << Err);
|
|
consumeError(std::move(Err));
|
|
}
|
|
}
|
|
|
|
class PDBSymbolHandler {
|
|
private:
|
|
PDBImporter &Importer;
|
|
DenseMap<TypeIndex, model::TypePath> &ProcessedTypes;
|
|
NativeSession &Session;
|
|
InputFile &Input;
|
|
|
|
public:
|
|
PDBSymbolHandler(PDBImporter &Importer,
|
|
DenseMap<TypeIndex, model::TypePath> &ProcessedTypes,
|
|
NativeSession &Session,
|
|
InputFile &Input) :
|
|
Importer(Importer),
|
|
ProcessedTypes(ProcessedTypes),
|
|
Session(Session),
|
|
Input(Input) {}
|
|
|
|
Error operator()(uint32_t Modi, const SymbolGroup &SG) {
|
|
auto ExpectedModS = getModuleDebugStream(*Importer.getPDBFile(), Modi);
|
|
if (ExpectedModS) {
|
|
ModuleDebugStreamRef &ModS = *ExpectedModS;
|
|
|
|
SymbolVisitorCallbackPipeline Pipeline;
|
|
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
|
|
PDBImporterSymbolVisitor SymVisitor(Importer.getModel(),
|
|
ProcessedTypes,
|
|
Session,
|
|
Importer.getBaseAddress());
|
|
|
|
Pipeline.addCallbackToPipeline(Deserializer);
|
|
Pipeline.addCallbackToPipeline(SymVisitor);
|
|
CVSymbolVisitor Visitor(Pipeline);
|
|
auto SS = ModS.getSymbolsSubstream();
|
|
if (auto Err = Visitor.visitSymbolStream(ModS.getSymbolArray(),
|
|
SS.Offset))
|
|
return createStringError(errorToErrorCode(std::move(Err)),
|
|
Input.getFilePath());
|
|
} else {
|
|
// If the module stream does not exist, it is not an
|
|
// error condition.
|
|
consumeError(ExpectedModS.takeError());
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
};
|
|
|
|
void PDBImporterImpl::populateSymbolsWithTypes(NativeSession &Session) {
|
|
auto InputFile = InputFile::open(Importer.getPDBFile()->getFilePath());
|
|
if (not InputFile) {
|
|
revng_log(DILogger, "Unable to open PDB file: " << InputFile.takeError());
|
|
consumeError(InputFile.takeError());
|
|
return;
|
|
}
|
|
|
|
FilterOptions Filters{};
|
|
LinePrinter Printer(/*Indent=*/2, false, nulls(), Filters);
|
|
const PrintScope HeaderScope(Printer, /*IndentLevel=*/2);
|
|
PDBSymbolHandler SymbolHandler(Importer, ProcessedTypes, Session, *InputFile);
|
|
if (auto Err = iterateSymbolGroups(*InputFile, HeaderScope, SymbolHandler)) {
|
|
revng_log(DILogger, "Unable to parse symbols: " << Err);
|
|
consumeError(std::move(Err));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PDBImporterImpl::run(NativeSession &Session) {
|
|
populateTypes();
|
|
populateSymbolsWithTypes(Session);
|
|
|
|
deduplicateEquivalentTypes(Importer.getModel());
|
|
promoteOriginalName(Importer.getModel());
|
|
purgeUnnamedAndUnreachableTypes(Importer.getModel());
|
|
revng_assert(Importer.getModel()->verify(true));
|
|
}
|
|
|
|
void PDBImporter::loadDataFromPDB(std::string PDBFileName) {
|
|
auto Err = loadDataForPDB(PDB_ReaderType::Native, PDBFileName, Session);
|
|
if (not Err) {
|
|
TheNativeSession = static_cast<NativeSession *>(Session.get());
|
|
// TODO: We are using the static_cast due to lack of an LLVM RTTI
|
|
// support for this. Once it is improved in LLVM, we should avoid this.
|
|
auto SessionLoadAddress = Session->getLoadAddress();
|
|
auto NativeSessionLoadAddress = TheNativeSession->getLoadAddress();
|
|
revng_assert(SessionLoadAddress == NativeSessionLoadAddress);
|
|
|
|
ThePDBFile = &TheNativeSession->getPDBFile();
|
|
} else {
|
|
revng_log(DILogger, "Unable to read PDB file: " << Err);
|
|
consumeError(std::move(Err));
|
|
}
|
|
}
|
|
|
|
// At first, check if we can find the file path on this device as is.
|
|
// If the XDG_CACHE_HOME was set, we will find there, if not, try finding it in
|
|
// the `~/.local/share/revng/debug-symbols/pe/`.
|
|
std::optional<std::string>
|
|
PDBImporter::getCachedPDBFilePath(std::string PDBFileID,
|
|
StringRef PDBFilePath,
|
|
StringRef InputFileName) {
|
|
llvm::SmallString<128> ResultPath;
|
|
if (llvm::sys::fs::exists(ResultPath.str()))
|
|
return std::string(ResultPath.str());
|
|
|
|
ResultPath.clear();
|
|
// Check in the same directory as InputFileName.
|
|
if (sys::path::is_absolute(InputFileName)) {
|
|
llvm::sys::path::append(ResultPath,
|
|
llvm::sys::path::parent_path(InputFileName),
|
|
PDBFilePath);
|
|
} else {
|
|
// Relative path.
|
|
llvm::SmallString<64> CurrentDirectory;
|
|
auto ErrorCode = llvm::sys::fs::current_path(CurrentDirectory);
|
|
if (!ErrorCode) {
|
|
llvm::sys::path::append(ResultPath,
|
|
CurrentDirectory,
|
|
llvm::sys::path::parent_path(InputFileName),
|
|
PDBFilePath);
|
|
} else {
|
|
revng_log(DILogger, "Can't get current working path.");
|
|
}
|
|
}
|
|
|
|
if (llvm::sys::fs::exists(ResultPath.str()))
|
|
return std::string(ResultPath.str());
|
|
|
|
ResultPath.clear();
|
|
auto XDGCacheHome = llvm::sys::Process::GetEnv("XDG_CACHE_HOME");
|
|
if (!XDGCacheHome) {
|
|
SmallString<64> PathHome;
|
|
sys::path::home_directory(PathHome);
|
|
// Default debug directory.
|
|
llvm::sys::path::append(ResultPath,
|
|
PathHome.str(),
|
|
".local/share/revng/debug-symbols/pe/",
|
|
PDBFileID,
|
|
PDBFilePath);
|
|
} else {
|
|
llvm::sys::path::append(ResultPath,
|
|
*XDGCacheHome,
|
|
".local/share/revng/debug-symbols/pe/",
|
|
PDBFileID,
|
|
PDBFilePath);
|
|
}
|
|
|
|
if (llvm::sys::fs::exists(ResultPath.str()))
|
|
return std::string(ResultPath.str());
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Construct PDB file ID.
|
|
static std::string formatPDBFileID(ArrayRef<uint8_t> Bytes, uint16_t Age) {
|
|
std::string PDBGUID;
|
|
raw_string_ostream StringPDBGUID(PDBGUID);
|
|
StringPDBGUID << format_bytes(Bytes,
|
|
/*FirstByteOffset*/ {},
|
|
/*NumPerLine*/ 16,
|
|
/*ByteGroupSize*/ 16);
|
|
StringPDBGUID.flush();
|
|
|
|
// Let's format the PDB file ID.
|
|
// The PDB GUID is `7209ac2725e5fe841a88b1fe70d1603b` and `Age` is 2.
|
|
// The PDB ID `Hash` is: `27ac0972e52584fe1a88b1fe70d1603b2`.
|
|
std::string PDBFileID;
|
|
PDBFileID += PDBGUID[6];
|
|
PDBFileID += PDBGUID[7];
|
|
PDBFileID += PDBGUID[4];
|
|
PDBFileID += PDBGUID[5];
|
|
PDBFileID += PDBGUID[2];
|
|
PDBFileID += PDBGUID[3];
|
|
PDBFileID += PDBGUID[0];
|
|
PDBFileID += PDBGUID[1];
|
|
|
|
PDBFileID += PDBGUID[10];
|
|
PDBFileID += PDBGUID[11];
|
|
PDBFileID += PDBGUID[8];
|
|
PDBFileID += PDBGUID[9];
|
|
PDBFileID += PDBGUID[14];
|
|
PDBFileID += PDBGUID[15];
|
|
PDBFileID += PDBGUID[12];
|
|
PDBFileID += PDBGUID[13];
|
|
|
|
PDBFileID += PDBGUID.substr(16);
|
|
PDBFileID += ('0' + Age);
|
|
|
|
return PDBFileID;
|
|
}
|
|
|
|
void PDBImporter::import(const COFFObjectFile &TheBinary,
|
|
const ImporterOptions &Options) {
|
|
// Parse debug info and populate types to Model.
|
|
const codeview::DebugInfo *DebugInfo;
|
|
StringRef PDBFilePath;
|
|
|
|
auto EC = TheBinary.getDebugPDBInfo(DebugInfo, PDBFilePath);
|
|
if (not EC and DebugInfo != nullptr and not PDBFilePath.empty()) {
|
|
if (not UsePDB.empty()) {
|
|
// Sometimes we may rename a PDB file, so we can force using that one.
|
|
loadDataFromPDB(UsePDB);
|
|
} else if (llvm::sys::fs::exists(PDBFilePath)) {
|
|
// Use the path of the PDB file if it exists on the device.
|
|
loadDataFromPDB(PDBFilePath.str());
|
|
} else {
|
|
if (Options.DebugInfo != DebugInfoLevel::No) {
|
|
// Usually the PDB files will be generated on a different machine,
|
|
// so the location read from the debug directory won't be up to date.
|
|
|
|
auto PositionOfLastDirectoryChar = PDBFilePath.rfind("\\");
|
|
if (PositionOfLastDirectoryChar != llvm::StringRef::npos) {
|
|
PDBFilePath = PDBFilePath.slice(PositionOfLastDirectoryChar + 1,
|
|
PDBFilePath.size());
|
|
}
|
|
|
|
// TODO: Handle PDB signature types other then PDB70, e.g. PDB20.
|
|
if (DebugInfo->Signature.CVSignature == OMF::Signature::PDB70) {
|
|
// Get debug info from canonical places.
|
|
auto PDBFileID = formatPDBFileID(DebugInfo->PDB70.Signature,
|
|
DebugInfo->PDB70.Age);
|
|
|
|
auto DebugInfoPath = getCachedPDBFilePath(PDBFileID,
|
|
PDBFilePath,
|
|
TheBinary.getFileName());
|
|
if (DebugInfoPath) {
|
|
loadDataFromPDB(*DebugInfoPath);
|
|
} else {
|
|
// Let's try finding it on web with the `fetch-debuginfo` tool.
|
|
// If the `revng` cannot be found, avoid finding debug info.
|
|
if (!::Runner.isProgramAvailable("revng")) {
|
|
revng_log(DILogger,
|
|
"Can't find `revng` binary to run `fetch-debuginfo`.");
|
|
return;
|
|
}
|
|
int ExitCode = runFetchDebugInfoWithLevel(TheBinary.getFileName());
|
|
if (ExitCode != 0) {
|
|
revng_log(DILogger,
|
|
"Failed to find debug info with `revng model "
|
|
"fetch-debuginfo`.");
|
|
return;
|
|
}
|
|
|
|
DebugInfoPath = getCachedPDBFilePath(PDBFileID,
|
|
PDBFilePath,
|
|
TheBinary.getFileName());
|
|
if (!DebugInfoPath) {
|
|
revng_log(DILogger, "Unable to find PDB file.");
|
|
return;
|
|
}
|
|
loadDataFromPDB(*DebugInfoPath);
|
|
}
|
|
} else {
|
|
revng_log(DILogger, "Handle signatures other than PDB70.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
revng_log(DILogger, "Unable to find PDB path in the binary.");
|
|
if (EC) {
|
|
revng_log(DILogger, "Unexpected debug directory: " << EC);
|
|
consumeError(std::move(EC));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (not ThePDBFile) {
|
|
revng_log(DILogger, "Unable to find PDB file.");
|
|
return;
|
|
} else {
|
|
// TODO: Handle PDB signature types other then PDB70, e.g. PDB20.
|
|
if (DebugInfo->Signature.CVSignature == OMF::Signature::PDB70) {
|
|
// According to llvm/docs/PDB/PdbStream.rst, the `Signature` was never
|
|
// used the way as it was the initial idea. Instead, GUID is a 128-bit
|
|
// identifier guaranteed to be unique ID for both executable and
|
|
// corresponding PDB.
|
|
codeview::GUID GUIDFromExe;
|
|
llvm::copy(DebugInfo->PDB70.Signature, std::begin(GUIDFromExe.Guid));
|
|
auto PDBInfoStrm = ThePDBFile->getPDBInfoStream();
|
|
if (!PDBInfoStrm)
|
|
revng_log(DILogger, "No PDB Info stream found.");
|
|
else {
|
|
codeview::GUID GUIDFromPDBFIle = PDBInfoStrm->getGuid();
|
|
if (GUIDFromExe != GUIDFromPDBFIle)
|
|
revng_log(DILogger, "Signatures from exe and PDB file mismatch.");
|
|
}
|
|
} else {
|
|
revng_log(DILogger, "Handle signatures other than PDB70.");
|
|
}
|
|
}
|
|
|
|
PDBImporterImpl ModelCreator(*this);
|
|
ModelCreator.run(*TheNativeSession);
|
|
}
|
|
|
|
// ==== Implementation of the Model type recordings. ==== //
|
|
|
|
Error PDBImporterTypeVisitor::visitTypeBegin(CVType &Record) {
|
|
return visitTypeBegin(Record, TypeIndex::fromArrayIndex(Types.size()));
|
|
}
|
|
|
|
Error PDBImporterTypeVisitor::visitTypeBegin(CVType &Record, TypeIndex TI) {
|
|
CurrentTypeIndex = TI;
|
|
return Error::success();
|
|
}
|
|
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
FieldListRecord &FieldList) {
|
|
if (auto EC = visitMemberRecordStream(FieldList.Data, *this))
|
|
return EC;
|
|
return Error::success();
|
|
}
|
|
|
|
// Determine the pointer size based on CodeView/PDB data.
|
|
static uint32_t getPointerSizeInBytes(codeview::PointerKind K) {
|
|
switch (K) {
|
|
case codeview::PointerKind::Near64:
|
|
return 8;
|
|
case codeview::PointerKind::Near32:
|
|
return 4;
|
|
default:
|
|
// TODO: Handle all pointer kinds.
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
// Parse LF_POINTER.
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
PointerRecord &Ptr) {
|
|
using namespace model;
|
|
auto TypeTypedef = makeType<TypedefType>();
|
|
|
|
TypeIndex ReferencedType = Ptr.getReferentType();
|
|
auto ReferencedTypeFromModel = getModelTypeForIndex(ReferencedType);
|
|
if (!ReferencedTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_POINTER: Unknown referenced type "
|
|
<< ReferencedType.getIndex());
|
|
} else {
|
|
auto PointerSize = getPointerSizeInBytes(Ptr.getPointerKind());
|
|
std::vector<Qualifier> Qualifiers{ Qualifier::createPointer(PointerSize) };
|
|
QualifiedType TheUnderlyingType(*ReferencedTypeFromModel, Qualifiers);
|
|
|
|
auto TheTypeTypeDef = cast<TypedefType>(TypeTypedef.get());
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto TypePath = Model->recordNewType(std::move(TypeTypedef));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
// Parse LF_ARRAY.
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
ArrayRecord &Array) {
|
|
using namespace model;
|
|
auto TypeTypedef = makeType<TypedefType>();
|
|
|
|
TypeIndex ElementType = Array.getElementType();
|
|
|
|
auto ElementTypeFromModel = getModelTypeForIndex(ElementType);
|
|
if (!ElementTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_ARRAY: Unknown element type " << ElementType.getIndex());
|
|
} else {
|
|
auto MaybeSize = ElementTypeFromModel->get()->size();
|
|
if (not MaybeSize or *MaybeSize == 0 or Array.getSize() == 0) {
|
|
revng_log(DILogger, "Skipping 0-sized array.");
|
|
return Error::success();
|
|
}
|
|
|
|
const uint64_t ArraySize = Array.getSize() / *MaybeSize;
|
|
std::vector<Qualifier> Qualifiers{ Qualifier::createArray(ArraySize) };
|
|
QualifiedType TheUnderlyingType(*ElementTypeFromModel, Qualifiers);
|
|
|
|
auto TheTypeTypeDef = cast<TypedefType>(TypeTypedef.get());
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto TypePath = Model->recordNewType(std::move(TypeTypedef));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
// Parse LF_MODIFIER.
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
ModifierRecord &Modifier) {
|
|
auto TypeTypedef = model::makeType<model::TypedefType>();
|
|
TypeIndex ReferencedType = Modifier.getModifiedType();
|
|
|
|
auto ReferencedTypeFromModel = getModelTypeForIndex(ReferencedType);
|
|
if (!ReferencedTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_MODIFIER: Unknown referenced type "
|
|
<< ReferencedType.getIndex());
|
|
} else {
|
|
std::vector<model::Qualifier> Qualifiers;
|
|
auto HasConst = Modifier.getModifiers() & ModifierOptions::Const;
|
|
if (HasConst != ModifierOptions::None)
|
|
Qualifiers.push_back(model::Qualifier::Qualifier::createConst());
|
|
|
|
if (Qualifiers.size() != 0) {
|
|
model::QualifiedType TheUnderlyingType(*ReferencedTypeFromModel,
|
|
Qualifiers);
|
|
|
|
auto TheTypeTypeDef = cast<model::TypedefType>(TypeTypedef.get());
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto TypePath = Model->recordNewType(std::move(TypeTypedef));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
// Parse LF_MEMBER.
|
|
Error PDBImporterTypeVisitor::visitKnownMember(CVMemberRecord &Record,
|
|
DataMemberRecord &Member) {
|
|
InProgressMemberTypes[CurrentTypeIndex].push_back(Member);
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
llvm::Error
|
|
PDBImporterTypeVisitor::visitKnownRecord(CVType &CVR,
|
|
MemberFunctionRecord &MemberFnRecord) {
|
|
InProgressConcreteFunctionMemberTypes[CurrentTypeIndex] = MemberFnRecord;
|
|
return Error::success();
|
|
}
|
|
|
|
// Parse LF_ONEMETHOD.
|
|
// This occurs within LF_CLASS and it references an LF_MFUNCTION.
|
|
Error PDBImporterTypeVisitor::visitKnownMember(CVMemberRecord &Record,
|
|
OneMethodRecord &FnMember) {
|
|
InProgressFunctionMemberTypes[CurrentTypeIndex].push_back(FnMember);
|
|
return Error::success();
|
|
}
|
|
|
|
// Parse LF_ENUMERATE.
|
|
Error PDBImporterTypeVisitor::visitKnownMember(CVMemberRecord &Record,
|
|
EnumeratorRecord &Member) {
|
|
InProgressEnumeratorTypes[CurrentTypeIndex].push_back(Member);
|
|
return Error::success();
|
|
}
|
|
|
|
// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI)
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
ClassRecord &Class) {
|
|
using namespace model;
|
|
// 0-sized structs are typedefed to void.
|
|
if (not Class.getSize()) {
|
|
auto TypeTypedef = makeType<TypedefType>();
|
|
TypeTypedef->OriginalName() = Class.getName();
|
|
|
|
using Values = model::PrimitiveTypeKind::Values;
|
|
QualifiedType TheUnderlyingType(Model->getPrimitiveType(Values::Void, 0),
|
|
{});
|
|
auto TheTypeTypeDef = cast<TypedefType>(TypeTypedef.get());
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto TypePath = Model->recordNewType(std::move(TypeTypedef));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
TypeIndex FieldsTypeIndex = Class.getFieldList();
|
|
if (InProgressMemberTypes.count(FieldsTypeIndex) != 0) {
|
|
auto NewType = makeType<model::StructType>();
|
|
NewType->OriginalName() = Class.getName();
|
|
auto Struct = cast<model::StructType>(NewType.get());
|
|
|
|
Struct->Size() = Class.getSize();
|
|
auto &TheFields = InProgressMemberTypes[FieldsTypeIndex];
|
|
uint64_t MaxOffset = 0;
|
|
|
|
for (const auto &Field : TheFields) {
|
|
// Create new field.
|
|
uint64_t Offset = Field.getFieldOffset();
|
|
auto FiledTypeFromModel = getModelTypeForIndex(Field.getType());
|
|
if (!FiledTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_STRUCTURE: Unknown field type "
|
|
<< Field.getType().getIndex());
|
|
} else {
|
|
auto MaybeSize = FiledTypeFromModel->get()->size();
|
|
uint64_t Size = MaybeSize.value_or(0);
|
|
if (Size == 0) {
|
|
// Skip 0-sized field.
|
|
revng_log(DILogger, "Skipping 0-sized struct field.");
|
|
continue;
|
|
}
|
|
|
|
// This is weird, but I've faced something like:
|
|
// PDB struct TYPE {
|
|
// offset_0: "sign" // size 1
|
|
// offset_1: "Local" // size 1
|
|
//
|
|
// // and again
|
|
// offset_0: "signLocal" // size 2
|
|
// }
|
|
uint64_t CurrFieldOffset = Offset + Size;
|
|
if (CurrFieldOffset > MaxOffset)
|
|
MaxOffset = CurrFieldOffset;
|
|
else
|
|
continue;
|
|
|
|
// TODO: How is this possible?
|
|
// Triggers:
|
|
// `Last field ends outside the struct`.
|
|
if (CurrFieldOffset > Struct->Size()) {
|
|
revng_log(DILogger,
|
|
"Skipping struct field that is outside the struct.");
|
|
continue;
|
|
}
|
|
|
|
auto &FieldType = Struct->Fields()[Offset];
|
|
FieldType.OriginalName() = Field.getName().str();
|
|
QualifiedType TheUnderlyingType(*FiledTypeFromModel, {});
|
|
FieldType.Type() = TheUnderlyingType;
|
|
}
|
|
}
|
|
auto TypePath = Model->recordNewType(std::move(NewType));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
}
|
|
|
|
// Process methods. Create C-like function prototype for it.
|
|
if (InProgressFunctionMemberTypes.contains(FieldsTypeIndex)) {
|
|
auto &TheFunctions = InProgressFunctionMemberTypes[FieldsTypeIndex];
|
|
for (auto &Function : TheFunctions) {
|
|
TypeIndex FnTypeIndex = Function.getType();
|
|
if (InProgressConcreteFunctionMemberTypes.count(FnTypeIndex) == 0)
|
|
continue;
|
|
|
|
// Get the proper LF_MFUNCTION.
|
|
auto &MemberFunction = InProgressConcreteFunctionMemberTypes[FnTypeIndex];
|
|
TypeIndex ReturnTypeIndex = MemberFunction.ReturnType;
|
|
auto ReferencedTypeFromModel = getModelTypeForIndex(ReturnTypeIndex);
|
|
if (not ReferencedTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_MFUNCTION: Unknown return type "
|
|
<< ReturnTypeIndex.getIndex());
|
|
continue;
|
|
}
|
|
|
|
auto NewType = makeType<CABIFunctionType>();
|
|
auto TypeFunction = cast<CABIFunctionType>(NewType.get());
|
|
TypeFunction->ABI() = Model->DefaultABI();
|
|
|
|
QualifiedType TheReturnType(*ReferencedTypeFromModel, {});
|
|
TypeFunction->ReturnType() = TheReturnType;
|
|
|
|
TypeIndex ArgListTyIndex = MemberFunction.getArgumentList();
|
|
revng_assert(InProgressArgumentsTypes.contains(ArgListTyIndex));
|
|
auto ArgList = InProgressArgumentsTypes[ArgListTyIndex];
|
|
|
|
auto Indices = ArgList.getIndices();
|
|
uint32_t Size = Indices.size();
|
|
uint32_t Index = 0;
|
|
|
|
// Add `this` pointer as an argument if the method is not marked
|
|
// as `static` or `friend`.
|
|
if (Function.getMethodKind() != MethodKind::Static
|
|
and Function.getMethodKind() != MethodKind::Friend
|
|
and ProcessedTypes.count(CurrentTypeIndex) != 0) {
|
|
auto MaybeSize = ProcessedTypes[CurrentTypeIndex].get()->size();
|
|
if (MaybeSize and *MaybeSize != 0) {
|
|
Argument &NewArgument = TypeFunction->Arguments()[Index];
|
|
auto PointerSize = getPointerSize(Model->Architecture());
|
|
QualifiedType TheType(ProcessedTypes[CurrentTypeIndex],
|
|
{ Qualifier::createPointer(PointerSize) });
|
|
NewArgument.Type() = TheType;
|
|
++Index;
|
|
} else {
|
|
revng_log(DILogger, "Skipping 0-sized argument.");
|
|
}
|
|
}
|
|
|
|
for (uint32_t I = 0; I < Size; ++I) {
|
|
TypeIndex ArgumentTypeIndex = Indices[I];
|
|
auto ArgumentTypeFromModel = getModelTypeForIndex(ArgumentTypeIndex);
|
|
if (not ArgumentTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_MFUNCTION: Unknown arg type "
|
|
<< ArgumentTypeIndex.getIndex());
|
|
} else {
|
|
auto MaybeSize = ArgumentTypeFromModel->get()->size();
|
|
uint64_t Size = MaybeSize.value_or(0);
|
|
if (Size == 0) {
|
|
// Skip 0-sized type.
|
|
revng_log(DILogger, "Skipping 0-sized argument.");
|
|
continue;
|
|
}
|
|
|
|
Argument &NewArgument = TypeFunction->Arguments()[Index];
|
|
|
|
QualifiedType TheUnderlyingType(*ArgumentTypeFromModel, {});
|
|
NewArgument.Type() = TheUnderlyingType;
|
|
++Index;
|
|
}
|
|
}
|
|
|
|
auto TypePath = Model->recordNewType(std::move(NewType));
|
|
ProcessedTypes[FnTypeIndex] = TypePath;
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
// LF_ENUM (TPI)
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
EnumRecord &Enum) {
|
|
TypeIndex FieldsTypeIndex = Enum.getFieldList();
|
|
auto NewType = model::makeType<model::EnumType>();
|
|
NewType->OriginalName() = Enum.getName();
|
|
|
|
TypeIndex UnderlyingTypeIndex = Enum.getUnderlyingType();
|
|
auto UnderlynigTypeFromModel = getModelTypeForIndex(UnderlyingTypeIndex);
|
|
if (not UnderlynigTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_ENUM: Unknown underlying type "
|
|
<< UnderlyingTypeIndex.getIndex());
|
|
return Error::success();
|
|
}
|
|
|
|
model::QualifiedType TheUnderlyingType(*UnderlynigTypeFromModel, {});
|
|
auto TypeEnum = cast<model::EnumType>(NewType.get());
|
|
TypeEnum->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto &TheFields = InProgressEnumeratorTypes[FieldsTypeIndex];
|
|
if (TheFields.empty())
|
|
return Error::success();
|
|
|
|
for (const auto &Entry : TheFields) {
|
|
auto &EnumEntry = TypeEnum->Entries()[Entry.getValue().getExtValue()];
|
|
EnumEntry.OriginalName() = Entry.getName().str();
|
|
}
|
|
|
|
auto TypePath = Model->recordNewType(std::move(NewType));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static inline constexpr model::ABI::Values
|
|
getMicrosoftABI(CallingConvention CallConv, model::Architecture::Values Arch) {
|
|
if (Arch == model::Architecture::x86_64) {
|
|
switch (CallConv) {
|
|
case CallingConvention::NearC:
|
|
case CallingConvention::NearFast:
|
|
case CallingConvention::NearStdCall:
|
|
case CallingConvention::NearSysCall:
|
|
case CallingConvention::ThisCall:
|
|
case CallingConvention::NearPascal:
|
|
return model::ABI::Microsoft_x86_64;
|
|
case CallingConvention::NearVector:
|
|
return model::ABI::Microsoft_x86_64_vectorcall;
|
|
case CallingConvention::ClrCall:
|
|
return model::ABI::Microsoft_x86_64_clrcall;
|
|
default:
|
|
revng_abort();
|
|
}
|
|
} else if (Arch == model::Architecture::x86) {
|
|
switch (CallConv) {
|
|
case CallingConvention::NearC:
|
|
return model::ABI::Microsoft_x86_cdecl;
|
|
case CallingConvention::NearFast:
|
|
return model::ABI::Microsoft_x86_fastcall;
|
|
case CallingConvention::NearStdCall:
|
|
return model::ABI::Microsoft_x86_stdcall;
|
|
case CallingConvention::NearSysCall:
|
|
return model::ABI::Microsoft_x86_stdcall;
|
|
case CallingConvention::ThisCall:
|
|
return model::ABI::Microsoft_x86_thiscall;
|
|
case CallingConvention::ClrCall:
|
|
return model::ABI::Microsoft_x86_clrcall;
|
|
case CallingConvention::NearPascal:
|
|
return model::ABI::Pascal_x86;
|
|
case CallingConvention::NearVector:
|
|
return model::ABI::Microsoft_x86_vectorcall;
|
|
default:
|
|
revng_abort();
|
|
}
|
|
} else if (Arch == model::Architecture::mips
|
|
and CallConv == CallingConvention::MipsCall) {
|
|
return model::ABI::SystemV_MIPS_o32;
|
|
} else if (Arch == model::Architecture::mipsel
|
|
and CallConv == CallingConvention::MipsCall) {
|
|
return model::ABI::SystemV_MIPSEL_o32;
|
|
} else if (Arch == model::Architecture::arm
|
|
and CallConv == CallingConvention::ArmCall) {
|
|
return model::ABI::AAPCS;
|
|
} else if (Arch == model::Architecture::aarch64
|
|
and CallConv == CallingConvention::ArmCall) {
|
|
return model::ABI::AAPCS64;
|
|
} else {
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
// LF_PROCEDURE (TPI)
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
ProcedureRecord &Proc) {
|
|
TypeIndex ReturnTypeIndex = Proc.ReturnType;
|
|
auto ReturnTypeFromModel = getModelTypeForIndex(ReturnTypeIndex);
|
|
if (not ReturnTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_PROCEDURE: Unknown return type "
|
|
<< ReturnTypeIndex.getIndex());
|
|
} else {
|
|
auto NewType = model::makeType<model::CABIFunctionType>();
|
|
auto TypeFunction = cast<model::CABIFunctionType>(NewType.get());
|
|
TypeFunction->ABI() = getMicrosoftABI(Proc.getCallConv(),
|
|
Model->Architecture());
|
|
|
|
model::QualifiedType TheReturnType(*ReturnTypeFromModel, {});
|
|
TypeFunction->ReturnType() = TheReturnType;
|
|
|
|
TypeIndex ArgListTyIndex = Proc.getArgumentList();
|
|
auto ArgumentList = InProgressArgumentsTypes[ArgListTyIndex];
|
|
|
|
auto Indices = ArgumentList.getIndices();
|
|
uint32_t Size = Indices.size();
|
|
uint32_t Index = 0;
|
|
for (uint32_t I = 0; I < Size; ++I) {
|
|
TypeIndex ArgumentTypeIndex = Indices[I];
|
|
auto ArgumentTypeFromModel = getModelTypeForIndex(ArgumentTypeIndex);
|
|
if (not ArgumentTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_PROCEDURE: Unknown argument type "
|
|
<< ArgumentTypeIndex.getIndex());
|
|
} else {
|
|
auto MaybeSize = ArgumentTypeFromModel->get()->size();
|
|
uint64_t Size = MaybeSize.value_or(0);
|
|
if (Size == 0) {
|
|
// Skip 0-sized type.
|
|
revng_log(DILogger, "Skipping 0-sized argument.");
|
|
continue;
|
|
}
|
|
|
|
model::Argument &NewArgument = TypeFunction->Arguments()[Index];
|
|
model::QualifiedType TheArgumentType(*ArgumentTypeFromModel, {});
|
|
|
|
NewArgument.Type() = TheArgumentType;
|
|
++Index;
|
|
}
|
|
}
|
|
|
|
auto TypePath = Model->recordNewType(std::move(NewType));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
// LF_UNION (TPI)
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
UnionRecord &Union) {
|
|
TypeIndex FieldsTypeIndex = Union.getFieldList();
|
|
auto NewType = model::makeType<model::UnionType>();
|
|
NewType->OriginalName() = Union.getName().str();
|
|
|
|
uint64_t Index = 0;
|
|
auto &TheFields = InProgressMemberTypes[FieldsTypeIndex];
|
|
|
|
// Handle an empty union, similar to 0-sized structs.
|
|
// Typedef it to void.
|
|
if (TheFields.size() == 0) {
|
|
auto TypeTypedef = model::makeType<model::TypedefType>();
|
|
TypeTypedef->OriginalName() = Union.getName().str();
|
|
|
|
auto TheTypeTypeDef = cast<model::TypedefType>(TypeTypedef.get());
|
|
using Values = model::PrimitiveTypeKind::Values;
|
|
auto ThePrimitiveType = Model->getPrimitiveType(Values::Void, 0);
|
|
model::QualifiedType TheUnderlyingType(ThePrimitiveType, {});
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto TypePath = Model->recordNewType(std::move(TypeTypedef));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
bool GeneratedOneFieldAtleast = false;
|
|
for (const auto &Field : TheFields) {
|
|
// Create new field.
|
|
auto FiledTypeFromModel = getModelTypeForIndex(Field.getType());
|
|
if (!FiledTypeFromModel) {
|
|
revng_log(DILogger,
|
|
"LF_UNION: Unknown field type " << Field.getType().getIndex());
|
|
} else {
|
|
auto MaybeSize = FiledTypeFromModel->get()->size();
|
|
uint64_t Size = MaybeSize.value_or(0);
|
|
if (Size == 0) {
|
|
// Skip 0-sized field.
|
|
revng_log(DILogger, "Skipping 0-sized union field.");
|
|
continue;
|
|
}
|
|
|
|
GeneratedOneFieldAtleast = true;
|
|
auto TypeUnion = cast<model::UnionType>(NewType.get());
|
|
auto &FieldType = TypeUnion->Fields()[Index];
|
|
FieldType.OriginalName() = Field.getName().str();
|
|
model::QualifiedType TheFieldType(*FiledTypeFromModel, {});
|
|
FieldType.Type() = TheFieldType;
|
|
|
|
Index++;
|
|
}
|
|
}
|
|
|
|
if (GeneratedOneFieldAtleast) {
|
|
auto TypePath = Model->recordNewType(std::move(NewType));
|
|
ProcessedTypes[CurrentTypeIndex] = TypePath;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error PDBImporterTypeVisitor::visitKnownRecord(CVType &Record,
|
|
ArgListRecord &Args) {
|
|
InProgressArgumentsTypes[CurrentTypeIndex] = Args;
|
|
return Error::success();
|
|
}
|
|
|
|
// TODO: This can go into LLVM, but there is an ongoing review that should
|
|
// implement this.
|
|
static std::optional<uint64_t> getSizeinBytes(TypeIndex TI) {
|
|
if (not TI.isSimple())
|
|
return std::nullopt;
|
|
switch (TI.getSimpleKind()) {
|
|
case SimpleTypeKind::Void:
|
|
return 0;
|
|
case SimpleTypeKind::HResult:
|
|
return 4;
|
|
case SimpleTypeKind::SByte:
|
|
case SimpleTypeKind::Byte:
|
|
return 1;
|
|
case SimpleTypeKind::Int16Short:
|
|
case SimpleTypeKind::UInt16Short:
|
|
case SimpleTypeKind::Int16:
|
|
case SimpleTypeKind::UInt16:
|
|
return 2;
|
|
case SimpleTypeKind::Int32Long:
|
|
case SimpleTypeKind::UInt32Long:
|
|
case SimpleTypeKind::Int32:
|
|
case SimpleTypeKind::UInt32:
|
|
return 4;
|
|
case SimpleTypeKind::Int64Quad:
|
|
case SimpleTypeKind::UInt64Quad:
|
|
case SimpleTypeKind::Int64:
|
|
case SimpleTypeKind::UInt64:
|
|
return 8;
|
|
case SimpleTypeKind::Int128Oct:
|
|
case SimpleTypeKind::UInt128Oct:
|
|
case SimpleTypeKind::Int128:
|
|
case SimpleTypeKind::UInt128:
|
|
return 16;
|
|
case SimpleTypeKind::SignedCharacter:
|
|
case SimpleTypeKind::UnsignedCharacter:
|
|
case SimpleTypeKind::NarrowCharacter:
|
|
return 1;
|
|
case SimpleTypeKind::WideCharacter:
|
|
case SimpleTypeKind::Character16:
|
|
return 2;
|
|
case SimpleTypeKind::Character32:
|
|
return 4;
|
|
case SimpleTypeKind::Float16:
|
|
return 2;
|
|
case SimpleTypeKind::Float32:
|
|
return 4;
|
|
case SimpleTypeKind::Float64:
|
|
return 8;
|
|
case SimpleTypeKind::Float80:
|
|
return 10;
|
|
case SimpleTypeKind::Float128:
|
|
return 16;
|
|
case SimpleTypeKind::Boolean8:
|
|
return 1;
|
|
case SimpleTypeKind::Boolean16:
|
|
return 2;
|
|
case SimpleTypeKind::Boolean32:
|
|
return 4;
|
|
case SimpleTypeKind::Boolean64:
|
|
return 8;
|
|
case SimpleTypeKind::Boolean128:
|
|
return 16;
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
static model::PrimitiveTypeKind::Values
|
|
codeviewSimpleTypeEncodingToModel(TypeIndex TI) {
|
|
if (not TI.isSimple())
|
|
return model::PrimitiveTypeKind::Invalid;
|
|
|
|
switch (TI.getSimpleKind()) {
|
|
case SimpleTypeKind::Void:
|
|
return model::PrimitiveTypeKind::Void;
|
|
case SimpleTypeKind::Boolean8:
|
|
case SimpleTypeKind::Boolean16:
|
|
case SimpleTypeKind::Boolean32:
|
|
case SimpleTypeKind::Boolean64:
|
|
case SimpleTypeKind::Boolean128:
|
|
case SimpleTypeKind::Byte:
|
|
case SimpleTypeKind::UInt16:
|
|
case SimpleTypeKind::UInt32:
|
|
case SimpleTypeKind::UInt64:
|
|
case SimpleTypeKind::UnsignedCharacter:
|
|
case SimpleTypeKind::UInt16Short:
|
|
case SimpleTypeKind::UInt32Long:
|
|
case SimpleTypeKind::UInt64Quad:
|
|
case SimpleTypeKind::UInt128Oct:
|
|
case SimpleTypeKind::UInt128:
|
|
return model::PrimitiveTypeKind::Unsigned;
|
|
case SimpleTypeKind::SignedCharacter:
|
|
case SimpleTypeKind::WideCharacter:
|
|
case SimpleTypeKind::NarrowCharacter:
|
|
case SimpleTypeKind::Character16:
|
|
case SimpleTypeKind::Character32:
|
|
case SimpleTypeKind::SByte:
|
|
case SimpleTypeKind::Int32Long:
|
|
case SimpleTypeKind::Int32:
|
|
case SimpleTypeKind::Int64Quad:
|
|
case SimpleTypeKind::Int64:
|
|
case SimpleTypeKind::Int128Oct:
|
|
case SimpleTypeKind::Int128:
|
|
return model::PrimitiveTypeKind::Signed;
|
|
case SimpleTypeKind::Float16:
|
|
case SimpleTypeKind::Float32:
|
|
case SimpleTypeKind::Float64:
|
|
case SimpleTypeKind::Float80:
|
|
case SimpleTypeKind::Float128:
|
|
return model::PrimitiveTypeKind::Float;
|
|
default:
|
|
return model::PrimitiveTypeKind::Invalid;
|
|
}
|
|
}
|
|
|
|
static bool isPointer(TypeIndex TI) {
|
|
if (TI.getSimpleMode() != SimpleTypeMode::Direct) {
|
|
// We have a native pointer.
|
|
switch (TI.getSimpleMode()) {
|
|
case SimpleTypeMode::NearPointer32:
|
|
case SimpleTypeMode::FarPointer32:
|
|
case SimpleTypeMode::NearPointer64:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool isTwoBytesLongPointer(TypeIndex TI) {
|
|
if (TI.getSimpleMode() != SimpleTypeMode::Direct) {
|
|
// We have a native pointer.
|
|
switch (TI.getSimpleMode()) {
|
|
case SimpleTypeMode::NearPointer:
|
|
case SimpleTypeMode::FarPointer:
|
|
case SimpleTypeMode::HugePointer:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isSixteenBytesLongPointer(TypeIndex TI) {
|
|
if (TI.getSimpleMode() != SimpleTypeMode::Direct) {
|
|
// We have a native pointer.
|
|
switch (TI.getSimpleMode()) {
|
|
case SimpleTypeMode::NearPointer128:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static std::optional<uint64_t> getPointerSizeFromPDB(TypeIndex TI) {
|
|
if (TI.getSimpleMode() != SimpleTypeMode::Direct) {
|
|
// We have a native pointer.
|
|
switch (TI.getSimpleMode()) {
|
|
case SimpleTypeMode::NearPointer:
|
|
case SimpleTypeMode::FarPointer:
|
|
case SimpleTypeMode::HugePointer:
|
|
return 2;
|
|
case SimpleTypeMode::NearPointer32:
|
|
case SimpleTypeMode::FarPointer32:
|
|
return 4;
|
|
case SimpleTypeMode::NearPointer64:
|
|
return 8;
|
|
case SimpleTypeMode::NearPointer128:
|
|
return 16;
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void PDBImporterTypeVisitor::createPrimitiveType(TypeIndex SimpleType) {
|
|
using namespace model;
|
|
using Values = PrimitiveTypeKind::Values;
|
|
Values Kind = codeviewSimpleTypeEncodingToModel(SimpleType);
|
|
|
|
// If it is a pointer of size 2, lets create a PointerOrNumber for it.
|
|
if (isTwoBytesLongPointer(SimpleType)) {
|
|
constexpr uint64_t MSDOS16PointerSize = 2;
|
|
auto ModelType = Model->getPrimitiveType(PrimitiveTypeKind::PointerOrNumber,
|
|
MSDOS16PointerSize);
|
|
ProcessedTypes[SimpleType] = ModelType;
|
|
} else if (isSixteenBytesLongPointer(SimpleType)) {
|
|
// If it is a 128-bit long pointer, typedef it to void for now. It can be
|
|
// represented as a `struct { pointee; offset; }` since it is how it is
|
|
// implemented in the msvc compiler.
|
|
auto VoidModelType = Model->getPrimitiveType(PrimitiveTypeKind::Void, 0);
|
|
auto TypeTypedef = makeType<TypedefType>();
|
|
auto TheTypeTypeDef = cast<TypedefType>(TypeTypedef.get());
|
|
QualifiedType TheUnderlyingType(VoidModelType, {});
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
ProcessedTypes[SimpleType] = VoidModelType;
|
|
} else {
|
|
|
|
auto TypeSize = getSizeinBytes(SimpleType);
|
|
if (TypeSize and Kind != PrimitiveTypeKind::Invalid) {
|
|
// Remember the type.
|
|
auto PrimitiveModelType = Model->getPrimitiveType(Kind, *TypeSize);
|
|
// If it is not a pointer `SimpleTypeIndex` will be the same as
|
|
// `SimpleType`. In the case of pointer we have some additional bits set
|
|
// in the TypeIndex representing the type.
|
|
TypeIndex SimpleTypeIndex(SimpleType.getSimpleKind());
|
|
ProcessedTypes[SimpleTypeIndex] = PrimitiveModelType;
|
|
|
|
if (not isPointer(SimpleType))
|
|
return;
|
|
// Create a pointer to the primitive type.
|
|
auto PointerSize = getPointerSizeFromPDB(SimpleType);
|
|
if (!PointerSize) {
|
|
revng_log(DILogger, "Invalid pointer size " << SimpleType.getIndex());
|
|
return;
|
|
}
|
|
|
|
auto TypeTypedef = makeType<TypedefType>();
|
|
auto TheTypeTypeDef = cast<TypedefType>(TypeTypedef.get());
|
|
std::vector<Qualifier> Qualifiers;
|
|
Qualifiers.push_back({ Qualifier::createPointer(*PointerSize) });
|
|
|
|
QualifiedType TheUnderlyingType(PrimitiveModelType, Qualifiers);
|
|
TheTypeTypeDef->UnderlyingType() = TheUnderlyingType;
|
|
|
|
auto TypePath = Model->recordNewType(std::move(TypeTypedef));
|
|
ProcessedTypes[SimpleType] = TypePath;
|
|
} else {
|
|
revng_log(DILogger, "Invalid simple type " << SimpleType.getIndex());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::optional<TupleTreeReference<model::Type, model::Binary>>
|
|
PDBImporterTypeVisitor::getModelTypeForIndex(TypeIndex Index) {
|
|
if (ProcessedTypes.count(Index) != 0)
|
|
return ProcessedTypes[Index];
|
|
|
|
if (Index.isSimple())
|
|
createPrimitiveType(Index);
|
|
|
|
if (ProcessedTypes.count(Index) != 0)
|
|
return ProcessedTypes[Index];
|
|
return std::nullopt;
|
|
}
|
|
|
|
// ==== Implementation of the Model Symbol-type connection. ==== //
|
|
|
|
Error PDBImporterSymbolVisitor::visitSymbolBegin(CVSymbol &Record) {
|
|
return visitSymbolBegin(Record, 0);
|
|
}
|
|
|
|
Error PDBImporterSymbolVisitor::visitSymbolBegin(CVSymbol &Record,
|
|
uint32_t Offset) {
|
|
return Error::success();
|
|
}
|
|
|
|
Error PDBImporterSymbolVisitor::visitKnownRecord(CVSymbol &Record,
|
|
ProcSym &Proc) {
|
|
revng_log(DILogger, "Importing " << Proc.Name);
|
|
|
|
// If it is not in the .idata already, we assume it is a static symbol.
|
|
if (not Model->ImportedDynamicFunctions().contains(Proc.Name.str())) {
|
|
uint64_t FunctionVirtualAddress = Session
|
|
.getRVAFromSectOffset(Proc.Segment,
|
|
Proc.CodeOffset);
|
|
// Relocate the symbol.
|
|
MetaAddress FunctionAddress = ImageBase + FunctionVirtualAddress;
|
|
|
|
if (not Model->Functions().contains(FunctionAddress)) {
|
|
model::Function &Function = Model->Functions()[FunctionAddress];
|
|
Function.OriginalName() = Proc.Name;
|
|
TypeIndex FunctionTypeIndex = Proc.FunctionType;
|
|
if (ProcessedTypes.find(FunctionTypeIndex) != ProcessedTypes.end())
|
|
Function.Prototype() = ProcessedTypes[FunctionTypeIndex];
|
|
} else {
|
|
auto It = Model->Functions().find(FunctionAddress);
|
|
TypeIndex FunctionTypeIndex = Proc.FunctionType;
|
|
if (ProcessedTypes.find(FunctionTypeIndex) != ProcessedTypes.end())
|
|
It->Prototype() = ProcessedTypes[FunctionTypeIndex];
|
|
}
|
|
}
|
|
|
|
// TODO: Handle Imported functions.
|
|
|
|
return Error::success();
|
|
}
|