summaryrefslogtreecommitdiff
path: root/COFF/Writer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'COFF/Writer.cpp')
-rw-r--r--COFF/Writer.cpp723
1 files changed, 575 insertions, 148 deletions
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index d17405ec26ab..258796ea6057 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -77,36 +77,38 @@ static const int SectorSize = 512;
static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram);
static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8");
-static const int NumberfOfDataDirectory = 16;
+static const int NumberOfDataDirectory = 16;
namespace {
class DebugDirectoryChunk : public Chunk {
public:
- DebugDirectoryChunk(const std::vector<Chunk *> &R) : Records(R) {}
+ DebugDirectoryChunk(const std::vector<Chunk *> &R, bool WriteRepro)
+ : Records(R), WriteRepro(WriteRepro) {}
size_t getSize() const override {
- return Records.size() * sizeof(debug_directory);
+ return (Records.size() + int(WriteRepro)) * sizeof(debug_directory);
}
void writeTo(uint8_t *B) const override {
auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
for (const 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();
OutputSection *OS = Record->getOutputSection();
uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA());
- D->PointerToRawData = Offs;
-
- TimeDateStamps.push_back(&D->TimeDateStamp);
+ fillEntry(D, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, Record->getSize(),
+ Record->getRVA(), Offs);
++D;
}
+
+ if (WriteRepro) {
+ // FIXME: The COFF spec allows either a 0-sized entry to just say
+ // "the timestamp field is really a hash", or a 4-byte size field
+ // followed by that many bytes containing a longer hash (with the
+ // lowest 4 bytes usually being the timestamp in little-endian order).
+ // Consider storing the full 8 bytes computed by xxHash64 here.
+ fillEntry(D, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0);
+ }
}
void setTimeDateStamp(uint32_t TimeDateStamp) {
@@ -115,8 +117,23 @@ public:
}
private:
+ void fillEntry(debug_directory *D, COFF::DebugType DebugType, size_t Size,
+ uint64_t RVA, uint64_t Offs) const {
+ D->Characteristics = 0;
+ D->TimeDateStamp = 0;
+ D->MajorVersion = 0;
+ D->MinorVersion = 0;
+ D->Type = DebugType;
+ D->SizeOfData = Size;
+ D->AddressOfRawData = RVA;
+ D->PointerToRawData = Offs;
+
+ TimeDateStamps.push_back(&D->TimeDateStamp);
+ }
+
mutable std::vector<support::ulittle32_t *> TimeDateStamps;
const std::vector<Chunk *> &Records;
+ bool WriteRepro;
};
class CVDebugRecordChunk : public Chunk {
@@ -150,14 +167,22 @@ private:
void createSections();
void createMiscChunks();
void createImportTables();
+ void appendImportThunks();
+ void locateImportTables(
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map);
void createExportTable();
void mergeSections();
+ void readRelocTargets();
+ void removeUnusedSections();
void assignAddresses();
+ void finalizeAddresses();
void removeEmptySections();
void createSymbolAndStringTable();
void openFile(StringRef OutputPath);
template <typename PEHeaderTy> void writeHeader();
void createSEHTable();
+ void createRuntimePseudoRelocs();
+ void insertCtorDtorSymbols();
void createGuardCFTables();
void markSymbolsForRVATable(ObjFile *File,
ArrayRef<SectionChunk *> SymIdxChunks,
@@ -168,6 +193,7 @@ private:
void writeSections();
void writeBuildId();
void sortExceptionTable();
+ void sortCRTSectionChunks(std::vector<Chunk *> &Chunks);
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
size_t addEntryToStringTable(StringRef Str);
@@ -184,6 +210,10 @@ private:
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
IdataContents Idata;
+ Chunk *ImportTableStart = nullptr;
+ uint64_t ImportTableSize = 0;
+ Chunk *IATStart = nullptr;
+ uint64_t IATSize = 0;
DelayLoadContents DelayIdata;
EdataContents Edata;
bool SetNoSEHCharacteristic = false;
@@ -191,7 +221,6 @@ private:
DebugDirectoryChunk *DebugDirectory = nullptr;
std::vector<Chunk *> DebugRecords;
CVDebugRecordChunk *BuildId = nullptr;
- Optional<codeview::DebugInfo> PreviousBuildId;
ArrayRef<uint8_t> SectionTable;
uint64_t FileSize;
@@ -209,6 +238,8 @@ private:
OutputSection *DidatSec;
OutputSection *RsrcSec;
OutputSection *RelocSec;
+ OutputSection *CtorsSec;
+ OutputSection *DtorsSec;
// The first and last .pdata sections in the output file.
//
@@ -237,6 +268,11 @@ void OutputSection::addChunk(Chunk *C) {
C->setOutputSection(this);
}
+void OutputSection::insertChunkAtStart(Chunk *C) {
+ Chunks.insert(Chunks.begin(), C);
+ C->setOutputSection(this);
+}
+
void OutputSection::setPermissions(uint32_t C) {
Header.Characteristics &= ~PermMask;
Header.Characteristics |= C;
@@ -267,77 +303,206 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
} // namespace coff
} // namespace lld
-// PDBs are matched against executables using a build id which consists of three
-// components:
-// 1. A 16-bit GUID
-// 2. An age
-// 3. A time stamp.
+// Check whether the target address S is in range from a relocation
+// of type RelType at address P.
+static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
+ assert(Config->Machine == ARMNT);
+ int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
+ switch (RelType) {
+ case IMAGE_REL_ARM_BRANCH20T:
+ return isInt<21>(Diff);
+ case IMAGE_REL_ARM_BRANCH24T:
+ case IMAGE_REL_ARM_BLX23T:
+ return isInt<25>(Diff);
+ default:
+ return true;
+ }
+}
+
+// Return the last thunk for the given target if it is in range,
+// or create a new one.
+static std::pair<Defined *, bool>
+getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
+ uint16_t Type, int Margin) {
+ Defined *&LastThunk = LastThunks[Target->getRVA()];
+ if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
+ return {LastThunk, false};
+ RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
+ Defined *D = make<DefinedSynthetic>("", C);
+ LastThunk = D;
+ return {D, true};
+}
+
+// This checks all relocations, and for any relocation which isn't in range
+// it adds a thunk after the section chunk that contains the relocation.
+// If the latest thunk for the specific target is in range, that is used
+// instead of creating a new thunk. All range checks are done with the
+// specified margin, to make sure that relocations that originally are in
+// range, but only barely, also get thunks - in case other added thunks makes
+// the target go out of range.
//
-// Debuggers and symbol servers match executables against debug info by checking
-// each of these components of the EXE/DLL against the corresponding value in
-// the PDB and failing a match if any of the components differ. In the case of
-// symbol servers, symbols are cached in a folder that is a function of the
-// GUID. As a result, in order to avoid symbol cache pollution where every
-// incremental build copies a new PDB to the symbol cache, we must try to re-use
-// the existing GUID if one exists, but bump the age. This way the match will
-// fail, so the symbol cache knows to use the new PDB, but the GUID matches, so
-// it overwrites the existing item in the symbol cache rather than making a new
-// one.
-static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) {
- // We don't need to incrementally update a previous build id if we're not
- // writing codeview debug info.
- if (!Config->Debug)
- return None;
+// After adding thunks, we verify that all relocations are in range (with
+// no extra margin requirements). If this failed, we restart (throwing away
+// the previously created thunks) and retry with a wider margin.
+static bool createThunks(std::vector<Chunk *> &Chunks, int Margin) {
+ bool AddressesChanged = false;
+ DenseMap<uint64_t, Defined *> LastThunks;
+ size_t ThunksSize = 0;
+ // Recheck Chunks.size() each iteration, since we can insert more
+ // elements into it.
+ for (size_t I = 0; I != Chunks.size(); ++I) {
+ SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Chunks[I]);
+ if (!SC)
+ continue;
+ size_t ThunkInsertionSpot = I + 1;
+
+ // Try to get a good enough estimate of where new thunks will be placed.
+ // Offset this by the size of the new thunks added so far, to make the
+ // estimate slightly better.
+ size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize;
+ for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
+ const coff_relocation &Rel = SC->Relocs[J];
+ Symbol *&RelocTarget = SC->RelocTargets[J];
+
+ // The estimate of the source address P should be pretty accurate,
+ // but we don't know whether the target Symbol address should be
+ // offset by ThunkSize or not (or by some of ThunksSize but not all of
+ // it), giving us some uncertainty once we have added one thunk.
+ uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize;
+
+ Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
+ if (!Sym)
+ continue;
- auto ExpectedBinary = llvm::object::createBinary(Path);
- if (!ExpectedBinary) {
- consumeError(ExpectedBinary.takeError());
- return None;
+ uint64_t S = Sym->getRVA();
+
+ if (isInRange(Rel.Type, S, P, Margin))
+ continue;
+
+ // If the target isn't in range, hook it up to an existing or new
+ // thunk.
+ Defined *Thunk;
+ bool WasNew;
+ std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin);
+ if (WasNew) {
+ Chunk *ThunkChunk = Thunk->getChunk();
+ ThunkChunk->setRVA(
+ ThunkInsertionRVA); // Estimate of where it will be located.
+ Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk);
+ ThunkInsertionSpot++;
+ ThunksSize += ThunkChunk->getSize();
+ ThunkInsertionRVA += ThunkChunk->getSize();
+ AddressesChanged = true;
+ }
+ RelocTarget = Thunk;
+ }
}
+ return AddressesChanged;
+}
- auto Binary = std::move(*ExpectedBinary);
- if (!Binary.getBinary()->isCOFF())
- return None;
+// Verify that all relocations are in range, with no extra margin requirements.
+static bool verifyRanges(const std::vector<Chunk *> Chunks) {
+ for (Chunk *C : Chunks) {
+ SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C);
+ if (!SC)
+ continue;
- std::error_code EC;
- COFFObjectFile File(Binary.getBinary()->getMemoryBufferRef(), EC);
- if (EC)
- return None;
+ for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
+ const coff_relocation &Rel = SC->Relocs[J];
+ Symbol *RelocTarget = SC->RelocTargets[J];
- // If the machine of the binary we're outputting doesn't match the machine
- // of the existing binary, don't try to re-use the build id.
- if (File.is64() != Config->is64() || File.getMachine() != Config->Machine)
- return None;
+ Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
+ if (!Sym)
+ continue;
- for (const auto &DebugDir : File.debug_directories()) {
- if (DebugDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
- continue;
+ uint64_t P = SC->getRVA() + Rel.VirtualAddress;
+ uint64_t S = Sym->getRVA();
- const codeview::DebugInfo *ExistingDI = nullptr;
- StringRef PDBFileName;
- if (auto EC = File.getDebugPDBInfo(ExistingDI, PDBFileName)) {
- (void)EC;
- return None;
+ if (!isInRange(Rel.Type, S, P, 0))
+ return false;
}
- // We only support writing PDBs in v70 format. So if this is not a build
- // id that we recognize / support, ignore it.
- if (ExistingDI->Signature.CVSignature != OMF::Signature::PDB70)
- return None;
- return *ExistingDI;
}
- return None;
+ return true;
+}
+
+// Assign addresses and add thunks if necessary.
+void Writer::finalizeAddresses() {
+ assignAddresses();
+ if (Config->Machine != ARMNT)
+ return;
+
+ size_t OrigNumChunks = 0;
+ for (OutputSection *Sec : OutputSections) {
+ Sec->OrigChunks = Sec->Chunks;
+ OrigNumChunks += Sec->Chunks.size();
+ }
+
+ int Pass = 0;
+ int Margin = 1024 * 100;
+ while (true) {
+ // First check whether we need thunks at all, or if the previous pass of
+ // adding them turned out ok.
+ bool RangesOk = true;
+ size_t NumChunks = 0;
+ for (OutputSection *Sec : OutputSections) {
+ if (!verifyRanges(Sec->Chunks)) {
+ RangesOk = false;
+ break;
+ }
+ NumChunks += Sec->Chunks.size();
+ }
+ if (RangesOk) {
+ if (Pass > 0)
+ log("Added " + Twine(NumChunks - OrigNumChunks) + " thunks with " +
+ "margin " + Twine(Margin) + " in " + Twine(Pass) + " passes");
+ return;
+ }
+
+ if (Pass >= 10)
+ fatal("adding thunks hasn't converged after " + Twine(Pass) + " passes");
+
+ if (Pass > 0) {
+ // If the previous pass didn't work out, reset everything back to the
+ // original conditions before retrying with a wider margin. This should
+ // ideally never happen under real circumstances.
+ for (OutputSection *Sec : OutputSections) {
+ Sec->Chunks = Sec->OrigChunks;
+ for (Chunk *C : Sec->Chunks)
+ C->resetRelocTargets();
+ }
+ Margin *= 2;
+ }
+
+ // Try adding thunks everywhere where it is needed, with a margin
+ // to avoid things going out of range due to the added thunks.
+ bool AddressesChanged = false;
+ for (OutputSection *Sec : OutputSections)
+ AddressesChanged |= createThunks(Sec->Chunks, Margin);
+ // If the verification above thought we needed thunks, we should have
+ // added some.
+ assert(AddressesChanged);
+
+ // Recalculate the layout for the whole image (and verify the ranges at
+ // the start of the next round).
+ assignAddresses();
+
+ Pass++;
+ }
}
// The main function of the writer.
void Writer::run() {
ScopedTimer T1(CodeLayoutTimer);
+ createImportTables();
createSections();
createMiscChunks();
- createImportTables();
+ appendImportThunks();
createExportTable();
mergeSections();
- assignAddresses();
+ readRelocTargets();
+ removeUnusedSections();
+ finalizeAddresses();
removeEmptySections();
setSectionPermissions();
createSymbolAndStringTable();
@@ -346,9 +511,6 @@ void Writer::run() {
fatal("image size (" + Twine(FileSize) + ") " +
"exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")");
- // We must do this before opening the output file, as it depends on being able
- // to read the contents of the existing output file.
- PreviousBuildId = loadExistingBuildId(Config->OutputFile);
openFile(Config->OutputFile);
if (Config->is64()) {
writeHeader<pe32plus_header>();
@@ -357,14 +519,14 @@ void Writer::run() {
}
writeSections();
sortExceptionTable();
- writeBuildId();
T1.stop();
if (!Config->PDBPath.empty() && Config->Debug) {
assert(BuildId);
- createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId);
+ createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId);
}
+ writeBuildId();
writeMapFile(OutputSections);
@@ -396,6 +558,110 @@ static void sortBySectionOrder(std::vector<Chunk *> &Chunks) {
});
}
+// Sort concrete section chunks from GNU import libraries.
+//
+// GNU binutils doesn't use short import files, but instead produces import
+// libraries that consist of object files, with section chunks for the .idata$*
+// sections. These are linked just as regular static libraries. Each import
+// library consists of one header object, one object file for every imported
+// symbol, and one trailer object. In order for the .idata tables/lists to
+// be formed correctly, the section chunks within each .idata$* section need
+// to be grouped by library, and sorted alphabetically within each library
+// (which makes sure the header comes first and the trailer last).
+static bool fixGnuImportChunks(
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
+ uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+
+ // Make sure all .idata$* section chunks are mapped as RDATA in order to
+ // be sorted into the same sections as our own synthesized .idata chunks.
+ for (auto &Pair : Map) {
+ StringRef SectionName = Pair.first.first;
+ uint32_t OutChars = Pair.first.second;
+ if (!SectionName.startswith(".idata"))
+ continue;
+ if (OutChars == RDATA)
+ continue;
+ std::vector<Chunk *> &SrcVect = Pair.second;
+ std::vector<Chunk *> &DestVect = Map[{SectionName, RDATA}];
+ DestVect.insert(DestVect.end(), SrcVect.begin(), SrcVect.end());
+ SrcVect.clear();
+ }
+
+ bool HasIdata = false;
+ // Sort all .idata$* chunks, grouping chunks from the same library,
+ // with alphabetical ordering of the object fils within a library.
+ for (auto &Pair : Map) {
+ StringRef SectionName = Pair.first.first;
+ if (!SectionName.startswith(".idata"))
+ continue;
+
+ std::vector<Chunk *> &Chunks = Pair.second;
+ if (!Chunks.empty())
+ HasIdata = true;
+ std::stable_sort(Chunks.begin(), Chunks.end(), [&](Chunk *S, Chunk *T) {
+ SectionChunk *SC1 = dyn_cast_or_null<SectionChunk>(S);
+ SectionChunk *SC2 = dyn_cast_or_null<SectionChunk>(T);
+ if (!SC1 || !SC2) {
+ // if SC1, order them ascending. If SC2 or both null,
+ // S is not less than T.
+ return SC1 != nullptr;
+ }
+ // Make a string with "libraryname/objectfile" for sorting, achieving
+ // both grouping by library and sorting of objects within a library,
+ // at once.
+ std::string Key1 =
+ (SC1->File->ParentName + "/" + SC1->File->getName()).str();
+ std::string Key2 =
+ (SC2->File->ParentName + "/" + SC2->File->getName()).str();
+ return Key1 < Key2;
+ });
+ }
+ return HasIdata;
+}
+
+// Add generated idata chunks, for imported symbols and DLLs, and a
+// terminator in .idata$2.
+static void addSyntheticIdata(
+ IdataContents &Idata,
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
+ uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+ Idata.create();
+
+ // Add the .idata content in the right section groups, to allow
+ // chunks from other linked in object files to be grouped together.
+ // See Microsoft PE/COFF spec 5.4 for details.
+ auto Add = [&](StringRef N, std::vector<Chunk *> &V) {
+ std::vector<Chunk *> &DestVect = Map[{N, RDATA}];
+ DestVect.insert(DestVect.end(), V.begin(), V.end());
+ };
+
+ // The loader assumes a specific order of data.
+ // Add each type in the correct order.
+ Add(".idata$2", Idata.Dirs);
+ Add(".idata$4", Idata.Lookups);
+ Add(".idata$5", Idata.Addresses);
+ Add(".idata$6", Idata.Hints);
+ Add(".idata$7", Idata.DLLNames);
+}
+
+// Locate the first Chunk and size of the import directory list and the
+// IAT.
+void Writer::locateImportTables(
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
+ uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+ std::vector<Chunk *> &ImportTables = Map[{".idata$2", RDATA}];
+ if (!ImportTables.empty())
+ ImportTableStart = ImportTables.front();
+ for (Chunk *C : ImportTables)
+ ImportTableSize += C->getSize();
+
+ std::vector<Chunk *> &IAT = Map[{".idata$5", RDATA}];
+ if (!IAT.empty())
+ IATStart = IAT.front();
+ for (Chunk *C : IAT)
+ IATSize += C->getSize();
+}
+
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, create the builtin sections.
@@ -429,12 +695,14 @@ void Writer::createSections() {
DidatSec = CreateSection(".didat", DATA | R);
RsrcSec = CreateSection(".rsrc", DATA | R);
RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R);
+ CtorsSec = CreateSection(".ctors", DATA | R | W);
+ DtorsSec = CreateSection(".dtors", DATA | R | W);
// Then bin chunks by name and output characteristics.
std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map;
for (Chunk *C : Symtab->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
- if (SC && !SC->isLive()) {
+ if (SC && !SC->Live) {
if (Config->Verbose)
SC->printDiscardedMessage();
continue;
@@ -442,26 +710,43 @@ void Writer::createSections() {
Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C);
}
+ // Even in non MinGW cases, we might need to link against GNU import
+ // libraries.
+ bool HasIdata = fixGnuImportChunks(Map);
+ if (!Idata.empty())
+ HasIdata = true;
+
+ if (HasIdata)
+ addSyntheticIdata(Idata, Map);
+
// Process an /order option.
if (!Config->Order.empty())
for (auto &Pair : Map)
sortBySectionOrder(Pair.second);
+ if (HasIdata)
+ locateImportTables(Map);
+
// Then create an OutputSection for each section.
// '$' and all following characters in input section names are
// discarded when determining output section. So, .text$foo
// contributes to .text, for example. See PE/COFF spec 3.2.
- for (auto Pair : Map) {
+ for (auto &Pair : Map) {
StringRef Name = getOutputSectionName(Pair.first.first);
uint32_t OutChars = Pair.first.second;
- // In link.exe, there is a special case for the I386 target where .CRT
- // sections are treated as if they have output characteristics DATA | R if
- // their characteristics are DATA | R | W. This implements the same special
- // case for all architectures.
- if (Name == ".CRT")
+ if (Name == ".CRT") {
+ // In link.exe, there is a special case for the I386 target where .CRT
+ // sections are treated as if they have output characteristics DATA | R if
+ // their characteristics are DATA | R | W. This implements the same
+ // special case for all architectures.
OutChars = DATA | R;
+ log("Processing section " + Pair.first.first + " -> " + Name);
+
+ sortCRTSectionChunks(Pair.second);
+ }
+
OutputSection *Sec = CreateSection(Name, OutChars);
std::vector<Chunk *> &Chunks = Pair.second;
for (Chunk *C : Chunks)
@@ -499,20 +784,20 @@ void Writer::createMiscChunks() {
}
// Create Debug Information Chunks
- if (Config->Debug) {
- DebugDirectory = make<DebugDirectoryChunk>(DebugRecords);
-
- OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec;
+ OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec;
+ if (Config->Debug || Config->Repro) {
+ DebugDirectory = make<DebugDirectoryChunk>(DebugRecords, Config->Repro);
+ DebugInfoSec->addChunk(DebugDirectory);
+ }
+ if (Config->Debug) {
// Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We
// output a PDB no matter what, and this chunk provides the only means of
// allowing a debugger to match a PDB and an executable. So we need it even
// if we're ultimately not going to write CodeView data to the PDB.
- auto *CVChunk = make<CVDebugRecordChunk>();
- BuildId = CVChunk;
- DebugRecords.push_back(CVChunk);
+ BuildId = make<CVDebugRecordChunk>();
+ DebugRecords.push_back(BuildId);
- DebugInfoSec->addChunk(DebugDirectory);
for (Chunk *C : DebugRecords)
DebugInfoSec->addChunk(C);
}
@@ -524,6 +809,12 @@ void Writer::createMiscChunks() {
// Create /guard:cf tables if requested.
if (Config->GuardCF != GuardCFLevel::Off)
createGuardCFTables();
+
+ if (Config->MinGW) {
+ createRuntimePseudoRelocs();
+
+ insertCtorDtorSymbols();
+ }
}
// Create .idata section for the DLL-imported symbol table.
@@ -531,9 +822,6 @@ void Writer::createMiscChunks() {
// IdataContents class abstracted away the details for us,
// so we just let it create chunks and add them to the section.
void Writer::createImportTables() {
- if (ImportFile::Instances.empty())
- return;
-
// Initialize DLLOrder so that import entries are ordered in
// the same order as in the command line. (That affects DLL
// initialization order, and this ordering is MSVC-compatible.)
@@ -545,14 +833,6 @@ void Writer::createImportTables() {
if (Config->DLLOrder.count(DLL) == 0)
Config->DLLOrder[DLL] = Config->DLLOrder.size();
- if (File->ThunkSym) {
- if (!isa<DefinedImportThunk>(File->ThunkSym))
- fatal(toString(*File->ThunkSym) + " was replaced");
- DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym);
- if (File->ThunkLive)
- TextSec->addChunk(Thunk->getChunk());
- }
-
if (File->ImpSym && !isa<DefinedImportData>(File->ImpSym))
fatal(toString(*File->ImpSym) + " was replaced");
DefinedImportData *ImpSym = cast_or_null<DefinedImportData>(File->ImpSym);
@@ -565,10 +845,25 @@ void Writer::createImportTables() {
Idata.add(ImpSym);
}
}
+}
- if (!Idata.empty())
- for (Chunk *C : Idata.getChunks())
- IdataSec->addChunk(C);
+void Writer::appendImportThunks() {
+ if (ImportFile::Instances.empty())
+ return;
+
+ for (ImportFile *File : ImportFile::Instances) {
+ if (!File->Live)
+ continue;
+
+ if (!File->ThunkSym)
+ continue;
+
+ if (!isa<DefinedImportThunk>(File->ThunkSym))
+ fatal(toString(*File->ThunkSym) + " was replaced");
+ DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym);
+ if (File->ThunkLive)
+ TextSec->addChunk(Thunk->getChunk());
+ }
if (!DelayIdata.empty()) {
Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
@@ -589,6 +884,21 @@ void Writer::createExportTable() {
EdataSec->addChunk(C);
}
+void Writer::removeUnusedSections() {
+ // Remove sections that we can be sure won't get content, to avoid
+ // allocating space for their section headers.
+ auto IsUnused = [this](OutputSection *S) {
+ if (S == RelocSec)
+ return false; // This section is populated later.
+ // MergeChunks have zero size at this point, as their size is finalized
+ // later. Only remove sections that have no Chunks at all.
+ return S->Chunks.empty();
+ };
+ OutputSections.erase(
+ std::remove_if(OutputSections.begin(), OutputSections.end(), IsUnused),
+ OutputSections.end());
+}
+
// The Windows loader doesn't seem to like empty sections,
// so we remove them if any.
void Writer::removeEmptySections() {
@@ -699,9 +1009,9 @@ void Writer::createSymbolAndStringTable() {
}
void Writer::mergeSections() {
- if (!PdataSec->getChunks().empty()) {
- FirstPdata = PdataSec->getChunks().front();
- LastPdata = PdataSec->getChunks().back();
+ if (!PdataSec->Chunks.empty()) {
+ FirstPdata = PdataSec->Chunks.front();
+ LastPdata = PdataSec->Chunks.back();
}
for (auto &P : Config->Merge) {
@@ -729,11 +1039,18 @@ void Writer::mergeSections() {
}
}
+// Visits all sections to initialize their relocation targets.
+void Writer::readRelocTargets() {
+ for (OutputSection *Sec : OutputSections)
+ for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
+ [&](Chunk *C) { C->readRelocTargets(); });
+}
+
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
- sizeof(data_directory) * NumberfOfDataDirectory +
+ sizeof(data_directory) * NumberOfDataDirectory +
sizeof(coff_section) * OutputSections.size();
SizeOfHeaders +=
Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
@@ -746,7 +1063,7 @@ void Writer::assignAddresses() {
addBaserels();
uint64_t RawSize = 0, VirtualSize = 0;
Sec->Header.VirtualAddress = RVA;
- for (Chunk *C : Sec->getChunks()) {
+ for (Chunk *C : Sec->Chunks) {
VirtualSize = alignTo(VirtualSize, C->Alignment);
C->setRVA(RVA + VirtualSize);
C->OutputSectionOff = VirtualSize;
@@ -808,7 +1125,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
if (!Config->Relocatable)
COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
COFF->SizeOfOptionalHeader =
- sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory;
+ sizeof(PEHeaderTy) + sizeof(data_directory) * NumberOfDataDirectory;
// Write PE header
auto *PE = reinterpret_cast<PEHeaderTy *>(Buf);
@@ -866,7 +1183,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
if (Config->TerminalServerAware)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
- PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
+ PE->NumberOfRvaAndSize = NumberOfDataDirectory;
if (TextSec->getVirtualSize()) {
PE->BaseOfCode = TextSec->getRVA();
PE->SizeOfCode = TextSec->getRawSize();
@@ -875,16 +1192,18 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
// Write data directory
auto *Dir = reinterpret_cast<data_directory *>(Buf);
- Buf += sizeof(*Dir) * NumberfOfDataDirectory;
+ Buf += sizeof(*Dir) * NumberOfDataDirectory;
if (!Config->Exports.empty()) {
Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA();
Dir[EXPORT_TABLE].Size = Edata.getSize();
}
- if (!Idata.empty()) {
- Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA();
- Dir[IMPORT_TABLE].Size = Idata.getDirSize();
- Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
- Dir[IAT].Size = Idata.getIATSize();
+ if (ImportTableStart) {
+ Dir[IMPORT_TABLE].RelativeVirtualAddress = ImportTableStart->getRVA();
+ Dir[IMPORT_TABLE].Size = ImportTableSize;
+ }
+ if (IATStart) {
+ Dir[IAT].RelativeVirtualAddress = IATStart->getRVA();
+ Dir[IAT].Size = IATSize;
}
if (RsrcSec->getVirtualSize()) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA();
@@ -907,7 +1226,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
: sizeof(object::coff_tls_directory32);
}
}
- if (Config->Debug) {
+ if (DebugDirectory) {
Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
}
@@ -1002,6 +1321,25 @@ static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) {
RVASet.insert({C, Off});
}
+// Given a symbol, add it to the GFIDs table if it is a live, defined, function
+// symbol in an executable section.
+static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms,
+ Symbol *S) {
+ auto *D = dyn_cast_or_null<DefinedCOFF>(S);
+
+ // Ignore undefined symbols and references to non-functions (e.g. globals and
+ // labels).
+ if (!D ||
+ D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION)
+ return;
+
+ // Mark the symbol as address taken if it's in an executable section.
+ Chunk *RefChunk = D->getChunk();
+ OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
+ if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ addSymbolToRVASet(AddressTakenSyms, D);
+}
+
// Visit all relocations from all section contributions of this object file and
// mark the relocation target as address-taken.
static void markSymbolsWithRelocations(ObjFile *File,
@@ -1010,21 +1348,17 @@ static void markSymbolsWithRelocations(ObjFile *File,
// We only care about live section chunks. Common chunks and other chunks
// don't generally contain relocations.
SectionChunk *SC = dyn_cast<SectionChunk>(C);
- if (!SC || !SC->isLive())
+ if (!SC || !SC->Live)
continue;
- // Look for relocations in this section against symbols in executable output
- // sections.
- for (Symbol *Ref : SC->symbols()) {
- // FIXME: Do further testing to see if the relocation type matters,
- // especially for 32-bit where taking the address of something usually
- // uses an absolute relocation instead of a relative one.
- if (auto *D = dyn_cast_or_null<Defined>(Ref)) {
- Chunk *RefChunk = D->getChunk();
- OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
- if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
- addSymbolToRVASet(UsedSymbols, D);
- }
+ for (const coff_relocation &Reloc : SC->Relocs) {
+ if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32)
+ // Ignore relative relocations on x86. On x86_64 they can't be ignored
+ // since they're also used to compute absolute addresses.
+ continue;
+
+ Symbol *Ref = SC->File->getSymbol(Reloc.SymbolTableIndex);
+ maybeAddAddressTakenFunction(UsedSymbols, Ref);
}
}
}
@@ -1051,7 +1385,11 @@ void Writer::createGuardCFTables() {
// Mark the image entry as address-taken.
if (Config->Entry)
- addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry));
+ maybeAddAddressTakenFunction(AddressTakenSyms, Config->Entry);
+
+ // Mark exported symbols in executable sections as address-taken.
+ for (Export &E : Config->Exports)
+ maybeAddAddressTakenFunction(AddressTakenSyms, E.Sym);
// Ensure sections referenced in the gfid table are 16-byte aligned.
for (const ChunkAndOffset &C : AddressTakenSyms)
@@ -1087,7 +1425,7 @@ void Writer::markSymbolsForRVATable(ObjFile *File,
// is associated with something like a vtable and the vtable is discarded.
// In this case, the associated gfids section is discarded, and we don't
// mark the virtual member functions as address-taken by the vtable.
- if (!C->isLive())
+ if (!C->Live)
continue;
// Validate that the contents look like symbol table indices.
@@ -1134,6 +1472,56 @@ void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4);
}
+// MinGW specific. Gather all relocations that are imported from a DLL even
+// though the code didn't expect it to, produce the table that the runtime
+// uses for fixing them up, and provide the synthetic symbols that the
+// runtime uses for finding the table.
+void Writer::createRuntimePseudoRelocs() {
+ std::vector<RuntimePseudoReloc> Rels;
+
+ for (Chunk *C : Symtab->getChunks()) {
+ auto *SC = dyn_cast<SectionChunk>(C);
+ if (!SC || !SC->Live)
+ continue;
+ SC->getRuntimePseudoRelocs(Rels);
+ }
+
+ if (!Rels.empty())
+ log("Writing " + Twine(Rels.size()) + " runtime pseudo relocations");
+ PseudoRelocTableChunk *Table = make<PseudoRelocTableChunk>(Rels);
+ RdataSec->addChunk(Table);
+ EmptyChunk *EndOfList = make<EmptyChunk>();
+ RdataSec->addChunk(EndOfList);
+
+ Symbol *HeadSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
+ Symbol *EndSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
+ replaceSymbol<DefinedSynthetic>(HeadSym, HeadSym->getName(), Table);
+ replaceSymbol<DefinedSynthetic>(EndSym, EndSym->getName(), EndOfList);
+}
+
+// MinGW specific.
+// The MinGW .ctors and .dtors lists have sentinels at each end;
+// a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end.
+// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__
+// and __DTOR_LIST__ respectively.
+void Writer::insertCtorDtorSymbols() {
+ AbsolutePointerChunk *CtorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *CtorListEnd = make<AbsolutePointerChunk>(0);
+ AbsolutePointerChunk *DtorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *DtorListEnd = make<AbsolutePointerChunk>(0);
+ CtorsSec->insertChunkAtStart(CtorListHead);
+ CtorsSec->addChunk(CtorListEnd);
+ DtorsSec->insertChunkAtStart(DtorListHead);
+ DtorsSec->addChunk(DtorListEnd);
+
+ Symbol *CtorListSym = Symtab->findUnderscore("__CTOR_LIST__");
+ Symbol *DtorListSym = Symtab->findUnderscore("__DTOR_LIST__");
+ replaceSymbol<DefinedSynthetic>(CtorListSym, CtorListSym->getName(),
+ CtorListHead);
+ replaceSymbol<DefinedSynthetic>(DtorListSym, DtorListSym->getName(),
+ DtorListHead);
+}
+
// Handles /section options to allow users to overwrite
// section attributes.
void Writer::setSectionPermissions() {
@@ -1160,7 +1548,7 @@ void Writer::writeSections() {
// ADD instructions).
if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE)
memset(SecBuf, 0xCC, Sec->getRawSize());
- for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(),
+ for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
[&](Chunk *C) { C->writeTo(SecBuf); });
}
}
@@ -1171,25 +1559,10 @@ void Writer::writeBuildId() {
// timestamp as well as a Guid and Age of the PDB.
// 2) In all cases, the PE COFF file header also contains a timestamp.
// For reproducibility, instead of a timestamp we want to use a hash of the
- // binary, however when building with debug info the hash needs to take into
- // account the debug info, since it's possible to add blank lines to a file
- // which causes the debug info to change but not the generated code.
- //
- // To handle this, we first set the Guid and Age in the debug directory (but
- // only if we're doing a debug build). Then, we hash the binary (thus causing
- // the hash to change if only the debug info changes, since the Age will be
- // different). Finally, we write that hash into the debug directory (if
- // present) as well as the COFF file header (always).
+ // PE contents.
if (Config->Debug) {
assert(BuildId && "BuildId is not set!");
- if (PreviousBuildId.hasValue()) {
- *BuildId->BuildId = *PreviousBuildId;
- BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
- } else {
- BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
- BuildId->BuildId->PDB70.Age = 1;
- llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
- }
+ // BuildId->BuildId was filled in when the PDB was written.
}
// At this point the only fields in the COFF file which remain unset are the
@@ -1201,8 +1574,25 @@ void Writer::writeBuildId() {
Buffer->getBufferSize());
uint32_t Timestamp = Config->Timestamp;
+ uint64_t Hash = 0;
+ bool GenerateSyntheticBuildId =
+ Config->MinGW && Config->Debug && Config->PDBPath.empty();
+
+ if (Config->Repro || GenerateSyntheticBuildId)
+ Hash = xxHash64(OutputFileData);
+
if (Config->Repro)
- Timestamp = static_cast<uint32_t>(xxHash64(OutputFileData));
+ Timestamp = static_cast<uint32_t>(Hash);
+
+ if (GenerateSyntheticBuildId) {
+ // For MinGW builds without a PDB file, we still generate a build id
+ // to allow associating a crash dump to the executable.
+ BuildId->BuildId->PDB70.CVSignature = OMF::Signature::PDB70;
+ BuildId->BuildId->PDB70.Age = 1;
+ memcpy(BuildId->BuildId->PDB70.Signature, &Hash, 8);
+ // xxhash only gives us 8 bytes, so put some fixed data in the other half.
+ memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8);
+ }
if (DebugDirectory)
DebugDirectory->setTimeDateStamp(Timestamp);
@@ -1240,6 +1630,42 @@ void Writer::sortExceptionTable() {
errs() << "warning: don't know how to handle .pdata.\n";
}
+// The CRT section contains, among other things, the array of function
+// pointers that initialize every global variable that is not trivially
+// constructed. The CRT calls them one after the other prior to invoking
+// main().
+//
+// As per C++ spec, 3.6.2/2.3,
+// "Variables with ordered initialization defined within a single
+// translation unit shall be initialized in the order of their definitions
+// in the translation unit"
+//
+// It is therefore critical to sort the chunks containing the function
+// pointers in the order that they are listed in the object file (top to
+// bottom), otherwise global objects might not be initialized in the
+// correct order.
+void Writer::sortCRTSectionChunks(std::vector<Chunk *> &Chunks) {
+ auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) {
+ auto SA = dyn_cast<SectionChunk>(A);
+ auto SB = dyn_cast<SectionChunk>(B);
+ assert(SA && SB && "Non-section chunks in CRT section!");
+
+ StringRef SAObj = SA->File->MB.getBufferIdentifier();
+ StringRef SBObj = SB->File->MB.getBufferIdentifier();
+
+ return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber();
+ };
+ std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder);
+
+ if (Config->Verbose) {
+ for (auto &C : Chunks) {
+ auto SC = dyn_cast<SectionChunk>(C);
+ log(" " + SC->File->MB.getBufferIdentifier().str() +
+ ", SectionID: " + Twine(SC->getSectionNumber()));
+ }
+ }
+}
+
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->Name == Name)
@@ -1259,12 +1685,13 @@ uint32_t Writer::getSizeOfInitializedData() {
void Writer::addBaserels() {
if (!Config->Relocatable)
return;
+ RelocSec->Chunks.clear();
std::vector<Baserel> V;
for (OutputSection *Sec : OutputSections) {
if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
continue;
// Collect all locations for base relocations.
- for (Chunk *C : Sec->getChunks())
+ for (Chunk *C : Sec->Chunks)
C->getBaserels(&V);
// Add the addresses to .reloc section.
if (!V.empty())