Author: eelco
Date: Sun Sep 11 20:10:14 2011
New Revision: 29192
URL: https://ssl.nixos.org/websvn/nix/?rev=29192&sc=1

Log:
* Support executables created by the Gold linker (NixOS/140).  These
  are marked as ET_DYN (not ET_EXEC) and have a starting virtual
  address of 0 so they cannot grow downwards.  In order not to run
  into a Linux kernel bug, the virtual address and the offset of the
  new PT_LOAD segment have to be equal; otherwise ld-linux segfaults.
  To ensure this, it may be necessary to add some padding to the
  executable (potentially a lot of padding, if the executable has a
  large uninitialised data segment).
* Use size_t rather than off_t in some places.

Modified:
   patchelf/trunk/src/patchelf.cc

Modified: patchelf/trunk/src/patchelf.cc
==============================================================================
--- patchelf/trunk/src/patchelf.cc      Sun Sep 11 18:58:24 2011        (r29191)
+++ patchelf/trunk/src/patchelf.cc      Sun Sep 11 20:10:14 2011        (r29192)
@@ -55,6 +55,8 @@
 
     bool changed;
 
+    bool isExecutable;
+
     typedef string SectionName;
     typedef map<SectionName, string> ReplacedSections;
 
@@ -245,6 +247,8 @@
 template<ElfFileParams>
 void ElfFile<ElfFileParamNames>::parse()
 {
+    isExecutable = false;
+    
     /* Check the ELF header for basic validity. */
     if (fileSize < (off_t) sizeof(Elf_Ehdr)) error("missing ELF header");
 
@@ -268,8 +272,10 @@
         error("program headers have wrong size");
 
     /* Copy the program and section headers. */
-    for (int i = 0; i < rdi(hdr->e_phnum); ++i)
+    for (int i = 0; i < rdi(hdr->e_phnum); ++i) {
         phdrs.push_back(* ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i));
+        if (rdi(phdrs[i].p_type) == PT_INTERP) isExecutable = true;
+    }
     
     for (int i = 0; i < rdi(hdr->e_shnum); ++i)
         shdrs.push_back(* ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i));
@@ -486,7 +492,7 @@
     {
         string sectionName = i->first;
         Elf_Shdr & shdr = findSection(sectionName);
-        debug("rewriting section `%s' from offset %d (size %d) to offset %d 
(size %d)\n",
+        debug("rewriting section `%s' from offset 0x%x (size %d) to offset 
0x%x (size %d)\n",
             sectionName.c_str(), rdi(shdr.sh_offset), rdi(shdr.sh_size), 
curOff, i->second.size());
 
         memcpy(contents + curOff, (unsigned char *) i->second.c_str(),
@@ -552,11 +558,40 @@
     debug("needed space is %d\n", neededSpace);
 
 
-    off_t startOffset = roundUp(fileSize, pageSize);
+    size_t startOffset = roundUp(fileSize, pageSize);
 
     growFile(startOffset + neededSpace);
 
 
+    /* Even though this file is of type ET_DYN, it could actually be
+       an executable.  For instance, Gold produces executables marked
+       ET_DYN.  In that case we can still hit the kernel bug that
+       necessitated rewriteSectionsExecutable().  However, such
+       executables also tend to start at virtual address 0, so
+       rewriteSectionsExecutable() won't work because it doesn't have
+       any virtual address space to grow downwards into.  As a
+       workaround, make sure that the virtual address of our new
+       PT_LOAD segment relative to the first PT_LOAD segment is equal
+       to its offset; otherwise we hit the kernel bug.  This may
+       require creating a hole in the executable.  The bigger the size
+       of the uninitialised data segment, the bigger the hole. */
+    if (isExecutable) {
+        if (startOffset >= startPage) {
+            debug("shifting new PT_LOAD segment by %d bytes to work around a 
Linux kernel bug\n", startOffset - startPage);
+        } else {
+            size_t hole = startPage - startOffset;
+            /* Print a warning, because the hole could be very big. */
+            fprintf(stderr, "warning: working around a Linux kernel bug by 
creating a hole of %zu bytes in ā€˜%sā€™\n", hole, fileName.c_str());
+            assert(hole % pageSize == 0);
+            /* !!! We could create an actual hole in the file here,
+               but it's probably not worth the effort. */
+            growFile(fileSize + hole);
+            startOffset += hole;
+        }
+        startPage = startOffset;
+    }
+
+    
     /* Add a segment that maps the replaced sections and program
        headers into memory. */
     phdrs.resize(rdi(hdr->e_phnum) + 1);
@@ -610,7 +645,7 @@
        SHT_PROGBITS).  These cannot be moved in virtual address space
        since that would invalidate absolute references to them. */
     assert(lastReplaced + 1 < shdrs.size()); /* !!! I'm lazy. */
-    off_t startOffset = rdi(shdrs[lastReplaced + 1].sh_offset);
+    size_t startOffset = rdi(shdrs[lastReplaced + 1].sh_offset);
     Elf_Addr startAddr = rdi(shdrs[lastReplaced + 1].sh_addr);
     string prevSection;
     for (unsigned int i = 1; i <= lastReplaced; ++i) {
@@ -651,7 +686,7 @@
     
     /* Compute the total space needed for the replaced sections, the
        ELF header, and the program headers. */
-    off_t neededSpace = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr);
+    size_t neededSpace = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr);
     for (ReplacedSections::iterator i = replacedSections.begin();
          i != replacedSections.end(); ++i)
         neededSpace += roundUp(i->second.size(), sectionAlignment);
_______________________________________________
nix-commits mailing list
nix-comm...@cs.uu.nl
http://mail.cs.uu.nl/mailman/listinfo/nix-commits

Reply via email to