594 lines · cpp
1//===- InputFile.cpp ------------------------------------------ *- C++ --*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/PDB/Native/InputFile.h"10 11#include "llvm/ADT/StringExtras.h"12#include "llvm/BinaryFormat/Magic.h"13#include "llvm/DebugInfo/CodeView/CodeView.h"14#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"15#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"16#include "llvm/DebugInfo/MSF/MappedBlockStream.h"17#include "llvm/DebugInfo/PDB/Native/DbiStream.h"18#include "llvm/DebugInfo/PDB/Native/FormatUtil.h"19#include "llvm/DebugInfo/PDB/Native/LinePrinter.h"20#include "llvm/DebugInfo/PDB/Native/NativeSession.h"21#include "llvm/DebugInfo/PDB/Native/PDBFile.h"22#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"23#include "llvm/DebugInfo/PDB/Native/RawError.h"24#include "llvm/DebugInfo/PDB/Native/TpiStream.h"25#include "llvm/DebugInfo/PDB/PDB.h"26#include "llvm/Object/COFF.h"27#include "llvm/Support/FileSystem.h"28#include "llvm/Support/FormatVariadic.h"29 30using namespace llvm;31using namespace llvm::codeview;32using namespace llvm::object;33using namespace llvm::pdb;34 35InputFile::InputFile() = default;36InputFile::~InputFile() = default;37 38Expected<ModuleDebugStreamRef>39llvm::pdb::getModuleDebugStream(PDBFile &File, StringRef &ModuleName,40 uint32_t Index) {41 Expected<DbiStream &> DbiOrErr = File.getPDBDbiStream();42 if (!DbiOrErr)43 return DbiOrErr.takeError();44 DbiStream &Dbi = *DbiOrErr;45 const auto &Modules = Dbi.modules();46 if (Index >= Modules.getModuleCount())47 return make_error<RawError>(raw_error_code::index_out_of_bounds,48 "Invalid module index");49 50 auto Modi = Modules.getModuleDescriptor(Index);51 52 ModuleName = Modi.getModuleName();53 54 uint16_t ModiStream = Modi.getModuleStreamIndex();55 if (ModiStream == kInvalidStreamIndex)56 return make_error<RawError>(raw_error_code::no_stream,57 "Module stream not present");58 59 auto ModStreamData = File.createIndexedStream(ModiStream);60 61 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));62 if (auto EC = ModS.reload())63 return make_error<RawError>(raw_error_code::corrupt_file,64 "Invalid module stream");65 66 return std::move(ModS);67}68 69Expected<ModuleDebugStreamRef> llvm::pdb::getModuleDebugStream(PDBFile &File,70 uint32_t Index) {71 Expected<DbiStream &> DbiOrErr = File.getPDBDbiStream();72 if (!DbiOrErr)73 return DbiOrErr.takeError();74 DbiStream &Dbi = *DbiOrErr;75 const auto &Modules = Dbi.modules();76 auto Modi = Modules.getModuleDescriptor(Index);77 78 uint16_t ModiStream = Modi.getModuleStreamIndex();79 if (ModiStream == kInvalidStreamIndex)80 return make_error<RawError>(raw_error_code::no_stream,81 "Module stream not present");82 83 auto ModStreamData = File.createIndexedStream(ModiStream);84 85 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));86 if (Error Err = ModS.reload())87 return make_error<RawError>(raw_error_code::corrupt_file,88 "Invalid module stream");89 90 return std::move(ModS);91}92 93static inline bool isCodeViewDebugSubsection(object::SectionRef Section,94 StringRef Name,95 BinaryStreamReader &Reader) {96 if (Expected<StringRef> NameOrErr = Section.getName()) {97 if (*NameOrErr != Name)98 return false;99 } else {100 consumeError(NameOrErr.takeError());101 return false;102 }103 104 Expected<StringRef> ContentsOrErr = Section.getContents();105 if (!ContentsOrErr) {106 consumeError(ContentsOrErr.takeError());107 return false;108 }109 110 Reader = BinaryStreamReader(*ContentsOrErr, llvm::endianness::little);111 uint32_t Magic;112 if (Reader.bytesRemaining() < sizeof(uint32_t))113 return false;114 cantFail(Reader.readInteger(Magic));115 if (Magic != COFF::DEBUG_SECTION_MAGIC)116 return false;117 return true;118}119 120static inline bool isDebugSSection(object::SectionRef Section,121 DebugSubsectionArray &Subsections) {122 BinaryStreamReader Reader;123 if (!isCodeViewDebugSubsection(Section, ".debug$S", Reader))124 return false;125 126 cantFail(Reader.readArray(Subsections, Reader.bytesRemaining()));127 return true;128}129 130static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {131 BinaryStreamReader Reader;132 if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) &&133 !isCodeViewDebugSubsection(Section, ".debug$P", Reader))134 return false;135 cantFail(Reader.readArray(Types, Reader.bytesRemaining()));136 return true;137}138 139static std::string formatChecksumKind(FileChecksumKind Kind) {140 switch (Kind) {141 RETURN_CASE(FileChecksumKind, None, "None");142 RETURN_CASE(FileChecksumKind, MD5, "MD5");143 RETURN_CASE(FileChecksumKind, SHA1, "SHA-1");144 RETURN_CASE(FileChecksumKind, SHA256, "SHA-256");145 }146 return formatUnknownEnum(Kind);147}148 149template <typename... Args>150static void formatInternal(LinePrinter &Printer, bool Append, Args &&...args) {151 if (Append)152 Printer.format(std::forward<Args>(args)...);153 else154 Printer.formatLine(std::forward<Args>(args)...);155}156 157SymbolGroup::SymbolGroup(InputFile *File, uint32_t GroupIndex) : File(File) {158 if (!File)159 return;160 161 if (File->isPdb())162 initializeForPdb(GroupIndex);163 else {164 Name = ".debug$S";165 uint32_t I = 0;166 for (const auto &S : File->obj().sections()) {167 DebugSubsectionArray SS;168 if (!isDebugSSection(S, SS))169 continue;170 171 if (!SC.hasChecksums() || !SC.hasStrings())172 SC.initialize(SS);173 174 if (I == GroupIndex)175 Subsections = SS;176 177 if (SC.hasChecksums() && SC.hasStrings())178 break;179 }180 rebuildChecksumMap();181 }182}183 184StringRef SymbolGroup::name() const { return Name; }185 186void SymbolGroup::updateDebugS(const codeview::DebugSubsectionArray &SS) {187 Subsections = SS;188}189 190void SymbolGroup::updatePdbModi(uint32_t Modi) { initializeForPdb(Modi); }191 192void SymbolGroup::initializeForPdb(uint32_t Modi) {193 assert(File && File->isPdb());194 195 // PDB always uses the same string table, but each module has its own196 // checksums. So we only set the strings if they're not already set.197 if (!SC.hasStrings()) {198 auto StringTable = File->pdb().getStringTable();199 if (StringTable)200 SC.setStrings(StringTable->getStringTable());201 else202 consumeError(StringTable.takeError());203 }204 205 SC.resetChecksums();206 auto MDS = getModuleDebugStream(File->pdb(), Name, Modi);207 if (!MDS) {208 consumeError(MDS.takeError());209 return;210 }211 212 DebugStream = std::make_shared<ModuleDebugStreamRef>(std::move(*MDS));213 Subsections = DebugStream->getSubsectionsArray();214 SC.initialize(Subsections);215 rebuildChecksumMap();216}217 218void SymbolGroup::rebuildChecksumMap() {219 if (!SC.hasChecksums())220 return;221 222 for (const auto &Entry : SC.checksums()) {223 auto S = SC.strings().getString(Entry.FileNameOffset);224 if (!S)225 continue;226 ChecksumsByFile[*S] = Entry;227 }228}229 230const ModuleDebugStreamRef &SymbolGroup::getPdbModuleStream() const {231 assert(File && File->isPdb() && DebugStream);232 return *DebugStream;233}234 235Expected<StringRef> SymbolGroup::getNameFromStringTable(uint32_t Offset) const {236 return SC.strings().getString(Offset);237}238 239Expected<StringRef> SymbolGroup::getNameFromChecksums(uint32_t Offset) const {240 StringRef Name;241 if (!SC.hasChecksums()) {242 return std::move(Name);243 }244 245 auto Iter = SC.checksums().getArray().at(Offset);246 if (Iter == SC.checksums().getArray().end()) {247 return std::move(Name);248 }249 250 uint32_t FO = Iter->FileNameOffset;251 auto ExpectedFile = getNameFromStringTable(FO);252 if (!ExpectedFile) {253 return std::move(Name);254 }255 256 return *ExpectedFile;257}258 259void SymbolGroup::formatFromFileName(LinePrinter &Printer, StringRef File,260 bool Append) const {261 auto FC = ChecksumsByFile.find(File);262 if (FC == ChecksumsByFile.end()) {263 formatInternal(Printer, Append, "- (no checksum) {0}", File);264 return;265 }266 267 formatInternal(Printer, Append, "- ({0}: {1}) {2}",268 formatChecksumKind(FC->getValue().Kind),269 toHex(FC->getValue().Checksum), File);270}271 272void SymbolGroup::formatFromChecksumsOffset(LinePrinter &Printer,273 uint32_t Offset,274 bool Append) const {275 if (!SC.hasChecksums()) {276 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);277 return;278 }279 280 auto Iter = SC.checksums().getArray().at(Offset);281 if (Iter == SC.checksums().getArray().end()) {282 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);283 return;284 }285 286 uint32_t FO = Iter->FileNameOffset;287 auto ExpectedFile = getNameFromStringTable(FO);288 if (!ExpectedFile) {289 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);290 consumeError(ExpectedFile.takeError());291 return;292 }293 if (Iter->Kind == FileChecksumKind::None) {294 formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile);295 } else {296 formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile,297 formatChecksumKind(Iter->Kind), toHex(Iter->Checksum));298 }299}300 301Expected<InputFile> InputFile::open(StringRef Path, bool AllowUnknownFile) {302 InputFile IF;303 if (!llvm::sys::fs::exists(Path))304 return make_error<StringError>(formatv("File {0} not found", Path),305 inconvertibleErrorCode());306 307 file_magic Magic;308 if (auto EC = identify_magic(Path, Magic))309 return make_error<StringError>(310 formatv("Unable to identify file type for file {0}", Path), EC);311 312 if (Magic == file_magic::coff_object) {313 Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Path);314 if (!BinaryOrErr)315 return BinaryOrErr.takeError();316 317 IF.CoffObject = std::move(*BinaryOrErr);318 IF.PdbOrObj = llvm::cast<COFFObjectFile>(IF.CoffObject.getBinary());319 return std::move(IF);320 }321 322 if (Magic == file_magic::pdb) {323 std::unique_ptr<IPDBSession> Session;324 if (auto Err = loadDataForPDB(PDB_ReaderType::Native, Path, Session))325 return std::move(Err);326 327 IF.PdbSession.reset(static_cast<NativeSession *>(Session.release()));328 IF.PdbOrObj = &IF.PdbSession->getPDBFile();329 330 return std::move(IF);331 }332 333 if (!AllowUnknownFile)334 return make_error<StringError>(335 formatv("File {0} is not a supported file type", Path),336 inconvertibleErrorCode());337 338 auto Result = MemoryBuffer::getFile(Path, /*IsText=*/false,339 /*RequiresNullTerminator=*/false);340 if (!Result)341 return make_error<StringError>(342 formatv("File {0} could not be opened", Path), Result.getError());343 344 IF.UnknownFile = std::move(*Result);345 IF.PdbOrObj = IF.UnknownFile.get();346 return std::move(IF);347}348 349PDBFile &InputFile::pdb() {350 assert(isPdb());351 return *cast<PDBFile *>(PdbOrObj);352}353 354const PDBFile &InputFile::pdb() const {355 assert(isPdb());356 return *cast<PDBFile *>(PdbOrObj);357}358 359object::COFFObjectFile &InputFile::obj() {360 assert(isObj());361 return *cast<object::COFFObjectFile *>(PdbOrObj);362}363 364const object::COFFObjectFile &InputFile::obj() const {365 assert(isObj());366 return *cast<object::COFFObjectFile *>(PdbOrObj);367}368 369MemoryBuffer &InputFile::unknown() {370 assert(isUnknown());371 return *cast<MemoryBuffer *>(PdbOrObj);372}373 374const MemoryBuffer &InputFile::unknown() const {375 assert(isUnknown());376 return *cast<MemoryBuffer *>(PdbOrObj);377}378 379StringRef InputFile::getFilePath() const {380 if (isPdb())381 return pdb().getFilePath();382 if (isObj())383 return obj().getFileName();384 assert(isUnknown());385 return unknown().getBufferIdentifier();386}387 388bool InputFile::hasTypes() const {389 if (isPdb())390 return pdb().hasPDBTpiStream();391 392 for (const auto &Section : obj().sections()) {393 CVTypeArray Types;394 if (isDebugTSection(Section, Types))395 return true;396 }397 return false;398}399 400bool InputFile::hasIds() const {401 if (isObj())402 return false;403 return pdb().hasPDBIpiStream();404}405 406bool InputFile::isPdb() const { return isa<PDBFile *>(PdbOrObj); }407 408bool InputFile::isObj() const {409 return isa<object::COFFObjectFile *>(PdbOrObj);410}411 412bool InputFile::isUnknown() const { return isa<MemoryBuffer *>(PdbOrObj); }413 414codeview::LazyRandomTypeCollection &415InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) {416 if (Types && Kind == kTypes)417 return *Types;418 if (Ids && Kind == kIds)419 return *Ids;420 421 if (Kind == kIds) {422 assert(isPdb() && pdb().hasPDBIpiStream());423 }424 425 // If the collection was already initialized, we should have just returned it426 // in step 1.427 if (isPdb()) {428 TypeCollectionPtr &Collection = (Kind == kIds) ? Ids : Types;429 auto &Stream = cantFail((Kind == kIds) ? pdb().getPDBIpiStream()430 : pdb().getPDBTpiStream());431 432 auto &Array = Stream.typeArray();433 uint32_t Count = Stream.getNumTypeRecords();434 auto Offsets = Stream.getTypeIndexOffsets();435 Collection =436 std::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets);437 return *Collection;438 }439 440 assert(isObj());441 assert(Kind == kTypes);442 assert(!Types);443 444 for (const auto &Section : obj().sections()) {445 CVTypeArray Records;446 if (!isDebugTSection(Section, Records))447 continue;448 449 Types = std::make_unique<LazyRandomTypeCollection>(Records, 100);450 return *Types;451 }452 453 Types = std::make_unique<LazyRandomTypeCollection>(100);454 return *Types;455}456 457codeview::LazyRandomTypeCollection &InputFile::types() {458 return getOrCreateTypeCollection(kTypes);459}460 461codeview::LazyRandomTypeCollection &InputFile::ids() {462 // Object files have only one type stream that contains both types and ids.463 // Similarly, some PDBs don't contain an IPI stream, and for those both types464 // and IDs are in the same stream.465 if (isObj() || !pdb().hasPDBIpiStream())466 return types();467 468 return getOrCreateTypeCollection(kIds);469}470 471iterator_range<SymbolGroupIterator> InputFile::symbol_groups() {472 return make_range<SymbolGroupIterator>(symbol_groups_begin(),473 symbol_groups_end());474}475 476SymbolGroupIterator InputFile::symbol_groups_begin() {477 return SymbolGroupIterator(*this);478}479 480SymbolGroupIterator InputFile::symbol_groups_end() {481 return SymbolGroupIterator();482}483 484SymbolGroupIterator::SymbolGroupIterator() : Value(nullptr) {}485 486SymbolGroupIterator::SymbolGroupIterator(InputFile &File) : Value(&File) {487 if (File.isObj()) {488 SectionIter = File.obj().section_begin();489 scanToNextDebugS();490 }491}492 493bool SymbolGroupIterator::operator==(const SymbolGroupIterator &R) const {494 bool E = isEnd();495 bool RE = R.isEnd();496 if (E || RE)497 return E == RE;498 499 if (Value.File != R.Value.File)500 return false;501 return Index == R.Index;502}503 504const SymbolGroup &SymbolGroupIterator::operator*() const {505 assert(!isEnd());506 return Value;507}508SymbolGroup &SymbolGroupIterator::operator*() {509 assert(!isEnd());510 return Value;511}512 513SymbolGroupIterator &SymbolGroupIterator::operator++() {514 assert(Value.File && !isEnd());515 ++Index;516 if (isEnd())517 return *this;518 519 if (Value.File->isPdb()) {520 Value.updatePdbModi(Index);521 return *this;522 }523 524 scanToNextDebugS();525 return *this;526}527 528void SymbolGroupIterator::scanToNextDebugS() {529 assert(SectionIter);530 auto End = Value.File->obj().section_end();531 auto &Iter = *SectionIter;532 assert(!isEnd());533 534 while (++Iter != End) {535 DebugSubsectionArray SS;536 SectionRef SR = *Iter;537 if (!isDebugSSection(SR, SS))538 continue;539 540 Value.updateDebugS(SS);541 return;542 }543}544 545bool SymbolGroupIterator::isEnd() const {546 if (!Value.File)547 return true;548 if (Value.File->isPdb()) {549 DbiStream &Dbi = cantFail(Value.File->pdb().getPDBDbiStream());550 uint32_t Count = Dbi.modules().getModuleCount();551 assert(Index <= Count);552 return Index == Count;553 }554 555 assert(SectionIter);556 return *SectionIter == Value.File->obj().section_end();557}558 559static bool isMyCode(const SymbolGroup &Group) {560 if (Group.getFile().isObj())561 return true;562 563 StringRef Name = Group.name();564 if (Name.starts_with("Import:"))565 return false;566 if (Name.ends_with_insensitive(".dll"))567 return false;568 if (Name.equals_insensitive("* linker *"))569 return false;570 if (Name.starts_with_insensitive("f:\\binaries\\Intermediate\\vctools"))571 return false;572 if (Name.starts_with_insensitive("f:\\dd\\vctools\\crt"))573 return false;574 return true;575}576 577bool llvm::pdb::shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group,578 const FilterOptions &Filters) {579 if (Filters.JustMyCode && !isMyCode(Group))580 return false;581 582 // If the arg was not specified on the command line, always dump all modules.583 if (!Filters.DumpModi)584 return true;585 586 // Otherwise, only dump if this is the same module specified.587 return (Filters.DumpModi == Idx);588}589llvm::pdb::InputFile::InputFile(PDBFile *Pdb) { PdbOrObj = Pdb; }590 591llvm::pdb::InputFile::InputFile(object::COFFObjectFile *Obj) { PdbOrObj = Obj; }592 593llvm::pdb::InputFile::InputFile(MemoryBuffer *Buffer) { PdbOrObj = Buffer; }594