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