[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