Bug 1385783 - Insert the elfhack code before the first executable section. r?froydnj draft
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 02 Aug 2017 16:39:12 +0900
changeset 620090 98599314716c8da7db79684636198aadb1dcdd21
parent 620089 c9ce4b448eed6cae9d10bb2fec3f58574183f3fa
child 640577 eff1775d9867803b09ce64af9a666147cd242b71
push id71906
push userbmo:mh+mozilla@glandium.org
push dateThu, 03 Aug 2017 01:03:52 +0000
reviewersfroydnj
bugs1385783
milestone57.0a1
Bug 1385783 - Insert the elfhack code before the first executable section. r?froydnj The lld linker creates separate segments for purely executable sections (such as .text) and sections preceding those (such as .rel.dyn). Neither gold nor bfd ld do that, and just put all those sections in the same executable segment. Since elfhack is putting its executable code between the two relocation sections, it ends up in a non-executable segment, leading to a crash when it's time to run that code. We thus insert the elfhack code before the first executable section instead of between the two relocation sections (which is where the elfhack data lies, and stays).
build/unix/elfhack/elfhack.cpp
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -636,18 +636,16 @@ int do_relocation_section(Elf *elf, unsi
         }
     }
     if (relhack_entry.r_offset)
         relhack->push_back(relhack_entry);
     // Last entry must be nullptr
     relhack_entry.r_offset = relhack_entry.r_info = 0;
     relhack->push_back(relhack_entry);
 
-    unsigned int old_end = section->getOffset() + section->getSize();
-
     if (init_array && !init_array_reloc) {
         // Some linkers create a DT_INIT_ARRAY section that, for all purposes,
         // is empty: it only contains 0x0 or 0xffffffff pointers with no relocations.
         const size_t zero = 0;
         const size_t all = SIZE_MAX;
         const char *data = init_array->getData();
         size_t length = Elf_Addr::size(elf->getClass());
         bool empty = true;
@@ -749,19 +747,37 @@ int do_relocation_section(Elf *elf, unsi
             return -1;
         }
     }
 
     section->rels.assign(new_rels.begin(), new_rels.end());
     section->shrink(new_rels.size() * section->getEntSize());
 
     ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, *relhack, original_init, mprotect_cb);
-    relhackcode->insertBefore(section);
-    relhack->insertAfter(relhackcode);
-    if (section->getOffset() + section->getSize() >= old_end) {
+    // Find the first executable section, and insert the relhack code before
+    // that. The relhack data is inserted between .rel.dyn and .rel.plt.
+    ElfSection *first_executable = nullptr;
+    for (ElfSection *s = elf->getSection(1); s != nullptr;
+         s = s->getNext()) {
+        if (s->getFlags() & SHF_EXECINSTR) {
+            first_executable = s;
+            break;
+        }
+    }
+
+    unsigned int old_exec = first_executable->getOffset();
+
+    relhack->insertBefore(section);
+    relhackcode->insertBefore(first_executable);
+
+    // Trying to get first_executable->getOffset() now may throw if the new
+    // layout would require it to move, so we look at the end of the relhack
+    // code section instead, comparing it to where the first executable
+    // section used to start.
+    if (relhackcode->getOffset() + relhackcode->getSize() >= old_exec) {
         fprintf(stderr, "No gain. Skipping\n");
         return -1;
     }
 
     // Adjust PT_LOAD segments
     for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
          segment = elf->getSegmentByType(PT_LOAD, segment)) {
         maybe_split_segment(elf, segment, fill);