[PATCH] D16575: [ELF] - Redesign of program headers creation code.
Rafael EspĂndola via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 5 16:31:45 PST 2016
On 5 February 2016 at 18:47, George Rimar <grimar at accesssoftek.com> wrote:
> grimar added a comment.
>
> In http://reviews.llvm.org/D16575#345054, @rafael wrote:
>
>> I have applied this locally and I have some ideas on how to simplify
>> it. I will upload a proposed change after lunch.
>>
>> Cheers,
>> Rafael
>
>
> Interesting to see. Thanks !
This is what I have so far. There is more work to be done, but the idea is that
* scanHeaders does nothing more than map section to program headers.
* We then find out which sections need extra alignment because of page
boundaries.
* We assign address and offset to each *section*, which is now a simple loop
* We use the above info to assign sizes and address to each program header.
Things I want to try before considering this a real patch
* Create dummy sections for the file and program headers. This should
avoid the hacks regarding the first PT_LOAD.
* Store just first and last section. A program header is a contiguous range.
* Have the Phdr structure be just a Elf_Phdr and the first and last section.
Cheers,
Rafael
-------------- next part --------------
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 20a1dfb..908aa5a 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -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,16 @@ public:
void run();
private:
+ // This describes a programm header entry.
+ // Each contains type, access flags and list of output sections that will be
+ // placed in it.
+ struct Phdr {
+ Phdr(unsigned Type, unsigned Flags) : Type(Type), Flags(Flags) {}
+ unsigned Type;
+ unsigned Flags;
+ std::vector<OutputSectionBase<ELFT> *> OutSections;
+ };
+
void copyLocalSymbols();
void addReservedSymbols();
bool createSections();
@@ -52,7 +63,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);
+ std::vector<Phdr> scanHeaders();
void assignAddresses();
void buildSectionMap();
void fixAbsoluteSymbols();
@@ -67,7 +78,6 @@ private:
bool isOutputDynamic() const {
return !Symtab.getSharedFiles().empty() || Config->Shared;
}
- int getPhdrsNum() const;
OutputSection<ELFT> *getBss();
void addCommonSymbols(std::vector<DefinedCommon *> &Syms);
@@ -83,9 +93,6 @@ private:
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;
@@ -1164,140 +1171,149 @@ static uint32_t getAmdgpuPhdr(OutputSectionBase<ELFT> *Sec) {
return PT_LOAD;
}
+// Decide which program headers to create and which sections to include in each
+// one.
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;
-}
-
-// 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();
+std::vector<typename Writer<ELFT>::Phdr> Writer<ELFT>::scanHeaders() {
+ std::vector<Phdr> Map;
+ auto AddHdr = [this, &Map](unsigned Type, unsigned Flags) -> Phdr & {
+ return *Map.emplace(Map.end(), Type, Flags);
+ };
// The first phdr entry is PT_PHDR which describes the program header itself.
- setPhdr(&Phdrs[0], PT_PHDR, PF_R, FileOff, VA, PhdrSize, /*Align=*/8);
- FileOff += PhdrSize;
- VA += PhdrSize;
+ AddHdr(PT_PHDR, PF_R);
// 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()));
+ Hdr.OutSections.push_back(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);
- 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 (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 (!needsPhdr<ELFT>(Sec))
+ break;
+
+ // 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;
}
-
- 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;
+ // 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) {
+ TlsHdr.OutSections.push_back(Sec);
+ if (Sec->getType() == SHT_NOBITS)
+ continue;
}
- }
- 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];
+ Load->OutSections.push_back(Sec);
+
+ if (isRelroSection(Sec))
+ RelRo.OutSections.push_back(Sec);
}
+ // TLS header.
+ if (!TlsHdr.OutSections.empty())
+ Map.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 &Hdr = AddHdr(PT_DYNAMIC, toPhdrFlags(Out<ELFT>::Dynamic->getFlags()));
+ Hdr.OutSections.push_back(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 (HasRelro)
+ Map.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()));
+ Hdr.OutSections.push_back(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);
+
+ return Map;
+}
+
+static bool isLoadPhdr(unsigned H) {
+ return H == PT_LOAD || H == PT_AMDGPU_HSA_LOAD_CODE_AGENT ||
+ H == PT_AMDGPU_HSA_LOAD_GLOBAL_PROGRAM;
+}
+
+// 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() {
+ // We know the final amount of program headers here.
+ std::vector<Phdr> Map = scanHeaders();
+ Phdrs.resize(Map.size());
+ unsigned FirstLoadId = -1;
+ SmallPtrSet<OutputSectionBase<ELFT> *, 4> Foo;
+ for (unsigned I = 0, N = Map.size(); I != N; ++I) {
+ const Phdr &P = Map[I];
+ if (P.Type == PT_GNU_RELRO && !P.OutSections.empty()) {
+ OutputSectionBase<ELFT> *Last = P.OutSections.back();
+ auto I = std::find(OutputSections.begin(), OutputSections.end(), Last);
+ ++I;
+ if (I != OutputSections.end() && needsPhdr(*I) &&
+ !((*I)->getFlags() & SHF_TLS))
+ Foo.insert(*I);
+ }
+ if (P.Type != PT_LOAD)
+ continue;
+ if (FirstLoadId == (unsigned)-1)
+ FirstLoadId = I;
+ else if (!P.OutSections.empty())
+ Foo.insert(P.OutSections[0]);
}
- // 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;
+ size_t PhdrSize = sizeof(Elf_Phdr) * Phdrs.size();
+ uintX_t VA = Target->getVAStart() + sizeof(Elf_Ehdr) + PhdrSize;
+ uintX_t FileOff = sizeof(Elf_Ehdr) + PhdrSize;
+
+ for (OutputSectionBase<ELFT> *Sec : OutputSections) {
+ uintX_t Align = Sec->getAlign();
+ if (Foo.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();
+
+ 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.
@@ -1307,37 +1323,66 @@ 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 (size_t I = 0, E = Map.size(); I != E; ++I) {
+ Phdr &PHdr = Map[I];
+ bool HasSections = !PHdr.OutSections.empty();
+ uintX_t FileOff = 0;
+ uintX_t VA = 0;
+ uintX_t FileSize = 0;
+ uintX_t MemSize = 0;
+ if (HasSections) {
+ auto *Last = PHdr.OutSections.back();
+ FileSize = Last->getFileOff() - PHdr.OutSections[0]->getFileOff();
+ if (Last->getType() != SHT_NOBITS)
+ FileSize += Last->getSize();
+ MemSize = Last->getVA() + Last->getSize() - PHdr.OutSections[0]->getVA();
+ }
+ if (I == FirstLoadId) {
+ FileOff = 0;
+ VA = Target->getVAStart();
+ if (HasSections) {
+ FileSize += PHdr.OutSections[0]->getFileOff();
+ MemSize += PHdr.OutSections[0]->getFileOff();
+ } else {
+ FileSize += sizeof(Elf_Ehdr) + PhdrSize;
+ MemSize += sizeof(Elf_Ehdr) + PhdrSize;
+ }
+ } else if (HasSections) {
+ FileOff = PHdr.OutSections[0]->getFileOff();
+ VA = PHdr.OutSections[0]->getVA();
+ } else if (PHdr.Type == PT_PHDR) {
+ FileOff = sizeof(Elf_Ehdr);
+ VA = Target->getVAStart() + sizeof(Elf_Ehdr);
+ MemSize = FileSize = PhdrSize;
+ }
+ uintX_t Align = 0;
+ for (OutputSectionBase<ELFT> *Sec : PHdr.OutSections)
+ Align = std::max(Align, Sec->getAlign());
+ if (PHdr.Type == PT_LOAD)
+ Align = Target->PageSize;
+ else if (PHdr.Type == PT_PHDR)
+ Align = 8;
+ else if (PHdr.Type == PT_GNU_RELRO)
+ Align = 1;
+
+ Elf_Phdr *PH = &Phdrs[I];
+ PH->p_type = PHdr.Type;
+ PH->p_flags = PHdr.Flags;
+ PH->p_offset = FileOff;
+ PH->p_vaddr = VA;
+ PH->p_paddr = VA;
+ PH->p_filesz = FileSize;
+ PH->p_memsz = MemSize;
+ PH->p_align = Align;
+
+ // 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.Type == PT_TLS) {
+ Out<ELFT>::TlsPhdr = PH;
+ PH->p_memsz = alignTo(PH->p_memsz, PH->p_align);
}
}
- if (Tls)
- ++I;
- if (HasRelro)
- ++I;
- if (Out<ELFT>::EhFrameHdr->Live)
- ++I;
- return I;
}
static uint32_t getELFFlags() {
@@ -1444,31 +1489,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)
More information about the llvm-commits
mailing list