COFF: Create output sections early. NFCI.

With this, all output sections are created in one place. This will make
it simpler to implement merging of builtin sections.

Differential Revision: https://reviews.llvm.org/D45349

llvm-svn: 329370
This commit is contained in:
Peter Collingbourne 2018-04-06 03:25:49 +00:00
parent f108d57f9b
commit f2c0f39b91
1 changed files with 119 additions and 126 deletions

View File

@ -163,14 +163,13 @@ private:
void createSymbolAndStringTable();
void openFile(StringRef OutputPath);
template <typename PEHeaderTy> void writeHeader();
void createSEHTable(OutputSection *RData);
void createGuardCFTables(OutputSection *RData);
void createGLJmpTable(OutputSection *RData);
void createSEHTable();
void createGuardCFTables();
void markSymbolsForRVATable(ObjFile *File,
ArrayRef<SectionChunk *> SymIdxChunks,
SymbolRVASet &TableSymbols);
void maybeAddRVATable(OutputSection *RData, SymbolRVASet TableSymbols,
StringRef TableSym, StringRef CountSym);
void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
StringRef CountSym);
void setSectionPermissions();
void writeSections();
void writeBuildId();
@ -180,9 +179,8 @@ private:
size_t addEntryToStringTable(StringRef Str);
OutputSection *findSection(StringRef Name);
OutputSection *createSection(StringRef Name);
void addBaserels(OutputSection *Dest);
void addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V);
void addBaserels();
void addBaserelBlocks(std::vector<Baserel> &V);
uint32_t getSizeOfInitializedData();
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
@ -207,6 +205,16 @@ private:
uint32_t PointerToSymbolTable = 0;
uint64_t SizeOfImage;
uint64_t SizeOfHeaders;
OutputSection *TextSec;
OutputSection *RdataSec;
OutputSection *DataSec;
OutputSection *PdataSec;
OutputSection *IdataSec;
OutputSection *EdataSec;
OutputSection *DidatSec;
OutputSection *RsrcSec;
OutputSection *RelocSec;
};
} // anonymous namespace
@ -318,8 +326,6 @@ void Writer::run() {
createMiscChunks();
createImportTables();
createExportTable();
if (Config->Relocatable)
createSection(".reloc");
assignAddresses();
removeEmptySections();
setSectionPermissions();
@ -386,7 +392,37 @@ static void sortBySectionOrder(std::vector<Chunk *> &Chunks) {
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, bin chunks by name.
// First, create the builtin sections.
const uint32_t DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
const uint32_t BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
const uint32_t CODE = IMAGE_SCN_CNT_CODE;
const uint32_t DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE;
const uint32_t R = IMAGE_SCN_MEM_READ;
const uint32_t W = IMAGE_SCN_MEM_WRITE;
const uint32_t X = IMAGE_SCN_MEM_EXECUTE;
SmallDenseMap<StringRef, OutputSection *> Sections;
auto CreateSection = [&](StringRef Name, uint32_t Perms) {
auto Sec = make<OutputSection>(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
Sections[Name] = Sec;
return Sec;
};
// Try to match the section order used by link.exe.
TextSec = CreateSection(".text", CODE | R | X);
CreateSection(".bss", BSS | R | W);
RdataSec = CreateSection(".rdata", DATA | R);
DataSec = CreateSection(".data", DATA | R | W);
PdataSec = CreateSection(".pdata", DATA | R);
IdataSec = CreateSection(".idata", DATA | R);
EdataSec = CreateSection(".edata", DATA | R);
DidatSec = CreateSection(".didat", DATA | R);
RsrcSec = CreateSection(".rsrc", DATA | R);
RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R);
// Then bin chunks by name.
std::map<StringRef, std::vector<Chunk *>> Map;
for (Chunk *C : Symtab->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
@ -407,7 +443,6 @@ void Writer::createSections() {
// '$' 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.
SmallDenseMap<StringRef, OutputSection *> Sections;
for (auto Pair : Map) {
StringRef Name = getOutputSection(Pair.first);
OutputSection *&Sec = Sections[Name];
@ -421,18 +456,38 @@ void Writer::createSections() {
Sec->addPermissions(C->getPermissions());
}
}
// Finally, move some output sections to the end.
auto SectionOrder = [&](OutputSection *S) {
// .reloc should come last of all since it refers to RVAs of data in the
// previous sections.
if (S == RelocSec)
return 3;
// Move DISCARDABLE (or non-memory-mapped) sections to the end of file because
// the loader cannot handle holes.
if (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE)
return 2;
// .rsrc should come at the end of the non-discardable sections because its
// size may change by the Win32 UpdateResources() function, causing
// subsequent sections to move (see https://crbug.com/827082).
if (S == RsrcSec)
return 1;
return 0;
};
std::stable_sort(OutputSections.begin(), OutputSections.end(),
[&](OutputSection *S, OutputSection *T) {
return SectionOrder(S) < SectionOrder(T);
});
}
void Writer::createMiscChunks() {
OutputSection *RData = createSection(".rdata");
for (auto &P : MergeChunk::Instances)
RData->addChunk(P.second);
RdataSec->addChunk(P.second);
// Create thunks for locally-dllimported symbols.
if (!Symtab->LocalImportChunks.empty()) {
for (Chunk *C : Symtab->LocalImportChunks)
RData->addChunk(C);
RdataSec->addChunk(C);
}
// Create Debug Information Chunks
@ -447,18 +502,18 @@ void Writer::createMiscChunks() {
BuildId = CVChunk;
DebugRecords.push_back(CVChunk);
RData->addChunk(DebugDirectory);
RdataSec->addChunk(DebugDirectory);
for (Chunk *C : DebugRecords)
RData->addChunk(C);
RdataSec->addChunk(C);
}
// Create SEH table. x86-only.
if (Config->Machine == I386)
createSEHTable(RData);
createSEHTable();
// Create /guard:cf tables if requested.
if (Config->GuardCF != GuardCFLevel::Off)
createGuardCFTables(RData);
createGuardCFTables();
}
// Create .idata section for the DLL-imported symbol table.
@ -481,13 +536,12 @@ void Writer::createImportTables() {
Config->DLLOrder[DLL] = Config->DLLOrder.size();
}
OutputSection *Text = createSection(".text");
for (ImportFile *File : ImportFile::Instances) {
if (!File->Live)
continue;
if (DefinedImportThunk *Thunk = File->ThunkSym)
Text->addChunk(Thunk->getChunk());
TextSec->addChunk(Thunk->getChunk());
if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) {
if (!File->ThunkSym)
@ -499,33 +553,27 @@ void Writer::createImportTables() {
}
}
if (!Idata.empty()) {
OutputSection *Sec = createSection(".idata");
if (!Idata.empty())
for (Chunk *C : Idata.getChunks())
Sec->addChunk(C);
}
IdataSec->addChunk(C);
if (!DelayIdata.empty()) {
Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
DelayIdata.create(Helper);
OutputSection *Sec = createSection(".didat");
for (Chunk *C : DelayIdata.getChunks())
Sec->addChunk(C);
Sec = createSection(".data");
DidatSec->addChunk(C);
for (Chunk *C : DelayIdata.getDataChunks())
Sec->addChunk(C);
Sec = createSection(".text");
DataSec->addChunk(C);
for (Chunk *C : DelayIdata.getCodeChunks())
Sec->addChunk(C);
TextSec->addChunk(C);
}
}
void Writer::createExportTable() {
if (Config->Exports.empty())
return;
OutputSection *Sec = createSection(".edata");
for (Chunk *C : Edata.Chunks)
Sec->addChunk(C);
EdataSec->addChunk(C);
}
// The Windows loader doesn't seem to like empty sections,
@ -644,29 +692,6 @@ void Writer::createSymbolAndStringTable() {
FileSize = alignTo(FileOff, SectorSize);
}
static int sectionIndex(OutputSection *S) {
// Move DISCARDABLE (or non-memory-mapped) sections to the end of file because
// the loader cannot handle holes.
if (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE)
return 101;
// Try to match the section order used by link.exe. In particular, it's
// important that .reloc comes last since it refers to RVA's of data in
// the previous sections. .rsrc should come late because its size may
// change by the Win32 UpdateResources() function, causing subsequent
// sections to move (see https://crbug.com/827082).
return StringSwitch<int>(S->Name)
.Case(".text", 1)
.Case(".bss", 2)
.Case(".rdata", 3)
.Case(".data", 4)
.Case(".pdata", 5)
.Case(".idata", 6)
.Case(".rsrc", 99)
.Case(".reloc", 100)
.Default(50); // Default to somewhere in the middle.
}
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
@ -679,15 +704,9 @@ void Writer::assignAddresses() {
uint64_t RVA = PageSize; // The first page is kept unmapped.
FileSize = SizeOfHeaders;
// Reorder the sections.
std::stable_sort(OutputSections.begin(), OutputSections.end(),
[](OutputSection *S, OutputSection *T) {
return sectionIndex(S) < sectionIndex(T);
});
for (OutputSection *Sec : OutputSections) {
if (Sec->Name == ".reloc")
addBaserels(Sec);
if (Sec == RelocSec)
addBaserels();
uint64_t RawSize = 0, VirtualSize = 0;
Sec->Header.VirtualAddress = RVA;
for (Chunk *C : Sec->getChunks()) {
@ -810,18 +829,18 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
if (Config->TerminalServerAware)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
if (OutputSection *Text = findSection(".text")) {
PE->BaseOfCode = Text->getRVA();
PE->SizeOfCode = Text->getRawSize();
if (TextSec->getVirtualSize()) {
PE->BaseOfCode = TextSec->getRVA();
PE->SizeOfCode = TextSec->getRawSize();
}
PE->SizeOfInitializedData = getSizeOfInitializedData();
// Write data directory
auto *Dir = reinterpret_cast<data_directory *>(Buf);
Buf += sizeof(*Dir) * NumberfOfDataDirectory;
if (OutputSection *Sec = findSection(".edata")) {
Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXPORT_TABLE].Size = Sec->getVirtualSize();
if (EdataSec->getVirtualSize()) {
Dir[EXPORT_TABLE].RelativeVirtualAddress = EdataSec->getRVA();
Dir[EXPORT_TABLE].Size = EdataSec->getVirtualSize();
}
if (!Idata.empty()) {
Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA();
@ -829,17 +848,17 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
Dir[IAT].Size = Idata.getIATSize();
}
if (OutputSection *Sec = findSection(".rsrc")) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize();
if (RsrcSec->getVirtualSize()) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA();
Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize();
}
if (OutputSection *Sec = findSection(".pdata")) {
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize();
if (PdataSec->getVirtualSize()) {
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = PdataSec->getRVA();
Dir[EXCEPTION_TABLE].Size = PdataSec->getVirtualSize();
}
if (OutputSection *Sec = findSection(".reloc")) {
Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
if (RelocSec->getVirtualSize()) {
Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA();
Dir[BASE_RELOCATION_TABLE].Size = RelocSec->getVirtualSize();
}
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
if (Defined *B = dyn_cast<Defined>(Sym)) {
@ -908,7 +927,7 @@ void Writer::openFile(StringRef Path) {
"failed to open " + Path);
}
void Writer::createSEHTable(OutputSection *RData) {
void Writer::createSEHTable() {
SymbolRVASet Handlers;
for (ObjFile *File : ObjFile::Instances) {
// FIXME: We should error here instead of earlier unless /safeseh:no was
@ -919,7 +938,7 @@ void Writer::createSEHTable(OutputSection *RData) {
markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers);
}
maybeAddRVATable(RData, std::move(Handlers), "__safe_se_handler_table",
maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table",
"__safe_se_handler_count");
}
@ -964,7 +983,7 @@ static void markSymbolsWithRelocations(ObjFile *File,
// Create the guard function id table. This is a table of RVAs of all
// address-taken functions. It is sorted and uniqued, just like the safe SEH
// table.
void Writer::createGuardCFTables(OutputSection *RData) {
void Writer::createGuardCFTables() {
SymbolRVASet AddressTakenSyms;
SymbolRVASet LongJmpTargets;
for (ObjFile *File : ObjFile::Instances) {
@ -985,12 +1004,12 @@ void Writer::createGuardCFTables(OutputSection *RData) {
if (Config->Entry)
addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry));
maybeAddRVATable(RData, std::move(AddressTakenSyms), "__guard_fids_table",
maybeAddRVATable(std::move(AddressTakenSyms), "__guard_fids_table",
"__guard_fids_count");
// Add the longjmp target table unless the user told us not to.
if (Config->GuardCF == GuardCFLevel::Full)
maybeAddRVATable(RData, std::move(LongJmpTargets), "__guard_longjmp_table",
maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table",
"__guard_longjmp_count");
// Set __guard_flags, which will be used in the load config to indicate that
@ -1047,14 +1066,13 @@ void Writer::markSymbolsForRVATable(ObjFile *File,
// Replace the absolute table symbol with a synthetic symbol pointing to
// TableChunk so that we can emit base relocations for it and resolve section
// relative relocations.
void Writer::maybeAddRVATable(OutputSection *RData,
SymbolRVASet TableSymbols,
StringRef TableSym, StringRef CountSym) {
void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
StringRef CountSym) {
if (TableSymbols.empty())
return;
RVATableChunk *TableChunk = make<RVATableChunk>(std::move(TableSymbols));
RData->addChunk(TableChunk);
RdataSec->addChunk(TableChunk);
Symbol *T = Symtab->findUnderscore(TableSym);
Symbol *C = Symtab->findUnderscore(CountSym);
@ -1141,12 +1159,11 @@ void Writer::writeBuildId() {
// Sort .pdata section contents according to PE/COFF spec 5.5.
void Writer::sortExceptionTable() {
OutputSection *Sec = findSection(".pdata");
if (!Sec)
if (PdataSec->getVirtualSize() == 0)
return;
// We assume .pdata contains function table entries only.
uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff();
uint8_t *End = Begin + Sec->getVirtualSize();
uint8_t *Begin = Buffer->getBufferStart() + PdataSec->getFileOff();
uint8_t *End = Begin + PdataSec->getVirtualSize();
if (Config->Machine == AMD64) {
struct Entry { ulittle32_t Begin, End, Unwind; };
sort(parallel::par, (Entry *)Begin, (Entry *)End,
@ -1177,50 +1194,26 @@ uint32_t Writer::getSizeOfInitializedData() {
return Res;
}
// Returns an existing section or create a new one if not found.
OutputSection *Writer::createSection(StringRef Name) {
if (auto *Sec = findSection(Name))
return Sec;
const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
const auto CODE = IMAGE_SCN_CNT_CODE;
const auto DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE;
const auto R = IMAGE_SCN_MEM_READ;
const auto W = IMAGE_SCN_MEM_WRITE;
const auto X = IMAGE_SCN_MEM_EXECUTE;
uint32_t Perms = StringSwitch<uint32_t>(Name)
.Case(".bss", BSS | R | W)
.Case(".data", DATA | R | W)
.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 = make<OutputSection>(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
return Sec;
}
// Dest is .reloc section. Add contents to that section.
void Writer::addBaserels(OutputSection *Dest) {
// Add base relocations to .reloc section.
void Writer::addBaserels() {
if (!Config->Relocatable)
return;
std::vector<Baserel> V;
for (OutputSection *Sec : OutputSections) {
if (Sec == Dest)
if (Sec == RelocSec)
continue;
// Collect all locations for base relocations.
for (Chunk *C : Sec->getChunks())
C->getBaserels(&V);
// Add the addresses to .reloc section.
if (!V.empty())
addBaserelBlocks(Dest, V);
addBaserelBlocks(V);
V.clear();
}
}
// Add addresses to .reloc section. Note that addresses are grouped by page.
void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
void Writer::addBaserelBlocks(std::vector<Baserel> &V) {
const uint32_t Mask = ~uint32_t(PageSize - 1);
uint32_t Page = V[0].RVA & Mask;
size_t I = 0, J = 1;
@ -1228,11 +1221,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
uint32_t P = V[J].RVA & Mask;
if (P == Page)
continue;
Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
I = J;
Page = P;
}
if (I == J)
return;
Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
}