From 8076adf2d58cc5fb8c9e083ac22967ceeee7b278 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic Date: Wed, 1 Jun 2022 11:27:25 +0200 Subject: [PATCH] Model: Add PDB support --- include/revng/Model/ABI.h | 21 + .../{Dwarf => DebugInfo}/DwarfImporter.h | 0 .../Model/Importer/DebugInfo/PDBImporter.h | 35 + lib/Lift/CMakeLists.txt | 2 +- lib/Lift/CodeGenerator.cpp | 2 +- lib/Model/Importer/Binary/CMakeLists.txt | 2 +- lib/Model/Importer/Binary/ELFImporter.cpp | 2 +- lib/Model/Importer/Binary/PECOFFImporter.cpp | 11 + lib/Model/Importer/CMakeLists.txt | 2 +- .../{Dwarf => DebugInfo}/CMakeLists.txt | 5 +- .../{Dwarf => DebugInfo}/DwarfImporter.cpp | 3 +- lib/Model/Importer/DebugInfo/PDBImporter.cpp | 1143 +++++++++++++++++ lib/Model/Type.cpp | 29 +- lib/Pipes/ImportBinaryPipe.cpp | 2 +- tests/unit/ModelType.cpp | 5 +- tools/model/import/binary/Main.cpp | 2 +- tools/model/import/dwarf/CMakeLists.txt | 2 +- tools/model/import/dwarf/Main.cpp | 2 +- 18 files changed, 1244 insertions(+), 26 deletions(-) rename include/revng/Model/Importer/{Dwarf => DebugInfo}/DwarfImporter.h (100%) create mode 100644 include/revng/Model/Importer/DebugInfo/PDBImporter.h rename lib/Model/Importer/{Dwarf => DebugInfo}/CMakeLists.txt (51%) rename lib/Model/Importer/{Dwarf => DebugInfo}/DwarfImporter.cpp (99%) create mode 100644 lib/Model/Importer/DebugInfo/PDBImporter.cpp diff --git a/include/revng/Model/ABI.h b/include/revng/Model/ABI.h index 881b98e69..c74ed0335 100644 --- a/include/revng/Model/ABI.h +++ b/include/revng/Model/ABI.h @@ -220,6 +220,27 @@ inline constexpr model::ABI::Values getDefault(model::Architecture::Values V) { } } +// TODO: Consider factoring these binary specific things into a ELF/PEModel.h. +inline constexpr model::ABI::Values +getDefaultMicrosoftABI(model::Architecture::Values V) { + switch (V) { + case model::Architecture::x86_64: + return model::ABI::Microsoft_x86_64; + case model::Architecture::x86: + return model::ABI::Microsoft_x86_cdecl; + case model::Architecture::mips: + return model::ABI::SystemV_MIPS_o32; + case model::Architecture::mipsel: + return model::ABI::SystemV_MIPSEL_o32; + case model::Architecture::arm: + return model::ABI::AAPCS; + case model::Architecture::aarch64: + return model::ABI::AAPCS64; + default: + revng_abort(); + } +} + inline constexpr llvm::StringRef getDescription(model::ABI::Values V) { switch (V) { case model::ABI::SystemV_x86_64: diff --git a/include/revng/Model/Importer/Dwarf/DwarfImporter.h b/include/revng/Model/Importer/DebugInfo/DwarfImporter.h similarity index 100% rename from include/revng/Model/Importer/Dwarf/DwarfImporter.h rename to include/revng/Model/Importer/DebugInfo/DwarfImporter.h diff --git a/include/revng/Model/Importer/DebugInfo/PDBImporter.h b/include/revng/Model/Importer/DebugInfo/PDBImporter.h new file mode 100644 index 000000000..d42e24fb7 --- /dev/null +++ b/include/revng/Model/Importer/DebugInfo/PDBImporter.h @@ -0,0 +1,35 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/PDB/Native/InputFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" + +#include "revng/Model/Binary.h" + +class PDBImporter { +private: + TupleTree &Model; + MetaAddress ImageBase; + llvm::pdb::PDBFile *ThePDBFile = nullptr; + llvm::pdb::NativeSession *TheNativeSession = nullptr; + std::unique_ptr Session; + +public: + PDBImporter(TupleTree &Model, MetaAddress ImageBase) : + Model(Model), ImageBase(ImageBase) {} + + TupleTree &getModel() { return Model; } + MetaAddress &getBaseAddress() { return ImageBase; } + llvm::pdb::PDBFile *getPDBFile() { return ThePDBFile; } + + void import(const llvm::object::COFFObjectFile &TheBinary); + void loadDataFromPDB(std::string PDBFileName); +}; diff --git a/lib/Lift/CMakeLists.txt b/lib/Lift/CMakeLists.txt index b15988cb3..603d1f931 100644 --- a/lib/Lift/CMakeLists.txt +++ b/lib/Lift/CMakeLists.txt @@ -22,7 +22,7 @@ target_link_libraries( m revngABI revngBasicAnalyses - revngModelImporterDwarf + revngModelImporterDebugInfo revngFunctionCallIdentification revngModel revngSupport diff --git a/lib/Lift/CodeGenerator.cpp b/lib/Lift/CodeGenerator.cpp index 503518709..37ad9fec1 100644 --- a/lib/Lift/CodeGenerator.cpp +++ b/lib/Lift/CodeGenerator.cpp @@ -41,7 +41,7 @@ #include "revng/FunctionCallIdentification/FunctionCallIdentification.h" #include "revng/FunctionCallIdentification/PruneRetSuccessors.h" #include "revng/Model/Architecture.h" -#include "revng/Model/Importer/Dwarf/DwarfImporter.h" +#include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Model/RawBinaryView.h" #include "revng/Model/SerializeModelPass.h" #include "revng/Support/CommandLine.h" diff --git a/lib/Model/Importer/Binary/CMakeLists.txt b/lib/Model/Importer/Binary/CMakeLists.txt index 72e8886e1..853803151 100644 --- a/lib/Model/Importer/Binary/CMakeLists.txt +++ b/lib/Model/Importer/Binary/CMakeLists.txt @@ -8,4 +8,4 @@ revng_add_analyses_library_internal( llvm_map_components_to_libnames(LLVM_LIBRARIES Object) target_link_libraries(revngModelImporterBinary revngModel - revngModelImporterDwarf revngABI ${LLVM_LIBRARIES}) + revngModelImporterDebugInfo revngABI ${LLVM_LIBRARIES}) diff --git a/lib/Model/Importer/Binary/ELFImporter.cpp b/lib/Model/Importer/Binary/ELFImporter.cpp index e5bf7fa1c..c5472d8a2 100644 --- a/lib/Model/Importer/Binary/ELFImporter.cpp +++ b/lib/Model/Importer/Binary/ELFImporter.cpp @@ -15,8 +15,8 @@ #include "revng/ABI/DefaultFunctionPrototype.h" #include "revng/Model/Binary.h" +#include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Model/IRHelpers.h" -#include "revng/Model/Importer/Dwarf/DwarfImporter.h" #include "revng/Model/RawBinaryView.h" #include "revng/Support/Debug.h" diff --git a/lib/Model/Importer/Binary/PECOFFImporter.cpp b/lib/Model/Importer/Binary/PECOFFImporter.cpp index d6ba20466..a725066e9 100644 --- a/lib/Model/Importer/Binary/PECOFFImporter.cpp +++ b/lib/Model/Importer/Binary/PECOFFImporter.cpp @@ -8,7 +8,9 @@ #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" +#include "revng/ABI/DefaultFunctionPrototype.h" #include "revng/Model/Binary.h" +#include "revng/Model/Importer/DebugInfo/PDBImporter.h" #include "revng/Model/IRHelpers.h" #include "revng/Support/Debug.h" @@ -320,6 +322,15 @@ Error PECOFFImporter::import() { // linking). parseDelayImportedSymbols(); + if (Model->DefaultABI == model::ABI::Invalid) + Model->DefaultABI = model::ABI::getDefaultMicrosoftABI(Model->Architecture); + + // Create a default prototype. + Model->DefaultPrototype = abi::registerDefaultFunctionPrototype(*Model); + + PDBImporter PDBI(Model, ImageBase); + PDBI.import(TheBinary); + return Error::success(); } diff --git a/lib/Model/Importer/CMakeLists.txt b/lib/Model/Importer/CMakeLists.txt index 8ef1a9e99..a1f5861e2 100644 --- a/lib/Model/Importer/CMakeLists.txt +++ b/lib/Model/Importer/CMakeLists.txt @@ -3,4 +3,4 @@ # add_subdirectory(Binary) -add_subdirectory(Dwarf) +add_subdirectory(DebugInfo) diff --git a/lib/Model/Importer/Dwarf/CMakeLists.txt b/lib/Model/Importer/DebugInfo/CMakeLists.txt similarity index 51% rename from lib/Model/Importer/Dwarf/CMakeLists.txt rename to lib/Model/Importer/DebugInfo/CMakeLists.txt index 072017794..d3e22788d 100644 --- a/lib/Model/Importer/Dwarf/CMakeLists.txt +++ b/lib/Model/Importer/DebugInfo/CMakeLists.txt @@ -2,10 +2,11 @@ # This file is distributed under the MIT License. See LICENSE.md for details. # -revng_add_library_internal(revngModelImporterDwarf SHARED DwarfImporter.cpp) +revng_add_library_internal(revngModelImporterDebugInfo SHARED DwarfImporter.cpp PDBImporter.cpp) llvm_map_components_to_libnames(LLVM_LIBRARIES Object Support DebugInfoDWARF + DebugInfoCodeView DebugInfoMSF DebugInfoPDB BinaryFormat) -target_link_libraries(revngModelImporterDwarf revngSupport revngModel +target_link_libraries(revngModelImporterDebugInfo revngSupport revngModel revngModelPasses ${LLVM_LIBRARIES}) diff --git a/lib/Model/Importer/Dwarf/DwarfImporter.cpp b/lib/Model/Importer/DebugInfo/DwarfImporter.cpp similarity index 99% rename from lib/Model/Importer/Dwarf/DwarfImporter.cpp rename to lib/Model/Importer/DebugInfo/DwarfImporter.cpp index 1ed0c6617..a8599e4f4 100644 --- a/lib/Model/Importer/Dwarf/DwarfImporter.cpp +++ b/lib/Model/Importer/DebugInfo/DwarfImporter.cpp @@ -20,8 +20,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" - -#include "revng/Model/Importer/Dwarf/DwarfImporter.h" +#include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Model/Pass/AllPasses.h" #include "revng/Model/Processing.h" #include "revng/Model/QualifiedType.h" diff --git a/lib/Model/Importer/DebugInfo/PDBImporter.cpp b/lib/Model/Importer/DebugInfo/PDBImporter.cpp new file mode 100644 index 000000000..ffd21f81d --- /dev/null +++ b/lib/Model/Importer/DebugInfo/PDBImporter.cpp @@ -0,0 +1,1143 @@ +/// \file PDBImporter.cpp +/// \brief + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.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 "revng/Model/Binary.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" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::object; +using namespace llvm::pdb; + +static Logger<> DILogger("pdb-importer"); + +namespace { +class PDBImporterImpl { +private: + PDBImporter &Importer; + DenseMap 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 corresponging 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; + LazyRandomTypeCollection &Types; + DenseMap &ProcessedTypes; + + TypeIndex CurrentTypeIndex = TypeIndex::None(); + std::map> InProgressMemberTypes; + std::map> + InProgressEnumeratorTypes; + std::map InProgressArgumentsTypes; + + // Methods of a Class type. It references conrete MemberFunctionRecord. + std::map> + InProgressFunctionMemberTypes; + DenseMap + InProgressConcreteFunctionMemberTypes; + +public: + PDBImporterTypeVisitor(TupleTree &M, + LazyRandomTypeCollection &Types, + DenseMap &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> + 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; + DenseMap &ProcessedTypes; + + NativeSession &Session; + MetaAddress &ImageBase; + +public: + PDBImporterSymbolVisitor(TupleTree &M, + DenseMap &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 &ProcessedTypes; + NativeSession &Session; + InputFile &Input; + +public: + PDBSymbolHandler(PDBImporter &Importer, + DenseMap &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; + } + + 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(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)); + } +} + +void PDBImporter::import(const COFFObjectFile &TheBinary) { + // 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 (llvm::sys::fs::exists(PDBFilePath)) { + loadDataFromPDB(PDBFilePath.str()); + } else { + // Usualy the PDB files will be generated on a different machine, + // so the location read from the debug directory wont be up to date. At + // first try to find it in the current `.` dir. + auto PDBFileNameOnly = PDBFilePath.substr(PDBFilePath.find_last_of('\\') + + 1); + if (llvm::sys::fs::exists(PDBFileNameOnly.str())) + loadDataFromPDB(PDBFileNameOnly.str()); + } + } 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 { + // 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."); + } + } + + 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(); + + 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 Qualifiers{ Qualifier::createPointer(PointerSize) }; + QualifiedType TheUnderlyingType(*ReferencedTypeFromModel, Qualifiers); + + auto TheTypeTypeDef = cast(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(); + + 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 Qualifiers{ Qualifier::createArray(ArraySize) }; + QualifiedType TheUnderlyingType(*ElementTypeFromModel, Qualifiers); + + auto TheTypeTypeDef = cast(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(); + TypeIndex ReferencedType = Modifier.getModifiedType(); + + auto ReferencedTypeFromModel = getModelTypeForIndex(ReferencedType); + if (!ReferencedTypeFromModel) { + revng_log(DILogger, + "LF_MODIFIER: Unknown referenced type " + << ReferencedType.getIndex()); + } else { + std::vector 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(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(); + TypeTypedef->OriginalName = Class.getName(); + + using Values = model::PrimitiveTypeKind::Values; + QualifiedType TheUnderlyingType(Model->getPrimitiveType(Values::Void, 0), + {}); + auto TheTypeTypeDef = cast(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)) { + auto NewType = makeType(); + NewType->OriginalName = Class.getName(); + auto Struct = cast(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 posible? + // Trigers: + // `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.count(FieldsTypeIndex)) { + auto &TheFunctions = InProgressFunctionMemberTypes[FieldsTypeIndex]; + for (auto &Function : TheFunctions) { + TypeIndex FnTypeIndex = Function.getType(); + if (not InProgressConcreteFunctionMemberTypes.count(FnTypeIndex)) + 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(); + auto TypeFunction = cast(NewType.get()); + TypeFunction->ABI = Model->DefaultABI; + + QualifiedType TheReturnType(*ReferencedTypeFromModel, {}); + TypeFunction->ReturnType = TheReturnType; + + TypeIndex ArgListTyIndex = MemberFunction.getArgumentList(); + revng_assert(InProgressArgumentsTypes.count(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)) { + 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(); + 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(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::ClrCall: + case CallingConvention::NearPascal: + case CallingConvention::NearVector: + return model::ABI::Microsoft_x86_64; + 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(); + auto TypeFunction = cast(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(); + 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(); + TypeTypedef->OriginalName = Union.getName().str(); + + auto TheTypeTypeDef = cast(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. + uint64_t Offset = Field.getFieldOffset(); + 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(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 thre is an ongoing review that should +// implement this. +static std::optional 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 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(); + auto TheTypeTypeDef = cast(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(); + auto TheTypeTypeDef = cast(TypeTypedef.get()); + std::vector 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> +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) { + // If it is not in the .idata already, we assume it is a static symbol. + if (not Model->ImportedDynamicFunctions.count(Proc.Name.str())) { + uint64_t FunctionVirtualAddress = Session + .getRVAFromSectOffset(Proc.Segment, + Proc.CodeOffset); + // Relocate the symbol. + MetaAddress FunctionAddress = ImageBase + FunctionVirtualAddress; + + if (not Model->Functions.count(FunctionAddress)) { + model::Function &Function = Model->Functions[FunctionAddress]; + Function.OriginalName = Proc.Name; + TypeIndex FunctionTypeIndex = Proc.FunctionType; + if (ProcessedTypes.count(FunctionTypeIndex)) { + model::QualifiedType ThePrototype(ProcessedTypes[FunctionTypeIndex], + {}); + Function.Prototype = ThePrototype.UnqualifiedType; + } + } else { + auto It = Model->Functions.find(FunctionAddress); + TypeIndex FunctionTypeIndex = Proc.FunctionType; + if (ProcessedTypes.count(FunctionTypeIndex)) { + model::QualifiedType ThePrototype(ProcessedTypes[FunctionTypeIndex], + {}); + It->Prototype = ThePrototype.UnqualifiedType; + } + } + } + + // TODO: Handle Imported functions. + + return Error::success(); +} diff --git a/lib/Model/Type.cpp b/lib/Model/Type.cpp index 53e822730..819eb5d6f 100644 --- a/lib/Model/Type.cpp +++ b/lib/Model/Type.cpp @@ -407,8 +407,13 @@ isValidPrimitiveSize(PrimitiveTypeKind::Values PrimKind, uint8_t BS) { case PrimitiveTypeKind::Signed: return BS == 1 or BS == 2 or BS == 4 or BS == 8 or BS == 16; + // NOTE: We are supporting floats that are 10 bytes long, since we found such + // cases in some PDB files by using VS on Windows platforms. The source code + // of those cases could be written in some language other than C/C++ (probably + // Swift). We faced some struct fields by using this (10b long float) type, so + // by ignoring it we would not have accurate layout for the structs. case PrimitiveTypeKind::Float: - return BS == 2 or BS == 4 or BS == 8 or BS == 12 or BS == 16; + return BS == 2 or BS == 4 or BS == 8 or BS == 10 or BS == 12 or BS == 16; default: revng_abort(); @@ -1027,10 +1032,11 @@ verifyImpl(VerifyHelper &VH, const StructType *T) { if (NextFieldIt != FieldEnd) { // If this field is not the last, check that it does not overlap with the // following field. - if (FieldEndOffset > NextFieldIt->Offset) + if (FieldEndOffset > NextFieldIt->Offset) { rc_return VH.fail("Field " + Twine(Index + 1) + " overlaps with the next one", *T); + } } else if (FieldEndOffset > T->Size) { // Otherwise, if this field is the last, check that it's not larger than // size. @@ -1097,31 +1103,31 @@ verifyImpl(VerifyHelper &VH, const CABIFunctionType *T) { rc_return VH.fail(); if (T->ABI == model::ABI::Invalid) - rc_return VH.fail(); + rc_return VH.fail("An invalid ABI", *T); for (auto &Group : llvm::enumerate(T->Arguments)) { auto &Argument = Group.value(); uint64_t ArgPos = Group.index(); if (not Argument.CustomName.verify(VH)) - rc_return VH.fail(); + rc_return VH.fail("An argument has invalid CustomName", *T); if (Argument.Index != ArgPos) - rc_return VH.fail(); + rc_return VH.fail("An argument has invalid index", *T); if (not rc_recur Argument.Type.verify(VH)) - rc_return VH.fail(); + rc_return VH.fail("An argument has invalid type", *T); VoidConstResult VoidConst = isVoidConst(&Argument.Type); if (VoidConst.IsVoid) { // If we have a void argument it must be the only one, and the function // cannot be vararg. if (T->Arguments.size() > 1) - rc_return VH.fail(); + rc_return VH.fail("More than 1 void argument", *T); // Cannot have const-qualified void as argument. if (VoidConst.IsConst) - rc_return VH.fail(); + rc_return VH.fail("Cannot have const void argument", *T); } } @@ -1260,9 +1266,10 @@ RecursiveCoroutine QualifiedType::verify(VerifyHelper &VH) const { if (Qualifier::isPointer(Q)) { // Don't proceed the verification, just make sure the pointer is either - // 32- or 64-bits + // 32- or 64-bit rc_return VH.maybeFail(Q.Size == 4 or Q.Size == 8, - "Only 32-bit and 64-bit pointers are currently " + "Only 32-bit and 64-bit pointers " + "are currently " "supported", *this); @@ -1395,7 +1402,7 @@ RecursiveCoroutine UnionField::verify(VerifyHelper &VH) const { // Aggregated fields cannot be zero-sized fields auto MaybeSize = rc_recur Type.size(VH); if (not MaybeSize) - rc_return VH.fail("Aggregate field is zero-sized"); + rc_return VH.fail("Aggregate field is zero-sized", Type); rc_return VH.maybeFail(CustomName.verify(VH)); } diff --git a/lib/Pipes/ImportBinaryPipe.cpp b/lib/Pipes/ImportBinaryPipe.cpp index dc9e06f7a..d5ce5f045 100644 --- a/lib/Pipes/ImportBinaryPipe.cpp +++ b/lib/Pipes/ImportBinaryPipe.cpp @@ -8,7 +8,7 @@ #include "revng/Model/Binary.h" #include "revng/Model/Importer/Binary/BinaryImporter.h" #include "revng/Model/Importer/Binary/BinaryImporterOptions.h" -#include "revng/Model/Importer/Dwarf/DwarfImporter.h" +#include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Pipeline/RegisterPipe.h" #include "revng/Pipes/ImportBinaryPipe.h" #include "revng/Pipes/ModelGlobal.h" diff --git a/tests/unit/ModelType.cpp b/tests/unit/ModelType.cpp index cab8a53bf..a78e69b91 100644 --- a/tests/unit/ModelType.cpp +++ b/tests/unit/ModelType.cpp @@ -58,6 +58,7 @@ BOOST_AUTO_TEST_CASE(PrimitiveTypes) { revng_check(PrimitiveType(PrimitiveTypeKind::Float, 2).verify(true)); revng_check(PrimitiveType(PrimitiveTypeKind::Float, 4).verify(true)); revng_check(PrimitiveType(PrimitiveTypeKind::Float, 8).verify(true)); + revng_check(PrimitiveType(PrimitiveTypeKind::Float, 10).verify(true)); revng_check(PrimitiveType(PrimitiveTypeKind::Float, 16).verify(true)); auto Unsigned = PrimitiveType(PrimitiveTypeKind::Unsigned, 1); @@ -87,8 +88,8 @@ BOOST_AUTO_TEST_CASE(PrimitiveTypes) { using namespace std::string_literals; Float = PrimitiveType(PrimitiveTypeKind::Float, ByteSize); - if (ByteSize == 2 or ByteSize == 4 or ByteSize == 8 or ByteSize == 12 - or ByteSize == 16) { + if (ByteSize == 2 or ByteSize == 4 or ByteSize == 8 or ByteSize == 10 + or ByteSize == 12 or ByteSize == 16) { revng_check(Float.verify(true)); revng_check(Float.name() == ("float" + Twine(8 * ByteSize) + "_t").str()); } else { diff --git a/tools/model/import/binary/Main.cpp b/tools/model/import/binary/Main.cpp index 37204192a..e0307bb07 100644 --- a/tools/model/import/binary/Main.cpp +++ b/tools/model/import/binary/Main.cpp @@ -11,7 +11,7 @@ #include "revng/Model/Importer/Binary/BinaryImporter.h" #include "revng/Model/Importer/Binary/BinaryImporterOptions.h" -#include "revng/Model/Importer/Dwarf/DwarfImporter.h" +#include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Model/ToolHelpers.h" #include "revng/Support/CommandLine.h" #include "revng/Support/InitRevng.h" diff --git a/tools/model/import/dwarf/CMakeLists.txt b/tools/model/import/dwarf/CMakeLists.txt index 6d3cf6327..a45f46958 100644 --- a/tools/model/import/dwarf/CMakeLists.txt +++ b/tools/model/import/dwarf/CMakeLists.txt @@ -5,4 +5,4 @@ revng_add_executable(revng-model-import-dwarf Main.cpp) target_link_libraries(revng-model-import-dwarf revngModel - revngModelImporterDwarf) + revngModelImporterDebugInfo) diff --git a/tools/model/import/dwarf/Main.cpp b/tools/model/import/dwarf/Main.cpp index b8bd52354..9d7556da0 100644 --- a/tools/model/import/dwarf/Main.cpp +++ b/tools/model/import/dwarf/Main.cpp @@ -9,7 +9,7 @@ #include "llvm/Support/CommandLine.h" -#include "revng/Model/Importer/Dwarf/DwarfImporter.h" +#include "revng/Model/Importer/DebugInfo/DwarfImporter.h" #include "revng/Model/ToolHelpers.h" #include "revng/Support/InitRevng.h"