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