/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF/DebugTypes.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 5 | : 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 | 17 | : 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 | 4 | 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 | 8 | : 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 | 93 | TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { |
96 | 93 | GC.push_back(std::unique_ptr<TpiSource>(this)); |
97 | 93 | } |
98 | | |
99 | 59 | TpiSource *lld::coff::makeTpiSource(const ObjFile *f) { |
100 | 59 | return new TpiSource(TpiSource::Regular, f); |
101 | 59 | } |
102 | | |
103 | | TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, |
104 | 17 | const TypeServer2Record *ts) { |
105 | 17 | TypeServerSource::enqueue(f, *ts); |
106 | 17 | return new UseTypeServerSource(f, ts); |
107 | 17 | } |
108 | | |
109 | 4 | TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { |
110 | 4 | return new PrecompSource(f); |
111 | 4 | } |
112 | | |
113 | | TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, |
114 | 8 | const PrecompRecord *precomp) { |
115 | 8 | return new UsePrecompSource(f, precomp); |
116 | 8 | } |
117 | | |
118 | | namespace lld { |
119 | | namespace coff { |
120 | | template <> |
121 | 16 | const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { |
122 | 16 | assert(source->kind == TpiSource::UsingPCH); |
123 | 16 | return ((const UsePrecompSource *)source)->precompDependency; |
124 | 16 | } |
125 | | |
126 | | template <> |
127 | 17 | const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { |
128 | 17 | assert(source->kind == TpiSource::UsingPDB); |
129 | 17 | return ((const UseTypeServerSource *)source)->typeServerDependency; |
130 | 17 | } |
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 | 28 | static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) { |
139 | 28 | StringRef localPath = |
140 | 28 | !file->parentName.empty() ? file->parentName0 : file->getName(); |
141 | 28 | SmallString<128> path = sys::path::parent_path(localPath); |
142 | 28 | |
143 | 28 | // Currently, type server PDBs are only created by MSVC cl, which only runs |
144 | 28 | // on Windows, so we can assume type server paths are Windows style. |
145 | 28 | sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows)); |
146 | 28 | return path.str(); |
147 | 28 | } |
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 | 20 | static std::string normalizePdbPath(StringRef path) { |
153 | | #if defined(_WIN32) |
154 | | return path.lower(); |
155 | | #else // LINUX |
156 | | return path; |
157 | 20 | #endif |
158 | 20 | } |
159 | | |
160 | | // If existing, return the actual PDB path on disk. |
161 | | static Optional<std::string> findPdbPath(StringRef pdbPath, |
162 | 34 | const ObjFile *dependentFile) { |
163 | 34 | // Ensure the file exists before anything else. In some cases, if the path |
164 | 34 | // points to a removable device, Driver::enqueuePath() would fail with an |
165 | 34 | // error (EAGAIN, "resource unavailable try again") which we want to skip |
166 | 34 | // silently. |
167 | 34 | if (llvm::sys::fs::exists(pdbPath)) |
168 | 6 | return normalizePdbPath(pdbPath); |
169 | 28 | std::string ret = getPdbBaseName(dependentFile, pdbPath); |
170 | 28 | if (llvm::sys::fs::exists(ret)) |
171 | 8 | return normalizePdbPath(ret); |
172 | 20 | return None; |
173 | 20 | } |
174 | | |
175 | | // Fetch the PDB instance that was already loaded by the COFF Driver. |
176 | | Expected<TypeServerSource *> |
177 | 17 | TypeServerSource::findFromFile(const ObjFile *dependentFile) { |
178 | 17 | const TypeServer2Record &ts = |
179 | 17 | retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj); |
180 | 17 | |
181 | 17 | Optional<std::string> p = findPdbPath(ts.Name, dependentFile); |
182 | 17 | if (!p) |
183 | 10 | return createFileError(ts.Name, errorCodeToError(std::error_code( |
184 | 10 | ENOENT, std::generic_category()))); |
185 | 7 | |
186 | 7 | auto it = TypeServerSource::instances.find(*p); |
187 | 7 | // The PDB file exists on disk, at this point we expect it to have been |
188 | 7 | // inserted in the map by TypeServerSource::loadPDB() |
189 | 7 | assert(it != TypeServerSource::instances.end()); |
190 | 7 | |
191 | 7 | std::pair<std::string, TypeServerSource *> &pdb = it->second; |
192 | 7 | |
193 | 7 | if (!pdb.second) |
194 | 1 | return createFileError( |
195 | 1 | *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str())); |
196 | 6 | |
197 | 6 | pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile(); |
198 | 6 | pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); |
199 | 6 | |
200 | 6 | // Just because a file with a matching name was found doesn't mean it can be |
201 | 6 | // used. The GUID must match between the PDB header and the OBJ |
202 | 6 | // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. |
203 | 6 | if (info.getGuid() != ts.getGuid()) |
204 | 1 | return createFileError( |
205 | 1 | ts.Name, |
206 | 1 | make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date)); |
207 | 5 | |
208 | 5 | return pdb.second; |
209 | 5 | } |
210 | | |
211 | | // FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is |
212 | | // moved here. |
213 | | Expected<llvm::pdb::NativeSession *> |
214 | 17 | lld::coff::findTypeServerSource(const ObjFile *f) { |
215 | 17 | Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f); |
216 | 17 | if (!ts) |
217 | 12 | return ts.takeError(); |
218 | 5 | return ts.get()->session.get(); |
219 | 5 | } |
220 | | |
221 | | // Queue a PDB type server for loading in the COFF Driver |
222 | | void TypeServerSource::enqueue(const ObjFile *dependentFile, |
223 | 17 | const TypeServer2Record &ts) { |
224 | 17 | // Start by finding where the PDB is located (either the record path or next |
225 | 17 | // to the OBJ file) |
226 | 17 | Optional<std::string> p = findPdbPath(ts.Name, dependentFile); |
227 | 17 | if (!p) |
228 | 10 | return; |
229 | 7 | auto it = TypeServerSource::instances.emplace( |
230 | 7 | *p, std::pair<std::string, TypeServerSource *>{}); |
231 | 7 | if (!it.second) |
232 | 1 | return; // another OBJ already scheduled this PDB for load |
233 | 6 | |
234 | 6 | driver->enqueuePath(*p, false); |
235 | 6 | } |
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 | 6 | void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { |
243 | 6 | std::string path = normalizePdbPath(m.getBufferIdentifier()); |
244 | 6 | |
245 | 6 | Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m); |
246 | 6 | if (!ts) |
247 | 1 | TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; |
248 | 5 | else |
249 | 5 | TypeServerSource::instances[path] = {{}, *ts}; |
250 | 6 | } |
251 | | |
252 | 6 | Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) { |
253 | 6 | std::unique_ptr<llvm::pdb::IPDBSession> iSession; |
254 | 6 | Error err = pdb::NativeSession::createFromPdb( |
255 | 6 | MemoryBuffer::getMemBuffer(m, false), iSession); |
256 | 6 | if (err) |
257 | 1 | return std::move(err); |
258 | 5 | |
259 | 5 | std::unique_ptr<llvm::pdb::NativeSession> session( |
260 | 5 | static_cast<pdb::NativeSession *>(iSession.release())); |
261 | 5 | |
262 | 5 | pdb::PDBFile &pdbFile = session->getPDBFile(); |
263 | 5 | Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream(); |
264 | 5 | // All PDB Files should have an Info stream. |
265 | 5 | if (!info) |
266 | 0 | return info.takeError(); |
267 | 5 | return new TypeServerSource(m, session.release()); |
268 | 5 | } |