| //===-- LVReaderHandler.cpp -----------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This class implements the Reader Handler. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" |
| #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" |
| #include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" |
| #include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h" |
| #include "llvm/DebugInfo/PDB/Native/NativeSession.h" |
| #include "llvm/DebugInfo/PDB/PDB.h" |
| #include "llvm/Object/COFF.h" |
| |
| using namespace llvm; |
| using namespace llvm::object; |
| using namespace llvm::pdb; |
| using namespace llvm::logicalview; |
| |
| #define DEBUG_TYPE "ReaderHandler" |
| |
| Error LVReaderHandler::process() { |
| if (Error Err = createReaders()) |
| return Err; |
| if (Error Err = printReaders()) |
| return Err; |
| if (Error Err = compareReaders()) |
| return Err; |
| |
| return Error::success(); |
| } |
| |
| Error LVReaderHandler::createReader(StringRef Filename, LVReaders &Readers, |
| PdbOrObj &Input, StringRef FileFormatName, |
| StringRef ExePath) { |
| auto CreateOneReader = [&]() -> std::unique_ptr<LVReader> { |
| if (isa<ObjectFile *>(Input)) { |
| ObjectFile &Obj = *cast<ObjectFile *>(Input); |
| if (Obj.isCOFF()) { |
| COFFObjectFile *COFF = cast<COFFObjectFile>(&Obj); |
| return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, |
| *COFF, W, ExePath); |
| } |
| if (Obj.isELF() || Obj.isMachO()) |
| return std::make_unique<LVELFReader>(Filename, FileFormatName, Obj, W); |
| } |
| if (isa<PDBFile *>(Input)) { |
| PDBFile &Pdb = *cast<PDBFile *>(Input); |
| return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, Pdb, |
| W, ExePath); |
| } |
| return nullptr; |
| }; |
| |
| std::unique_ptr<LVReader> ReaderObj = CreateOneReader(); |
| if (!ReaderObj) |
| return createStringError(errc::invalid_argument, |
| "unable to create reader for: '%s'", |
| Filename.str().c_str()); |
| |
| LVReader *Reader = ReaderObj.get(); |
| Readers.emplace_back(std::move(ReaderObj)); |
| return Reader->doLoad(); |
| } |
| |
| Error LVReaderHandler::handleArchive(LVReaders &Readers, StringRef Filename, |
| Archive &Arch) { |
| Error Err = Error::success(); |
| for (const Archive::Child &Child : Arch.children(Err)) { |
| Expected<MemoryBufferRef> BuffOrErr = Child.getMemoryBufferRef(); |
| if (Error Err = BuffOrErr.takeError()) |
| return createStringError(errorToErrorCode(std::move(Err)), "%s", |
| Filename.str().c_str()); |
| Expected<StringRef> NameOrErr = Child.getName(); |
| if (Error Err = NameOrErr.takeError()) |
| return createStringError(errorToErrorCode(std::move(Err)), "%s", |
| Filename.str().c_str()); |
| std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); |
| if (Error Err = handleBuffer(Readers, Name, BuffOrErr.get())) |
| return createStringError(errorToErrorCode(std::move(Err)), "%s", |
| Filename.str().c_str()); |
| } |
| |
| return Error::success(); |
| } |
| |
| // Search for a matching executable image for the given PDB path. |
| static std::string searchForExe(const StringRef Path, |
| const StringRef Extension) { |
| SmallString<128> ExePath(Path); |
| llvm::sys::path::replace_extension(ExePath, Extension); |
| |
| std::unique_ptr<IPDBSession> Session; |
| if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) { |
| consumeError(std::move(Err)); |
| return {}; |
| } |
| // We have a candidate for the executable image. |
| Expected<std::string> PdbPathOrErr = NativeSession::searchForPdb({ExePath}); |
| if (!PdbPathOrErr) { |
| consumeError(PdbPathOrErr.takeError()); |
| return {}; |
| } |
| // Convert any Windows backslashes into forward slashes to get the path. |
| std::string ConvertedPath = sys::path::convert_to_slash( |
| PdbPathOrErr.get(), sys::path::Style::windows); |
| if (ConvertedPath == Path) |
| return std::string(ExePath); |
| |
| return {}; |
| } |
| |
| // Search for a matching object image for the given PDB path. |
| static std::string searchForObj(const StringRef Path, |
| const StringRef Extension) { |
| SmallString<128> ObjPath(Path); |
| llvm::sys::path::replace_extension(ObjPath, Extension); |
| if (llvm::sys::fs::exists(ObjPath)) { |
| ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = |
| MemoryBuffer::getFileOrSTDIN(ObjPath); |
| if (!BuffOrErr) |
| return {}; |
| return std::string(ObjPath); |
| } |
| |
| return {}; |
| } |
| |
| Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename, |
| MemoryBufferRef Buffer, StringRef ExePath) { |
| // As PDB does not support the Binary interface, at this point we can check |
| // if the buffer corresponds to a PDB or PE file. |
| file_magic FileMagic = identify_magic(Buffer.getBuffer()); |
| if (FileMagic == file_magic::pdb) { |
| if (!ExePath.empty()) |
| return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); |
| |
| // Search in the directory derived from the given 'Filename' for a |
| // matching object file (.o, .obj, .lib) or a matching executable file |
| // (.exe/.dll) and try to create the reader based on the matched file. |
| // If no matching file is found then we load the original PDB file. |
| std::vector<StringRef> ExecutableExtensions = {"exe", "dll"}; |
| for (StringRef Extension : ExecutableExtensions) { |
| std::string ExecutableImage = searchForExe(Filename, Extension); |
| if (ExecutableImage.empty()) |
| continue; |
| if (Error Err = handleObject(Readers, Filename, Buffer.getBuffer(), |
| ExecutableImage)) { |
| consumeError(std::move(Err)); |
| continue; |
| } |
| return Error::success(); |
| } |
| |
| std::vector<StringRef> ObjectExtensions = {"o", "obj", "lib"}; |
| for (StringRef Extension : ObjectExtensions) { |
| std::string ObjectImage = searchForObj(Filename, Extension); |
| if (ObjectImage.empty()) |
| continue; |
| if (Error Err = handleFile(Readers, ObjectImage)) { |
| consumeError(std::move(Err)); |
| continue; |
| } |
| return Error::success(); |
| } |
| |
| // No matching executable/object image was found. Load the given PDB. |
| return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); |
| } |
| if (FileMagic == file_magic::pecoff_executable) { |
| // If we have a valid executable, try to find a matching PDB file. |
| Expected<std::string> PdbPath = NativeSession::searchForPdb({Filename}); |
| if (errorToErrorCode(PdbPath.takeError())) { |
| return createStringError( |
| errc::not_supported, |
| "Binary object format in '%s' does not have debug info.", |
| Filename.str().c_str()); |
| } |
| // Process the matching PDB file and pass the executable filename. |
| return handleFile(Readers, PdbPath.get(), Filename); |
| } |
| |
| Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buffer); |
| if (errorToErrorCode(BinOrErr.takeError())) { |
| return createStringError(errc::not_supported, |
| "Binary object format in '%s' is not supported.", |
| Filename.str().c_str()); |
| } |
| return handleObject(Readers, Filename, *BinOrErr.get()); |
| } |
| |
| Error LVReaderHandler::handleFile(LVReaders &Readers, StringRef Filename, |
| StringRef ExePath) { |
| // Convert any Windows backslashes into forward slashes to get the path. |
| std::string ConvertedPath = |
| sys::path::convert_to_slash(Filename, sys::path::Style::windows); |
| ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = |
| MemoryBuffer::getFileOrSTDIN(ConvertedPath); |
| if (BuffOrErr.getError()) { |
| return createStringError(errc::bad_file_descriptor, |
| "File '%s' does not exist.", |
| ConvertedPath.c_str()); |
| } |
| std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get()); |
| return handleBuffer(Readers, ConvertedPath, *Buffer, ExePath); |
| } |
| |
| Error LVReaderHandler::handleMach(LVReaders &Readers, StringRef Filename, |
| MachOUniversalBinary &Mach) { |
| for (const MachOUniversalBinary::ObjectForArch &ObjForArch : Mach.objects()) { |
| std::string ObjName = (Twine(Filename) + Twine("(") + |
| Twine(ObjForArch.getArchFlagName()) + Twine(")")) |
| .str(); |
| if (Expected<std::unique_ptr<MachOObjectFile>> MachOOrErr = |
| ObjForArch.getAsObjectFile()) { |
| MachOObjectFile &Obj = **MachOOrErr; |
| PdbOrObj Input = &Obj; |
| if (Error Err = |
| createReader(Filename, Readers, Input, Obj.getFileFormatName())) |
| return Err; |
| continue; |
| } else |
| consumeError(MachOOrErr.takeError()); |
| if (Expected<std::unique_ptr<Archive>> ArchiveOrErr = |
| ObjForArch.getAsArchive()) { |
| if (Error Err = handleArchive(Readers, ObjName, *ArchiveOrErr.get())) |
| return Err; |
| continue; |
| } else |
| consumeError(ArchiveOrErr.takeError()); |
| } |
| return Error::success(); |
| } |
| |
| Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, |
| Binary &Binary) { |
| if (PdbOrObj Input = dyn_cast<ObjectFile>(&Binary)) |
| return createReader(Filename, Readers, Input, |
| cast<ObjectFile *>(Input)->getFileFormatName()); |
| |
| if (MachOUniversalBinary *Fat = dyn_cast<MachOUniversalBinary>(&Binary)) |
| return handleMach(Readers, Filename, *Fat); |
| |
| if (Archive *Arch = dyn_cast<Archive>(&Binary)) |
| return handleArchive(Readers, Filename, *Arch); |
| |
| return createStringError(errc::not_supported, |
| "Binary object format in '%s' is not supported.", |
| Filename.str().c_str()); |
| } |
| |
| Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, |
| StringRef Buffer, StringRef ExePath) { |
| std::unique_ptr<IPDBSession> Session; |
| if (Error Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session)) |
| return createStringError(errorToErrorCode(std::move(Err)), "%s", |
| Filename.str().c_str()); |
| |
| std::unique_ptr<NativeSession> PdbSession; |
| PdbSession.reset(static_cast<NativeSession *>(Session.release())); |
| PdbOrObj Input = &PdbSession->getPDBFile(); |
| StringRef FileFormatName; |
| size_t Pos = Buffer.find_first_of("\r\n"); |
| if (Pos) |
| FileFormatName = Buffer.substr(0, Pos - 1); |
| return createReader(Filename, Readers, Input, FileFormatName, ExePath); |
| } |
| |
| Error LVReaderHandler::createReaders() { |
| LLVM_DEBUG(dbgs() << "createReaders\n"); |
| for (std::string &Object : Objects) { |
| LVReaders Readers; |
| if (Error Err = createReader(Object, Readers)) |
| return Err; |
| TheReaders.insert(TheReaders.end(), |
| std::make_move_iterator(Readers.begin()), |
| std::make_move_iterator(Readers.end())); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error LVReaderHandler::printReaders() { |
| LLVM_DEBUG(dbgs() << "printReaders\n"); |
| if (options().getPrintExecute()) |
| for (const std::unique_ptr<LVReader> &Reader : TheReaders) |
| if (Error Err = Reader->doPrint()) |
| return Err; |
| |
| return Error::success(); |
| } |
| |
| Error LVReaderHandler::compareReaders() { |
| LLVM_DEBUG(dbgs() << "compareReaders\n"); |
| size_t ReadersCount = TheReaders.size(); |
| if (options().getCompareExecute() && ReadersCount >= 2) { |
| // If we have more than 2 readers, compare them by pairs. |
| size_t ViewPairs = ReadersCount / 2; |
| LVCompare Compare(OS); |
| for (size_t Pair = 0, Index = 0; Pair < ViewPairs; ++Pair) { |
| if (Error Err = Compare.execute(TheReaders[Index].get(), |
| TheReaders[Index + 1].get())) |
| return Err; |
| Index += 2; |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| void LVReaderHandler::print(raw_ostream &OS) const { OS << "ReaderHandler\n"; } |