Bug 816494 part 4 - Add a -r option to elfhack that re-merges the split PT_LOADs. r=nfroyd,a=akeybl
authorMike Hommey <mh+mozilla@glandium.org>
Mon, 10 Dec 2012 10:33:08 +0100
changeset 117927 3598645a878d061aa710da3f65ed253e002e6852
parent 117926 704dddf018e5335084ce9c65947d24f41fe0c431
child 117928 477ef6390903be5c99678a27565bd9a99d358ef0
push id118
push usermh@glandium.org
push dateFri, 04 Jan 2013 14:53:15 +0000
reviewersnfroyd, akeybl
bugs816494
milestone18.0
Bug 816494 part 4 - Add a -r option to elfhack that re-merges the split PT_LOADs. r=nfroyd,a=akeybl Sections are positioned accordingly, which means the resulting ELF binary will have a big gap full of zero between .rel.plt and .plt.
build/unix/elfhack/elf.cpp
build/unix/elfhack/elfhack.cpp
build/unix/elfhack/elfxx.h
--- a/build/unix/elfhack/elf.cpp
+++ b/build/unix/elfhack/elf.cpp
@@ -338,16 +338,28 @@ ElfSegment *Elf::getSegmentByType(unsign
     } else
         seg = segments.begin();
     for (; seg != segments.end(); seg++)
         if ((*seg)->getType() == type)
             return *seg;
     return NULL;
 }
 
+void Elf::removeSegment(ElfSegment *segment)
+{
+    if (!segment)
+        return;
+    std::vector<ElfSegment *>::iterator seg;
+    seg = std::find(segments.begin(), segments.end(), segment);
+    if (seg == segments.end())
+        return;
+    segment->clear();
+    segments.erase(seg);
+}
+
 ElfDynamic_Section *Elf::getDynSection()
 {
     for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
         if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != NULL) &&
             (*seg)->getFirstSection()->getType() == SHT_DYNAMIC)
             return (ElfDynamic_Section *)(*seg)->getFirstSection();
 
     return NULL;
@@ -616,16 +628,23 @@ unsigned int ElfSegment::getAddr()
 {
     if ((type == PT_GNU_RELRO) && !sections.empty() &&
         (sections.front()->getAddr() != vaddr))
         throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start");
 
     return sections.empty() ? 0 : sections.front()->getAddr();
 }
 
+void ElfSegment::clear()
+{
+    for (std::list<ElfSection *>::iterator i = sections.begin(); i != sections.end(); ++i)
+        (*i)->removeFromSegment(this);
+    sections.clear();
+}
+
 ElfValue *ElfDynamic_Section::getValueForType(unsigned int tag)
 {
     for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
         if (dyns[i].tag == tag)
             return dyns[i].value;
 
     return NULL;
 }
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -639,28 +639,87 @@ void do_file(const char *name, bool back
         } else {
             std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
             elf.write(ofile);
             fprintf(stderr, "Reduced by %d bytes\n", size - elf.getSize());
         }
     }
 }
 
+void undo_file(const char *name, bool backup = false)
+{
+    std::ifstream file(name, std::ios::in|std::ios::binary);
+    Elf elf(file);
+    unsigned int size = elf.getSize();
+    fprintf(stderr, "%s: ", name);
+    if (elf.getType() != ET_DYN) {
+        fprintf(stderr, "Not a shared object. Skipping\n");
+        return;
+    }
+
+    ElfSection *data = NULL, *text = NULL;
+    for (ElfSection *section = elf.getSection(1); section != NULL;
+         section = section->getNext()) {
+        if (section->getName() &&
+            (strcmp(section->getName(), elfhack_data) == 0))
+            data = section;
+        if (section->getName() &&
+            (strcmp(section->getName(), elfhack_text) == 0))
+            text = section;
+    }
+
+    if (!data || !text) {
+        fprintf(stderr, "Not elfhacked. Skipping\n");
+        return;
+    }
+    if (data != text->getNext()) {
+        fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n");
+        return;
+    }
+
+    ElfSegment *first = elf.getSegmentByType(PT_LOAD);
+    ElfSegment *second = elf.getSegmentByType(PT_LOAD, first);
+    if (second->getFlags() != first->getFlags()) {
+        fprintf(stderr, "First two PT_LOAD segments don't have the same flags. Skipping\n");
+        return;
+    }
+    // Move sections from the second PT_LOAD to the first, and remove the
+    // second PT_LOAD segment.
+    for (std::list<ElfSection *>::iterator section = second->begin();
+         section != second->end(); ++section)
+        first->addSection(*section);
+
+    elf.removeSegment(second);
+
+    if (backup && backup_file(name) != 0) {
+        fprintf(stderr, "Couln't create backup file\n");
+    } else {
+        std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
+        elf.write(ofile);
+        fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size);
+    }
+}
+
 int main(int argc, char *argv[])
 {
     int arg;
     bool backup = false;
     bool force = false;
+    bool revert = false;
     char *lastSlash = rindex(argv[0], '/');
     if (lastSlash != NULL)
         rundir = strndup(argv[0], lastSlash - argv[0]);
     for (arg = 1; arg < argc; arg++) {
         if (strcmp(argv[arg], "-f") == 0)
             force = true;
         else if (strcmp(argv[arg], "-b") == 0)
             backup = true;
+        else if (strcmp(argv[arg], "-r") == 0)
+            revert = true;
+        else if (revert)
+            undo_file(argv[arg], backup);
         else
             do_file(argv[arg], backup, force);
     }
 
     free(rundir);
     return 0;
 }
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -283,16 +283,18 @@ public:
     char getMachine();
     unsigned int getSize();
 
     void insertSegmentAfter(ElfSegment *previous, ElfSegment *segment) {
         std::vector<ElfSegment *>::iterator prev = std::find(segments.begin(), segments.end(), previous);
         segments.insert(prev + 1, segment);
     }
 
+    void removeSegment(ElfSegment *segment);
+
 private:
     Elf_Ehdr *ehdr;
     ElfLocation eh_entry;
     ElfStrtab_Section *eh_shstrndx;
     ElfSection **sections;
     std::vector<ElfSegment *> segments;
     ElfSection *shdr_section, *phdr_section;
     /* Values used only during initialization */
@@ -451,16 +453,18 @@ public:
     unsigned int getOffset();
     unsigned int getAddr();
 
     void addSection(ElfSection *section);
     void removeSection(ElfSection *section);
 
     std::list<ElfSection *>::iterator begin() { return sections.begin(); }
     std::list<ElfSection *>::iterator end() { return sections.end(); }
+
+    void clear();
 private:
     unsigned int type;
     int v_p_diff; // Difference between physical and virtual address
     unsigned int flags;
     unsigned int align;
     std::list<ElfSection *> sections;
     // The following are only really used for PT_GNU_RELRO until something
     // better is found.