1 //===- DebugTypes.cpp -----------------------------------------------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "DebugTypes.h" 10 #include "Driver.h" 11 #include "InputFiles.h" 12 #include "lld/Common/ErrorHandler.h" 13 #include "llvm/DebugInfo/CodeView/TypeRecord.h" 14 #include "llvm/DebugInfo/PDB/GenericError.h" 15 #include "llvm/DebugInfo/PDB/Native/InfoStream.h" 16 #include "llvm/DebugInfo/PDB/Native/NativeSession.h" 17 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 18 #include "llvm/Support/Path.h" 19 20 using namespace lld; 21 using namespace lld::coff; 22 using namespace llvm; 23 using namespace llvm::codeview; 24 25 namespace { 26 // The TypeServerSource class represents a PDB type server, a file referenced by 27 // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ 28 // files, therefore there must be only once instance per OBJ lot. The file path 29 // is discovered from the dependent OBJ's debug type stream. The 30 // TypeServerSource object is then queued and loaded by the COFF Driver. The 31 // debug type stream for such PDB files will be merged first in the final PDB, 32 // before any dependent OBJ. 33 class TypeServerSource : public TpiSource { 34 public: 35 explicit TypeServerSource(MemoryBufferRef M, llvm::pdb::NativeSession *S) 36 : TpiSource(PDB, nullptr), Session(S), MB(M) {} 37 38 // Queue a PDB type server for loading in the COFF Driver 39 static void enqueue(const ObjFile *DependentFile, 40 const TypeServer2Record &TS); 41 42 // Create an instance 43 static Expected<TypeServerSource *> getInstance(MemoryBufferRef M); 44 45 // Fetch the PDB instance loaded for a corresponding dependent OBJ. 46 static Expected<TypeServerSource *> 47 findFromFile(const ObjFile *DependentFile); 48 49 static std::map<std::string, std::pair<std::string, TypeServerSource *>> 50 Instances; 51 52 // The interface to the PDB (if it was opened successfully) 53 std::unique_ptr<llvm::pdb::NativeSession> Session; 54 55 private: 56 MemoryBufferRef MB; 57 }; 58 59 // This class represents the debug type stream of an OBJ file that depends on a 60 // PDB type server (see TypeServerSource). 61 class UseTypeServerSource : public TpiSource { 62 public: 63 UseTypeServerSource(const ObjFile *F, const TypeServer2Record *TS) 64 : TpiSource(UsingPDB, F), TypeServerDependency(*TS) {} 65 66 // Information about the PDB type server dependency, that needs to be loaded 67 // in before merging this OBJ. 68 TypeServer2Record TypeServerDependency; 69 }; 70 71 // This class represents the debug type stream of a Microsoft precompiled 72 // headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output 73 // PDB, before any other OBJs that depend on this. Note that only MSVC generate 74 // such files, clang does not. 75 class PrecompSource : public TpiSource { 76 public: 77 PrecompSource(const ObjFile *F) : TpiSource(PCH, F) {} 78 }; 79 80 // This class represents the debug type stream of an OBJ file that depends on a 81 // Microsoft precompiled headers OBJ (see PrecompSource). 82 class UsePrecompSource : public TpiSource { 83 public: 84 UsePrecompSource(const ObjFile *F, const PrecompRecord *Precomp) 85 : TpiSource(UsingPCH, F), PrecompDependency(*Precomp) {} 86 87 // Information about the Precomp OBJ dependency, that needs to be loaded in 88 // before merging this OBJ. 89 PrecompRecord PrecompDependency; 90 }; 91 } // namespace 92 93 static std::vector<std::unique_ptr<TpiSource>> GC; 94 95 TpiSource::TpiSource(TpiKind K, const ObjFile *F) : Kind(K), File(F) { 96 GC.push_back(std::unique_ptr<TpiSource>(this)); 97 } 98 99 TpiSource *lld::coff::makeTpiSource(const ObjFile *F) { 100 return new TpiSource(TpiSource::Regular, F); 101 } 102 103 TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *F, 104 const TypeServer2Record *TS) { 105 TypeServerSource::enqueue(F, *TS); 106 return new UseTypeServerSource(F, TS); 107 } 108 109 TpiSource *lld::coff::makePrecompSource(const ObjFile *F) { 110 return new PrecompSource(F); 111 } 112 113 TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *F, 114 const PrecompRecord *Precomp) { 115 return new UsePrecompSource(F, Precomp); 116 } 117 118 namespace lld { 119 namespace coff { 120 template <> 121 const PrecompRecord &retrieveDependencyInfo(const TpiSource *Source) { 122 assert(Source->Kind == TpiSource::UsingPCH); 123 return ((const UsePrecompSource *)Source)->PrecompDependency; 124 } 125 126 template <> 127 const TypeServer2Record &retrieveDependencyInfo(const TpiSource *Source) { 128 assert(Source->Kind == TpiSource::UsingPDB); 129 return ((const UseTypeServerSource *)Source)->TypeServerDependency; 130 } 131 } // namespace coff 132 } // namespace lld 133 134 std::map<std::string, std::pair<std::string, TypeServerSource *>> 135 TypeServerSource::Instances; 136 137 // Make a PDB path assuming the PDB is in the same folder as the OBJ 138 static std::string getPdbBaseName(const ObjFile *File, StringRef TSPath) { 139 StringRef LocalPath = 140 !File->ParentName.empty() ? File->ParentName : File->getName(); 141 SmallString<128> Path = sys::path::parent_path(LocalPath); 142 143 // Currently, type server PDBs are only created by MSVC cl, which only runs 144 // on Windows, so we can assume type server paths are Windows style. 145 sys::path::append(Path, sys::path::filename(TSPath, sys::path::Style::windows)); 146 return Path.str(); 147 } 148 149 // The casing of the PDB path stamped in the OBJ can differ from the actual path 150 // on disk. With this, we ensure to always use lowercase as a key for the 151 // PDBInputFile::Instances map, at least on Windows. 152 static std::string normalizePdbPath(StringRef path) { 153 #if defined(_WIN32) 154 return path.lower(); 155 #else // LINUX 156 return path; 157 #endif 158 } 159 160 // If existing, return the actual PDB path on disk. 161 static Optional<std::string> findPdbPath(StringRef PDBPath, 162 const ObjFile *DependentFile) { 163 // Ensure the file exists before anything else. In some cases, if the path 164 // points to a removable device, Driver::enqueuePath() would fail with an 165 // error (EAGAIN, "resource unavailable try again") which we want to skip 166 // silently. 167 if (llvm::sys::fs::exists(PDBPath)) 168 return normalizePdbPath(PDBPath); 169 std::string Ret = getPdbBaseName(DependentFile, PDBPath); 170 if (llvm::sys::fs::exists(Ret)) 171 return normalizePdbPath(Ret); 172 return None; 173 } 174 175 // Fetch the PDB instance that was already loaded by the COFF Driver. 176 Expected<TypeServerSource *> 177 TypeServerSource::findFromFile(const ObjFile *DependentFile) { 178 const TypeServer2Record &TS = 179 retrieveDependencyInfo<TypeServer2Record>(DependentFile->DebugTypesObj); 180 181 Optional<std::string> P = findPdbPath(TS.Name, DependentFile); 182 if (!P) 183 return createFileError(TS.Name, errorCodeToError(std::error_code( 184 ENOENT, std::generic_category()))); 185 186 auto It = TypeServerSource::Instances.find(*P); 187 // The PDB file exists on disk, at this point we expect it to have been 188 // inserted in the map by TypeServerSource::loadPDB() 189 assert(It != TypeServerSource::Instances.end()); 190 191 std::pair<std::string, TypeServerSource *> &PDB = It->second; 192 193 if (!PDB.second) 194 return createFileError( 195 *P, createStringError(inconvertibleErrorCode(), PDB.first.c_str())); 196 197 pdb::PDBFile &PDBFile = (PDB.second)->Session->getPDBFile(); 198 pdb::InfoStream &Info = cantFail(PDBFile.getPDBInfoStream()); 199 200 // Just because a file with a matching name was found doesn't mean it can be 201 // used. The GUID must match between the PDB header and the OBJ 202 // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. 203 if (Info.getGuid() != TS.getGuid()) 204 return createFileError( 205 TS.Name, 206 make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date)); 207 208 return PDB.second; 209 } 210 211 // FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is 212 // moved here. 213 Expected<llvm::pdb::NativeSession *> 214 lld::coff::findTypeServerSource(const ObjFile *F) { 215 Expected<TypeServerSource *> TS = TypeServerSource::findFromFile(F); 216 if (!TS) 217 return TS.takeError(); 218 return TS.get()->Session.get(); 219 } 220 221 // Queue a PDB type server for loading in the COFF Driver 222 void TypeServerSource::enqueue(const ObjFile *DependentFile, 223 const TypeServer2Record &TS) { 224 // Start by finding where the PDB is located (either the record path or next 225 // to the OBJ file) 226 Optional<std::string> P = findPdbPath(TS.Name, DependentFile); 227 if (!P) 228 return; 229 auto It = TypeServerSource::Instances.emplace( 230 *P, std::pair<std::string, TypeServerSource *>{}); 231 if (!It.second) 232 return; // another OBJ already scheduled this PDB for load 233 234 Driver->enqueuePath(*P, false); 235 } 236 237 // Create an instance of TypeServerSource or an error string if the PDB couldn't 238 // be loaded. The error message will be displayed later, when the referring OBJ 239 // will be merged in. NOTE - a PDB load failure is not a link error: some 240 // debug info will simply be missing from the final PDB - that is the default 241 // accepted behavior. 242 void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef M) { 243 std::string Path = normalizePdbPath(M.getBufferIdentifier()); 244 245 Expected<TypeServerSource *> TS = TypeServerSource::getInstance(M); 246 if (!TS) 247 TypeServerSource::Instances[Path] = {toString(TS.takeError()), nullptr}; 248 else 249 TypeServerSource::Instances[Path] = {{}, *TS}; 250 } 251 252 Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef M) { 253 std::unique_ptr<llvm::pdb::IPDBSession> ISession; 254 Error Err = pdb::NativeSession::createFromPdb( 255 MemoryBuffer::getMemBuffer(M, false), ISession); 256 if (Err) 257 return std::move(Err); 258 259 std::unique_ptr<llvm::pdb::NativeSession> Session( 260 static_cast<pdb::NativeSession *>(ISession.release())); 261 262 pdb::PDBFile &PDBFile = Session->getPDBFile(); 263 Expected<pdb::InfoStream &> Info = PDBFile.getPDBInfoStream(); 264 // All PDB Files should have an Info stream. 265 if (!Info) 266 return Info.takeError(); 267 return new TypeServerSource(M, Session.release()); 268 } 269