summaryrefslogtreecommitdiff
path: root/COFF/Writer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'COFF/Writer.cpp')
-rw-r--r--COFF/Writer.cpp198
1 files changed, 157 insertions, 41 deletions
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index d8077df95701..3e69aebbb424 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -7,13 +7,15 @@
//
//===----------------------------------------------------------------------===//
+#include "Writer.h"
#include "Config.h"
#include "DLL.h"
#include "Error.h"
#include "InputFiles.h"
+#include "Memory.h"
+#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
-#include "Writer.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
@@ -21,6 +23,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/RandomNumberGenerator.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
@@ -42,6 +45,61 @@ static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;
namespace {
+
+class DebugDirectoryChunk : public Chunk {
+public:
+ DebugDirectoryChunk(const std::vector<std::unique_ptr<Chunk>> &R)
+ : Records(R) {}
+
+ size_t getSize() const override {
+ return Records.size() * sizeof(debug_directory);
+ }
+
+ void writeTo(uint8_t *B) const override {
+ auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
+
+ for (const std::unique_ptr<Chunk> &Record : Records) {
+ D->Characteristics = 0;
+ D->TimeDateStamp = 0;
+ D->MajorVersion = 0;
+ D->MinorVersion = 0;
+ D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
+ D->SizeOfData = Record->getSize();
+ D->AddressOfRawData = Record->getRVA();
+ // TODO(compnerd) get the file offset
+ D->PointerToRawData = 0;
+
+ ++D;
+ }
+ }
+
+private:
+ const std::vector<std::unique_ptr<Chunk>> &Records;
+};
+
+class CVDebugRecordChunk : public Chunk {
+ size_t getSize() const override {
+ return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1;
+ }
+
+ void writeTo(uint8_t *B) const override {
+ // Save off the DebugInfo entry to backfill the file signature (build id)
+ // in Writer::writeBuildId
+ DI = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
+
+ DI->Signature.CVSignature = OMF::Signature::PDB70;
+
+ // variable sized field (PDB Path)
+ auto *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*DI));
+ if (!Config->PDBPath.empty())
+ memcpy(P, Config->PDBPath.data(), Config->PDBPath.size());
+ P[Config->PDBPath.size()] = '\0';
+ }
+
+public:
+ mutable codeview::DebugInfo *DI = nullptr;
+};
+
// The writer writes a SymbolTable result to a file.
class Writer {
public:
@@ -62,6 +120,7 @@ private:
void setSectionPermissions();
void writeSections();
void sortExceptionTable();
+ void writeBuildId();
void applyRelocations();
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
@@ -76,9 +135,7 @@ private:
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
SymbolTable *Symtab;
- std::unique_ptr<llvm::FileOutputBuffer> Buffer;
- llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
- llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
+ std::unique_ptr<FileOutputBuffer> Buffer;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
@@ -87,6 +144,11 @@ private:
EdataContents Edata;
std::unique_ptr<SEHTableChunk> SEHTable;
+ std::unique_ptr<Chunk> DebugDirectory;
+ std::vector<std::unique_ptr<Chunk>> DebugRecords;
+ CVDebugRecordChunk *BuildId = nullptr;
+ ArrayRef<uint8_t> SectionTable;
+
uint64_t FileSize;
uint32_t PointerToSymbolTable = 0;
uint64_t SizeOfImage;
@@ -239,6 +301,11 @@ void Writer::run() {
fixSafeSEHSymbols();
writeSections();
sortExceptionTable();
+ writeBuildId();
+
+ if (!Config->PDBPath.empty())
+ createPDB(Config->PDBPath, Symtab, SectionTable);
+
if (auto EC = Buffer->commit())
fatal(EC, "failed to write the output file");
}
@@ -274,7 +341,7 @@ void Writer::createSections() {
StringRef Name = getOutputSection(Pair.first);
OutputSection *&Sec = Sections[Name];
if (!Sec) {
- Sec = new (CAlloc.Allocate()) OutputSection(Name);
+ Sec = make<OutputSection>(Name);
OutputSections.push_back(Sec);
}
std::vector<Chunk *> &Chunks = Pair.second;
@@ -286,25 +353,46 @@ void Writer::createSections() {
}
void Writer::createMiscChunks() {
+ OutputSection *RData = createSection(".rdata");
+
// Create thunks for locally-dllimported symbols.
if (!Symtab->LocalImportChunks.empty()) {
- OutputSection *Sec = createSection(".rdata");
for (Chunk *C : Symtab->LocalImportChunks)
- Sec->addChunk(C);
+ RData->addChunk(C);
+ }
+
+ // Create Debug Information Chunks
+ if (Config->Debug) {
+ DebugDirectory = llvm::make_unique<DebugDirectoryChunk>(DebugRecords);
+
+ // TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled
+ if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV)) {
+ auto Chunk = llvm::make_unique<CVDebugRecordChunk>();
+
+ BuildId = Chunk.get();
+ DebugRecords.push_back(std::move(Chunk));
+ }
+
+ RData->addChunk(DebugDirectory.get());
+ for (const std::unique_ptr<Chunk> &C : DebugRecords)
+ RData->addChunk(C.get());
}
// Create SEH table. x86-only.
if (Config->Machine != I386)
return;
+
std::set<Defined *> Handlers;
+
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
if (!File->SEHCompat)
return;
for (SymbolBody *B : File->SEHandlers)
- Handlers.insert(cast<Defined>(B->repl()));
+ Handlers.insert(cast<Defined>(B));
}
+
SEHTable.reset(new SEHTableChunk(Handlers));
- createSection(".rdata")->addChunk(SEHTable.get());
+ RData->addChunk(SEHTable.get());
}
// Create .idata section for the DLL-imported symbol table.
@@ -340,7 +428,7 @@ void Writer::createImportTables() {
Sec->addChunk(C);
}
if (!DelayIdata.empty()) {
- Defined *Helper = cast<Defined>(Config->DelayLoadHelper->repl());
+ Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
DelayIdata.create(Helper);
OutputSection *Sec = createSection(".didat");
for (Chunk *C : DelayIdata.getChunks())
@@ -383,6 +471,10 @@ size_t Writer::addEntryToStringTable(StringRef Str) {
}
Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
+ // Relative symbols are unrepresentable in a COFF symbol table.
+ if (isa<DefinedRelative>(Def))
+ return None;
+
if (auto *D = dyn_cast<DefinedRegular>(Def))
if (!D->getChunk()->isLive())
return None;
@@ -409,7 +501,6 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
switch (Def->kind()) {
case SymbolBody::DefinedAbsoluteKind:
- case SymbolBody::DefinedRelativeKind:
Sym.Value = Def->getRVA();
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
break;
@@ -445,13 +536,11 @@ void Writer::createSymbolAndStringTable() {
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles)
for (SymbolBody *B : File->getSymbols())
if (auto *D = dyn_cast<Defined>(B))
- if (Optional<coff_symbol16> Sym = createSymbol(D))
- OutputSymtab.push_back(*Sym);
-
- for (ImportFile *File : Symtab->ImportFiles)
- for (SymbolBody *B : File->getSymbols())
- if (Optional<coff_symbol16> Sym = createSymbol(cast<Defined>(B)))
- OutputSymtab.push_back(*Sym);
+ if (!D->WrittenToSymtab) {
+ D->WrittenToSymtab = true;
+ if (Optional<coff_symbol16> Sym = createSymbol(D))
+ OutputSymtab.push_back(*Sym);
+ }
OutputSection *LastSection = OutputSections.back();
// We position the symbol table to be adjacent to the end of the last section.
@@ -542,7 +631,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->SizeOfImage = SizeOfImage;
PE->SizeOfHeaders = SizeOfHeaders;
if (!Config->NoEntry) {
- Defined *Entry = cast<Defined>(Config->Entry->repl());
+ Defined *Entry = cast<Defined>(Config->Entry);
PE->AddressOfEntryPoint = Entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
if (Config->Machine == ARMNT)
@@ -584,33 +673,32 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
Dir[IAT].Size = Idata.getIATSize();
}
- if (!DelayIdata.empty()) {
- Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
- DelayIdata.getDirRVA();
- Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
- }
if (OutputSection *Sec = findSection(".rsrc")) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize();
}
- if (OutputSection *Sec = findSection(".reloc")) {
- Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
- Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
- }
if (OutputSection *Sec = findSection(".pdata")) {
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize();
}
+ if (OutputSection *Sec = findSection(".reloc")) {
+ Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
+ Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
+ }
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
- if (Defined *B = dyn_cast<Defined>(Sym->Body)) {
+ if (Defined *B = dyn_cast<Defined>(Sym->body())) {
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
Dir[TLS_TABLE].Size = Config->is64()
? sizeof(object::coff_tls_directory64)
: sizeof(object::coff_tls_directory32);
}
}
+ if (Config->Debug) {
+ Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
+ Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
+ }
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
- if (auto *B = dyn_cast<DefinedRegular>(Sym->Body)) {
+ if (auto *B = dyn_cast<DefinedRegular>(Sym->body())) {
SectionChunk *SC = B->getChunk();
assert(B->getRVA() >= SC->getRVA());
uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
@@ -626,12 +714,19 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize;
}
}
+ if (!DelayIdata.empty()) {
+ Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
+ DelayIdata.getDirRVA();
+ Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
+ }
// Write section table
for (OutputSection *Sec : OutputSections) {
Sec->writeHeaderTo(Buf);
Buf += sizeof(coff_section);
}
+ SectionTable = ArrayRef<uint8_t>(
+ Buf - OutputSections.size() * sizeof(coff_section), Buf);
if (OutputSymtab.empty())
return;
@@ -660,8 +755,10 @@ void Writer::openFile(StringRef Path) {
void Writer::fixSafeSEHSymbols() {
if (!SEHTable)
return;
- Config->SEHTable->setRVA(SEHTable->getRVA());
- Config->SEHCount->setVA(SEHTable->getSize() / 4);
+ if (auto *T = dyn_cast<DefinedRelative>(Config->SEHTable->body()))
+ T->setRVA(SEHTable->getRVA());
+ if (auto *C = dyn_cast<DefinedAbsolute>(Config->SEHCount->body()))
+ C->setVA(SEHTable->getSize() / 4);
}
// Handles /section options to allow users to overwrite
@@ -715,6 +812,30 @@ void Writer::sortExceptionTable() {
errs() << "warning: don't know how to handle .pdata.\n";
}
+// Backfill the CVSignature in a PDB70 Debug Record. This backfilling allows us
+// to get reproducible builds.
+void Writer::writeBuildId() {
+ // There is nothing to backfill if BuildId was not setup.
+ if (BuildId == nullptr)
+ return;
+
+ MD5 Hash;
+ MD5::MD5Result Res;
+
+ Hash.update(ArrayRef<uint8_t>{Buffer->getBufferStart(),
+ Buffer->getBufferEnd()});
+ Hash.final(Res);
+
+ assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 &&
+ "only PDB 7.0 is supported");
+ assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) &&
+ "signature size mismatch");
+ memcpy(BuildId->DI->PDB70.Signature, Res,
+ sizeof(codeview::PDB70DebugInfo::Signature));
+ // TODO(compnerd) track the Age
+ BuildId->DI->PDB70.Age = 1;
+}
+
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->getName() == Name)
@@ -744,16 +865,13 @@ OutputSection *Writer::createSection(StringRef Name) {
uint32_t Perms = StringSwitch<uint32_t>(Name)
.Case(".bss", BSS | R | W)
.Case(".data", DATA | R | W)
- .Case(".didat", DATA | R)
- .Case(".edata", DATA | R)
- .Case(".idata", DATA | R)
- .Case(".rdata", DATA | R)
+ .Cases(".didat", ".edata", ".idata", ".rdata", DATA | R)
.Case(".reloc", DATA | DISCARDABLE | R)
.Case(".text", CODE | R | X)
.Default(0);
if (!Perms)
llvm_unreachable("unknown section name");
- auto Sec = new (CAlloc.Allocate()) OutputSection(Name);
+ auto Sec = make<OutputSection>(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
return Sec;
@@ -784,13 +902,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
uint32_t P = V[J].RVA & Mask;
if (P == Page)
continue;
- BaserelChunk *Buf = BAlloc.Allocate();
- Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
+ Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
I = J;
Page = P;
}
if (I == J)
return;
- BaserelChunk *Buf = BAlloc.Allocate();
- Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
+ Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
}