1 | //===-- LVReaderHandler.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 | // This class implements the Reader Handler. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" |
14 | #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" |
15 | #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" |
16 | #include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" |
17 | #include "llvm/DebugInfo/LogicalView/Readers/LVDWARFReader.h" |
18 | #include "llvm/DebugInfo/PDB/Native/NativeSession.h" |
19 | #include "llvm/DebugInfo/PDB/PDB.h" |
20 | #include "llvm/Object/COFF.h" |
21 | |
22 | using namespace llvm; |
23 | using namespace llvm::object; |
24 | using namespace llvm::pdb; |
25 | using namespace llvm::logicalview; |
26 | |
27 | #define DEBUG_TYPE "ReaderHandler" |
28 | |
29 | Error LVReaderHandler::process() { |
30 | if (Error Err = createReaders()) |
31 | return Err; |
32 | if (Error Err = printReaders()) |
33 | return Err; |
34 | if (Error Err = compareReaders()) |
35 | return Err; |
36 | |
37 | return Error::success(); |
38 | } |
39 | |
40 | Error LVReaderHandler::createReader(StringRef Filename, LVReaders &Readers, |
41 | PdbOrObj &Input, StringRef FileFormatName, |
42 | StringRef ExePath) { |
43 | auto CreateOneReader = [&]() -> std::unique_ptr<LVReader> { |
44 | if (isa<ObjectFile *>(Val: Input)) { |
45 | ObjectFile &Obj = *cast<ObjectFile *>(Val&: Input); |
46 | if (Obj.isCOFF()) { |
47 | COFFObjectFile *COFF = cast<COFFObjectFile>(Val: &Obj); |
48 | return std::make_unique<LVCodeViewReader>(args&: Filename, args&: FileFormatName, |
49 | args&: *COFF, args&: W, args&: ExePath); |
50 | } |
51 | if (Obj.isELF() || Obj.isMachO() || Obj.isWasm()) |
52 | return std::make_unique<LVDWARFReader>(args&: Filename, args&: FileFormatName, args&: Obj, |
53 | args&: W); |
54 | } |
55 | if (isa<PDBFile *>(Val: Input)) { |
56 | PDBFile &Pdb = *cast<PDBFile *>(Val&: Input); |
57 | return std::make_unique<LVCodeViewReader>(args&: Filename, args&: FileFormatName, args&: Pdb, |
58 | args&: W, args&: ExePath); |
59 | } |
60 | return nullptr; |
61 | }; |
62 | |
63 | std::unique_ptr<LVReader> ReaderObj = CreateOneReader(); |
64 | if (!ReaderObj) |
65 | return createStringError(EC: errc::invalid_argument, |
66 | Fmt: "unable to create reader for: '%s'" , |
67 | Vals: Filename.str().c_str()); |
68 | |
69 | LVReader *Reader = ReaderObj.get(); |
70 | Readers.emplace_back(args: std::move(ReaderObj)); |
71 | return Reader->doLoad(); |
72 | } |
73 | |
74 | Error LVReaderHandler::handleArchive(LVReaders &Readers, StringRef Filename, |
75 | Archive &Arch) { |
76 | Error Err = Error::success(); |
77 | for (const Archive::Child &Child : Arch.children(Err)) { |
78 | Expected<MemoryBufferRef> BuffOrErr = Child.getMemoryBufferRef(); |
79 | if (Error Err = BuffOrErr.takeError()) |
80 | return createStringError(EC: errorToErrorCode(Err: std::move(Err)), Fmt: "%s" , |
81 | Vals: Filename.str().c_str()); |
82 | Expected<StringRef> NameOrErr = Child.getName(); |
83 | if (Error Err = NameOrErr.takeError()) |
84 | return createStringError(EC: errorToErrorCode(Err: std::move(Err)), Fmt: "%s" , |
85 | Vals: Filename.str().c_str()); |
86 | std::string Name = (Filename + "(" + NameOrErr.get() + ")" ).str(); |
87 | if (Error Err = handleBuffer(Readers, Filename: Name, Buffer: BuffOrErr.get())) |
88 | return createStringError(EC: errorToErrorCode(Err: std::move(Err)), Fmt: "%s" , |
89 | Vals: Filename.str().c_str()); |
90 | } |
91 | |
92 | return Error::success(); |
93 | } |
94 | |
95 | // Search for a matching executable image for the given PDB path. |
96 | static std::string searchForExe(const StringRef Path, |
97 | const StringRef Extension) { |
98 | SmallString<128> ExePath(Path); |
99 | llvm::sys::path::replace_extension(path&: ExePath, extension: Extension); |
100 | |
101 | std::unique_ptr<IPDBSession> Session; |
102 | if (Error Err = loadDataForEXE(Type: PDB_ReaderType::Native, Path: ExePath, Session)) { |
103 | consumeError(Err: std::move(Err)); |
104 | return {}; |
105 | } |
106 | // We have a candidate for the executable image. |
107 | Expected<std::string> PdbPathOrErr = NativeSession::searchForPdb(Opts: {.ExePath: ExePath}); |
108 | if (!PdbPathOrErr) { |
109 | consumeError(Err: PdbPathOrErr.takeError()); |
110 | return {}; |
111 | } |
112 | // Convert any Windows backslashes into forward slashes to get the path. |
113 | std::string ConvertedPath = sys::path::convert_to_slash( |
114 | path: PdbPathOrErr.get(), style: sys::path::Style::windows); |
115 | if (ConvertedPath == Path) |
116 | return std::string(ExePath); |
117 | |
118 | return {}; |
119 | } |
120 | |
121 | // Search for a matching object image for the given PDB path. |
122 | static std::string searchForObj(const StringRef Path, |
123 | const StringRef Extension) { |
124 | SmallString<128> ObjPath(Path); |
125 | llvm::sys::path::replace_extension(path&: ObjPath, extension: Extension); |
126 | if (llvm::sys::fs::exists(Path: ObjPath)) { |
127 | ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = |
128 | MemoryBuffer::getFileOrSTDIN(Filename: ObjPath); |
129 | if (!BuffOrErr) |
130 | return {}; |
131 | return std::string(ObjPath); |
132 | } |
133 | |
134 | return {}; |
135 | } |
136 | |
137 | Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename, |
138 | MemoryBufferRef Buffer, StringRef ExePath) { |
139 | // As PDB does not support the Binary interface, at this point we can check |
140 | // if the buffer corresponds to a PDB or PE file. |
141 | file_magic FileMagic = identify_magic(magic: Buffer.getBuffer()); |
142 | if (FileMagic == file_magic::pdb) { |
143 | if (!ExePath.empty()) |
144 | return handleObject(Readers, Filename, Buffer: Buffer.getBuffer(), ExePath); |
145 | |
146 | // Search in the directory derived from the given 'Filename' for a |
147 | // matching object file (.o, .obj, .lib) or a matching executable file |
148 | // (.exe/.dll) and try to create the reader based on the matched file. |
149 | // If no matching file is found then we load the original PDB file. |
150 | std::vector<StringRef> ExecutableExtensions = {"exe" , "dll" }; |
151 | for (StringRef Extension : ExecutableExtensions) { |
152 | std::string ExecutableImage = searchForExe(Path: Filename, Extension); |
153 | if (ExecutableImage.empty()) |
154 | continue; |
155 | if (Error Err = handleObject(Readers, Filename, Buffer: Buffer.getBuffer(), |
156 | ExePath: ExecutableImage)) { |
157 | consumeError(Err: std::move(Err)); |
158 | continue; |
159 | } |
160 | return Error::success(); |
161 | } |
162 | |
163 | std::vector<StringRef> ObjectExtensions = {"o" , "obj" , "lib" }; |
164 | for (StringRef Extension : ObjectExtensions) { |
165 | std::string ObjectImage = searchForObj(Path: Filename, Extension); |
166 | if (ObjectImage.empty()) |
167 | continue; |
168 | if (Error Err = handleFile(Readers, Filename: ObjectImage)) { |
169 | consumeError(Err: std::move(Err)); |
170 | continue; |
171 | } |
172 | return Error::success(); |
173 | } |
174 | |
175 | // No matching executable/object image was found. Load the given PDB. |
176 | return handleObject(Readers, Filename, Buffer: Buffer.getBuffer(), ExePath); |
177 | } |
178 | if (FileMagic == file_magic::pecoff_executable) { |
179 | // If we have a valid executable, try to find a matching PDB file. |
180 | Expected<std::string> PdbPath = NativeSession::searchForPdb(Opts: {.ExePath: Filename}); |
181 | if (errorToErrorCode(Err: PdbPath.takeError())) { |
182 | return createStringError( |
183 | EC: errc::not_supported, |
184 | Fmt: "Binary object format in '%s' does not have debug info." , |
185 | Vals: Filename.str().c_str()); |
186 | } |
187 | // Process the matching PDB file and pass the executable filename. |
188 | return handleFile(Readers, Filename: PdbPath.get(), ExePath: Filename); |
189 | } |
190 | |
191 | Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Source: Buffer); |
192 | if (errorToErrorCode(Err: BinOrErr.takeError())) { |
193 | return createStringError(EC: errc::not_supported, |
194 | Fmt: "Binary object format in '%s' is not supported." , |
195 | Vals: Filename.str().c_str()); |
196 | } |
197 | return handleObject(Readers, Filename, Binary&: *BinOrErr.get()); |
198 | } |
199 | |
200 | Error LVReaderHandler::handleFile(LVReaders &Readers, StringRef Filename, |
201 | StringRef ExePath) { |
202 | // Convert any Windows backslashes into forward slashes to get the path. |
203 | std::string ConvertedPath = |
204 | sys::path::convert_to_slash(path: Filename, style: sys::path::Style::windows); |
205 | ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = |
206 | MemoryBuffer::getFileOrSTDIN(Filename: ConvertedPath); |
207 | if (BuffOrErr.getError()) { |
208 | return createStringError(EC: errc::bad_file_descriptor, |
209 | Fmt: "File '%s' does not exist." , |
210 | Vals: ConvertedPath.c_str()); |
211 | } |
212 | std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get()); |
213 | return handleBuffer(Readers, Filename: ConvertedPath, Buffer: *Buffer, ExePath); |
214 | } |
215 | |
216 | Error LVReaderHandler::handleMach(LVReaders &Readers, StringRef Filename, |
217 | MachOUniversalBinary &Mach) { |
218 | for (const MachOUniversalBinary::ObjectForArch &ObjForArch : Mach.objects()) { |
219 | std::string ObjName = (Twine(Filename) + Twine("(" ) + |
220 | Twine(ObjForArch.getArchFlagName()) + Twine(")" )) |
221 | .str(); |
222 | if (Expected<std::unique_ptr<MachOObjectFile>> MachOOrErr = |
223 | ObjForArch.getAsObjectFile()) { |
224 | MachOObjectFile &Obj = **MachOOrErr; |
225 | PdbOrObj Input = &Obj; |
226 | if (Error Err = |
227 | createReader(Filename, Readers, Input, FileFormatName: Obj.getFileFormatName())) |
228 | return Err; |
229 | continue; |
230 | } else |
231 | consumeError(Err: MachOOrErr.takeError()); |
232 | if (Expected<std::unique_ptr<Archive>> ArchiveOrErr = |
233 | ObjForArch.getAsArchive()) { |
234 | if (Error Err = handleArchive(Readers, Filename: ObjName, Arch&: *ArchiveOrErr.get())) |
235 | return Err; |
236 | continue; |
237 | } else |
238 | consumeError(Err: ArchiveOrErr.takeError()); |
239 | } |
240 | return Error::success(); |
241 | } |
242 | |
243 | Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, |
244 | Binary &Binary) { |
245 | if (PdbOrObj Input = dyn_cast<ObjectFile>(Val: &Binary)) |
246 | return createReader(Filename, Readers, Input, |
247 | FileFormatName: cast<ObjectFile *>(Val&: Input)->getFileFormatName()); |
248 | |
249 | if (MachOUniversalBinary *Fat = dyn_cast<MachOUniversalBinary>(Val: &Binary)) |
250 | return handleMach(Readers, Filename, Mach&: *Fat); |
251 | |
252 | if (Archive *Arch = dyn_cast<Archive>(Val: &Binary)) |
253 | return handleArchive(Readers, Filename, Arch&: *Arch); |
254 | |
255 | return createStringError(EC: errc::not_supported, |
256 | Fmt: "Binary object format in '%s' is not supported." , |
257 | Vals: Filename.str().c_str()); |
258 | } |
259 | |
260 | Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, |
261 | StringRef Buffer, StringRef ExePath) { |
262 | std::unique_ptr<IPDBSession> Session; |
263 | if (Error Err = loadDataForPDB(Type: PDB_ReaderType::Native, Path: Filename, Session)) |
264 | return createStringError(EC: errorToErrorCode(Err: std::move(Err)), Fmt: "%s" , |
265 | Vals: Filename.str().c_str()); |
266 | |
267 | std::unique_ptr<NativeSession> PdbSession; |
268 | PdbSession.reset(p: static_cast<NativeSession *>(Session.release())); |
269 | PdbOrObj Input = &PdbSession->getPDBFile(); |
270 | StringRef FileFormatName; |
271 | size_t Pos = Buffer.find_first_of(Chars: "\r\n" ); |
272 | if (Pos) |
273 | FileFormatName = Buffer.substr(Start: 0, N: Pos - 1); |
274 | return createReader(Filename, Readers, Input, FileFormatName, ExePath); |
275 | } |
276 | |
277 | Error LVReaderHandler::createReaders() { |
278 | LLVM_DEBUG(dbgs() << "createReaders\n" ); |
279 | for (std::string &Object : Objects) { |
280 | LVReaders Readers; |
281 | if (Error Err = createReader(Filename: Object, Readers)) |
282 | return Err; |
283 | TheReaders.insert(position: TheReaders.end(), |
284 | first: std::make_move_iterator(i: Readers.begin()), |
285 | last: std::make_move_iterator(i: Readers.end())); |
286 | } |
287 | |
288 | return Error::success(); |
289 | } |
290 | |
291 | Error LVReaderHandler::printReaders() { |
292 | LLVM_DEBUG(dbgs() << "printReaders\n" ); |
293 | if (options().getPrintExecute()) |
294 | for (const std::unique_ptr<LVReader> &Reader : TheReaders) |
295 | if (Error Err = Reader->doPrint()) |
296 | return Err; |
297 | |
298 | return Error::success(); |
299 | } |
300 | |
301 | Error LVReaderHandler::compareReaders() { |
302 | LLVM_DEBUG(dbgs() << "compareReaders\n" ); |
303 | size_t ReadersCount = TheReaders.size(); |
304 | if (options().getCompareExecute() && ReadersCount >= 2) { |
305 | // If we have more than 2 readers, compare them by pairs. |
306 | size_t ViewPairs = ReadersCount / 2; |
307 | LVCompare Compare(OS); |
308 | for (size_t Pair = 0, Index = 0; Pair < ViewPairs; ++Pair) { |
309 | if (Error Err = Compare.execute(ReferenceReader: TheReaders[Index].get(), |
310 | TargetReader: TheReaders[Index + 1].get())) |
311 | return Err; |
312 | Index += 2; |
313 | } |
314 | } |
315 | |
316 | return Error::success(); |
317 | } |
318 | |
319 | void LVReaderHandler::print(raw_ostream &OS) const { OS << "ReaderHandler\n" ; } |
320 | |