Bug 816494 part 2 - Move the PT_LOAD splitting logic in elfhack.cpp. r=nfroyd,a=akeybl
authorMike Hommey <mh+mozilla@glandium.org>
Mon, 10 Dec 2012 10:33:08 +0100
changeset 119178 9f952d7c6bb1d74d4e7fbb03186a4b478d2d7cab
parent 119177 bad85e46f9d5b9e2d8183110b46c5e17d5d7ade5
child 119179 0881ada9a7499aea3cee53a8a312c63ad301aca3
push id3101
push usermh@glandium.org
push dateFri, 04 Jan 2013 14:49:20 +0000
treeherdermozilla-aurora@84efed665a29 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd, akeybl
bugs816494
milestone19.0a2
Bug 816494 part 2 - Move the PT_LOAD splitting logic in elfhack.cpp. r=nfroyd,a=akeybl 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);
     }
 }