|
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/. */ |
|
4 |
|
5 #undef NDEBUG |
|
6 #include <assert.h> |
|
7 #include <cstring> |
|
8 #include <cstdlib> |
|
9 #include <cstdio> |
|
10 #include "elfxx.h" |
|
11 |
|
12 #define ver "0" |
|
13 #define elfhack_data ".elfhack.data.v" ver |
|
14 #define elfhack_text ".elfhack.text.v" ver |
|
15 |
|
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 |
|
28 |
|
29 char *rundir = nullptr; |
|
30 |
|
31 template <typename T> |
|
32 struct wrapped { |
|
33 T value; |
|
34 }; |
|
35 |
|
36 class Elf_Addr_Traits { |
|
37 public: |
|
38 typedef wrapped<Elf32_Addr> Type32; |
|
39 typedef wrapped<Elf64_Addr> Type64; |
|
40 |
|
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 }; |
|
46 |
|
47 typedef serializable<Elf_Addr_Traits> Elf_Addr; |
|
48 |
|
49 class Elf_RelHack_Traits { |
|
50 public: |
|
51 typedef Elf32_Rel Type32; |
|
52 typedef Elf32_Rel Type64; |
|
53 |
|
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 }; |
|
60 |
|
61 typedef serializable<Elf_RelHack_Traits> Elf_RelHack; |
|
62 |
|
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 }; |
|
70 |
|
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 } |
|
77 |
|
78 bool isRelocatable() { |
|
79 return true; |
|
80 } |
|
81 |
|
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 }; |
|
89 |
|
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"); |
|
116 |
|
117 ElfSymtab_Section *symtab = nullptr; |
|
118 |
|
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"); |
|
127 |
|
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"); |
|
133 |
|
134 entry_point = sym->value.getValue(); |
|
135 |
|
136 // Get all relevant sections from the injected code object. |
|
137 add_code_section(sym->value.getSection()); |
|
138 |
|
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 } |
|
161 |
|
162 ~ElfRelHackCode_Section() { |
|
163 delete elf; |
|
164 } |
|
165 |
|
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(); |
|
171 |
|
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 } |
|
184 |
|
185 ElfSection::serialize(file, ei_class, ei_data); |
|
186 } |
|
187 |
|
188 bool isRelocatable() { |
|
189 return true; |
|
190 } |
|
191 |
|
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 } |
|
208 |
|
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 } |
|
225 |
|
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 } |
|
235 |
|
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 }; |
|
244 |
|
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 }; |
|
257 |
|
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; |
|
271 |
|
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>"); |
|
276 |
|
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 } |
|
291 |
|
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; |
|
297 |
|
298 tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1)); |
|
299 |
|
300 s = (tmp & (1 << 24)) >> 24; |
|
301 j1 = ((tmp & (1 << 23)) >> 23) ^ !s; |
|
302 j2 = ((tmp & (1 << 22)) >> 22) ^ !s; |
|
303 |
|
304 return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) | |
|
305 (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15); |
|
306 } |
|
307 }; |
|
308 |
|
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 }; |
|
317 |
|
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 } |
|
327 |
|
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 } |
|
335 |
|
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 } |
|
402 |
|
403 Elf *elf, &parent; |
|
404 std::vector<ElfSection *> code; |
|
405 unsigned int init; |
|
406 int entry_point; |
|
407 }; |
|
408 |
|
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 } |
|
414 |
|
415 unsigned int get_addend(Elf_Rela *rel, Elf *elf) { |
|
416 return rel->r_addend; |
|
417 } |
|
418 |
|
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 } |
|
425 |
|
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 } |
|
432 |
|
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 } |
|
496 |
|
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 } |
|
505 |
|
506 ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO); |
|
507 |
|
508 ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag); |
|
509 assert(section->getType() == Rel_Type::sh_type); |
|
510 |
|
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 }; |
|
517 |
|
518 unsigned int entry_sz = Elf_Addr::size(elf->getClass()); |
|
519 |
|
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 } |
|
542 |
|
543 Elf_Shdr relhack_section(relhack32_section); |
|
544 Elf_Shdr relhackcode_section(relhackcode32_section); |
|
545 ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section); |
|
546 |
|
547 ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink(); |
|
548 Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual"); |
|
549 |
|
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); |
|
621 |
|
622 unsigned int old_end = section->getOffset() + section->getSize(); |
|
623 |
|
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 } |
|
643 |
|
644 section->rels.assign(new_rels.begin(), new_rels.end()); |
|
645 section->shrink(new_rels.size() * section->getEntSize()); |
|
646 |
|
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 } |
|
654 |
|
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 } |
|
660 |
|
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 = §ion->rels[init_array_reloc - 1]; |
|
669 rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation |
|
670 set_relative_reloc(§ion->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)); |
|
678 |
|
679 return 0; |
|
680 } |
|
681 |
|
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 } |
|
688 |
|
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 } |
|
699 |
|
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 } |
|
708 |
|
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 } |
|
733 |
|
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 } |
|
744 |
|
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 } |
|
755 |
|
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 } |
|
764 |
|
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); |
|
782 |
|
783 elf.removeSegment(second); |
|
784 if (filler) |
|
785 elf.removeSegment(filler); |
|
786 |
|
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 } |
|
795 |
|
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 } |
|
820 |
|
821 free(rundir); |
|
822 return 0; |
|
823 } |