Index: COFF/InputFiles.h =================================================================== --- COFF/InputFiles.h +++ COFF/InputFiles.h @@ -14,6 +14,7 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" @@ -124,6 +125,9 @@ // Returns the underying COFF file. COFFObjectFile *getCOFFObj() { return COFFObj.get(); } + // Whether the .obj was already merged into the final PDB or not + bool wasProcessedForPDB() const { return !!ModuleDBI; } + static std::vector Instances; // Flags in the absolute @feat.00 symbol if it is present. These usually @@ -144,6 +148,10 @@ // if we are not producing a PDB. llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr; + // If the .obj represents a precompiled types type map, we need the signature + // through the following record + llvm::Optional EndPrecomp; + private: void initializeChunks(); void initializeSymbols(); Index: COFF/PDB.cpp =================================================================== --- COFF/PDB.cpp +++ COFF/PDB.cpp @@ -48,6 +48,7 @@ #include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" @@ -79,6 +80,7 @@ SmallVector TPIMap; SmallVector IPIMap; bool IsTypeServerMap = false; + bool IsPrecompiledTypeMap = false; }; class PDBLinker { @@ -102,7 +104,7 @@ void addObjectsToPDB(); /// Link CodeView from a single object file into the PDB. - void addObjFile(ObjFile *File); + void addObjFile(ObjFile *File, CVIndexMap *CV = nullptr); /// Produce a mapping from the type and item indices used in the object /// file to those in the destination PDB. @@ -115,12 +117,19 @@ /// If the object does not use a type server PDB (compiled with /Z7), we merge /// all the type and item records from the .debug$S stream and fill in the /// caller-provided ObjectIndexMap. - Expected mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap); + Expected mergeDebugT(ObjFile *File, + CVIndexMap *ObjectIndexMap); - Expected maybeMergeTypeServerPDB(ObjFile *File, - TypeServer2Record &TS); + Expected maybeMergeTypeServerPDB(ObjFile *File, + const CVType &FirstType); + Expected + mergeInPrecompHeaderOBJ(ObjFile *File, const CVType &FirstType, + CVIndexMap *ObjectIndexMap); + + Expected + PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp); + /// Add the section map and section contributions to the PDB. void addSections(ArrayRef OutputSections, ArrayRef SectionTable); @@ -163,11 +172,14 @@ /// Type index mappings of type server PDBs that we've loaded so far. std::map TypeServerIndexMappings; + /// Type index mappings of precompiled type type map that we've loaded so far. + std::map PrecompTypeIndexMappings; + /// List of TypeServer PDBs which cannot be loaded. /// Cached to prevent repeated load attempts. std::set MissingTypeServerPDBs; }; -} +} // namespace static SectionChunk *findByName(ArrayRef Sections, StringRef Name) { @@ -242,28 +254,71 @@ }); } -static Optional -maybeReadTypeServerRecord(CVTypeArray &Types) { - auto I = Types.begin(); - if (I == Types.end()) - return None; - const CVType &Type = *I; - if (Type.kind() != LF_TYPESERVER2) - return None; - TypeServer2Record TS; - if (auto EC = TypeDeserializer::deserializeAs(const_cast(Type), TS)) - fatal("error reading type server record: " + toString(std::move(EC))); - return std::move(TS); +static uint32_t extractPCHSignature(ObjFile *File) { + for (SectionChunk *DbgC : File->getDebugChunks()) { + if (DbgC->getSectionName() != ".debug$S") + continue; + + ArrayRef Contents = + consumeDebugMagic(DbgC->getContents(), ".debug$S"); + DebugSubsectionArray Subsections; + BinaryStreamReader Reader(Contents, support::little); + ExitOnErr(Reader.readArray(Subsections, Contents.size())); + + for (const DebugSubsectionRecord &SS : Subsections) { + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + // If it's there, the S_OBJNAME record shall come first in the stream. + Expected Sym = readSymbolFromStream(SS.getRecordData(), 0); + if (!Sym) { + consumeError(Sym.takeError()); + continue; + } + if (auto ObjName = + SymbolDeserializer::deserializeAs(Sym.get())) + return ObjName->Signature; + } + } + return 0; } -Expected PDBLinker::mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap) { +Expected +PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { ScopedTimer T(TypeMergingTimer); + bool IsPrecompiledHeader = false; + ArrayRef Data = getDebugSection(File, ".debug$T"); + if (Data.empty()) { + // Try again, Microsoft precompiled headers use .debug$P instead of + // .debug$T + Data = getDebugSection(File, ".debug$P"); + IsPrecompiledHeader = true; + } if (Data.empty()) - return ObjectIndexMap; + return *ObjectIndexMap; // no debug info + // Precompiled headers .objs need to save the index map for further reference + // by other .objs which use the precompiled headers. + if (IsPrecompiledHeader) { + uint32_t PCHSignature = extractPCHSignature(File); + assert(PCHSignature > 0 && + "No signature found for the precompiled headers .obj"); + + if (!ObjectIndexMap->IsPrecompiledTypeMap) { + auto Insertion = + PrecompTypeIndexMappings.insert({PCHSignature, CVIndexMap()}); + ObjectIndexMap = &Insertion.first->second; + + // Mark this map as a precompiled types map. + ObjectIndexMap->IsPrecompiledTypeMap = true; + + assert(Insertion.second && "A precompiled headers .obj with the same " + "signature was already provided!"); + } + } + BinaryByteStream Stream(Data, support::little); CVTypeArray Types; BinaryStreamReader Reader(Stream); @@ -270,13 +325,30 @@ if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal("Reader::readArray failed: " + toString(std::move(EC))); - // Look through type servers. If we've already seen this type server, don't - // merge any type information. - if (Optional TS = maybeReadTypeServerRecord(Types)) - return maybeMergeTypeServerPDB(File, *TS); + auto FirstType = Types.begin(); + if (FirstType == Types.end()) + return *ObjectIndexMap; - // This is a /Z7 object. Fill in the temporary, caller-provided - // ObjectIndexMap. + if (FirstType->kind() == LF_TYPESERVER2) { + // Look through type servers. If we've already seen this type server, + // don't merge any type information. + return maybeMergeTypeServerPDB(File, *FirstType); + } else if (FirstType->kind() == LF_PRECOMP) { + // Drop LF_PRECOMP record from the input stream, as it needs to be replaced + // with the precompiled headers .obj type stream. + Types.setUnderlyingStream( + Types.getUnderlyingStream().drop_front(FirstType->length())); + + // The .obj was compiled with /Z7 /Yu, process the corresponding precompiled + // headers .obj (/Yc) first. Some type indices in the current .obj are + // referencing data in the precompiled headers .obj, so we need both to be + // loaded. + auto E = mergeInPrecompHeaderOBJ(File, *FirstType, ObjectIndexMap); + if (!E) + return E.takeError(); + } + + // Fill in the temporary, caller-provided ObjectIndexMap. if (Config->DebugGHashes) { ArrayRef Hashes; std::vector OwnedHashes; @@ -288,16 +360,19 @@ } if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable, - ObjectIndexMap.TPIMap, Types, Hashes)) + ObjectIndexMap->TPIMap, Types, Hashes, + File->EndPrecomp)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(Err))); } else { - if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable, - ObjectIndexMap.TPIMap, Types)) + if (auto Err = + mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap, + Types, File->EndPrecomp)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(Err))); } - return ObjectIndexMap; + + return *ObjectIndexMap; } static Expected> @@ -332,15 +407,20 @@ return std::move(NS); } -Expected PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, - TypeServer2Record &TS) { - const GUID& TSId = TS.getGuid(); +Expected +PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { + TypeServer2Record TS; + if (auto EC = + TypeDeserializer::deserializeAs(const_cast(FirstType), TS)) + fatal("error reading record: " + toString(std::move(EC))); + + const GUID &TSId = TS.getGuid(); StringRef TSPath = TS.getName(); // First, check if the PDB has previously failed to load. if (MissingTypeServerPDBs.count(TSId)) return make_error( - pdb::generic_error_code::type_server_not_found, TSPath); + pdb::generic_error_code::type_server_not_found, TSPath); // Second, check if we already loaded a PDB with this GUID. Return the type // index mapping if we have it. @@ -361,8 +441,8 @@ StringRef LocalPath = !File->ParentName.empty() ? File->ParentName : File->getName(); SmallString<128> Path = sys::path::parent_path(LocalPath); - sys::path::append( - Path, sys::path::filename(TSPath, sys::path::Style::windows)); + sys::path::append(Path, + sys::path::filename(TSPath, sys::path::Style::windows)); ExpectedSession = tryToLoadPDB(TSId, Path); } if (auto E = ExpectedSession.takeError()) { @@ -394,9 +474,11 @@ auto IpiHashes = GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes); + Optional EndPrecomp; // Merge TPI first, because the IPI stream will reference type indices. - if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap, - ExpectedTpi->typeArray(), TpiHashes)) + if (auto Err = + mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap, + ExpectedTpi->typeArray(), TpiHashes, EndPrecomp)) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err))); // Merge IPI. @@ -419,6 +501,101 @@ return IndexMap; } +Expected +PDBLinker::mergeInPrecompHeaderOBJ(ObjFile *File, const CVType &FirstType, + CVIndexMap *ObjectIndexMap) { + PrecompRecord Precomp; + if (auto EC = TypeDeserializer::deserializeAs(const_cast(FirstType), + Precomp)) + fatal("error reading record: " + toString(std::move(EC))); + + auto E = aquirePrecompObj(File, Precomp); + if (!E) + return E.takeError(); + + const CVIndexMap &PrecompIndexMap = *E; + assert(PrecompIndexMap.IsPrecompiledTypeMap); + + if (PrecompIndexMap.TPIMap.empty()) + return PrecompIndexMap; + + assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); + assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size()); + // Use the previously remapped index map from the precompiled headers. + ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(), + PrecompIndexMap.TPIMap.begin() + + Precomp.getTypesCount()); + return *ObjectIndexMap; +} + +Optional> +findObjByName(StringRef nameOrPath) { + SmallString<128> CurrentPath; + + for (ObjFile *F : ObjFile::Instances) { + CurrentPath = F->getName(); + sys::fs::make_absolute(CurrentPath); + sys::path::native(CurrentPath, sys::path::Style::windows); + + if (CurrentPath.equals_lower(nameOrPath)) + return std::make_tuple(F, CurrentPath.c_str()); + } + return {}; +} + +Expected +PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { + // First, check if we already loaded the precompiled headers .obj with this + // signature. Return the type index mapping if we have it. + auto Insertion = + PrecompTypeIndexMappings.insert({Precomp.getSignature(), CVIndexMap()}); + CVIndexMap &IndexMap = Insertion.first->second; + if (!Insertion.second) + return IndexMap; + + // Mark this map as a precompiled types map. + IndexMap.IsPrecompiledTypeMap = true; + + SmallString<128> PrecompPath = Precomp.getPrecompFilePath(); + sys::fs::make_absolute(PrecompPath); + sys::path::native(PrecompPath, sys::path::Style::windows); + + // link.exe requires that a precompiled headers .obj must always be provided + // on the command-line, even if that's not necessary. + // First, try comparing the whole path if possible + auto PrecompFilePath = findObjByName(PrecompPath); + if (!PrecompFilePath) { + // If the precompiled headers .obj was on a different path, we can still use + // it, although only if its unique on the command-line + StringRef PrecompName = sys::path::filename(PrecompPath); + + PrecompFilePath = findObjByName(PrecompName); + } + + if (!PrecompFilePath) { + warn("dependent precompiled headers .obj not found: " + PrecompPath + + ". Linking as if no debug info (" + File->getName() + ")"); + + return make_error(pdb::generic_error_code::invalid_path, + PrecompPath); + } + + ObjFile *CurrentFile = std::get<0>(PrecompFilePath.getValue()); + StringRef CurrentPath = std::get<1>(PrecompFilePath.getValue()); + + addObjFile(CurrentFile, &IndexMap); + + if (!CurrentFile->EndPrecomp) + fatal(CurrentPath + " is not a precompiled headers .obj"); + + if (Precomp.getSignature() != CurrentFile->EndPrecomp->getSignature()) + warn("the signature of the precompiled headers .obj " + CurrentPath + + " does not match; the file(s) might be out of date. Linking as if " + "no debug info."); + + return IndexMap; +} + static bool remapTypeIndex(TypeIndex &TI, ArrayRef TypeIndexMap) { if (TI.isSimple()) return true; @@ -803,7 +980,10 @@ return SC; } -void PDBLinker::addObjFile(ObjFile *File) { +void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *CV) { + if (File->wasProcessedForPDB()) + return; + // Add a module descriptor for every object file. We need to put an absolute // path to the object into the PDB. If this is a plain object, we make its // path absolute. If it's an object in an archive, we make the archive path @@ -833,7 +1013,7 @@ // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. CVIndexMap ObjectIndexMap; - auto IndexMapResult = mergeDebugT(File, ObjectIndexMap); + auto IndexMapResult = mergeDebugT(File, CV ? CV : &ObjectIndexMap); // If the .debug$T sections fail to merge, assume there is no debug info. if (!IndexMapResult) { Index: COFF/SymbolTable.h =================================================================== --- COFF/SymbolTable.h +++ COFF/SymbolTable.h @@ -107,6 +107,14 @@ Callback(Pair.second); } + // Returns the first symbol that matches the callback, or null + template Symbol *any(T Callback) { + for (auto &Pair : SymMap) + if (Callback(Pair.second)) + return Pair.second; + return nullptr; + } + private: std::pair insert(StringRef Name); StringRef findByPrefix(StringRef Prefix); Index: COFF/SymbolTable.cpp =================================================================== --- COFF/SymbolTable.cpp +++ COFF/SymbolTable.cpp @@ -20,6 +20,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include +#include "lld/Common/Strings.h" using namespace llvm; @@ -169,6 +170,25 @@ } } + if (Name.contains("__m128")) { + Optional DemangledName = lld::demangleMSVC(Sym->getName()); + auto FindMangled = [&Sym, &DemangledName](Symbol *SymJ) -> bool { + Optional NameJ = lld::demangleMSVC(SymJ->getName()); + if (SymJ == Sym || !NameJ || *DemangledName != *NameJ) + return false; + + if (isa(SymJ)) + memcpy(Sym, SymJ, sizeof(DefinedRegular)); + else if (isa(SymJ)) + memcpy(Sym, SymJ, sizeof(DefinedAbsolute)); + else + memcpy(Sym, SymJ, sizeof(SymbolUnion)); + return true; + }; + if (any(FindMangled)) + continue; + } + // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. if (Config->Force) Index: test/COFF/precomp-link.test =================================================================== --- /dev/null +++ test/COFF/precomp-link.test @@ -0,0 +1,32 @@ + +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj \ +RUN: %S/Inputs/precomp.obj /nodefaultlib /entry:main \ +RUN: /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf +RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s + +CHECK: Types (TPI Stream) +CHECK-NOT: LF_PRECOMP +CHECK-NOT: LF_ENDPRECOMP + +# // precomp.h +# #pragma once +# int Function(char A); + +# // precomp.cpp +# #include "precomp.h" + +# // a.cpp +# #include "precomp.h" +# int main(void) { +# Function('a'); +# return 0; +# } + +# // b.cpp +# #include "precomp.h" +# int Function(char a) { +# return (int)a; +# } + +# // cl.exe precomp.cpp /Z7 /Ycprecomp.h /c +# // cl.exe a.cpp b.cpp /Z7 /Yuprecomp.h /c