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
22using namespace llvm;
23using namespace llvm::object;
24using namespace llvm::pdb;
25using namespace llvm::logicalview;
26
27#define DEBUG_TYPE "ReaderHandler"
28
29Error 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
40Error 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
74Error 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.
96static 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.
122static 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
137Error 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
200Error 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
216Error 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
243Error 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
260Error 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
277Error 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
291Error 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
301Error 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
319void LVReaderHandler::print(raw_ostream &OS) const { OS << "ReaderHandler\n"; }
320