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
21using namespace llvm;
22using namespace llvm::object;
23using namespace llvm::pdb;
24using namespace llvm::logicalview;
25
26#define DEBUG_TYPE "ReaderHandler"
27
28Error 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
39Error 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
73Error 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.
98static 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.
124static 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
139Error 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
202Error 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
218Error 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
245Error 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
262Error 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
279Error 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
293Error 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
303Error 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
321void LVReaderHandler::print(raw_ostream &OS) const { OS << "ReaderHandler\n"; }
322