aboutsummaryrefslogtreecommitdiff
path: root/COFF/Writer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'COFF/Writer.cpp')
-rw-r--r--COFF/Writer.cpp94
1 files changed, 69 insertions, 25 deletions
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index 36ef87de4263..9729c6938ec8 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -40,8 +40,9 @@ using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-using namespace lld;
-using namespace lld::coff;
+
+namespace lld {
+namespace coff {
/* To re-generate DOSProgram:
$ cat > /tmp/DOSProgram.asm
@@ -240,6 +241,8 @@ private:
IdataContents idata;
Chunk *importTableStart = nullptr;
uint64_t importTableSize = 0;
+ Chunk *edataStart = nullptr;
+ Chunk *edataEnd = nullptr;
Chunk *iatStart = nullptr;
uint64_t iatSize = 0;
DelayLoadContents delayIdata;
@@ -283,9 +286,6 @@ private:
};
} // anonymous namespace
-namespace lld {
-namespace coff {
-
static Timer codeLayoutTimer("Code Layout", Timer::root());
static Timer diskCommitTimer("Commit Output File", Timer::root());
@@ -331,9 +331,6 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
contribSections.push_back(sec);
}
-} // namespace coff
-} // namespace lld
-
// 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) {
@@ -626,6 +623,9 @@ void Writer::run() {
writeMapFile(outputSections);
+ if (errorCount())
+ return;
+
ScopedTimer t2(diskCommitTimer);
if (auto e = buffer->commit())
fatal("failed to write the output file: " + toString(std::move(e)));
@@ -738,7 +738,8 @@ void Writer::addSyntheticIdata() {
add(".idata$2", idata.dirs);
add(".idata$4", idata.lookups);
add(".idata$5", idata.addresses);
- add(".idata$6", idata.hints);
+ if (!idata.hints.empty())
+ add(".idata$6", idata.hints);
add(".idata$7", idata.dllNames);
}
@@ -762,6 +763,28 @@ void Writer::locateImportTables() {
}
}
+// Return whether a SectionChunk's suffix (the dollar and any trailing
+// suffix) should be removed and sorted into the main suffixless
+// PartialSection.
+static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
+ // On MinGW, comdat groups are formed by putting the comdat group name
+ // after the '$' in the section name. For .eh_frame$<symbol>, that must
+ // still be sorted before the .eh_frame trailer from crtend.o, thus just
+ // strip the section name trailer. For other sections, such as
+ // .tls$$<symbol> (where non-comdat .tls symbols are otherwise stored in
+ // ".tls$"), they must be strictly sorted after .tls. And for the
+ // hypothetical case of comdat .CRT$XCU, we definitely need to keep the
+ // suffix for sorting. Thus, to play it safe, only strip the suffix for
+ // the standard sections.
+ if (!config->mingw)
+ return false;
+ if (!sc || !sc->isCOMDAT())
+ return false;
+ return name.startswith(".text$") || name.startswith(".data$") ||
+ name.startswith(".rdata$") || name.startswith(".pdata$") ||
+ name.startswith(".xdata$") || name.startswith(".eh_frame$");
+}
+
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, create the builtin sections.
@@ -807,10 +830,7 @@ void Writer::createSections() {
continue;
}
StringRef name = c->getSectionName();
- // On MinGW, comdat groups are formed by putting the comdat group name
- // after the '$' in the section name. Such a section name suffix shouldn't
- // imply separate alphabetical sorting of those section chunks though.
- if (config->mingw && sc && sc->isCOMDAT())
+ if (shouldStripSectionSuffix(sc, name))
name = name.split('$').first;
PartialSection *pSec = createPartialSection(name,
c->getOutputCharacteristics());
@@ -818,6 +838,7 @@ void Writer::createSections() {
}
fixPartialSectionChars(".rsrc", data | r);
+ fixPartialSectionChars(".edata", data | r);
// Even in non MinGW cases, we might need to link against GNU import
// libraries.
bool hasIdata = fixGnuImportChunks();
@@ -992,10 +1013,19 @@ void Writer::appendImportThunks() {
}
void Writer::createExportTable() {
- if (config->exports.empty())
- return;
- for (Chunk *c : edata.chunks)
- edataSec->addChunk(c);
+ if (!edataSec->chunks.empty()) {
+ // Allow using a custom built export table from input object files, instead
+ // of having the linker synthesize the tables.
+ if (config->hadExplicitExports)
+ warn("literal .edata sections override exports");
+ } else if (!config->exports.empty()) {
+ for (Chunk *c : edata.chunks)
+ edataSec->addChunk(c);
+ }
+ if (!edataSec->chunks.empty()) {
+ edataStart = edataSec->chunks.front();
+ edataEnd = edataSec->chunks.back();
+ }
}
void Writer::removeUnusedSections() {
@@ -1076,6 +1106,13 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
}
}
+ // Symbols that are runtime pseudo relocations don't point to the actual
+ // symbol data itself (as they are imported), but points to the IAT entry
+ // instead. Avoid emitting them to the symbol table, as they can confuse
+ // debuggers.
+ if (def->isRuntimePseudoReloc)
+ return None;
+
StringRef name = def->getName();
if (name.size() > COFF::NameSize) {
sym.Name.Offset.Zeroes = 0;
@@ -1179,9 +1216,11 @@ void Writer::assignAddresses() {
sizeOfHeaders +=
config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);
- uint64_t rva = pageSize; // The first page is kept unmapped.
fileSize = sizeOfHeaders;
+ // The first page is kept unmapped.
+ uint64_t rva = alignTo(sizeOfHeaders, config->align);
+
for (OutputSection *sec : outputSections) {
if (sec == relocSec)
addBaserels();
@@ -1211,10 +1250,10 @@ void Writer::assignAddresses() {
sec->header.SizeOfRawData = rawSize;
if (rawSize != 0)
sec->header.PointerToRawData = fileSize;
- rva += alignTo(virtualSize, pageSize);
+ rva += alignTo(virtualSize, config->align);
fileSize += alignTo(rawSize, config->fileAlign);
}
- sizeOfImage = alignTo(rva, pageSize);
+ sizeOfImage = alignTo(rva, config->align);
// Assign addresses to sections in MergeChunks.
for (MergeChunk *mc : MergeChunk::instances)
@@ -1283,7 +1322,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
pe->MinorLinkerVersion = 0;
pe->ImageBase = config->imageBase;
- pe->SectionAlignment = pageSize;
+ pe->SectionAlignment = config->align;
pe->FileAlignment = config->fileAlign;
pe->MajorImageVersion = config->majorImageVersion;
pe->MinorImageVersion = config->minorImageVersion;
@@ -1335,9 +1374,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
// Write data directory
auto *dir = reinterpret_cast<data_directory *>(buf);
buf += sizeof(*dir) * numberOfDataDirectory;
- if (!config->exports.empty()) {
- dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA();
- dir[EXPORT_TABLE].Size = edata.getSize();
+ if (edataStart) {
+ dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA();
+ dir[EXPORT_TABLE].Size =
+ edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA();
}
if (importTableStart) {
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
@@ -1475,7 +1515,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
// Absolute is never code, synthetic generally isn't and usually isn't
// determinable.
break;
- case Symbol::LazyKind:
+ case Symbol::LazyArchiveKind:
+ case Symbol::LazyObjectKind:
case Symbol::UndefinedKind:
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
// symbols shouldn't have relocations.
@@ -1899,3 +1940,6 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
return it->second;
return nullptr;
}
+
+} // namespace coff
+} // namespace lld