1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "crazy_linker_elf_relocations.h"
6
7 #include <errno.h>
8
9 #include "crazy_linker_debug.h"
10 #include "crazy_linker_elf_symbols.h"
11 #include "crazy_linker_elf_view.h"
12 #include "crazy_linker_error.h"
13 #include "crazy_linker_util.h"
14 #include "linker_phdr.h"
15
16 #define DEBUG_RELOCATIONS 0
17
18 #define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
19 #define RLOG_ERRNO(...) LOG_ERRNO_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
20
21 #ifndef DF_SYMBOLIC
22 #define DF_SYMBOLIC 2
23 #endif
24
25 #ifndef DF_TEXTREL
26 #define DF_TEXTREL 4
27 #endif
28
29 #ifndef DT_FLAGS
30 #define DT_FLAGS 30
31 #endif
32
33 // Processor-specific relocation types supported by the linker.
34 #ifdef __arm__
35
36 /* arm32 relocations */
37 #define R_ARM_ABS32 2
38 #define R_ARM_REL32 3
39 #define R_ARM_GLOB_DAT 21
40 #define R_ARM_JUMP_SLOT 22
41 #define R_ARM_COPY 20
42 #define R_ARM_RELATIVE 23
43
44 #endif // __arm__
45
46 #ifdef __aarch64__
47
48 /* arm64 relocations */
49 #define R_AARCH64_ABS64 257
50 #define R_AARCH64_COPY 1024
51 #define R_AARCH64_GLOB_DAT 1025
52 #define R_AARCH64_JUMP_SLOT 1026
53 #define R_AARCH64_RELATIVE 1027
54
55 #endif // __aarch64__
56
57 #ifdef __i386__
58
59 /* i386 relocations */
60 #define R_386_32 1
61 #define R_386_PC32 2
62 #define R_386_GLOB_DAT 6
63 #define R_386_JMP_SLOT 7
64 #define R_386_RELATIVE 8
65
66 #endif // __i386__
67
68 namespace crazy {
69
70 namespace {
71
72 // List of known relocation types the relocator knows about.
73 enum RelocationType {
74 RELOCATION_TYPE_UNKNOWN = 0,
75 RELOCATION_TYPE_ABSOLUTE = 1,
76 RELOCATION_TYPE_RELATIVE = 2,
77 RELOCATION_TYPE_PC_RELATIVE = 3,
78 RELOCATION_TYPE_COPY = 4,
79 };
80
81 // Convert an ELF relocation type info a RelocationType value.
GetRelocationType(ELF::Word r_type)82 RelocationType GetRelocationType(ELF::Word r_type) {
83 switch (r_type) {
84 #ifdef __arm__
85 case R_ARM_JUMP_SLOT:
86 case R_ARM_GLOB_DAT:
87 case R_ARM_ABS32:
88 return RELOCATION_TYPE_ABSOLUTE;
89
90 case R_ARM_REL32:
91 case R_ARM_RELATIVE:
92 return RELOCATION_TYPE_RELATIVE;
93
94 case R_ARM_COPY:
95 return RELOCATION_TYPE_COPY;
96 #endif
97
98 #ifdef __aarch64__
99 case R_AARCH64_JUMP_SLOT:
100 case R_AARCH64_GLOB_DAT:
101 case R_AARCH64_ABS64:
102 return RELOCATION_TYPE_ABSOLUTE;
103
104 case R_AARCH64_RELATIVE:
105 return RELOCATION_TYPE_RELATIVE;
106
107 case R_AARCH64_COPY:
108 return RELOCATION_TYPE_COPY;
109 #endif
110
111 #ifdef __i386__
112 case R_386_JMP_SLOT:
113 case R_386_GLOB_DAT:
114 case R_386_32:
115 return RELOCATION_TYPE_ABSOLUTE;
116
117 case R_386_RELATIVE:
118 return RELOCATION_TYPE_RELATIVE;
119
120 case R_386_PC32:
121 return RELOCATION_TYPE_PC_RELATIVE;
122 #endif
123
124 #ifdef __mips__
125 case R_MIPS_REL32:
126 return RELOCATION_TYPE_RELATIVE;
127 #endif
128
129 default:
130 return RELOCATION_TYPE_UNKNOWN;
131 }
132 }
133
134 } // namespace
135
Init(const ElfView * view,Error * error)136 bool ElfRelocations::Init(const ElfView* view, Error* error) {
137 // Save these for later.
138 phdr_ = view->phdr();
139 phdr_count_ = view->phdr_count();
140 load_bias_ = view->load_bias();
141
142 // We handle only Rel or Rela, but not both. If DT_RELA or DT_RELASZ
143 // then we require DT_PLTREL to agree.
144 bool has_rela_relocations = false;
145 bool has_rel_relocations = false;
146
147 // Parse the dynamic table.
148 ElfView::DynamicIterator dyn(view);
149 for (; dyn.HasNext(); dyn.GetNext()) {
150 ELF::Addr dyn_value = dyn.GetValue();
151 uintptr_t dyn_addr = dyn.GetAddress(view->load_bias());
152
153 const ELF::Addr tag = dyn.GetTag();
154 switch (tag) {
155 case DT_PLTREL:
156 RLOG(" DT_PLTREL value=%d\n", dyn_value);
157 if (dyn_value != DT_REL && dyn_value != DT_RELA) {
158 *error = "Invalid DT_PLTREL value in dynamic section";
159 return false;
160 }
161 relocations_type_ = dyn_value;
162 break;
163 case DT_JMPREL:
164 RLOG(" DT_JMPREL addr=%p\n", dyn_addr);
165 plt_relocations_ = dyn_addr;
166 break;
167 case DT_PLTRELSZ:
168 plt_relocations_size_ = dyn_value;
169 RLOG(" DT_PLTRELSZ size=%d\n", dyn_value);
170 break;
171 case DT_RELA:
172 case DT_REL:
173 RLOG(" %s addr=%p\n",
174 (tag == DT_RELA) ? "DT_RELA" : "DT_REL",
175 dyn_addr);
176 if (relocations_) {
177 *error = "Unsupported DT_RELA/DT_REL combination in dynamic section";
178 return false;
179 }
180 relocations_ = dyn_addr;
181 if (tag == DT_RELA)
182 has_rela_relocations = true;
183 else
184 has_rel_relocations = true;
185 break;
186 case DT_RELASZ:
187 case DT_RELSZ:
188 RLOG(" %s size=%d\n",
189 (tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ",
190 dyn_addr);
191 if (relocations_size_) {
192 *error = "Unsupported DT_RELASZ/DT_RELSZ combination in dyn section";
193 return false;
194 }
195 relocations_size_ = dyn_value;
196 if (tag == DT_RELASZ)
197 has_rela_relocations = true;
198 else
199 has_rel_relocations = true;
200 break;
201 case DT_PLTGOT:
202 // Only used on MIPS currently. Could also be used on other platforms
203 // when lazy binding (i.e. RTLD_LAZY) is implemented.
204 RLOG(" DT_PLTGOT addr=%p\n", dyn_addr);
205 plt_got_ = reinterpret_cast<ELF::Addr*>(dyn_addr);
206 break;
207 case DT_TEXTREL:
208 RLOG(" DT_TEXTREL\n");
209 has_text_relocations_ = true;
210 break;
211 case DT_SYMBOLIC:
212 RLOG(" DT_SYMBOLIC\n");
213 has_symbolic_ = true;
214 break;
215 case DT_FLAGS:
216 if (dyn_value & DF_TEXTREL)
217 has_text_relocations_ = true;
218 if (dyn_value & DF_SYMBOLIC)
219 has_symbolic_ = true;
220 RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n",
221 has_text_relocations_ ? "true" : "false",
222 has_symbolic_ ? "true" : "false");
223 break;
224 #if defined(__mips__)
225 case DT_MIPS_SYMTABNO:
226 RLOG(" DT_MIPS_SYMTABNO value=%d\n", dyn_value);
227 mips_symtab_count_ = dyn_value;
228 break;
229
230 case DT_MIPS_LOCAL_GOTNO:
231 RLOG(" DT_MIPS_LOCAL_GOTNO value=%d\n", dyn_value);
232 mips_local_got_count_ = dyn_value;
233 break;
234
235 case DT_MIPS_GOTSYM:
236 RLOG(" DT_MIPS_GOTSYM value=%d\n", dyn_value);
237 mips_gotsym_ = dyn_value;
238 break;
239 #endif
240 default:
241 ;
242 }
243 }
244
245 if (relocations_type_ != DT_REL && relocations_type_ != DT_RELA) {
246 *error = "Unsupported or missing DT_PLTREL in dynamic section";
247 return false;
248 }
249
250 if (relocations_type_ == DT_REL && has_rela_relocations) {
251 *error = "Found DT_RELA in dyn section, but DT_PLTREL is DT_REL";
252 return false;
253 }
254 if (relocations_type_ == DT_RELA && has_rel_relocations) {
255 *error = "Found DT_REL in dyn section, but DT_PLTREL is DT_RELA";
256 return false;
257 }
258
259 return true;
260 }
261
ApplyAll(const ElfSymbols * symbols,SymbolResolver * resolver,Error * error)262 bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
263 SymbolResolver* resolver,
264 Error* error) {
265 LOG("%s: Enter\n", __FUNCTION__);
266
267 if (has_text_relocations_) {
268 if (phdr_table_unprotect_segments(phdr_, phdr_count_, load_bias_) < 0) {
269 error->Format("Can't unprotect loadable segments: %s", strerror(errno));
270 return false;
271 }
272 }
273
274 if (relocations_type_ == DT_REL) {
275 if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(plt_relocations_),
276 plt_relocations_size_ / sizeof(ELF::Rel),
277 symbols,
278 resolver,
279 error))
280 return false;
281 if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(relocations_),
282 relocations_size_ / sizeof(ELF::Rel),
283 symbols,
284 resolver,
285 error))
286 return false;
287 }
288
289 else if (relocations_type_ == DT_RELA) {
290 if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(plt_relocations_),
291 plt_relocations_size_ / sizeof(ELF::Rela),
292 symbols,
293 resolver,
294 error))
295 return false;
296 if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(relocations_),
297 relocations_size_ / sizeof(ELF::Rela),
298 symbols,
299 resolver,
300 error))
301 return false;
302 }
303
304 #ifdef __mips__
305 if (!RelocateMipsGot(symbols, resolver, error))
306 return false;
307 #endif
308
309 if (has_text_relocations_) {
310 if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) {
311 error->Format("Can't reprotect loadable segments: %s", strerror(errno));
312 return false;
313 }
314 }
315
316 LOG("%s: Done\n", __FUNCTION__);
317 return true;
318 }
319
ApplyRelaReloc(const ELF::Rela * rela,ELF::Addr sym_addr,bool resolved CRAZY_UNUSED,Error * error)320 bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela,
321 ELF::Addr sym_addr,
322 bool resolved CRAZY_UNUSED,
323 Error* error) {
324 const ELF::Word rela_type = ELF_R_TYPE(rela->r_info);
325 const ELF::Word CRAZY_UNUSED rela_symbol = ELF_R_SYM(rela->r_info);
326 const ELF::Sword CRAZY_UNUSED addend = rela->r_addend;
327
328 const ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_);
329
330 RLOG(" rela reloc=%p offset=%p type=%d addend=%p\n",
331 reloc,
332 rela->r_offset,
333 rela_type,
334 addend);
335
336 // Apply the relocation.
337 ELF::Addr* CRAZY_UNUSED target = reinterpret_cast<ELF::Addr*>(reloc);
338 switch (rela_type) {
339 #ifdef __aarch64__
340 case R_AARCH64_JUMP_SLOT:
341 RLOG(" R_AARCH64_JUMP_SLOT target=%p addr=%p\n",
342 target,
343 sym_addr + addend);
344 *target = sym_addr + addend;
345 break;
346
347 case R_AARCH64_GLOB_DAT:
348 RLOG(" R_AARCH64_GLOB_DAT target=%p addr=%p\n",
349 target,
350 sym_addr + addend);
351 *target = sym_addr + addend;
352 break;
353
354 case R_AARCH64_ABS64:
355 RLOG(" R_AARCH64_ABS64 target=%p (%p) addr=%p\n",
356 target,
357 *target,
358 sym_addr + addend);
359 *target += sym_addr + addend;
360 break;
361
362 case R_AARCH64_RELATIVE:
363 RLOG(" R_AARCH64_RELATIVE target=%p (%p) bias=%p\n",
364 target,
365 *target,
366 load_bias_ + addend);
367 if (__builtin_expect(rela_symbol, 0)) {
368 *error = "Invalid relative relocation with symbol";
369 return false;
370 }
371 *target = load_bias_ + addend;
372 break;
373
374 case R_AARCH64_COPY:
375 // NOTE: These relocations are forbidden in shared libraries.
376 RLOG(" R_AARCH64_COPY\n");
377 *error = "Invalid R_AARCH64_COPY relocation in shared library";
378 return false;
379 #endif // __aarch64__
380
381 default:
382 error->Format("Invalid relocation type (%d)", rela_type);
383 return false;
384 }
385
386 return true;
387 }
388
ApplyRelReloc(const ELF::Rel * rel,ELF::Addr sym_addr,bool resolved CRAZY_UNUSED,Error * error)389 bool ElfRelocations::ApplyRelReloc(const ELF::Rel* rel,
390 ELF::Addr sym_addr,
391 bool resolved CRAZY_UNUSED,
392 Error* error) {
393 const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
394 const ELF::Word CRAZY_UNUSED rel_symbol = ELF_R_SYM(rel->r_info);
395
396 const ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
397
398 RLOG(" rel reloc=%p offset=%p type=%d\n", reloc, rel->r_offset, rel_type);
399
400 // Apply the relocation.
401 ELF::Addr* CRAZY_UNUSED target = reinterpret_cast<ELF::Addr*>(reloc);
402 switch (rel_type) {
403 #ifdef __arm__
404 case R_ARM_JUMP_SLOT:
405 RLOG(" R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr);
406 *target = sym_addr;
407 break;
408
409 case R_ARM_GLOB_DAT:
410 RLOG(" R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr);
411 *target = sym_addr;
412 break;
413
414 case R_ARM_ABS32:
415 RLOG(" R_ARM_ABS32 target=%p (%p) addr=%p\n",
416 target,
417 *target,
418 sym_addr);
419 *target += sym_addr;
420 break;
421
422 case R_ARM_REL32:
423 RLOG(" R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n",
424 target,
425 *target,
426 sym_addr,
427 rel->r_offset);
428 *target += sym_addr - rel->r_offset;
429 break;
430
431 case R_ARM_RELATIVE:
432 RLOG(" R_ARM_RELATIVE target=%p (%p) bias=%p\n",
433 target,
434 *target,
435 load_bias_);
436 if (__builtin_expect(rel_symbol, 0)) {
437 *error = "Invalid relative relocation with symbol";
438 return false;
439 }
440 *target += load_bias_;
441 break;
442
443 case R_ARM_COPY:
444 // NOTE: These relocations are forbidden in shared libraries.
445 // The Android linker has special code to deal with this, which
446 // is not needed here.
447 RLOG(" R_ARM_COPY\n");
448 *error = "Invalid R_ARM_COPY relocation in shared library";
449 return false;
450 #endif // __arm__
451
452 #ifdef __i386__
453 case R_386_JMP_SLOT:
454 *target = sym_addr;
455 break;
456
457 case R_386_GLOB_DAT:
458 *target = sym_addr;
459 break;
460
461 case R_386_RELATIVE:
462 if (rel_symbol) {
463 *error = "Invalid relative relocation with symbol";
464 return false;
465 }
466 *target += load_bias_;
467 break;
468
469 case R_386_32:
470 *target += sym_addr;
471 break;
472
473 case R_386_PC32:
474 *target += (sym_addr - reloc);
475 break;
476 #endif // __i386__
477
478 #ifdef __mips__
479 case R_MIPS_REL32:
480 if (resolved)
481 *target += sym_addr;
482 else
483 *target += load_bias_;
484 break;
485 #endif // __mips__
486
487 default:
488 error->Format("Invalid relocation type (%d)", rel_type);
489 return false;
490 }
491
492 return true;
493 }
494
ResolveSymbol(ELF::Word rel_type,ELF::Word rel_symbol,const ElfSymbols * symbols,SymbolResolver * resolver,ELF::Addr reloc,ELF::Addr * sym_addr,Error * error)495 bool ElfRelocations::ResolveSymbol(ELF::Word rel_type,
496 ELF::Word rel_symbol,
497 const ElfSymbols* symbols,
498 SymbolResolver* resolver,
499 ELF::Addr reloc,
500 ELF::Addr* sym_addr,
501 Error* error) {
502 const char* sym_name = symbols->LookupNameById(rel_symbol);
503 RLOG(" symbol name='%s'\n", sym_name);
504 void* address = resolver->Lookup(sym_name);
505
506 if (address) {
507 // The symbol was found, so compute its address.
508 RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, sym_name, address);
509 *sym_addr = reinterpret_cast<ELF::Addr>(address);
510 return true;
511 }
512
513 // The symbol was not found. Normally this is an error except
514 // if this is a weak reference.
515 if (!symbols->IsWeakById(rel_symbol)) {
516 error->Format("Could not find symbol '%s'", sym_name);
517 return false;
518 }
519
520 RLOG("%s: weak reference to unresolved symbol %s\n", __FUNCTION__, sym_name);
521
522 // IHI0044C AAELF 4.5.1.1:
523 // Libraries are not searched to resolve weak references.
524 // It is not an error for a weak reference to remain
525 // unsatisfied.
526 //
527 // During linking, the value of an undefined weak reference is:
528 // - Zero if the relocation type is absolute
529 // - The address of the place if the relocation is pc-relative
530 // - The address of nominal base address if the relocation
531 // type is base-relative.
532 RelocationType r = GetRelocationType(rel_type);
533 if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE) {
534 *sym_addr = 0;
535 return true;
536 }
537
538 if (r == RELOCATION_TYPE_PC_RELATIVE) {
539 *sym_addr = reloc;
540 return true;
541 }
542
543 error->Format(
544 "Invalid weak relocation type (%d) for unknown symbol '%s'",
545 r,
546 sym_name);
547 return false;
548 }
549
ApplyRelRelocs(const ELF::Rel * rel,size_t rel_count,const ElfSymbols * symbols,SymbolResolver * resolver,Error * error)550 bool ElfRelocations::ApplyRelRelocs(const ELF::Rel* rel,
551 size_t rel_count,
552 const ElfSymbols* symbols,
553 SymbolResolver* resolver,
554 Error* error) {
555 RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count);
556
557 if (!rel)
558 return true;
559
560 for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
561 const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
562 const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info);
563
564 ELF::Addr sym_addr = 0;
565 ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
566 RLOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n",
567 rel_n + 1,
568 rel_count,
569 reloc,
570 rel->r_offset,
571 rel_type,
572 rel_symbol);
573
574 if (rel_type == 0)
575 continue;
576
577 bool resolved = false;
578
579 // If this is a symbolic relocation, compute the symbol's address.
580 if (__builtin_expect(rel_symbol != 0, 0)) {
581 resolved = ResolveSymbol(rel_type,
582 rel_symbol,
583 symbols,
584 resolver,
585 reloc,
586 &sym_addr,
587 error);
588 }
589
590 if (!ApplyRelReloc(rel, sym_addr, resolved, error))
591 return false;
592 }
593
594 return true;
595 }
596
ApplyRelaRelocs(const ELF::Rela * rela,size_t rela_count,const ElfSymbols * symbols,SymbolResolver * resolver,Error * error)597 bool ElfRelocations::ApplyRelaRelocs(const ELF::Rela* rela,
598 size_t rela_count,
599 const ElfSymbols* symbols,
600 SymbolResolver* resolver,
601 Error* error) {
602 RLOG("%s: rela=%p rela_count=%d\n", __FUNCTION__, rela, rela_count);
603
604 if (!rela)
605 return true;
606
607 for (size_t rel_n = 0; rel_n < rela_count; rela++, rel_n++) {
608 const ELF::Word rel_type = ELF_R_TYPE(rela->r_info);
609 const ELF::Word rel_symbol = ELF_R_SYM(rela->r_info);
610
611 ELF::Addr sym_addr = 0;
612 ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_);
613 RLOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n",
614 rel_n + 1,
615 rela_count,
616 reloc,
617 rela->r_offset,
618 rel_type,
619 rel_symbol);
620
621 if (rel_type == 0)
622 continue;
623
624 bool resolved = false;
625
626 // If this is a symbolic relocation, compute the symbol's address.
627 if (__builtin_expect(rel_symbol != 0, 0)) {
628 resolved = ResolveSymbol(rel_type,
629 rel_symbol,
630 symbols,
631 resolver,
632 reloc,
633 &sym_addr,
634 error);
635 }
636
637 if (!ApplyRelaReloc(rela, sym_addr, resolved, error))
638 return false;
639 }
640
641 return true;
642 }
643
644 #ifdef __mips__
RelocateMipsGot(const ElfSymbols * symbols,SymbolResolver * resolver,Error * error)645 bool ElfRelocations::RelocateMipsGot(const ElfSymbols* symbols,
646 SymbolResolver* resolver,
647 Error* error) {
648 if (!plt_got_)
649 return true;
650
651 // Handle the local GOT entries.
652 // This mimics what the system linker does.
653 // Note from the system linker:
654 // got[0]: lazy resolver function address.
655 // got[1]: may be used for a GNU extension.
656 // Set it to a recognizable address in case someone calls it
657 // (should be _rtld_bind_start).
658 ELF::Addr* got = plt_got_;
659 got[0] = 0xdeadbeef;
660 if (got[1] & 0x80000000)
661 got[1] = 0xdeadbeef;
662
663 for (ELF::Addr n = 2; n < mips_local_got_count_; ++n)
664 got[n] += load_bias_;
665
666 // Handle the global GOT entries.
667 got += mips_local_got_count_;
668 for (size_t idx = mips_gotsym_; idx < mips_symtab_count_; idx++, got++) {
669 const char* sym_name = symbols->LookupNameById(idx);
670 void* sym_addr = resolver->Lookup(sym_name);
671 if (sym_addr) {
672 // Found symbol, update GOT entry.
673 *got = reinterpret_cast<ELF::Addr>(sym_addr);
674 continue;
675 }
676
677 if (symbols->IsWeakById(idx)) {
678 // Undefined symbols are only ok if this is a weak reference.
679 // Update GOT entry to 0 though.
680 *got = 0;
681 continue;
682 }
683
684 error->Format("Cannot locate symbol %s", sym_name);
685 return false;
686 }
687
688 return true;
689 }
690 #endif // __mips__
691
AdjustRelocation(ELF::Word rel_type,ELF::Addr src_reloc,size_t dst_delta,size_t map_delta)692 void ElfRelocations::AdjustRelocation(ELF::Word rel_type,
693 ELF::Addr src_reloc,
694 size_t dst_delta,
695 size_t map_delta) {
696 ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta);
697
698 switch (rel_type) {
699 #ifdef __arm__
700 case R_ARM_RELATIVE:
701 *dst_ptr += map_delta;
702 break;
703 #endif // __arm__
704
705 #ifdef __aarch64__
706 case R_AARCH64_RELATIVE:
707 *dst_ptr += map_delta;
708 break;
709 #endif // __aarch64__
710
711 #ifdef __i386__
712 case R_386_RELATIVE:
713 *dst_ptr += map_delta;
714 break;
715 #endif
716
717 #ifdef __mips__
718 case R_MIPS_REL32:
719 *dst_ptr += map_delta;
720 break;
721 #endif
722 default:
723 ;
724 }
725 }
726
RelocateRela(size_t src_addr,size_t dst_addr,size_t map_addr,size_t size)727 void ElfRelocations::RelocateRela(size_t src_addr,
728 size_t dst_addr,
729 size_t map_addr,
730 size_t size) {
731 // Add this value to each source address to get the corresponding
732 // destination address.
733 const size_t dst_delta = dst_addr - src_addr;
734 const size_t map_delta = map_addr - src_addr;
735
736 // Ignore PLT relocations, which all target symbols (ignored here).
737 const ELF::Rela* rel = reinterpret_cast<ELF::Rela*>(relocations_);
738 const size_t relocations_count = relocations_size_ / sizeof(ELF::Rela);
739 const ELF::Rela* rel_limit = rel + relocations_count;
740
741 for (; rel < rel_limit; ++rel) {
742 const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
743 const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info);
744 ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
745
746 if (rel_type == 0 || rel_symbol != 0) {
747 // Ignore empty and symbolic relocations
748 continue;
749 }
750
751 if (src_reloc < src_addr || src_reloc >= src_addr + size) {
752 // Ignore entries that don't relocate addresses inside the source section.
753 continue;
754 }
755
756 AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta);
757 }
758 }
759
RelocateRel(size_t src_addr,size_t dst_addr,size_t map_addr,size_t size)760 void ElfRelocations::RelocateRel(size_t src_addr,
761 size_t dst_addr,
762 size_t map_addr,
763 size_t size) {
764 // Add this value to each source address to get the corresponding
765 // destination address.
766 const size_t dst_delta = dst_addr - src_addr;
767 const size_t map_delta = map_addr - src_addr;
768
769 // Ignore PLT relocations, which all target symbols (ignored here).
770 const ELF::Rel* rel = reinterpret_cast<ELF::Rel*>(relocations_);
771 const size_t relocations_count = relocations_size_ / sizeof(ELF::Rel);
772 const ELF::Rel* rel_limit = rel + relocations_count;
773
774 for (; rel < rel_limit; ++rel) {
775 const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
776 const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info);
777 ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
778
779 if (rel_type == 0 || rel_symbol != 0) {
780 // Ignore empty and symbolic relocations
781 continue;
782 }
783
784 if (src_reloc < src_addr || src_reloc >= src_addr + size) {
785 // Ignore entries that don't relocate addresses inside the source section.
786 continue;
787 }
788
789 AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta);
790 }
791 }
792
CopyAndRelocate(size_t src_addr,size_t dst_addr,size_t map_addr,size_t size)793 void ElfRelocations::CopyAndRelocate(size_t src_addr,
794 size_t dst_addr,
795 size_t map_addr,
796 size_t size) {
797 // First, a straight copy.
798 ::memcpy(reinterpret_cast<void*>(dst_addr),
799 reinterpret_cast<void*>(src_addr),
800 size);
801
802 // Relocate relocations.
803 if (relocations_type_ == DT_REL)
804 RelocateRel(src_addr, dst_addr, map_addr, size);
805
806 else if (relocations_type_ == DT_RELA)
807 RelocateRela(src_addr, dst_addr, map_addr, size);
808
809 #ifdef __mips__
810 // Add this value to each source address to get the corresponding
811 // destination address.
812 const size_t dst_delta = dst_addr - src_addr;
813 const size_t map_delta = map_addr - src_addr;
814
815 // Only relocate local GOT entries.
816 ELF::Addr* got = plt_got_;
817 if (got) {
818 for (ELF::Addr n = 2; n < mips_local_got_count_; ++n) {
819 size_t got_addr = reinterpret_cast<size_t>(&got[n]);
820 if (got_addr < src_addr || got_addr >= src_addr + size)
821 continue;
822 ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(got_addr + dst_delta);
823 *dst_ptr += map_delta;
824 }
825 }
826 #endif
827 }
828
829 } // namespace crazy
830