Split the creation of program headers in a few steps.

IMHO this makes the code easier to read and should help with linker
scripts.

This is strongly based on D16575. The main differences are:

We record a range of sections, not every section in a program header.

scanHeaders takes case of deciding what goes in every program header,
including PT_GNU_RELRO

We create dummy sections for the start of the file

With this, program header creation has 3 isolated stages:

Map sections to program headers.
Assign addresses to *sections*
Looking at sections find the address and size of each program header.

Thanks to George Rimar for the initial version.

llvm-svn: 260453
This commit is contained in:
Rafael Espindola 2016-02-10 22:43:13 +00:00
parent b4a5aa239c
commit 4fc6044a5e
2 changed files with 190 additions and 177 deletions

View File

@ -94,7 +94,7 @@ public:
}
virtual void finalize() {}
virtual void writeTo(uint8_t *Buf) = 0;
virtual void writeTo(uint8_t *Buf) {}
virtual ~OutputSectionBase() = default;
protected:
@ -540,6 +540,8 @@ template <class ELFT> struct Out {
static SymbolTableSection<ELFT> *DynSymTab;
static SymbolTableSection<ELFT> *SymTab;
static Elf_Phdr *TlsPhdr;
static OutputSectionBase<ELFT> *ElfHeader;
static OutputSectionBase<ELFT> *ProgramHeaders;
};
template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
@ -562,6 +564,8 @@ template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::StrTab;
template <class ELFT> SymbolTableSection<ELFT> *Out<ELFT>::DynSymTab;
template <class ELFT> SymbolTableSection<ELFT> *Out<ELFT>::SymTab;
template <class ELFT> typename Out<ELFT>::Elf_Phdr *Out<ELFT>::TlsPhdr;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::ElfHeader;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::ProgramHeaders;
} // namespace elf2
} // namespace lld

View File

@ -13,6 +13,7 @@
#include "SymbolTable.h"
#include "Target.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/FileOutputBuffer.h"
@ -41,6 +42,19 @@ public:
void run();
private:
// This describes a program header entry.
// Each contains type, access flags and range of output sections that will be
// placed in it.
struct Phdr {
Phdr(unsigned Type, unsigned Flags) {
H.p_type = Type;
H.p_flags = Flags;
}
Elf_Phdr H = {};
OutputSectionBase<ELFT> *First = nullptr;
OutputSectionBase<ELFT> *Last = nullptr;
};
void copyLocalSymbols();
void addReservedSymbols();
bool createSections();
@ -52,7 +66,7 @@ private:
void scanRelocs(InputSection<ELFT> &C);
void scanRelocs(InputSectionBase<ELFT> &S, const Elf_Shdr &RelSec);
void updateRelro(Elf_Phdr *Cur, Elf_Phdr *GnuRelroPhdr, uintX_t VA);
void createPhdrs();
void assignAddresses();
void buildSectionMap();
void fixAbsoluteSymbols();
@ -67,7 +81,6 @@ private:
bool isOutputDynamic() const {
return !Symtab.getSharedFiles().empty() || Config->Shared;
}
int getPhdrsNum() const;
OutputSection<ELFT> *getBss();
void addCommonSymbols(std::vector<DefinedCommon *> &Syms);
@ -78,18 +91,22 @@ private:
BumpPtrAllocator Alloc;
std::vector<OutputSectionBase<ELFT> *> OutputSections;
std::vector<std::unique_ptr<OutputSectionBase<ELFT>>> OwningSections;
unsigned getNumSections() const { return OutputSections.size() + 1; }
// We create a section for the ELF header and one for the program headers.
const unsigned NumDummySections = 2;
ArrayRef<OutputSectionBase<ELFT> *> getSections() const {
return makeArrayRef(OutputSections).slice(NumDummySections);
}
unsigned getNumSections() const {
return OutputSections.size() + 1 - NumDummySections;
}
void addRelIpltSymbols();
void addStartEndSymbols();
void addStartStopSymbols(OutputSectionBase<ELFT> *Sec);
void setPhdr(Elf_Phdr *PH, uint32_t Type, uint32_t Flags, uintX_t FileOff,
uintX_t VA, uintX_t Size, uintX_t Align);
void copyPhdr(Elf_Phdr *PH, OutputSectionBase<ELFT> *From);
bool HasRelro = false;
SymbolTable<ELFT> &Symtab;
std::vector<Elf_Phdr> Phdrs;
std::vector<Phdr> Phdrs;
uintX_t FileSize;
uintX_t SectionHeaderOff;
@ -105,6 +122,8 @@ private:
template <class ELFT> static bool shouldUseRela() { return ELFT::Is64Bits; }
template <class ELFT> void elf2::writeResult(SymbolTable<ELFT> *Symtab) {
typedef typename ELFFile<ELFT>::uintX_t uintX_t;
// Create singleton output sections.
bool IsRela = shouldUseRela<ELFT>();
DynamicSection<ELFT> Dynamic(*Symtab);
@ -117,6 +136,10 @@ template <class ELFT> void elf2::writeResult(SymbolTable<ELFT> *Symtab) {
StringTableSection<ELFT> ShStrTab(".shstrtab", false);
SymbolTableSection<ELFT> DynSymTab(*Symtab, DynStrTab);
OutputSectionBase<ELFT> ElfHeader("", 0, SHF_ALLOC);
OutputSectionBase<ELFT> ProgramHeaders("", 0, SHF_ALLOC);
ProgramHeaders.updateAlign(sizeof(uintX_t));
// Instantiate optional output sections if they are needed.
std::unique_ptr<GnuHashTableSection<ELFT>> GnuHashTab;
std::unique_ptr<GotPltSection<ELFT>> GotPlt;
@ -159,6 +182,8 @@ template <class ELFT> void elf2::writeResult(SymbolTable<ELFT> *Symtab) {
Out<ELFT>::Opd = nullptr;
Out<ELFT>::OpdBuf = nullptr;
Out<ELFT>::TlsPhdr = nullptr;
Out<ELFT>::ElfHeader = &ElfHeader;
Out<ELFT>::ProgramHeaders = &ProgramHeaders;
Writer<ELFT>(*Symtab).run();
}
@ -171,6 +196,7 @@ template <class ELFT> void Writer<ELFT>::run() {
addReservedSymbols();
if (!createSections())
return;
createPhdrs();
assignAddresses();
fixAbsoluteSymbols();
if (!openFile())
@ -547,6 +573,8 @@ static int getPPC64SectionRank(StringRef SectionName) {
}
template <class ELFT> static bool isRelroSection(OutputSectionBase<ELFT> *Sec) {
if (!Config->ZRelro)
return false;
typename OutputSectionBase<ELFT>::uintX_t Flags = Sec->getFlags();
if (!(Flags & SHF_ALLOC) || !(Flags & SHF_WRITE))
return false;
@ -889,6 +917,9 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
// Create output section objects and add them to OutputSections.
template <class ELFT> bool Writer<ELFT>::createSections() {
OutputSections.push_back(Out<ELFT>::ElfHeader);
OutputSections.push_back(Out<ELFT>::ProgramHeaders);
// Add .interp first because some loaders want to see that section
// on the first page of the executable file when loaded into memory.
if (needsInterpSection())
@ -994,12 +1025,10 @@ template <class ELFT> bool Writer<ELFT>::createSections() {
std::stable_sort(OutputSections.begin(), OutputSections.end(),
compareSections<ELFT>);
for (unsigned I = 0, N = OutputSections.size(); I < N; ++I) {
OutputSections[I]->SectionIndex = I + 1;
HasRelro |= (Config->ZRelro && isRelroSection(OutputSections[I]));
}
for (unsigned I = NumDummySections, N = OutputSections.size(); I < N; ++I)
OutputSections[I]->SectionIndex = I + 1 - NumDummySections;
for (OutputSectionBase<ELFT> *Sec : OutputSections)
for (OutputSectionBase<ELFT> *Sec : getSections())
Sec->setSHName(Out<ELFT>::ShStrTab->addString(Sec->getName()));
// Finalizers fix each section's size.
@ -1165,141 +1194,150 @@ static uint32_t getAmdgpuPhdr(OutputSectionBase<ELFT> *Sec) {
return PT_LOAD;
}
template <class ELFT>
void Writer<ELFT>::updateRelro(Elf_Phdr *Cur, Elf_Phdr *GnuRelroPhdr,
uintX_t VA) {
if (!GnuRelroPhdr->p_type)
setPhdr(GnuRelroPhdr, PT_GNU_RELRO, PF_R, Cur->p_offset, Cur->p_vaddr,
VA - Cur->p_vaddr, 1 /*p_align*/);
GnuRelroPhdr->p_filesz = VA - Cur->p_vaddr;
GnuRelroPhdr->p_memsz = VA - Cur->p_vaddr;
}
// Decide which program headers to create and which sections to include in each
// one.
template <class ELFT> void Writer<ELFT>::createPhdrs() {
auto AddHdr = [this](unsigned Type, unsigned Flags) {
return &*Phdrs.emplace(Phdrs.end(), Type, Flags);
};
// Visits all sections to create PHDRs and to assign incremental,
// non-overlapping addresses to output sections.
template <class ELFT> void Writer<ELFT>::assignAddresses() {
uintX_t VA = Target->getVAStart() + sizeof(Elf_Ehdr);
uintX_t FileOff = sizeof(Elf_Ehdr);
// Calculate and reserve the space for the program header first so that
// the first section can start right after the program header.
Phdrs.resize(getPhdrsNum());
size_t PhdrSize = sizeof(Elf_Phdr) * Phdrs.size();
auto AddSec = [](Phdr &Hdr, OutputSectionBase<ELFT> *Sec) {
Hdr.Last = Sec;
if (!Hdr.First)
Hdr.First = Sec;
Hdr.H.p_align = std::max<uintX_t>(Hdr.H.p_align, Sec->getAlign());
};
// The first phdr entry is PT_PHDR which describes the program header itself.
setPhdr(&Phdrs[0], PT_PHDR, PF_R, FileOff, VA, PhdrSize,
/*Align=*/sizeof(uintX_t));
FileOff += PhdrSize;
VA += PhdrSize;
Phdr &Hdr = *AddHdr(PT_PHDR, PF_R);
AddSec(Hdr, Out<ELFT>::ProgramHeaders);
// PT_INTERP must be the second entry if exists.
int PhdrIdx = 0;
Elf_Phdr *Interp = nullptr;
if (needsInterpSection())
Interp = &Phdrs[++PhdrIdx];
if (needsInterpSection()) {
Phdr &Hdr = *AddHdr(PT_INTERP, toPhdrFlags(Out<ELFT>::Interp->getFlags()));
AddSec(Hdr, Out<ELFT>::Interp);
}
// Add the first PT_LOAD segment for regular output sections.
setPhdr(&Phdrs[++PhdrIdx], PT_LOAD, PF_R, 0, Target->getVAStart(), FileOff,
Target->PageSize);
uintX_t Flags = PF_R;
Phdr *Load = AddHdr(PT_LOAD, Flags);
AddSec(*Load, Out<ELFT>::ElfHeader);
Elf_Phdr GnuRelroPhdr = {};
Elf_Phdr TlsPhdr{};
bool RelroAligned = false;
uintX_t ThreadBssOffset = 0;
// Create phdrs as we assign VAs and file offsets to all output sections.
Phdr TlsHdr(PT_TLS, PF_R);
Phdr RelRo(PT_GNU_RELRO, PF_R);
for (OutputSectionBase<ELFT> *Sec : OutputSections) {
Elf_Phdr *PH = &Phdrs[PhdrIdx];
if (needsPhdr<ELFT>(Sec)) {
uintX_t Flags = toPhdrFlags(Sec->getFlags());
bool InRelRo = Config->ZRelro && (Flags & PF_W) && isRelroSection(Sec);
bool FirstNonRelRo = GnuRelroPhdr.p_type && !InRelRo && !RelroAligned;
if (FirstNonRelRo || PH->p_flags != Flags) {
VA = alignTo(VA, Target->PageSize);
FileOff = alignTo(FileOff, Target->PageSize);
if (FirstNonRelRo)
RelroAligned = true;
}
if (!needsPhdr<ELFT>(Sec))
break;
if (PH->p_flags != Flags) {
// Flags changed. Create a new PT_LOAD.
PH = &Phdrs[++PhdrIdx];
uint32_t PTType = (Config->EMachine != EM_AMDGPU) ? (uint32_t)PT_LOAD
: getAmdgpuPhdr(Sec);
setPhdr(PH, PTType, Flags, FileOff, VA, 0, Target->PageSize);
}
if (Sec->getFlags() & SHF_TLS) {
if (!TlsPhdr.p_vaddr)
setPhdr(&TlsPhdr, PT_TLS, PF_R, FileOff, VA, 0, Sec->getAlign());
if (Sec->getType() != SHT_NOBITS)
VA = alignTo(VA, Sec->getAlign());
uintX_t TVA = alignTo(VA + ThreadBssOffset, Sec->getAlign());
Sec->setVA(TVA);
TlsPhdr.p_memsz += Sec->getSize();
if (Sec->getType() == SHT_NOBITS) {
ThreadBssOffset = TVA - VA + Sec->getSize();
} else {
TlsPhdr.p_filesz += Sec->getSize();
VA += Sec->getSize();
}
TlsPhdr.p_align = std::max<uintX_t>(TlsPhdr.p_align, Sec->getAlign());
} else {
VA = alignTo(VA, Sec->getAlign());
Sec->setVA(VA);
VA += Sec->getSize();
}
if (InRelRo)
updateRelro(PH, &GnuRelroPhdr, VA);
// If flags changed then we want new load segment.
uintX_t NewFlags = toPhdrFlags(Sec->getFlags());
if (Flags != NewFlags) {
uint32_t LoadType = (Config->EMachine == EM_AMDGPU) ? getAmdgpuPhdr(Sec)
: (uint32_t)PT_LOAD;
Load = AddHdr(LoadType, NewFlags);
Flags = NewFlags;
}
// If we meet TLS section then we create TLS header
// and put all TLS sections inside for futher use when
// assign addresses.
if (Sec->getFlags() & SHF_TLS) {
AddSec(TlsHdr, Sec);
if (Sec->getType() == SHT_NOBITS)
continue;
}
FileOff = alignTo(FileOff, Sec->getAlign());
Sec->setFileOffset(FileOff);
if (Sec->getType() != SHT_NOBITS)
FileOff += Sec->getSize();
if (needsPhdr<ELFT>(Sec)) {
PH->p_filesz = FileOff - PH->p_offset;
PH->p_memsz = VA - PH->p_vaddr;
}
AddSec(*Load, Sec);
if (isRelroSection(Sec))
AddSec(RelRo, Sec);
}
if (TlsPhdr.p_vaddr) {
// The TLS pointer goes after PT_TLS. At least glibc will align it,
// so round up the size to make sure the offsets are correct.
TlsPhdr.p_memsz = alignTo(TlsPhdr.p_memsz, TlsPhdr.p_align);
Phdrs[++PhdrIdx] = TlsPhdr;
Out<ELFT>::TlsPhdr = &Phdrs[PhdrIdx];
}
// Add the TLS segment unless it's empty.
if (TlsHdr.First)
Phdrs.push_back(std::move(TlsHdr));
// Add an entry for .dynamic.
if (isOutputDynamic()) {
Elf_Phdr *PH = &Phdrs[++PhdrIdx];
PH->p_type = PT_DYNAMIC;
copyPhdr(PH, Out<ELFT>::Dynamic);
Phdr &H = *AddHdr(PT_DYNAMIC, toPhdrFlags(Out<ELFT>::Dynamic->getFlags()));
AddSec(H, Out<ELFT>::Dynamic);
}
if (HasRelro) {
Elf_Phdr *PH = &Phdrs[++PhdrIdx];
*PH = GnuRelroPhdr;
}
// PT_GNU_RELRO includes all sections that should be marked as
// read-only by dynamic linker after proccessing relocations.
if (RelRo.First)
Phdrs.push_back(std::move(RelRo));
// PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr.
if (Out<ELFT>::EhFrameHdr->Live) {
Elf_Phdr *PH = &Phdrs[++PhdrIdx];
PH->p_type = PT_GNU_EH_FRAME;
copyPhdr(PH, Out<ELFT>::EhFrameHdr);
Phdr &Hdr = *AddHdr(PT_GNU_EH_FRAME,
toPhdrFlags(Out<ELFT>::EhFrameHdr->getFlags()));
AddSec(Hdr, Out<ELFT>::EhFrameHdr);
}
// PT_GNU_STACK is a special section to tell the loader to make the
// pages for the stack non-executable.
if (!Config->ZExecStack) {
Elf_Phdr *PH = &Phdrs[++PhdrIdx];
PH->p_type = PT_GNU_STACK;
PH->p_flags = PF_R | PF_W;
if (!Config->ZExecStack)
AddHdr(PT_GNU_STACK, PF_R | PF_W);
}
// Visits all headers in PhdrTable and assigns the adresses to
// the output sections. Also creates common and special headers.
template <class ELFT> void Writer<ELFT>::assignAddresses() {
Out<ELFT>::ElfHeader->setSize(sizeof(Elf_Ehdr));
size_t PhdrSize = sizeof(Elf_Phdr) * Phdrs.size();
Out<ELFT>::ProgramHeaders->setSize(PhdrSize);
// The first section of each PT_LOAD and the first section after PT_GNU_RELRO
// have to be page aligned so that the dynamic linker can set the permissions.
SmallPtrSet<OutputSectionBase<ELFT> *, 4> PageAlign;
for (const Phdr &P : Phdrs) {
if (P.H.p_type == PT_GNU_RELRO) {
// Find the first section after PT_GNU_RELRO. If it is in a PT_LOAD
// and is not tls, we have to align it to a page. We don't have to
// align tls since TLS NOBITS takes no space.
auto I = std::find(OutputSections.begin(), OutputSections.end(), P.Last);
++I;
if (I != OutputSections.end() && needsPhdr(*I) &&
!((*I)->getFlags() & SHF_TLS))
PageAlign.insert(*I);
}
// FIXME: why create empty PT_LOAD?
if (P.H.p_type == PT_LOAD && P.First)
PageAlign.insert(P.First);
}
// Fix up PT_INTERP as we now know the address of .interp section.
if (Interp) {
Interp->p_type = PT_INTERP;
copyPhdr(Interp, Out<ELFT>::Interp);
uintX_t ThreadBssOffset = 0;
uintX_t VA = Target->getVAStart();
uintX_t FileOff = 0;
for (OutputSectionBase<ELFT> *Sec : OutputSections) {
uintX_t Align = Sec->getAlign();
if (PageAlign.count(Sec))
Align = std::max<uintX_t>(Align, Target->PageSize);
FileOff = alignTo(FileOff, Align);
Sec->setFileOffset(FileOff);
if (Sec->getType() != SHT_NOBITS)
FileOff += Sec->getSize();
// We only assign VAs to allocated sections.
if (needsPhdr<ELFT>(Sec)) {
// Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is
// responsible for allocating space for them, not the PT_LOAD that
// contains the TLS initialization image.
bool IsTls = Sec->getFlags() & SHF_TLS;
if (IsTls && Sec->getType() == SHT_NOBITS) {
uintX_t TVA = VA + ThreadBssOffset;
TVA = alignTo(TVA, Align);
Sec->setVA(TVA);
ThreadBssOffset = TVA - VA + Sec->getSize();
} else {
VA = alignTo(VA, Align);
Sec->setVA(VA);
VA += Sec->getSize();
}
}
}
// Add space for section headers.
@ -1309,37 +1347,31 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
// Update "_end" and "end" symbols so that they
// point to the end of the data segment.
ElfSym<ELFT>::End.st_value = VA;
}
// Returns the number of PHDR entries.
template <class ELFT> int Writer<ELFT>::getPhdrsNum() const {
bool Tls = false;
int I = 2; // 2 for PT_PHDR and first PT_LOAD
if (needsInterpSection())
++I;
if (isOutputDynamic())
++I;
if (!Config->ZExecStack)
++I;
uintX_t Last = PF_R;
for (OutputSectionBase<ELFT> *Sec : OutputSections) {
if (!needsPhdr<ELFT>(Sec))
continue;
if (Sec->getFlags() & SHF_TLS)
Tls = true;
uintX_t Flags = toPhdrFlags(Sec->getFlags());
if (Last != Flags) {
Last = Flags;
++I;
for (Phdr &PHdr : Phdrs) {
Elf_Phdr &H = PHdr.H;
if (PHdr.First) {
OutputSectionBase<ELFT> *Last = PHdr.Last;
H.p_filesz = Last->getFileOff() - PHdr.First->getFileOff();
if (Last->getType() != SHT_NOBITS)
H.p_filesz += Last->getSize();
H.p_memsz = Last->getVA() + Last->getSize() - PHdr.First->getVA();
H.p_offset = PHdr.First->getFileOff();
H.p_vaddr = PHdr.First->getVA();
}
if (PHdr.H.p_type == PT_LOAD)
H.p_align = Target->PageSize;
else if (PHdr.H.p_type == PT_GNU_RELRO)
H.p_align = 1;
H.p_paddr = H.p_vaddr;
// The TLS pointer goes after PT_TLS. At least glibc will align it,
// so round up the size to make sure the offsets are correct.
if (PHdr.H.p_type == PT_TLS) {
Out<ELFT>::TlsPhdr = &H;
H.p_memsz = alignTo(H.p_memsz, H.p_align);
}
}
if (Tls)
++I;
if (HasRelro)
++I;
if (Out<ELFT>::EhFrameHdr->Live)
++I;
return I;
}
static uint32_t getELFFlags() {
@ -1412,11 +1444,13 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
EHdr->e_shstrndx = Out<ELFT>::ShStrTab->SectionIndex;
// Write the program header table.
memcpy(Buf + EHdr->e_phoff, &Phdrs[0], Phdrs.size() * sizeof(Phdrs[0]));
auto *HBuf = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
for (Phdr &P : Phdrs)
*HBuf++ = P.H;
// Write the section header table. Note that the first table entry is null.
auto SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff);
for (OutputSectionBase<ELFT> *Sec : OutputSections)
for (OutputSectionBase<ELFT> *Sec : getSections())
Sec->writeHeaderTo(++SHdrs);
}
@ -1446,31 +1480,6 @@ template <class ELFT> void Writer<ELFT>::writeSections() {
Sec->writeTo(Buf + Sec->getFileOff());
}
template <class ELFT>
void Writer<ELFT>::setPhdr(Elf_Phdr *PH, uint32_t Type, uint32_t Flags,
uintX_t FileOff, uintX_t VA, uintX_t Size,
uintX_t Align) {
PH->p_type = Type;
PH->p_flags = Flags;
PH->p_offset = FileOff;
PH->p_vaddr = VA;
PH->p_paddr = VA;
PH->p_filesz = Size;
PH->p_memsz = Size;
PH->p_align = Align;
}
template <class ELFT>
void Writer<ELFT>::copyPhdr(Elf_Phdr *PH, OutputSectionBase<ELFT> *From) {
PH->p_flags = toPhdrFlags(From->getFlags());
PH->p_offset = From->getFileOff();
PH->p_vaddr = From->getVA();
PH->p_paddr = From->getVA();
PH->p_filesz = From->getSize();
PH->p_memsz = From->getSize();
PH->p_align = From->getAlign();
}
template <class ELFT> void Writer<ELFT>::buildSectionMap() {
for (const std::pair<StringRef, std::vector<StringRef>> &OutSec :
Config->OutputSections)