• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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