build/unix/elfhack/elfhack.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #undef NDEBUG
     6 #include <assert.h>
     7 #include <cstring>
     8 #include <cstdlib>
     9 #include <cstdio>
    10 #include "elfxx.h"
    12 #define ver "0"
    13 #define elfhack_data ".elfhack.data.v" ver
    14 #define elfhack_text ".elfhack.text.v" ver
    16 #ifndef R_ARM_V4BX
    17 #define R_ARM_V4BX 0x28
    18 #endif
    19 #ifndef R_ARM_CALL
    20 #define R_ARM_CALL 0x1c
    21 #endif
    22 #ifndef R_ARM_JUMP24
    23 #define R_ARM_JUMP24 0x1d
    24 #endif
    25 #ifndef R_ARM_THM_JUMP24
    26 #define R_ARM_THM_JUMP24 0x1e
    27 #endif
    29 char *rundir = nullptr;
    31 template <typename T>
    32 struct wrapped {
    33     T value;
    34 };
    36 class Elf_Addr_Traits {
    37 public:
    38     typedef wrapped<Elf32_Addr> Type32;
    39     typedef wrapped<Elf64_Addr> Type64;
    41     template <class endian, typename R, typename T>
    42     static inline void swap(T &t, R &r) {
    43         r.value = endian::swap(t.value);
    44     }
    45 };
    47 typedef serializable<Elf_Addr_Traits> Elf_Addr;
    49 class Elf_RelHack_Traits {
    50 public:
    51     typedef Elf32_Rel Type32;
    52     typedef Elf32_Rel Type64;
    54     template <class endian, typename R, typename T>
    55     static inline void swap(T &t, R &r) {
    56         r.r_offset = endian::swap(t.r_offset);
    57         r.r_info = endian::swap(t.r_info);
    58     }
    59 };
    61 typedef serializable<Elf_RelHack_Traits> Elf_RelHack;
    63 class ElfRelHack_Section: public ElfSection {
    64 public:
    65     ElfRelHack_Section(Elf_Shdr &s)
    66     : ElfSection(s, nullptr, nullptr)
    67     {
    68         name = elfhack_data;
    69     };
    71     void serialize(std::ofstream &file, char ei_class, char ei_data)
    72     {
    73         for (std::vector<Elf_RelHack>::iterator i = rels.begin();
    74              i != rels.end(); ++i)
    75             (*i).serialize(file, ei_class, ei_data);
    76     }
    78     bool isRelocatable() {
    79         return true;
    80     }
    82     void push_back(Elf_RelHack &r) {
    83         rels.push_back(r);
    84         shdr.sh_size = rels.size() * shdr.sh_entsize;
    85     }
    86 private:
    87     std::vector<Elf_RelHack> rels;
    88 };
    90 class ElfRelHackCode_Section: public ElfSection {
    91 public:
    92     ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init)
    93     : ElfSection(s, nullptr, nullptr), parent(e), init(init) {
    94         std::string file(rundir);
    95         file += "/inject/";
    96         switch (parent.getMachine()) {
    97         case EM_386:
    98             file += "x86";
    99             break;
   100         case EM_X86_64:
   101             file += "x86_64";
   102             break;
   103         case EM_ARM:
   104             file += "arm";
   105             break;
   106         default:
   107             throw std::runtime_error("unsupported architecture");
   108         }
   109         file += ".o";
   110         std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary);
   111         elf = new Elf(inject);
   112         if (elf->getType() != ET_REL)
   113             throw std::runtime_error("object for injected code is not ET_REL");
   114         if (elf->getMachine() != parent.getMachine())
   115             throw std::runtime_error("architecture of object for injected code doesn't match");
   117         ElfSymtab_Section *symtab = nullptr;
   119         // Find the symbol table.
   120         for (ElfSection *section = elf->getSection(1); section != nullptr;
   121              section = section->getNext()) {
   122             if (section->getType() == SHT_SYMTAB)
   123                 symtab = (ElfSymtab_Section *) section;
   124         }
   125         if (symtab == nullptr)
   126             throw std::runtime_error("Couldn't find a symbol table for the injected code");
   128         // Find the init symbol
   129         entry_point = -1;
   130         Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit");
   131         if (!sym)
   132             throw std::runtime_error("Couldn't find an 'init' symbol in the injected code");
   134         entry_point = sym->value.getValue();
   136         // Get all relevant sections from the injected code object.
   137         add_code_section(sym->value.getSection());
   139         // Adjust code sections offsets according to their size
   140         std::vector<ElfSection *>::iterator c = code.begin();
   141         (*c)->getShdr().sh_addr = 0;
   142         for(ElfSection *last = *(c++); c != code.end(); c++) {
   143             unsigned int addr = last->getShdr().sh_addr + last->getSize();
   144             if (addr & ((*c)->getAddrAlign() - 1))
   145                 addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
   146             (*c)->getShdr().sh_addr = addr;
   147             // We need to align this section depending on the greater
   148             // alignment required by code sections.
   149             if (shdr.sh_addralign < (*c)->getAddrAlign())
   150                 shdr.sh_addralign = (*c)->getAddrAlign();
   151         }
   152         shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
   153         data = new char[shdr.sh_size];
   154         char *buf = data;
   155         for (c = code.begin(); c != code.end(); c++) {
   156             memcpy(buf, (*c)->getData(), (*c)->getSize());
   157             buf += (*c)->getSize();
   158         }
   159         name = elfhack_text;
   160     }
   162     ~ElfRelHackCode_Section() {
   163         delete elf;
   164     }
   166     void serialize(std::ofstream &file, char ei_class, char ei_data)
   167     {
   168         // Readjust code offsets
   169         for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++)
   170             (*c)->getShdr().sh_addr += getAddr();
   172         // Apply relocations
   173         for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) {
   174             for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext())
   175                 if (((rel->getType() == SHT_REL) ||
   176                      (rel->getType() == SHT_RELA)) &&
   177                     (rel->getInfo().section == *c)) {
   178                     if (rel->getType() == SHT_REL)
   179                         apply_relocations((ElfRel_Section<Elf_Rel> *)rel, *c);
   180                     else
   181                         apply_relocations((ElfRel_Section<Elf_Rela> *)rel, *c);
   182                 }
   183             }
   185         ElfSection::serialize(file, ei_class, ei_data);
   186     }
   188     bool isRelocatable() {
   189         return true;
   190     }
   192     unsigned int getEntryPoint() {
   193         return entry_point;
   194     }
   195 private:
   196     void add_code_section(ElfSection *section)
   197     {
   198         if (section) {
   199             /* Don't add section if it's already been added in the past */
   200             for (auto s = code.begin(); s != code.end(); ++s) {
   201                 if (section == *s)
   202                     return;
   203             }
   204             code.push_back(section);
   205             find_code(section);
   206         }
   207     }
   209     /* Look at the relocations associated to the given section to find other
   210      * sections that it requires */
   211     void find_code(ElfSection *section)
   212     {
   213         for (ElfSection *s = elf->getSection(1); s != nullptr;
   214              s = s->getNext()) {
   215             if (((s->getType() == SHT_REL) ||
   216                  (s->getType() == SHT_RELA)) &&
   217                 (s->getInfo().section == section)) {
   218                 if (s->getType() == SHT_REL)
   219                     scan_relocs_for_code((ElfRel_Section<Elf_Rel> *)s);
   220                 else
   221                     scan_relocs_for_code((ElfRel_Section<Elf_Rela> *)s);
   222             }
   223         }
   224     }
   226     template <typename Rel_Type>
   227     void scan_relocs_for_code(ElfRel_Section<Rel_Type> *rel)
   228     {
   229         ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
   230         for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) {
   231             ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
   232             add_code_section(section);
   233         }
   234     }
   236     class pc32_relocation {
   237     public:
   238         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
   239                               Elf32_Word addend, unsigned int addr)
   240         {
   241             return addr + addend - offset - base_addr;
   242         }
   243     };
   245     class arm_plt32_relocation {
   246     public:
   247         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
   248                               Elf32_Word addend, unsigned int addr)
   249         {
   250             // We don't care about sign_extend because the only case where this is
   251             // going to be used only jumps forward.
   252             Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2;
   253             tmp = (addend + tmp) & 0x00ffffff;
   254             return (addend & 0xff000000) | tmp;
   255         }
   256     };
   258     class arm_thm_jump24_relocation {
   259     public:
   260         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
   261                               Elf32_Word addend, unsigned int addr)
   262         {
   263             /* Follows description of b.w and bl instructions as per
   264                ARM Architecture Reference Manual ARMĀ® v7-A and ARMĀ® v7-R edition, A8.6.16
   265                We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl.
   266                We don't care about sign_extend because the only case where this is
   267                going to be used only jumps forward. */
   268             Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr);
   269             unsigned int word0 = addend & 0xffff,
   270                          word1 = addend >> 16;
   272             /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */
   273             unsigned int type = (word1 & 0xd000) >> 12;
   274             if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9))
   275                 throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>");
   277             /* When the target address points to ARM code, switch a BL to a
   278              * BLX. This however can't be done with a B.W without adding a
   279              * trampoline, which is not supported as of now. */
   280             if ((addr & 0x1) == 0) {
   281                 if (type == 0x9)
   282                     throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code");
   283                 /* The address of the target is always relative to a 4-bytes
   284                  * aligned address, so if the address of the BL instruction is
   285                  * not 4-bytes aligned, adjust for it. */
   286                 if ((base_addr + offset) & 0x2)
   287                     tmp += 2;
   288                 /* Encoding T2 of BLX is 11x0. */
   289                 type = 0xc;
   290             }
   292             unsigned int s = (word0 & (1 << 10)) >> 10;
   293             unsigned int j1 = (word1 & (1 << 13)) >> 13;
   294             unsigned int j2 = (word1 & (1 << 11)) >> 11;
   295             unsigned int i1 = j1 ^ s ? 0 : 1;
   296             unsigned int i2 = j2 ^ s ? 0 : 1;
   298             tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1));
   300             s = (tmp & (1 << 24)) >> 24;
   301             j1 = ((tmp & (1 << 23)) >> 23) ^ !s;
   302             j2 = ((tmp & (1 << 22)) >> 22) ^ !s;
   304             return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) |
   305                    (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
   306         }
   307     };
   309     class gotoff_relocation {
   310     public:
   311         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
   312                               Elf32_Word addend, unsigned int addr)
   313         {
   314             return addr + addend;
   315         }
   316     };
   318     template <class relocation_type>
   319     void apply_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
   320     {
   321         relocation_type relocation;
   322         Elf32_Addr value;
   323         memcpy(&value, base + r->r_offset, 4);
   324         value = relocation(the_code->getAddr(), r->r_offset, value, addr);
   325         memcpy(base + r->r_offset, &value, 4);
   326     }
   328     template <class relocation_type>
   329     void apply_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
   330     {
   331         relocation_type relocation;
   332         Elf32_Addr value = relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr);
   333         memcpy(base + r->r_offset, &value, 4);
   334     }
   336     template <typename Rel_Type>
   337     void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code)
   338     {
   339         assert(rel->getType() == Rel_Type::sh_type);
   340         char *buf = data + (the_code->getAddr() - code.front()->getAddr());
   341         // TODO: various checks on the sections
   342         ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
   343         for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) {
   344             // TODO: various checks on the symbol
   345             const char *name = symtab->syms[ELF32_R_SYM(r->r_info)].name;
   346             unsigned int addr;
   347             if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == nullptr) {
   348                 if (strcmp(name, "relhack") == 0) {
   349                     addr = getNext()->getAddr();
   350                 } else if (strcmp(name, "elf_header") == 0) {
   351                     // TODO: change this ungly hack to something better
   352                     ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
   353                     addr = ehdr->getAddr();
   354                 } else if (strcmp(name, "original_init") == 0) {
   355                     addr = init;
   356                 } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
   357                     // We actually don't need a GOT, but need it as a reference for
   358                     // GOTOFF relocations. We'll just use the start of the ELF file
   359                     addr = 0;
   360                 } else if (strcmp(name, "") == 0) {
   361                     // This is for R_ARM_V4BX, until we find something better
   362                     addr = -1;
   363                 } else {
   364                     throw std::runtime_error("Unsupported symbol in relocation");
   365                 }
   366             } else {
   367                 ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
   368                 assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR));
   369                 addr = symtab->syms[ELF32_R_SYM(r->r_info)].value.getValue();
   370             }
   371             // Do the relocation
   372 #define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8))
   373             switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) {
   374             case REL(X86_64, PC32):
   375             case REL(386, PC32):
   376             case REL(386, GOTPC):
   377             case REL(ARM, GOTPC):
   378             case REL(ARM, REL32):
   379                 apply_relocation<pc32_relocation>(the_code, buf, &*r, addr);
   380                 break;
   381             case REL(ARM, CALL):
   382             case REL(ARM, JUMP24):
   383             case REL(ARM, PLT32):
   384                 apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr);
   385                 break;
   386             case REL(ARM, THM_PC22 /* THM_CALL */):
   387             case REL(ARM, THM_JUMP24):
   388                 apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr);
   389                 break;
   390             case REL(386, GOTOFF):
   391             case REL(ARM, GOTOFF):
   392                 apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr);
   393                 break;
   394             case REL(ARM, V4BX):
   395                 // Ignore R_ARM_V4BX relocations
   396                 break;
   397             default:
   398                 throw std::runtime_error("Unsupported relocation type");
   399             }
   400         }
   401     }
   403     Elf *elf, &parent;
   404     std::vector<ElfSection *> code;
   405     unsigned int init;
   406     int entry_point;
   407 };
   409 unsigned int get_addend(Elf_Rel *rel, Elf *elf) {
   410     ElfLocation loc(rel->r_offset, elf);
   411     Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
   412     return addr.value;
   413 }
   415 unsigned int get_addend(Elf_Rela *rel, Elf *elf) {
   416     return rel->r_addend;
   417 }
   419 void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) {
   420     ElfLocation loc(rel->r_offset, elf);
   421     Elf_Addr addr;
   422     addr.value = value;
   423     addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
   424 }
   426 void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) {
   427     // ld puts the value of relocated relocations both in the addend and
   428     // at r_offset. For consistency, keep it that way.
   429     set_relative_reloc((Elf_Rel *)rel, elf, value);
   430     rel->r_addend = value;
   431 }
   433 void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill)
   434 {
   435     std::list<ElfSection *>::iterator it = segment->begin();
   436     for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) {
   437         // When two consecutive non-SHT_NOBITS sections are apart by more
   438         // than the alignment of the section, the second can be moved closer
   439         // to the first, but this requires the segment to be split.
   440         if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) &&
   441             ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) {
   442             // Probably very wrong.
   443             Elf_Phdr phdr;
   444             phdr.p_type = PT_LOAD;
   445             phdr.p_vaddr = 0;
   446             phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
   447             phdr.p_flags = segment->getFlags();
   448             phdr.p_align = segment->getAlign();
   449             phdr.p_filesz = (unsigned int)-1;
   450             phdr.p_memsz = (unsigned int)-1;
   451             ElfSegment *newSegment = new ElfSegment(&phdr);
   452             elf->insertSegmentAfter(segment, newSegment);
   453             ElfSection *section = *it;
   454             for (; it != segment->end(); ++it) {
   455                 newSegment->addSection(*it);
   456             }
   457             for (it = newSegment->begin(); it != newSegment->end(); it++) {
   458                 segment->removeSection(*it);
   459             }
   460             // Fill the virtual address space gap left between the two PT_LOADs
   461             // with a new PT_LOAD with no permissions. This avoids the linker
   462             // (especially bionic's) filling the gap with anonymous memory,
   463             // which breakpad doesn't like.
   464             // /!\ running strip on a elfhacked binary will break this filler
   465             // PT_LOAD.
   466             if (!fill)
   467                 break;
   468             // Insert dummy segment to normalize the entire Elf with the header
   469             // sizes adjusted, before inserting a filler segment.
   470             {
   471               memset(&phdr, 0, sizeof(phdr));
   472               ElfSegment dummySegment(&phdr);
   473               elf->insertSegmentAfter(segment, &dummySegment);
   474               elf->normalize();
   475               elf->removeSegment(&dummySegment);
   476             }
   477             ElfSection *previous = section->getPrevious();
   478             phdr.p_type = PT_LOAD;
   479             phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1);
   480             phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
   481             phdr.p_flags = 0;
   482             phdr.p_align = 0;
   483             phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr;
   484             phdr.p_memsz = phdr.p_filesz;
   485             if (phdr.p_filesz) {
   486                 newSegment = new ElfSegment(&phdr);
   487                 assert(newSegment->isElfHackFillerSegment());
   488                 elf->insertSegmentAfter(segment, newSegment);
   489             } else {
   490                 elf->normalize();
   491             }
   492             break;
   493         }
   494     }
   495 }
   497 template <typename Rel_Type>
   498 int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill)
   499 {
   500     ElfDynamic_Section *dyn = elf->getDynSection();
   501     if (dyn == nullptr) {
   502         fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
   503         return -1;
   504     }
   506     ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);
   508     ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
   509     assert(section->getType() == Rel_Type::sh_type);
   511     Elf32_Shdr relhack32_section =
   512         { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0,
   513           Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
   514     Elf32_Shdr relhackcode32_section =
   515         { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0,
   516           SHN_UNDEF, 0, 1, 0 };
   518     unsigned int entry_sz = Elf_Addr::size(elf->getClass());
   520     // The injected code needs to be executed before any init code in the
   521     // binary. There are three possible cases:
   522     // - The binary has no init code at all. In this case, we will add a
   523     //   DT_INIT entry pointing to the injected code.
   524     // - The binary has a DT_INIT entry. In this case, we will interpose:
   525     //   we change DT_INIT to point to the injected code, and have the
   526     //   injected code call the original DT_INIT entry point.
   527     // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this
   528     //   case, we interpose as well, by replacing the first entry in the
   529     //   array to point to the injected code, and have the injected code
   530     //   call the original first entry.
   531     // The binary may have .ctors instead of DT_INIT_ARRAY, for its init
   532     // functions, but this falls into the second case above, since .ctors
   533     // are actually run by DT_INIT code.
   534     ElfValue *value = dyn->getValueForType(DT_INIT);
   535     unsigned int original_init = value ? value->getValue() : 0;
   536     ElfSection *init_array = nullptr;
   537     if (!value || !value->getValue()) {
   538         value = dyn->getValueForType(DT_INIT_ARRAYSZ);
   539         if (value && value->getValue() >= entry_sz)
   540             init_array = dyn->getSectionForType(DT_INIT_ARRAY);
   541     }
   543     Elf_Shdr relhack_section(relhack32_section);
   544     Elf_Shdr relhackcode_section(relhackcode32_section);
   545     ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
   547     ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink();
   548     Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual");
   550     std::vector<Rel_Type> new_rels;
   551     Elf_RelHack relhack_entry;
   552     relhack_entry.r_offset = relhack_entry.r_info = 0;
   553     size_t init_array_reloc = 0;
   554     for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
   555          i != section->rels.end(); i++) {
   556         // We don't need to keep R_*_NONE relocations
   557         if (!ELF32_R_TYPE(i->r_info))
   558             continue;
   559         ElfLocation loc(i->r_offset, elf);
   560         // __cxa_pure_virtual is a function used in vtables to point at pure
   561         // virtual methods. The __cxa_pure_virtual function usually abort()s.
   562         // These functions are however normally never called. In the case
   563         // where they would, jumping to the null address instead of calling
   564         // __cxa_pure_virtual is going to work just as well. So we can remove
   565         // relocations for the __cxa_pure_virtual symbol and null out the
   566         // content at the offset pointed by the relocation.
   567         if (sym) {
   568             if (sym->defined) {
   569                 // If we are statically linked to libstdc++, the
   570                 // __cxa_pure_virtual symbol is defined in our lib, and we
   571                 // have relative relocations (rel_type) for it.
   572                 if (ELF32_R_TYPE(i->r_info) == rel_type) {
   573                     Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData());
   574                     if (addr.value == sym->value.getValue()) {
   575                         memset((char *)loc.getBuffer(), 0, entry_sz);
   576                         continue;
   577                     }
   578                 }
   579             } else {
   580                 // If we are dynamically linked to libstdc++, the
   581                 // __cxa_pure_virtual symbol is undefined in our lib, and we
   582                 // have absolute relocations (rel_type2) for it.
   583                 if ((ELF32_R_TYPE(i->r_info) == rel_type2) &&
   584                     (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) {
   585                     memset((char *)loc.getBuffer(), 0, entry_sz);
   586                     continue;
   587                 }
   588             }
   589         }
   590         // Keep track of the relocation associated with the first init_array entry.
   591         if (init_array && i->r_offset == init_array->getAddr()) {
   592             if (init_array_reloc) {
   593                 fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n");
   594                 return -1;
   595             }
   596             new_rels.push_back(*i);
   597             init_array_reloc = new_rels.size();
   598         } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
   599                    (relro && (i->r_offset >= relro->getAddr()) &&
   600                    (i->r_offset < relro->getAddr() + relro->getMemSize()))) {
   601             // Don't pack relocations happening in non writable sections.
   602             // Our injected code is likely not to be allowed to write there.
   603             new_rels.push_back(*i);
   604         } else {
   605             // TODO: check that i->r_addend == *i->r_offset
   606             if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
   607                 relhack_entry.r_info++;
   608             } else {
   609                 if (relhack_entry.r_offset)
   610                     relhack->push_back(relhack_entry);
   611                 relhack_entry.r_offset = i->r_offset;
   612                 relhack_entry.r_info = 1;
   613             }
   614         }
   615     }
   616     if (relhack_entry.r_offset)
   617         relhack->push_back(relhack_entry);
   618     // Last entry must be nullptr
   619     relhack_entry.r_offset = relhack_entry.r_info = 0;
   620     relhack->push_back(relhack_entry);
   622     unsigned int old_end = section->getOffset() + section->getSize();
   624     if (init_array) {
   625         if (! init_array_reloc) {
   626             fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n");
   627             return -1;
   628         }
   629         Rel_Type *rel = &new_rels[init_array_reloc - 1];
   630         unsigned int addend = get_addend(rel, elf);
   631         // Use relocated value of DT_INIT_ARRAY's first entry for the
   632         // function to be called by the injected code.
   633         if (ELF32_R_TYPE(rel->r_info) == rel_type) {
   634             original_init = addend;
   635         } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) {
   636             ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
   637             original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend;
   638         } else {
   639             fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n");
   640             return -1;
   641         }
   642     }
   644     section->rels.assign(new_rels.begin(), new_rels.end());
   645     section->shrink(new_rels.size() * section->getEntSize());
   647     ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init);
   648     relhackcode->insertBefore(section);
   649     relhack->insertAfter(relhackcode);
   650     if (section->getOffset() + section->getSize() >= old_end) {
   651         fprintf(stderr, "No gain. Skipping\n");
   652         return -1;
   653     }
   655     // Adjust PT_LOAD segments
   656     for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
   657          segment = elf->getSegmentByType(PT_LOAD, segment)) {
   658         maybe_split_segment(elf, segment, fill);
   659     }
   661     // Ensure Elf sections will be at their final location.
   662     elf->normalize();
   663     ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
   664     if (init_array) {
   665         // Adjust the first DT_INIT_ARRAY entry to point at the injected code
   666         // by transforming its relocation into a relative one pointing to the
   667         // address of the injected code.
   668         Rel_Type *rel = &section->rels[init_array_reloc - 1];
   669         rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation
   670         set_relative_reloc(&section->rels[init_array_reloc - 1], elf, init->getValue());
   671     } else if (!dyn->setValueForType(DT_INIT, init)) {
   672         fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n");
   673         return -1;
   674     }
   675     // TODO: adjust the value according to the remaining number of relative relocations
   676     if (dyn->getValueForType(Rel_Type::d_tag_count))
   677         dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));
   679     return 0;
   680 }
   682 static inline int backup_file(const char *name)
   683 {
   684     std::string fname(name);
   685     fname += ".bak";
   686     return rename(name, fname.c_str());
   687 }
   689 void do_file(const char *name, bool backup = false, bool force = false, bool fill = false)
   690 {
   691     std::ifstream file(name, std::ios::in|std::ios::binary);
   692     Elf elf(file);
   693     unsigned int size = elf.getSize();
   694     fprintf(stderr, "%s: ", name);
   695     if (elf.getType() != ET_DYN) {
   696         fprintf(stderr, "Not a shared object. Skipping\n");
   697         return;
   698     }
   700     for (ElfSection *section = elf.getSection(1); section != nullptr;
   701          section = section->getNext()) {
   702         if (section->getName() &&
   703             (strncmp(section->getName(), ".elfhack.", 9) == 0)) {
   704             fprintf(stderr, "Already elfhacked. Skipping\n");
   705             return;
   706         }
   707     }
   709     int exit = -1;
   710     switch (elf.getMachine()) {
   711     case EM_386:
   712         exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill);
   713         break;
   714     case EM_X86_64:
   715         exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE, R_X86_64_64, force, fill);
   716         break;
   717     case EM_ARM:
   718         exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32, force, fill);
   719         break;
   720     }
   721     if (exit == 0) {
   722         if (!force && (elf.getSize() >= size)) {
   723             fprintf(stderr, "No gain. Skipping\n");
   724         } else if (backup && backup_file(name) != 0) {
   725             fprintf(stderr, "Couln't create backup file\n");
   726         } else {
   727             std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
   728             elf.write(ofile);
   729             fprintf(stderr, "Reduced by %d bytes\n", size - elf.getSize());
   730         }
   731     }
   732 }
   734 void undo_file(const char *name, bool backup = false)
   735 {
   736     std::ifstream file(name, std::ios::in|std::ios::binary);
   737     Elf elf(file);
   738     unsigned int size = elf.getSize();
   739     fprintf(stderr, "%s: ", name);
   740     if (elf.getType() != ET_DYN) {
   741         fprintf(stderr, "Not a shared object. Skipping\n");
   742         return;
   743     }
   745     ElfSection *data = nullptr, *text = nullptr;
   746     for (ElfSection *section = elf.getSection(1); section != nullptr;
   747          section = section->getNext()) {
   748         if (section->getName() &&
   749             (strcmp(section->getName(), elfhack_data) == 0))
   750             data = section;
   751         if (section->getName() &&
   752             (strcmp(section->getName(), elfhack_text) == 0))
   753             text = section;
   754     }
   756     if (!data || !text) {
   757         fprintf(stderr, "Not elfhacked. Skipping\n");
   758         return;
   759     }
   760     if (data != text->getNext()) {
   761         fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n");
   762         return;
   763     }
   765     ElfSegment *first = elf.getSegmentByType(PT_LOAD);
   766     ElfSegment *second = elf.getSegmentByType(PT_LOAD, first);
   767     ElfSegment *filler = nullptr;
   768     // If the second PT_LOAD is a filler from elfhack --fill, check the third.
   769     if (second->isElfHackFillerSegment()) {
   770         filler = second;
   771         second = elf.getSegmentByType(PT_LOAD, filler);
   772     }
   773     if (second->getFlags() != first->getFlags()) {
   774         fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n");
   775         return;
   776     }
   777     // Move sections from the second PT_LOAD to the first, and remove the
   778     // second PT_LOAD segment.
   779     for (std::list<ElfSection *>::iterator section = second->begin();
   780          section != second->end(); ++section)
   781         first->addSection(*section);
   783     elf.removeSegment(second);
   784     if (filler)
   785         elf.removeSegment(filler);
   787     if (backup && backup_file(name) != 0) {
   788         fprintf(stderr, "Couln't create backup file\n");
   789     } else {
   790         std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
   791         elf.write(ofile);
   792         fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size);
   793     }
   794 }
   796 int main(int argc, char *argv[])
   797 {
   798     int arg;
   799     bool backup = false;
   800     bool force = false;
   801     bool revert = false;
   802     bool fill = false;
   803     char *lastSlash = rindex(argv[0], '/');
   804     if (lastSlash != nullptr)
   805         rundir = strndup(argv[0], lastSlash - argv[0]);
   806     for (arg = 1; arg < argc; arg++) {
   807         if (strcmp(argv[arg], "-f") == 0)
   808             force = true;
   809         else if (strcmp(argv[arg], "-b") == 0)
   810             backup = true;
   811         else if (strcmp(argv[arg], "-r") == 0)
   812             revert = true;
   813         else if (strcmp(argv[arg], "--fill") == 0)
   814             fill = true;
   815         else if (revert) {
   816             undo_file(argv[arg], backup);
   817         } else
   818             do_file(argv[arg], backup, force, fill);
   819     }
   821     free(rundir);
   822     return 0;
   823 }

mercurial