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