mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 20:16:06 +00:00

As part of the WebAssembly support work review https://github.com/llvm/llvm-project/pull/82588 It was decided to rename: Files: LVElfReader.cpp[h] -> LVDWARFReader.cpp[h] ELFReaderTest.cpp -> DWARFReaderTest.cpp Class: LVELFReader -> LVDWARFReader The name LVDWARFReader would match the another reader LVCodeViewReader as they will reflect the type of debug information format that they are parsing.
320 lines
12 KiB
C++
320 lines
12 KiB
C++
//===-- 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/LVDWARFReader.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() || Obj.isWasm())
|
|
return std::make_unique<LVDWARFReader>(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"; }
|