Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lld/COFF/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ add_lld_library(lldCOFF
MapFile.cpp
MarkLive.cpp
MinGW.cpp
Pdb2Symbols.cpp
Pdb2TypeServer.cpp
PDB.cpp
SymbolTable.cpp
Symbols.cpp
Expand Down
23 changes: 23 additions & 0 deletions lld/COFF/Chunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,9 @@ StringRef SectionChunk::getDebugName() const {
}

ArrayRef<uint8_t> SectionChunk::getContents() const {
if (ArrayRef<uint8_t> override = file->getDebugSOverride(this);
!override.empty())
return override;
ArrayRef<uint8_t> a;
cantFail(file->getCOFFObj()->getSectionContents(header, a));
return a;
Expand Down Expand Up @@ -729,6 +732,26 @@ ArrayRef<uint8_t> SectionChunk::consumeDebugMagic(ArrayRef<uint8_t> data,
? DEBUG_HASHES_SECTION_MAGIC
: DEBUG_SECTION_MAGIC;
if (magic != expectedMagic) {
// MSVC 6.0 /Zi emits .debug$T and .debug$S with the old CodeView "C11"
// signature (CV_SIGNATURE_C11 = 2) instead of C13 (4).
//
// For .debug$T, the record that follows is a normal CVType -- an old
// LF_TYPESERVER / LF_TYPESERVER_ST pointing at a PDB 2.0 type server --
// which the type-server path in ObjFile::initializeDependencies()
// already handles.
//
// For .debug$S, ObjFile::readSection() already rewrote this chunk's old
// "_ST" symbol kinds to modern equivalents in place (see Pdb2Symbols.h)
// and installed the result via setDebugSOverride -- what getContents()
// returns here IS that converted buffer, just still C11-tagged since
// the rewrite doesn't touch the magic. Old .debug$S has no subsection
// framing (unlike modern C13), so callers must not treat the stripped
// bytes as a subsection array -- see the callers in PDB.cpp, which check
// isOldCodeViewSymbols() before parsing.
constexpr uint32_t CV_SIGNATURE_C11 = 2;
if ((sectionName == ".debug$T" || sectionName == ".debug$S") &&
magic == CV_SIGNATURE_C11)
return data.slice(4);
warn("ignoring section " + sectionName + " with unrecognized magic 0x" +
utohexstr(magic));
return {};
Expand Down
112 changes: 98 additions & 14 deletions lld/COFF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//

#include "InputFiles.h"
#include "Pdb2Symbols.h"
#include "Pdb2TypeServer.h"
#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Config.h"
Expand Down Expand Up @@ -319,8 +321,27 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,

// CodeView sections are stored to a different vector because they are not
// linked in the regular manner.
if (c->isCodeView())
if (c->isCodeView()) {
debugChunks.push_back(c);
// MSVC 6.0 /Zi emits .debug$S with the old CV_SIGNATURE_C11 format (a
// flat symbol stream using old "_ST" kinds; see Pdb2Symbols.h) -- and an
// object with more than one function has additional .debug$S sections,
// one per function, that carry no magic at all (a "bare" continuation
// starting directly with S_GPROC32_ST/S_LPROC32_ST). Convert either kind
// in place now, once, rather than at each of the several places that
// later read this chunk's contents.
if (name == ".debug$S" &&
(isOldCodeViewSymbols(c->getContents()) ||
isBareOldCodeViewSymbols(c->getContents()))) {
if (Expected<ArrayRef<uint8_t>> converted =
convertOldCodeViewSymbols(c->getContents(), bAlloc()))
setDebugSOverride(c, *converted);
else
Warn(symtab.ctx) << "failed to convert VC6 .debug$S in "
<< toString(this) << ": "
<< toString(converted.takeError());
}
}
else if (name == ".gfids$y")
guardFidChunks.push_back(c);
else if (name == ".giats$y")
Expand Down Expand Up @@ -866,6 +887,11 @@ void ObjFile::initializeFlags() {
}
}

// Forward declaration; defined further down, needed by initializeDependencies
// to resolve a type-server path relative to this OBJ / the output file.
static std::optional<std::string>
findPdbPath(StringRef pdbPath, ObjFile *dependentFile, StringRef outputPath);

// Depending on the compilation flags, OBJs can refer to external files,
// necessary to merge this OBJ into the final PDB. We currently support two
// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu.
Expand Down Expand Up @@ -922,23 +948,81 @@ void ObjFile::initializeDependencies() {
}

// Handle older LF_TYPESERVER (0x1501) and LF_TYPESERVER_ST (0x0016) formats.
// These use a 4-byte CRC signature instead of a 16-byte GUID. Parse manually
// and create a TypeServer2Record with a zeroed GUID so that GUID matching is
// skipped in UseTypeServerSource::getTypeServerSource().
// These are MSVC 6.0 /Zi objects pointing at a PDB 2.0 ("JG") type server,
// an older MSF container that PDBFile/NativeSession cannot open (hence no
// TypeServerSource/UseTypeServerSource route here, unlike the modern
// LF_TYPESERVER2 case above). Resolve the path ourselves, read the file
// directly, and hand the reindexed+fixed-up type records to this object as
// if they were a plain inline .debug$T -- see Pdb2TypeServer.h.
if (firstType->kind() == LF_TYPESERVER ||
firstType->kind() == LF_TYPESERVER_ST) {
// Every failure path below falls back to a plain, empty TpiSource
// (mirroring the "no data" case above) rather than leaving
// debugTypesObj null: this object's .debug$S may still have real symbol
// records even though its type-server sidecar couldn't be found/opened/
// parsed (a renamed build tree, a missing .o.pdb, an unrecognized
// format), and PDBLinker::writeSymbolRecord dereferences debugTypesObj
// unconditionally for every symbol record it processes -- leaving it
// null here would crash the whole link on what should just be "this
// object's symbols get remapped against no extra local types."
ArrayRef<uint8_t> content = firstType->content();
// Record body: [4 bytes CRC sig][4 bytes age][null-terminated name]
if (content.size() >= 9) {
uint32_t age = support::endian::read32le(content.data() + 4);
StringRef name(reinterpret_cast<const char *>(content.data() + 8));
TypeServer2Record ts(TypeRecordKind::TypeServer2);
ts.Age = age;
ts.Name = name;
// Guid is zero-initialized; GUID matching skipped for old-style records.
debugTypesObj = makeUseTypeServerSource(ctx, this, ts);
enqueuePdbFile(ts.getName(), this);
// Record body: [4 bytes CRC sig][4 bytes age][name]. The old "_ST" form
// (MSVC 6.0 /Zi) stores the name length-prefixed (1 byte length + chars);
// the non-ST form stores it null-terminated.
if (content.size() < 9) {
debugTypesObj = makeTpiSource(ctx, this);
return;
}
StringRef name;
if (firstType->kind() == LF_TYPESERVER_ST) {
uint8_t len = content[8];
if (9 + static_cast<size_t>(len) <= content.size())
name = StringRef(reinterpret_cast<const char *>(content.data() + 9),
len);
} else {
name = StringRef(reinterpret_cast<const char *>(content.data() + 8));
}
if (name.empty()) {
debugTypesObj = makeTpiSource(ctx, this);
return;
}

std::optional<std::string> path =
findPdbPath(name.str(), this, symtab.ctx.config.outputFile);
if (!path) {
Warn(ctx) << "VC6 type server not found: " << name;
debugTypesObj = makeTpiSource(ctx, this);
return;
}
ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr =
MemoryBuffer::getFile(*path, /*IsText=*/false,
/*RequiresNullTerminator=*/false);
if (!mbOrErr) {
Warn(ctx) << "failed to open VC6 type server " << *path << ": "
<< mbOrErr.getError().message();
debugTypesObj = makeTpiSource(ctx, this);
return;
}
MemoryBufferRef mbRef = (*mbOrErr)->getMemBufferRef();
if (!isPdb2TypeServer(mbRef)) {
Warn(ctx) << *path << " is not a recognized PDB 2.0 type server";
debugTypesObj = makeTpiSource(ctx, this);
return;
}
Expected<ArrayRef<uint8_t>> typesOrErr =
readPdb2TypeServerTypes(mbRef, bAlloc());
if (!typesOrErr) {
Warn(ctx) << "failed to read VC6 type server " << *path << ": "
<< toString(typesOrErr.takeError());
debugTypesObj = makeTpiSource(ctx, this);
return;
}
// Keep the backing MemoryBuffer alive for the life of the link; the types
// above were copied out into bAlloc(), but nothing else needs `mbOrErr`.
ctx.driver.takeBuffer(std::move(*mbOrErr));

debugTypes = *typesOrErr;
debugTypesObj = makeTpiSource(ctx, this);
return;
}

Expand Down
19 changes: 19 additions & 0 deletions lld/COFF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ class ObjFile : public InputFile {
MachineTypes getMachineType() const override;
ArrayRef<Chunk *> getChunks() { return chunks; }
ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }

// See debugSOverrides below.
void setDebugSOverride(const SectionChunk *sc, ArrayRef<uint8_t> data) {
debugSOverrides[sc] = data;
}
ArrayRef<uint8_t> getDebugSOverride(const SectionChunk *sc) const {
auto it = debugSOverrides.find(sc);
return it == debugSOverrides.end() ? ArrayRef<uint8_t>() : it->second;
}
ArrayRef<SectionChunk *> getSXDataChunks() { return sxDataChunks; }
ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
ArrayRef<SectionChunk *> getGuardIATChunks() { return guardIATChunks; }
Expand Down Expand Up @@ -289,6 +298,16 @@ class ObjFile : public InputFile {
// CodeView debug info sections.
std::vector<SectionChunk *> debugChunks;

// Overrides the contents SectionChunk::getContents() would otherwise
// derive from the raw COFF section, for VC6 .debug$S chunks whose old
// "_ST" symbol kinds have been rewritten to modern equivalents (see
// Pdb2Symbols.h). Same total size as the original, so this chunk's COFF
// relocations remain valid without adjustment. Keyed by ObjFile rather
// than added as a SectionChunk field: SectionChunk has a static_assert on
// its size (there can be very many of them), while ObjFile instances are
// comparatively rare.
llvm::DenseMap<const SectionChunk *, ArrayRef<uint8_t>> debugSOverrides;

// Chunks containing symbol table indices of exception handlers. Only used for
// 32-bit x86.
std::vector<SectionChunk *> sxDataChunks;
Expand Down
102 changes: 84 additions & 18 deletions lld/COFF/PDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Config.h"
#include "DebugTypes.h"
#include "Driver.h"
#include "Pdb2Symbols.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "TypeMerger.h"
Expand Down Expand Up @@ -657,23 +658,25 @@ Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file,
continue;

ArrayRef<uint8_t> sectionContents = debugChunk->getContents();
auto contents =
SectionChunk::consumeDebugMagic(sectionContents, ".debug$S");
DebugSubsectionArray subsections;
BinaryStreamReader reader(contents, llvm::endianness::little);
exitOnErr(reader.readArray(subsections, contents.size()));
// Whether this chunk got an old-format override at load time (see
// Pdb2Symbols.h) is the signal to use here, not re-inspecting
// sectionContents: conversion rewrites the bare case's leading kind
// value (what isBareOldCodeViewSymbols keys on) from old to modern, so
// checking it again post-conversion would always come back false. The
// magic bytes are never touched by conversion, so isOldCodeViewSymbols
// remains valid to tell whether there's a magic to strip.
bool oldFormat = !file->getDebugSOverride(debugChunk).empty();
bool magicPrefixed = isOldCodeViewSymbols(sectionContents);
ArrayRef<uint8_t> contents =
magicPrefixed ? SectionChunk::consumeDebugMagic(sectionContents,
".debug$S")
: sectionContents;

uint32_t nextRelocIndex = 0;
for (const DebugSubsectionRecord &ss : subsections) {
if (ss.kind() != DebugSubsectionKind::Symbols)
continue;

auto processSymbols = [&](ArrayRef<uint8_t> symsBuffer) -> Error {
uint32_t moduleSymStart = writer.getOffset();
scopes.clear();
storage.clear();
ArrayRef<uint8_t> symsBuffer;
BinaryStreamRef sr = ss.getRecordData();
cantFail(sr.readBytes(0, sr.getLength(), symsBuffer));
auto ec = forEachCodeViewRecord<CVSymbol>(
symsBuffer, [&](CVSymbol sym) -> llvm::Error {
// Track the current scope. Only update records in the postmerge
Expand Down Expand Up @@ -704,7 +707,31 @@ Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file,
// at once.
// TODO: Consider buffering symbols for the entire object file to reduce
// overhead even further.
if (Error e = writer.writeBytes(storage))
return writer.writeBytes(storage);
};

// Old (VC6 CV_SIGNATURE_C11, or bare per-function continuation) .debug$S
// has no subsection framing: the whole thing (already converted from
// "_ST" kinds; see Pdb2Symbols.h) IS one flat symbol stream. Modern
// .debug$S wraps symbol data in a Kind=Symbols subsection alongside
// others (StringTable, Lines, ...).
if (oldFormat) {
if (Error e = processSymbols(contents))
return e;
continue;
}

DebugSubsectionArray subsections;
BinaryStreamReader reader(contents, llvm::endianness::little);
exitOnErr(reader.readArray(subsections, contents.size()));

for (const DebugSubsectionRecord &ss : subsections) {
if (ss.kind() != DebugSubsectionKind::Symbols)
continue;
ArrayRef<uint8_t> symsBuffer;
BinaryStreamRef sr = ss.getRecordData();
cantFail(sr.readBytes(0, sr.getLength(), symsBuffer));
if (Error e = processSymbols(symsBuffer))
return e;
}
}
Expand Down Expand Up @@ -759,17 +786,41 @@ translateStringTableIndex(COFFLinkerContext &ctx, uint32_t objIndex,
void DebugSHandler::handleDebugS(SectionChunk *debugChunk) {
// Note that we are processing the *unrelocated* section contents. They will
// be relocated later during PDB writing.
ArrayRef<uint8_t> contents = debugChunk->getContents();
contents = SectionChunk::consumeDebugMagic(contents, ".debug$S");
DebugSubsectionArray subsections;
BinaryStreamReader reader(contents, llvm::endianness::little);
ArrayRef<uint8_t> rawContents = debugChunk->getContents();
// Whether this chunk got an old-format override at load time (see
// Pdb2Symbols.h) is the signal to use here, not re-inspecting
// rawContents: conversion rewrites the bare case's leading kind value
// from old to modern, so checking it again post-conversion would always
// come back false. The magic bytes are never touched by conversion, so
// isOldCodeViewSymbols remains valid to tell whether there's a magic to
// strip.
bool oldFormat = !file.getDebugSOverride(debugChunk).empty();
bool magicPrefixed = isOldCodeViewSymbols(rawContents);
ArrayRef<uint8_t> contents =
magicPrefixed ? SectionChunk::consumeDebugMagic(rawContents, ".debug$S")
: rawContents;
ExitOnError exitOnErr;
exitOnErr(reader.readArray(subsections, contents.size()));
debugChunk->sortRelocations();

// Reset the relocation index, since this is a new section.
nextRelocIndex = 0;

// Old (VC6 CV_SIGNATURE_C11, or bare per-function continuation) .debug$S
// has no subsection framing: the whole thing (already converted from
// "_ST" kinds; see Pdb2Symbols.h) IS one flat symbol stream, and this
// format has no StringTable/FileChecksums/Lines/etc subsections to look
// for.
if (oldFormat) {
linker.analyzeSymbolSubsection(
debugChunk, moduleStreamSize, nextRelocIndex, stringTableFixups,
BinaryStreamRef(contents, llvm::endianness::little));
return;
}

DebugSubsectionArray subsections;
BinaryStreamReader reader(contents, llvm::endianness::little);
exitOnErr(reader.readArray(subsections, contents.size()));

for (const DebugSubsectionRecord &ss : subsections) {
// Ignore subsections with the 'ignore' bit. Some versions of the Visual C++
// runtime have subsections with this bit set.
Expand Down Expand Up @@ -1522,6 +1573,11 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,

// Output COFF groups for individual chunks of this section.
for (PartialSection *sec : os.contribSections) {
// A contributing section can end up with no chunks (e.g. after section
// merging such as .idata folded into .rdata); it has nothing to emit and
// would otherwise dereference an empty chunk list below.
if (sec->chunks.empty())
continue;
addLinkerModuleCoffGroup(sec, mod, os);
}
}
Expand Down Expand Up @@ -1771,6 +1827,16 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
for (SectionChunk *dbgC : c->file->getDebugChunks()) {
if (dbgC->getSectionName() != ".debug$S")
continue;
// Old (VC6 CV_SIGNATURE_C11, or bare per-function continuation)
// .debug$S is a flat symbol stream with no StringTable/FileChecksums/
// Lines subsections to look for (see Pdb2Symbols.h) -- nothing here
// applies to it. Whether this chunk got an old-format override at load
// time is the signal (see the comment in handleDebugS): conversion
// rewrites the bare case's leading kind value, so re-checking
// isBareOldCodeViewSymbols against the (already-converted) contents
// here would always come back false.
if (!dbgC->file->getDebugSOverride(dbgC).empty())
continue;

// Build a mapping of SECREL relocations in dbgC that refer to `c`.
DenseMap<uint32_t, uint32_t> secrels;
Expand Down
Loading
Loading