Bug 816494 part 2 - Move the PT_LOAD splitting logic in elfhack.cpp. r=nfroyd
authorMike Hommey <mh+mozilla@glandium.org>
Mon, 10 Dec 2012 10:33:08 +0100
changeset 115472 84ad94351bf3586470298c8543cb8bb025ec246d
parent 115471 1ae98ad3b851ef37a7a6ac6c7459308027181e4a
child 115473 d2ebcd9235d106fd42b5a16a5c866873fe75823e
push id19360
push usermh@glandium.org
push dateMon, 10 Dec 2012 09:33:29 +0000
treeherdermozilla-inbound@0e9bc6febd7f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs816494
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 816494 part 2 - Move the PT_LOAD splitting logic in elfhack.cpp. r=nfroyd Also, section offsets are not adjusted until the split is done.
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
@@ -324,19 +324,25 @@ ElfSection *Elf::getSectionAt(unsigned i
         ElfSection *section = getSection(i);
         if ((section != NULL) && (section->getFlags() & SHF_ALLOC) && !(section->getFlags() & SHF_TLS) &&
             (offset >= section->getAddr()) && (offset < section->getAddr() + section->getSize()))
             return section;
     }
     return NULL;
 }
 
-ElfSegment *Elf::getSegmentByType(unsigned int type)
+ElfSegment *Elf::getSegmentByType(unsigned int type, ElfSegment *last)
 {
-    for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+    std::vector<ElfSegment *>::iterator seg;
+    if (last) {
+        seg = std::find(segments.begin(), segments.end(), last);
+        ++seg;
+    } else
+        seg = segments.begin();
+    for (; seg != segments.end(); seg++)
         if ((*seg)->getType() == type)
             return *seg;
     return NULL;
 }
 
 ElfDynamic_Section *Elf::getDynSection()
 {
     for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
@@ -354,30 +360,25 @@ void Elf::normalize()
     for (ElfSection *section = ehdr; section != NULL; section = section->getNext()) {
         if (section->getIndex() == 0)
             continue;
         else
             ehdr->e_shnum = section->getIndex() + 1;
         section->getShdr().sh_name = eh_shstrndx->getStrIndex(section->getName());
     }
     ehdr->markDirty();
-    // Adjust PT_LOAD segments
+    // Check segments consistency
     int i = 0;
     for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) {
-        if ((*seg)->getType() == PT_LOAD) {
-            std::list<ElfSection *>::iterator it = (*seg)->begin();
-            for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) {
-               if (((*it)->getType() != SHT_NOBITS) &&
-                   ((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) {
-                   std::vector<ElfSegment *>::iterator next = seg;
-                   segments.insert(++next, (*seg)->splitBefore(*it));
-                   seg = segments.begin() + i;
-                   break;
-               }
-           }
+        std::list<ElfSection *>::iterator it = (*seg)->begin();
+        for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) {
+            if (((*it)->getType() != SHT_NOBITS) &&
+                ((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) {
+                    throw std::runtime_error("Segments inconsistency");
+            }
         }
     }
     // fixup ehdr before writing
     if (ehdr->e_phnum != segments.size()) {
         ehdr->e_phnum = segments.size();
         phdr_section->getShdr().sh_size = segments.size() * Elf_Phdr::size(ehdr->e_ident[EI_CLASS]);
         phdr_section->getNext()->markDirty();
     }
@@ -476,16 +477,25 @@ unsigned int ElfSection::getOffset()
 {
     if (shdr.sh_offset != (Elf32_Word)-1)
         return shdr.sh_offset;
 
     if (previous == NULL)
         return (shdr.sh_offset = 0);
 
     unsigned int offset = previous->getOffset();
+
+    ElfSegment *ptload = getSegmentByType(PT_LOAD);
+    ElfSegment *prev_ptload = previous->getSegmentByType(PT_LOAD);
+
+    if (ptload && (ptload == prev_ptload)) {
+        offset += getAddr() - previous->getAddr();
+        return (shdr.sh_offset = offset);
+    }
+
     if (previous->getType() != SHT_NOBITS)
         offset += previous->getSize();
 
     Elf32_Word align = 0x1000;
     for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
         align = std::max(align, (*seg)->getAlign());
 
     Elf32_Word mask = align - 1;
@@ -550,16 +560,22 @@ void ElfSegment::addSection(ElfSection *
     std::list<ElfSection *>::iterator i;
     for (i = sections.begin(); i != sections.end(); ++i)
         if ((*i)->getAddr() > section->getAddr())
             break;
     sections.insert(i, section);
     section->addToSegment(this);
 }
 
+void ElfSegment::removeSection(ElfSection *section)
+{
+    sections.remove(section);
+    section->removeFromSegment(this);
+}
+
 unsigned int ElfSegment::getFileSize()
 {
     if (type == PT_GNU_RELRO)
         return filesz;
 
     if (sections.empty())
         return 0;
     // Search the last section that is not SHT_NOBITS
@@ -600,43 +616,16 @@ 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();
 }
 
-ElfSegment *ElfSegment::splitBefore(ElfSection *section)
-{
-    std::list<ElfSection *>::iterator i, rm;
-    for (i = sections.begin(); (*i != section) && (i != sections.end()); ++i);
-    if (i == sections.end())
-        return NULL;
-
-    // Probably very wrong.
-    Elf_Phdr phdr;
-    phdr.p_type = type;
-    phdr.p_vaddr = 0;
-    phdr.p_paddr = phdr.p_vaddr + v_p_diff;
-    phdr.p_flags = flags;
-    phdr.p_align = getAlign();
-    phdr.p_filesz = (unsigned int)-1;
-    phdr.p_memsz = (unsigned int)-1;
-    ElfSegment *segment = new ElfSegment(&phdr);
-
-    for (rm = i; i != sections.end(); ++i) {
-        (*i)->removeFromSegment(this);
-        segment->addSection(*i);
-    }
-    sections.erase(rm, sections.end());
-
-    return segment;
-}
-
 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
@@ -371,16 +371,47 @@ void set_relative_reloc(Elf_Rel *rel, El
 
 void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) {
     // ld puts the value of relocated relocations both in the addend and
     // at r_offset. For consistency, keep it that way.
     set_relative_reloc((Elf_Rel *)rel, elf, value);
     rel->r_addend = value;
 }
 
+void maybe_split_segment(Elf *elf, ElfSegment *segment)
+{
+    std::list<ElfSection *>::iterator it = segment->begin();
+    for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) {
+        // When two consecutive non-SHT_NOBITS sections are apart by more
+        // than the alignment of the section, the second can be moved closer
+        // to the first, but this requires the segment to be split.
+        if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) &&
+            ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) {
+            // Probably very wrong.
+            Elf_Phdr phdr;
+            phdr.p_type = PT_LOAD;
+            phdr.p_vaddr = 0;
+            phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
+            phdr.p_flags = segment->getFlags();
+            phdr.p_align = segment->getAlign();
+            phdr.p_filesz = (unsigned int)-1;
+            phdr.p_memsz = (unsigned int)-1;
+            ElfSegment *newSegment = new ElfSegment(&phdr);
+            elf->insertSegmentAfter(segment, newSegment);
+            for (; it != segment->end(); ++it) {
+                newSegment->addSection(*it);
+            }
+            for (it = newSegment->begin(); it != newSegment->end(); it++) {
+                segment->removeSection(*it);
+            }
+            break;
+        }
+    }
+}
+
 template <typename Rel_Type>
 int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force)
 {
     ElfDynamic_Section *dyn = elf->getDynSection();
     if (dyn ==NULL) {
         fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
         return -1;
     }
@@ -518,26 +549,33 @@ int do_relocation_section(Elf *elf, unsi
             ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
             original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend;
         } else {
             fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n");
             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, original_init);
     relhackcode->insertBefore(section);
     relhack->insertAfter(relhackcode);
-
-    section->rels.assign(new_rels.begin(), new_rels.end());
-    section->shrink(new_rels.size() * section->getEntSize());
     if (section->getOffset() + section->getSize() >= old_end) {
         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);
+    }
+
     // Ensure Elf sections will be at their final location.
     elf->normalize();
     ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
     if (init_array) {
         // Adjust the first DT_INIT_ARRAY entry to point at the injected code
         // by transforming its relocation into a relative one pointing to the
         // address of the injected code.
         Rel_Type *rel = &section->rels[init_array_reloc - 1];
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -265,28 +265,34 @@ public:
     Elf(std::ifstream &file);
     ~Elf();
 
     /* index == -1 is treated as index == ehdr.e_shstrndx */
     ElfSection *getSection(int index);
 
     ElfSection *getSectionAt(unsigned int offset);
 
-    ElfSegment *getSegmentByType(unsigned int type);
+    ElfSegment *getSegmentByType(unsigned int type, ElfSegment *last = NULL);
 
     ElfDynamic_Section *getDynSection();
 
     void normalize();
     void write(std::ofstream &file);
 
     char getClass();
     char getData();
     char getType();
     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);
+    }
+
 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 */
@@ -314,21 +320,18 @@ public:
     unsigned int getSize() { return shdr.sh_size; }
     unsigned int getAddrAlign() { return shdr.sh_addralign; }
     unsigned int getEntSize() { return shdr.sh_entsize; }
     const char *getData() { return data; }
     ElfSection *getLink() { return link; }
     SectionInfo getInfo() { return info; }
 
     void shrink(unsigned int newsize) {
-        if (newsize < shdr.sh_size) {
+        if (newsize < shdr.sh_size)
             shdr.sh_size = newsize;
-            if (next)
-                next->markDirty();
-        }
     }
 
     unsigned int getOffset();
     int getIndex();
     Elf_Shdr &getShdr();
 
     ElfSection *getNext() { return next; }
     ElfSection *getPrevious() { return previous; }
@@ -340,17 +343,17 @@ public:
                 (getType() == SHT_HASH) ||
                 (getType() == SHT_NOTE) ||
                 (getType() == SHT_REL) ||
                 (getType() == SHT_DYNSYM) ||
                 (getType() == SHT_GNU_HASH) ||
                 (getType() == SHT_GNU_verdef) ||
                 (getType() == SHT_GNU_verneed) ||
                 (getType() == SHT_GNU_versym) ||
-                isInSegmentType(PT_INTERP)) &&
+                getSegmentByType(PT_INTERP)) &&
                 (getFlags() & SHF_ALLOC);
     }
 
     void insertAfter(ElfSection *section, bool dirty = true) {
         if (previous != NULL)
             previous->next = next;
         if (next != NULL)
             next->previous = previous;
@@ -412,17 +415,17 @@ private:
         segments.push_back(segment);
     }
 
     void removeFromSegment(ElfSegment *segment) {
         std::vector<ElfSegment *>::iterator i = std::find(segments.begin(), segments.end(), segment);
         segments.erase(i, i + 1);
     }
 
-    bool isInSegmentType(unsigned int type);
+    ElfSegment *getSegmentByType(unsigned int type);
 
     void insertInSegments(std::vector<ElfSegment *> &segs);
 
 protected:
     Elf_Shdr shdr;
     char *data;
     const char *name;
 private:
@@ -444,21 +447,20 @@ public:
     ElfSection *getFirstSection() { return sections.empty() ? NULL : sections.front(); }
     int getVPDiff() { return v_p_diff; }
     unsigned int getFileSize();
     unsigned int getMemSize();
     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(); }
-
-    ElfSegment *splitBefore(ElfSection *section);
 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.
@@ -643,21 +645,21 @@ inline char Elf::getMachine() {
 
 inline unsigned int Elf::getSize() {
     ElfSection *section;
     for (section = shdr_section /* It's usually not far from the end */;
         section->getNext() != NULL; section = section->getNext());
     return section->getOffset() + section->getSize();
 }
 
-inline bool ElfSection::isInSegmentType(unsigned int type) {
+inline ElfSegment *ElfSection::getSegmentByType(unsigned int type) {
     for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
         if ((*seg)->getType() == type)
-            return true;
-    return false;
+            return *seg;
+    return NULL;
 }
 
 inline void ElfSection::insertInSegments(std::vector<ElfSegment *> &segs) {
     for (std::vector<ElfSegment *>::iterator it = segs.begin(); it != segs.end(); ++it) {
         (*it)->addSection(this);
     }
 }