forked from OSchip/llvm-project
269 lines
9.9 KiB
C++
269 lines
9.9 KiB
C++
//===- DebugTypes.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DebugTypes.h"
|
|
#include "Driver.h"
|
|
#include "InputFiles.h"
|
|
#include "lld/Common/ErrorHandler.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
|
#include "llvm/DebugInfo/PDB/GenericError.h"
|
|
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace lld;
|
|
using namespace lld::coff;
|
|
using namespace llvm;
|
|
using namespace llvm::codeview;
|
|
|
|
namespace {
|
|
// The TypeServerSource class represents a PDB type server, a file referenced by
|
|
// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
|
|
// files, therefore there must be only once instance per OBJ lot. The file path
|
|
// is discovered from the dependent OBJ's debug type stream. The
|
|
// TypeServerSource object is then queued and loaded by the COFF Driver. The
|
|
// debug type stream for such PDB files will be merged first in the final PDB,
|
|
// before any dependent OBJ.
|
|
class TypeServerSource : public TpiSource {
|
|
public:
|
|
explicit TypeServerSource(MemoryBufferRef M, llvm::pdb::NativeSession *S)
|
|
: TpiSource(PDB, nullptr), Session(S), MB(M) {}
|
|
|
|
// Queue a PDB type server for loading in the COFF Driver
|
|
static void enqueue(const ObjFile *DependentFile,
|
|
const TypeServer2Record &TS);
|
|
|
|
// Create an instance
|
|
static Expected<TypeServerSource *> getInstance(MemoryBufferRef M);
|
|
|
|
// Fetch the PDB instance loaded for a corresponding dependent OBJ.
|
|
static Expected<TypeServerSource *>
|
|
findFromFile(const ObjFile *DependentFile);
|
|
|
|
static std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
|
Instances;
|
|
|
|
// The interface to the PDB (if it was opened successfully)
|
|
std::unique_ptr<llvm::pdb::NativeSession> Session;
|
|
|
|
private:
|
|
MemoryBufferRef MB;
|
|
};
|
|
|
|
// This class represents the debug type stream of an OBJ file that depends on a
|
|
// PDB type server (see TypeServerSource).
|
|
class UseTypeServerSource : public TpiSource {
|
|
public:
|
|
UseTypeServerSource(const ObjFile *F, const TypeServer2Record *TS)
|
|
: TpiSource(UsingPDB, F), TypeServerDependency(*TS) {}
|
|
|
|
// Information about the PDB type server dependency, that needs to be loaded
|
|
// in before merging this OBJ.
|
|
TypeServer2Record TypeServerDependency;
|
|
};
|
|
|
|
// This class represents the debug type stream of a Microsoft precompiled
|
|
// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output
|
|
// PDB, before any other OBJs that depend on this. Note that only MSVC generate
|
|
// such files, clang does not.
|
|
class PrecompSource : public TpiSource {
|
|
public:
|
|
PrecompSource(const ObjFile *F) : TpiSource(PCH, F) {}
|
|
};
|
|
|
|
// This class represents the debug type stream of an OBJ file that depends on a
|
|
// Microsoft precompiled headers OBJ (see PrecompSource).
|
|
class UsePrecompSource : public TpiSource {
|
|
public:
|
|
UsePrecompSource(const ObjFile *F, const PrecompRecord *Precomp)
|
|
: TpiSource(UsingPCH, F), PrecompDependency(*Precomp) {}
|
|
|
|
// Information about the Precomp OBJ dependency, that needs to be loaded in
|
|
// before merging this OBJ.
|
|
PrecompRecord PrecompDependency;
|
|
};
|
|
} // namespace
|
|
|
|
static std::vector<std::unique_ptr<TpiSource>> GC;
|
|
|
|
TpiSource::TpiSource(TpiKind K, const ObjFile *F) : Kind(K), File(F) {
|
|
GC.push_back(std::unique_ptr<TpiSource>(this));
|
|
}
|
|
|
|
TpiSource *lld::coff::makeTpiSource(const ObjFile *F) {
|
|
return new TpiSource(TpiSource::Regular, F);
|
|
}
|
|
|
|
TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *F,
|
|
const TypeServer2Record *TS) {
|
|
TypeServerSource::enqueue(F, *TS);
|
|
return new UseTypeServerSource(F, TS);
|
|
}
|
|
|
|
TpiSource *lld::coff::makePrecompSource(const ObjFile *F) {
|
|
return new PrecompSource(F);
|
|
}
|
|
|
|
TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *F,
|
|
const PrecompRecord *Precomp) {
|
|
return new UsePrecompSource(F, Precomp);
|
|
}
|
|
|
|
namespace lld {
|
|
namespace coff {
|
|
template <>
|
|
const PrecompRecord &retrieveDependencyInfo(const TpiSource *Source) {
|
|
assert(Source->Kind == TpiSource::UsingPCH);
|
|
return ((const UsePrecompSource *)Source)->PrecompDependency;
|
|
}
|
|
|
|
template <>
|
|
const TypeServer2Record &retrieveDependencyInfo(const TpiSource *Source) {
|
|
assert(Source->Kind == TpiSource::UsingPDB);
|
|
return ((const UseTypeServerSource *)Source)->TypeServerDependency;
|
|
}
|
|
} // namespace coff
|
|
} // namespace lld
|
|
|
|
std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
|
TypeServerSource::Instances;
|
|
|
|
// Make a PDB path assuming the PDB is in the same folder as the OBJ
|
|
static std::string getPdbBaseName(const ObjFile *File, StringRef TSPath) {
|
|
StringRef LocalPath =
|
|
!File->ParentName.empty() ? File->ParentName : File->getName();
|
|
SmallString<128> Path = sys::path::parent_path(LocalPath);
|
|
|
|
// Currently, type server PDBs are only created by MSVC cl, which only runs
|
|
// on Windows, so we can assume type server paths are Windows style.
|
|
sys::path::append(Path, sys::path::filename(TSPath, sys::path::Style::windows));
|
|
return Path.str();
|
|
}
|
|
|
|
// The casing of the PDB path stamped in the OBJ can differ from the actual path
|
|
// on disk. With this, we ensure to always use lowercase as a key for the
|
|
// PDBInputFile::Instances map, at least on Windows.
|
|
static std::string normalizePdbPath(StringRef path) {
|
|
#if defined(_WIN32)
|
|
return path.lower();
|
|
#else // LINUX
|
|
return path;
|
|
#endif
|
|
}
|
|
|
|
// If existing, return the actual PDB path on disk.
|
|
static Optional<std::string> findPdbPath(StringRef PDBPath,
|
|
const ObjFile *DependentFile) {
|
|
// Ensure the file exists before anything else. In some cases, if the path
|
|
// points to a removable device, Driver::enqueuePath() would fail with an
|
|
// error (EAGAIN, "resource unavailable try again") which we want to skip
|
|
// silently.
|
|
if (llvm::sys::fs::exists(PDBPath))
|
|
return normalizePdbPath(PDBPath);
|
|
std::string Ret = getPdbBaseName(DependentFile, PDBPath);
|
|
if (llvm::sys::fs::exists(Ret))
|
|
return normalizePdbPath(Ret);
|
|
return None;
|
|
}
|
|
|
|
// Fetch the PDB instance that was already loaded by the COFF Driver.
|
|
Expected<TypeServerSource *>
|
|
TypeServerSource::findFromFile(const ObjFile *DependentFile) {
|
|
const TypeServer2Record &TS =
|
|
retrieveDependencyInfo<TypeServer2Record>(DependentFile->DebugTypesObj);
|
|
|
|
Optional<std::string> P = findPdbPath(TS.Name, DependentFile);
|
|
if (!P)
|
|
return createFileError(TS.Name, errorCodeToError(std::error_code(
|
|
ENOENT, std::generic_category())));
|
|
|
|
auto It = TypeServerSource::Instances.find(*P);
|
|
// The PDB file exists on disk, at this point we expect it to have been
|
|
// inserted in the map by TypeServerSource::loadPDB()
|
|
assert(It != TypeServerSource::Instances.end());
|
|
|
|
std::pair<std::string, TypeServerSource *> &PDB = It->second;
|
|
|
|
if (!PDB.second)
|
|
return createFileError(
|
|
*P, createStringError(inconvertibleErrorCode(), PDB.first.c_str()));
|
|
|
|
pdb::PDBFile &PDBFile = (PDB.second)->Session->getPDBFile();
|
|
pdb::InfoStream &Info = cantFail(PDBFile.getPDBInfoStream());
|
|
|
|
// Just because a file with a matching name was found doesn't mean it can be
|
|
// used. The GUID must match between the PDB header and the OBJ
|
|
// TypeServer2 record. The 'Age' is used by MSVC incremental compilation.
|
|
if (Info.getGuid() != TS.getGuid())
|
|
return createFileError(
|
|
TS.Name,
|
|
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
|
|
|
|
return PDB.second;
|
|
}
|
|
|
|
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
|
|
// moved here.
|
|
Expected<llvm::pdb::NativeSession *>
|
|
lld::coff::findTypeServerSource(const ObjFile *F) {
|
|
Expected<TypeServerSource *> TS = TypeServerSource::findFromFile(F);
|
|
if (!TS)
|
|
return TS.takeError();
|
|
return TS.get()->Session.get();
|
|
}
|
|
|
|
// Queue a PDB type server for loading in the COFF Driver
|
|
void TypeServerSource::enqueue(const ObjFile *DependentFile,
|
|
const TypeServer2Record &TS) {
|
|
// Start by finding where the PDB is located (either the record path or next
|
|
// to the OBJ file)
|
|
Optional<std::string> P = findPdbPath(TS.Name, DependentFile);
|
|
if (!P)
|
|
return;
|
|
auto It = TypeServerSource::Instances.emplace(
|
|
*P, std::pair<std::string, TypeServerSource *>{});
|
|
if (!It.second)
|
|
return; // another OBJ already scheduled this PDB for load
|
|
|
|
Driver->enqueuePath(*P, false);
|
|
}
|
|
|
|
// Create an instance of TypeServerSource or an error string if the PDB couldn't
|
|
// be loaded. The error message will be displayed later, when the referring OBJ
|
|
// will be merged in. NOTE - a PDB load failure is not a link error: some
|
|
// debug info will simply be missing from the final PDB - that is the default
|
|
// accepted behavior.
|
|
void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef M) {
|
|
std::string Path = normalizePdbPath(M.getBufferIdentifier());
|
|
|
|
Expected<TypeServerSource *> TS = TypeServerSource::getInstance(M);
|
|
if (!TS)
|
|
TypeServerSource::Instances[Path] = {toString(TS.takeError()), nullptr};
|
|
else
|
|
TypeServerSource::Instances[Path] = {{}, *TS};
|
|
}
|
|
|
|
Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef M) {
|
|
std::unique_ptr<llvm::pdb::IPDBSession> ISession;
|
|
Error Err = pdb::NativeSession::createFromPdb(
|
|
MemoryBuffer::getMemBuffer(M, false), ISession);
|
|
if (Err)
|
|
return std::move(Err);
|
|
|
|
std::unique_ptr<llvm::pdb::NativeSession> Session(
|
|
static_cast<pdb::NativeSession *>(ISession.release()));
|
|
|
|
pdb::PDBFile &PDBFile = Session->getPDBFile();
|
|
Expected<pdb::InfoStream &> Info = PDBFile.getPDBInfoStream();
|
|
// All PDB Files should have an Info stream.
|
|
if (!Info)
|
|
return Info.takeError();
|
|
return new TypeServerSource(M, Session.release());
|
|
}
|