//===- PDBTypeServerHandler.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Handles CodeView LF_TYPESERVER2 records by attempting to locate a matching // PDB file, then loading the PDB file and visiting all types from the // referenced PDB using the original supplied visitor. // // The net effect of this is that when visiting a PDB containing a TypeServer // record, the TypeServer record is "replaced" with all of the records in // the referenced PDB file. If a single instance of PDBTypeServerHandler // encounters the same TypeServer multiple times (for example reusing one // PDBTypeServerHandler across multiple visitations of distinct object files or // PDB files), PDBTypeServerHandler will optionally revisit all the records // again, or simply consume the record and do nothing. //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; static void ignoreErrors(Error EC) { llvm::handleAllErrors(std::move(EC), [&](ErrorInfoBase &EIB) {}); } PDBTypeServerHandler::PDBTypeServerHandler(bool RevisitAlways) : RevisitAlways(RevisitAlways) {} void PDBTypeServerHandler::addSearchPath(StringRef Path) { if (Path.empty() || !sys::fs::is_directory(Path)) return; SearchPaths.push_back(Path); } Expected PDBTypeServerHandler::handleInternal(PDBFile &File, TypeVisitorCallbacks &Callbacks) { auto ExpectedTpi = File.getPDBTpiStream(); if (!ExpectedTpi) return ExpectedTpi.takeError(); CVTypeVisitor Visitor(Callbacks); if (auto EC = Visitor.visitTypeStream(ExpectedTpi->types(nullptr))) return std::move(EC); return true; } Expected PDBTypeServerHandler::handle(TypeServer2Record &TS, TypeVisitorCallbacks &Callbacks) { if (Session) { // If we've already handled this TypeServer and we only want to handle each // TypeServer once, consume the record without doing anything. if (!RevisitAlways) return true; return handleInternal(Session->getPDBFile(), Callbacks); } StringRef File = sys::path::filename(TS.Name); if (File.empty()) return make_error( cv_error_code::corrupt_record, "TypeServer2Record does not contain filename!"); for (auto Path : SearchPaths) { sys::path::append(Path, File); if (!sys::fs::exists(Path)) continue; std::unique_ptr ThisSession; if (auto EC = loadDataForPDB(PDB_ReaderType::Native, Path, ThisSession)) { // It is not an error if this PDB fails to load, it just means that it // doesn't match and we should continue searching. ignoreErrors(std::move(EC)); continue; } std::unique_ptr NS( static_cast(ThisSession.release())); PDBFile &File = NS->getPDBFile(); auto ExpectedInfo = File.getPDBInfoStream(); // All PDB Files should have an Info stream. if (!ExpectedInfo) return ExpectedInfo.takeError(); // Just because a file with a matching name was found and it was an actual // PDB file doesn't mean it matches. For it to match the InfoStream's GUID // must match the GUID specified in the TypeServer2 record. ArrayRef GuidBytes(ExpectedInfo->getGuid().Guid); StringRef GuidStr(reinterpret_cast(GuidBytes.begin()), GuidBytes.size()); if (GuidStr != TS.Guid) continue; Session = std::move(NS); return handleInternal(File, Callbacks); } // We couldn't find a matching PDB, so let it be handled by someone else. return false; }