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