xref: /llvm-project/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp (revision 37e48e4a7360a6faf1b157e843160d9e65223890)
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 *>(Input)) {
44       ObjectFile &Obj = *cast<ObjectFile *>(Input);
45       if (Obj.isCOFF()) {
46         COFFObjectFile *COFF = cast<COFFObjectFile>(&Obj);
47         return std::make_unique<LVCodeViewReader>(Filename, FileFormatName,
48                                                   *COFF, W, ExePath);
49       }
50       if (Obj.isELF() || Obj.isMachO() || Obj.isWasm())
51         return std::make_unique<LVDWARFReader>(Filename, FileFormatName, Obj,
52                                                W);
53     }
54     if (isa<PDBFile *>(Input)) {
55       PDBFile &Pdb = *cast<PDBFile *>(Input);
56       return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, Pdb,
57                                                 W, ExePath);
58     }
59     return nullptr;
60   };
61 
62   std::unique_ptr<LVReader> ReaderObj = CreateOneReader();
63   if (!ReaderObj)
64     return createStringError(errc::invalid_argument,
65                              "unable to create reader for: '%s'",
66                              Filename.str().c_str());
67 
68   LVReader *Reader = ReaderObj.get();
69   Readers.emplace_back(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(errorToErrorCode(std::move(Err)), "%s",
80                                Filename.str().c_str());
81     Expected<StringRef> NameOrErr = Child.getName();
82     if (Error Err = NameOrErr.takeError())
83       return createStringError(errorToErrorCode(std::move(Err)), "%s",
84                                Filename.str().c_str());
85     std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
86     if (Error Err = handleBuffer(Readers, Name, BuffOrErr.get()))
87       return createStringError(errorToErrorCode(std::move(Err)), "%s",
88                                Filename.str().c_str());
89   }
90 
91   if (Err)
92     return createStringError(errorToErrorCode(std::move(Err)), "%s",
93                              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(ExePath, Extension);
102 
103   std::unique_ptr<IPDBSession> Session;
104   if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) {
105     consumeError(std::move(Err));
106     return {};
107   }
108   // We have a candidate for the executable image.
109   Expected<std::string> PdbPathOrErr = NativeSession::searchForPdb({ExePath});
110   if (!PdbPathOrErr) {
111     consumeError(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       PdbPathOrErr.get(), 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(ObjPath, Extension);
128   if (llvm::sys::fs::exists(ObjPath)) {
129     ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
130         MemoryBuffer::getFileOrSTDIN(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(Buffer.getBuffer());
144   if (FileMagic == file_magic::pdb) {
145     if (!ExePath.empty())
146       return handleObject(Readers, Filename, 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(Filename, Extension);
155       if (ExecutableImage.empty())
156         continue;
157       if (Error Err = handleObject(Readers, Filename, Buffer.getBuffer(),
158                                    ExecutableImage)) {
159         consumeError(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(Filename, Extension);
168       if (ObjectImage.empty())
169         continue;
170       if (Error Err = handleFile(Readers, ObjectImage)) {
171         consumeError(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.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({Filename});
183     if (errorToErrorCode(PdbPath.takeError())) {
184       return createStringError(
185           errc::not_supported,
186           "Binary object format in '%s' does not have debug info.",
187           Filename.str().c_str());
188     }
189     // Process the matching PDB file and pass the executable filename.
190     return handleFile(Readers, PdbPath.get(), Filename);
191   }
192 
193   Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buffer);
194   if (errorToErrorCode(BinOrErr.takeError())) {
195     return createStringError(errc::not_supported,
196                              "Binary object format in '%s' is not supported.",
197                              Filename.str().c_str());
198   }
199   return handleObject(Readers, Filename, *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(Filename, sys::path::Style::windows);
207   ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
208       MemoryBuffer::getFileOrSTDIN(ConvertedPath);
209   if (BuffOrErr.getError()) {
210     return createStringError(errc::bad_file_descriptor,
211                              "File '%s' does not exist.",
212                              ConvertedPath.c_str());
213   }
214   std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
215   return handleBuffer(Readers, ConvertedPath, *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, Obj.getFileFormatName()))
230         return Err;
231       continue;
232     } else
233       consumeError(MachOOrErr.takeError());
234     if (Expected<std::unique_ptr<Archive>> ArchiveOrErr =
235             ObjForArch.getAsArchive()) {
236       if (Error Err = handleArchive(Readers, ObjName, *ArchiveOrErr.get()))
237         return Err;
238       continue;
239     } else
240       consumeError(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>(&Binary))
248     return createReader(Filename, Readers, Input,
249                         cast<ObjectFile *>(Input)->getFileFormatName());
250 
251   if (MachOUniversalBinary *Fat = dyn_cast<MachOUniversalBinary>(&Binary))
252     return handleMach(Readers, Filename, *Fat);
253 
254   if (Archive *Arch = dyn_cast<Archive>(&Binary))
255     return handleArchive(Readers, Filename, *Arch);
256 
257   return createStringError(errc::not_supported,
258                            "Binary object format in '%s' is not supported.",
259                            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(PDB_ReaderType::Native, Filename, Session))
266     return createStringError(errorToErrorCode(std::move(Err)), "%s",
267                              Filename.str().c_str());
268 
269   std::unique_ptr<NativeSession> PdbSession;
270   PdbSession.reset(static_cast<NativeSession *>(Session.release()));
271   PdbOrObj Input = &PdbSession->getPDBFile();
272   StringRef FileFormatName;
273   size_t Pos = Buffer.find_first_of("\r\n");
274   if (Pos)
275     FileFormatName = Buffer.substr(0, 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(Object, Readers))
284       return Err;
285     TheReaders.insert(TheReaders.end(),
286                       std::make_move_iterator(Readers.begin()),
287                       std::make_move_iterator(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(TheReaders[Index].get(),
312                                       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