Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp

Issue 322433006: Fork of the Android NDK crazy linker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "crazy_linker_system.h"
15 #include "linker_phdr.h"
16
17 #define DEBUG_RELOCATIONS 1 // TODO(simonb): Unhack me later.
18 const char* global_lib_path = NULL;
19
20 #define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
21 #define RLOG_ERRNO(...) LOG_ERRNO_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
22
23 #ifndef DF_SYMBOLIC
24 #define DF_SYMBOLIC 2
25 #endif
26
27 #ifndef DF_TEXTREL
28 #define DF_TEXTREL 4
29 #endif
30
31 #ifndef DT_FLAGS
32 #define DT_FLAGS 30
33 #endif
34
35 // Processor-specific relocation types supported by the linker.
36 #ifdef __arm__
37
38 #define R_ARM_ABS32 2
39 #define R_ARM_REL32 3
40 #define R_ARM_GLOB_DAT 21
41 #define R_ARM_JUMP_SLOT 22
42 #define R_ARM_COPY 20
43 #define R_ARM_RELATIVE 23
44
45 #endif // __arm__
46
47 #ifdef __i386__
48
49 /* i386 relocations */
50 #define R_386_32 1
51 #define R_386_PC32 2
52 #define R_386_GLOB_DAT 6
53 #define R_386_JMP_SLOT 7
54 #define R_386_RELATIVE 8
55
56 #endif // __i386__
57
58 // Processor-specific extension dynamic tags, for packed relocations.
59 #ifdef __arm__
60
61 #define DT_ANDROID_ARM_REL_OFFSET (DT_LOPROC)
62 #define DT_ANDROID_ARM_REL_SIZE (DT_LOPROC + 1)
63
64 #define PACKED_RELOCATIONS_IDENTIFIER 0x31525041U // "APR1"
65
66 #endif // __arm__
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.
82 RelocationType GetRelocationType(unsigned 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 __i386__
99 case R_386_JMP_SLOT:
100 case R_386_GLOB_DAT:
101 case R_386_32:
102 return RELOCATION_TYPE_ABSOLUTE;
103
104 case R_386_RELATIVE:
105 return RELOCATION_TYPE_RELATIVE;
106
107 case R_386_PC32:
108 return RELOCATION_TYPE_PC_RELATIVE;
109 #endif
110
111 #ifdef __mips__
112 case R_MIPS_REL32:
113 return RELOCATION_TYPE_RELATIVE;
114 #endif
115
116 default:
117 return RELOCATION_TYPE_UNKNOWN;
118 }
119 }
120
121 } // namespace
122
123 bool ElfRelocations::Init(const ElfView* view, Error* error) {
124 // Save these for later.
125 phdr_ = view->phdr();
126 phdr_count_ = view->phdr_count();
127 load_bias_ = view->load_bias();
128
129 // Parse the dynamic table.
130 ElfView::DynamicIterator dyn(view);
131 for (; dyn.HasNext(); dyn.GetNext()) {
132 ELF::Addr dyn_value = dyn.GetValue();
133 uintptr_t dyn_addr = dyn.GetAddress(view->load_bias());
134
135 switch (dyn.GetTag()) {
136 #ifdef __arm__
137 case DT_ANDROID_ARM_REL_OFFSET:
138 // The packed relocations file offset excludes the load bias.
139 packed_relocations_ = dyn.GetAddress(0);
140 RLOG(" DT_ANDROID_ARM_REL_OFFSET addr=%p\n", packed_relocations_);
141 RLOG(" global_lib_path=%s\n", global_lib_path);
142 break;
143 case DT_ANDROID_ARM_REL_SIZE:
144 // The packed relocations file section size excludes the load bias.
145 packed_relocations_size_ = dyn.GetAddress(0);
146 RLOG(" DT_ANDROID_ARM_REL_SIZE=%d\n", packed_relocations_size_);
147 break;
148 #endif
149 case DT_PLTREL:
150 // NOTE: Yes, there is nothing to record here, the content of
151 // plt_rel_ will come from DT_JMPREL instead.
152 RLOG(" DT_PLTREL");
153 if (dyn_value != DT_REL) {
154 *error = "Unsupported DT_RELA entry in dynamic section";
155 return false;
156 }
157 break;
158 case DT_JMPREL:
159 RLOG(" DT_JMPREL addr=%p\n", dyn_addr);
160 plt_relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
161 break;
162 case DT_PLTRELSZ:
163 plt_relocations_count_ = dyn_value / sizeof(ELF::Rel);
164 RLOG(" DT_PLTRELSZ size=%d count=%d\n",
165 dyn_value,
166 plt_relocations_count_);
167 break;
168 case DT_REL:
169 RLOG(" DT_REL addr=%p\n", dyn_addr);
170 relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
171 break;
172 case DT_RELSZ:
173 relocations_count_ = dyn_value / sizeof(ELF::Rel);
174 RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, relocations_count_);
175 break;
176 case DT_PLTGOT:
177 // Only used on MIPS currently. Could also be used on other platforms
178 // when lazy binding (i.e. RTLD_LAZY) is implemented.
179 RLOG(" DT_PLTGOT addr=%p\n", dyn_addr);
180 plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr);
181 break;
182 case DT_RELA:
183 *error = "Unsupported DT_RELA entry in dynamic section";
184 return false;
185 case DT_TEXTREL:
186 RLOG(" DT_TEXTREL\n");
187 has_text_relocations_ = true;
188 break;
189 case DT_SYMBOLIC:
190 RLOG(" DT_SYMBOLIC\n");
191 has_symbolic_ = true;
192 break;
193 case DT_FLAGS:
194 if (dyn_value & DF_TEXTREL)
195 has_text_relocations_ = true;
196 if (dyn_value & DF_SYMBOLIC)
197 has_symbolic_ = true;
198 RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n",
199 has_text_relocations_ ? "true" : "false",
200 has_symbolic_ ? "true" : "false");
201 break;
202 #if defined(__mips__)
203 case DT_MIPS_SYMTABNO:
204 RLOG(" DT_MIPS_SYMTABNO value=%d\n", dyn_value);
205 mips_symtab_count_ = dyn_value;
206 break;
207
208 case DT_MIPS_LOCAL_GOTNO:
209 RLOG(" DT_MIPS_LOCAL_GOTNO value=%d\n", dyn_value);
210 mips_local_got_count_ = dyn_value;
211 break;
212
213 case DT_MIPS_GOTSYM:
214 RLOG(" DT_MIPS_GOTSYM value=%d\n", dyn_value);
215 mips_gotsym_ = dyn_value;
216 break;
217 #endif
218 default:
219 ;
220 }
221 }
222
223 return true;
224 }
225
226 bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
227 SymbolResolver* resolver,
228 Error* error) {
229 LOG("%s: Enter\n", __FUNCTION__);
230
231 if (has_text_relocations_) {
232 if (phdr_table_unprotect_segments(phdr_, phdr_count_, load_bias_) < 0) {
233 error->Format("Can't unprotect loadable segments: %s", strerror(errno));
234 return false;
235 }
236 }
237
238 if (!ApplyRelocs(plt_relocations_,
239 plt_relocations_count_,
240 symbols,
241 resolver,
242 error) ||
243 !ApplyRelocs(
244 relocations_, relocations_count_, symbols, resolver, error)) {
245 return false;
246 }
247
248 #ifdef __arm__
249 if (!ApplyPackedRelocs(error))
250 return false;
251 #endif
252
253 #ifdef __mips__
254 if (!RelocateMipsGot(symbols, resolver, error))
255 return false;
256 #endif
257
258 if (has_text_relocations_) {
259 if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) {
260 error->Format("Can't reprotect loadable segments: %s", strerror(errno));
261 return false;
262 }
263 }
264
265 LOG("%s: Done\n", __FUNCTION__);
266 return true;
267 }
268
269 bool ElfRelocations::ApplyReloc(const ELF::Rel* rel,
270 ELF::Addr sym_addr,
271 Error* error) {
272 const unsigned rel_type = ELF_R_TYPE(rel->r_info);
273 const unsigned rel_symbol = ELF_R_SYM(rel->r_info);
274
275 const ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
276 RLOG(" reloc=%p offset=%p type=%d\n", reloc, rel->r_offset, rel_type);
277
278 // Apply the relocation.
279 ELF::Addr* target = reinterpret_cast<ELF::Addr*>(reloc);
280 switch (rel_type) {
281 #ifdef __arm__
282 case R_ARM_JUMP_SLOT:
283 RLOG(" R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr);
284 *target = sym_addr;
285 break;
286
287 case R_ARM_GLOB_DAT:
288 RLOG(" R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr);
289 *target = sym_addr;
290 break;
291
292 case R_ARM_ABS32:
293 RLOG(" R_ARM_ABS32 target=%p (%p) addr=%p\n",
294 target,
295 *target,
296 sym_addr);
297 *target += sym_addr;
298 break;
299
300 case R_ARM_REL32:
301 RLOG(" R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n",
302 target,
303 *target,
304 sym_addr,
305 rel->r_offset);
306 *target += sym_addr - rel->r_offset;
307 break;
308
309 case R_ARM_RELATIVE:
310 RLOG(" R_ARM_RELATIVE target=%p (%p) bias=%p\n",
311 target,
312 *target,
313 load_bias_);
314 if (__builtin_expect(rel_symbol, 0)) {
315 *error = "Invalid relative relocation with symbol";
316 return false;
317 }
318 *target += load_bias_;
319 break;
320
321 case R_ARM_COPY:
322 // NOTE: These relocations are forbidden in shared libraries.
323 // The Android linker has special code to deal with this, which
324 // is not needed here.
325 RLOG(" R_ARM_COPY\n");
326 *error = "Invalid R_ARM_COPY relocation in shared library";
327 return false;
328 #endif // __arm__
329
330 #ifdef __i386__
331 case R_386_JMP_SLOT:
332 *target = sym_addr;
333 break;
334
335 case R_386_GLOB_DAT:
336 *target = sym_addr;
337 break;
338
339 case R_386_RELATIVE:
340 if (rel_symbol) {
341 *error = "Invalid relative relocation with symbol";
342 return false;
343 }
344 *target += load_bias_;
345 break;
346
347 case R_386_32:
348 *target += sym_addr;
349 break;
350
351 case R_386_PC32:
352 *target += (sym_addr - reloc);
353 break;
354 #endif // __i386__
355
356 #ifdef __mips__
357 case R_MIPS_REL32:
358 if (resolved)
359 *target += sym_addr;
360 else
361 *target += load_bias_;
362 break;
363 #endif // __mips__
364
365 default:
366 error->Format("Invalid relocation type (%d)", rel_type);
367 return false;
368 }
369
370 return true;
371 }
372
373 bool ElfRelocations::ApplyRelocs(const ELF::Rel* rel,
374 size_t rel_count,
375 const ElfSymbols* symbols,
376 SymbolResolver* resolver,
377 Error* error) {
378 RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count);
379
380 if (!rel)
381 return true;
382
383 for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
384 const unsigned rel_type = ELF_R_TYPE(rel->r_info);
385 const unsigned rel_symbol = ELF_R_SYM(rel->r_info);
386
387 ELF::Addr sym_addr = 0;
388 const ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
389 RLOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n",
390 rel_n + 1,
391 rel_count,
392 reloc,
393 rel->r_offset,
394 rel_type,
395 rel_symbol);
396
397 if (rel_type == 0)
398 continue;
399
400 bool CRAZY_UNUSED resolved = false;
401
402 // If this is a symbolic relocation, compute the symbol's address.
403 if (__builtin_expect(rel_symbol != 0, 0)) {
404 const char* sym_name = symbols->LookupNameById(rel_symbol);
405 RLOG(" symbol name='%s'\n", sym_name);
406 void* address = resolver->Lookup(sym_name);
407 if (address) {
408 // The symbol was found, so compute its address.
409 RLOG("%s: symbol %s resolved to %p\n",
410 __FUNCTION__, sym_name, address);
411 resolved = true;
412 sym_addr = reinterpret_cast<ELF::Addr>(address);
413 } else {
414 // The symbol was not found. Normally this is an error except
415 // if this is a weak reference.
416 if (!symbols->IsWeakById(rel_symbol)) {
417 error->Format("Could not find symbol '%s'", sym_name);
418 return false;
419 }
420
421 resolved = true;
422 RLOG("%s: weak reference to unresolved symbol %s\n",
423 __FUNCTION__,
424 sym_name);
425
426 // IHI0044C AAELF 4.5.1.1:
427 // Libraries are not searched to resolve weak references.
428 // It is not an error for a weak reference to remain
429 // unsatisfied.
430 //
431 // During linking, the value of an undefined weak reference is:
432 // - Zero if the relocation type is absolute
433 // - The address of the place if the relocation is pc-relative
434 // - The address of nominal base address if the relocation
435 // type is base-relative.
436 RelocationType r = GetRelocationType(rel_type);
437 if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE)
438 sym_addr = 0;
439 else if (r == RELOCATION_TYPE_PC_RELATIVE)
440 sym_addr = reloc;
441 else {
442 error->Format(
443 "Invalid weak relocation type (%d) for unknown symbol '%s'",
444 r,
445 sym_name);
446 return false;
447 }
448 }
449 }
450
451 if (!ApplyReloc(rel, sym_addr, error))
452 return false;
453 }
454
455 return true;
456 }
457
458 #ifdef __arm__
459 bool ElfRelocations::ApplyPackedRelocs(Error* error) {
460 LOG("%s: Enter\n", __FUNCTION__);
461
462 if (packed_relocations_size_ == 0) {
463 LOG("%s: Done (no actions)\n", __FUNCTION__);
464 return true;
465 }
466
467 const size_t packed_words = packed_relocations_size_ / sizeof(uint32_t);
468 uint32_t* packed = new uint32_t[packed_words];
469
470 FileDescriptor fd;
471 fd.OpenReadOnly(global_lib_path);
472 fd.SeekTo(packed_relocations_);
473 fd.Read(packed, packed_relocations_size_);
474 fd.Close();
475
476 if (packed[0] != PACKED_RELOCATIONS_IDENTIFIER) {
477 error->Format("Bad packed relocations ident, expected 0x%08x, got 0x%08x",
478 PACKED_RELOCATIONS_IDENTIFIER, packed[0]);
479 return false;
480 }
481
482 // The minimum packed relocations set is the ident word, the size word, and
483 // a single initial relocation offset.
484 if (packed_words < 3) {
485 error->Format("Invalid packed relocations length, %d", packed_words);
486 return false;
487 }
488
489 // The second packed word indicates the count of packed words that follow.
490 // It should match the count of words read from the file, less the two
491 // header words for ident and the count itself.
492 if (packed[1] != packed_words - 2) {
493 error->Format("Bad packed relocations length, expected %d, got %d",
494 packed_words - 2, packed[1]);
495 return false;
496 }
497
498 RLOG(" packed=%p packed_words=%d\n", packed, packed_words);
499
500 // Process the initial packed relocation element.
501 uint32_t addr = packed[2];
502 const ELF::Rel initial = {addr, R_ARM_RELATIVE};
503 RLOG(" initial offset=%p\n", initial.r_offset);
504 if (!ApplyReloc(&initial, 0, error))
505 return false;
506
507 size_t unpacked_count = 1;
508
509 // Read all words from the start index, treating each as either a packed
510 // delta-count pair or as (spilled) separate count and delta.
511 uint32_t delta = 0;
512 size_t count = 0;
513 for (size_t i = 3; i < packed_words; ++i) {
514 const uint32_t value = packed[i];
515 if (count) {
516 // Previous value was a count with zero delta. This indicates a
517 // 'spill' of delta > 65535, so the real delta is here.
518 delta = value;
519 } else {
520 // Unpack the pair into separate delta and count values.
521 delta = value >> 16;
522 count = value & 65535;
523 }
524 // Create and apply relocations only when we have both delta and count.
525 if (delta && count) {
526 RLOG(" delta=%u count=%d\n", delta, count);
527 while (count) {
528 addr += delta;
529 const ELF::Rel relocation = {addr, R_ARM_RELATIVE};
530 RLOG(" unpacked %d relocation offset=%p\n",
531 unpacked_count, relocation.r_offset);
532 if (!ApplyReloc(&relocation, 0, error))
533 return false;
534 unpacked_count++;
535 count--;
536 }
537 delta = 0;
538 }
539 }
540
541 RLOG(" unpacked_count=%d\n", unpacked_count);
542 delete [] packed;
543
544 LOG("%s: Done\n", __FUNCTION__);
545 return true;
546 }
547 #endif
548
549 #ifdef __mips__
550 bool ElfRelocations::RelocateMipsGot(const ElfSymbols* symbols,
551 SymbolResolver* resolver,
552 Error* error) {
553 if (!plt_got_)
554 return true;
555
556 // Handle the local GOT entries.
557 // This mimics what the system linker does.
558 // Note from the system linker:
559 // got[0]: lazy resolver function address.
560 // got[1]: may be used for a GNU extension.
561 // Set it to a recognizable address in case someone calls it
562 // (should be _rtld_bind_start).
563 ELF::Addr* got = plt_got_;
564 got[0] = 0xdeadbeef;
565 if (got[1] & 0x80000000)
566 got[1] = 0xdeadbeef;
567
568 for (ELF::Addr n = 2; n < mips_local_got_count_; ++n)
569 got[n] += load_bias_;
570
571 // Handle the global GOT entries.
572 got += mips_local_got_count_;
573 for (size_t idx = mips_gotsym_; idx < mips_symtab_count_; idx++, got++) {
574 const char* sym_name = symbols->LookupNameById(idx);
575 void* sym_addr = resolver->Lookup(sym_name);
576 if (sym_addr) {
577 // Found symbol, update GOT entry.
578 *got = reinterpret_cast<ELF::Addr>(sym_addr);
579 continue;
580 }
581
582 if (symbols->IsWeakById(idx)) {
583 // Undefined symbols are only ok if this is a weak reference.
584 // Update GOT entry to 0 though.
585 *got = 0;
586 continue;
587 }
588
589 error->Format("Cannot locate symbol %s", sym_name);
590 return false;
591 }
592
593 return true;
594 }
595 #endif // __mips__
596
597 void ElfRelocations::CopyAndRelocate(size_t src_addr,
598 size_t dst_addr,
599 size_t map_addr,
600 size_t size) {
601 // First, a straight copy.
602 ::memcpy(reinterpret_cast<void*>(dst_addr),
603 reinterpret_cast<void*>(src_addr),
604 size);
605
606 // Add this value to each source address to get the corresponding
607 // destination address.
608 size_t dst_delta = dst_addr - src_addr;
609 size_t map_delta = map_addr - src_addr;
610
611 // Ignore PLT relocations, which all target symbols (ignored here).
612 const ELF::Rel* rel = relocations_;
613 const ELF::Rel* rel_limit = rel + relocations_count_;
614
615 for (; rel < rel_limit; ++rel) {
616 unsigned rel_type = ELF_R_TYPE(rel->r_info);
617 unsigned rel_symbol = ELF_R_SYM(rel->r_info);
618 ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
619
620 if (rel_type == 0 || rel_symbol != 0) {
621 // Ignore empty and symbolic relocations
622 continue;
623 }
624
625 if (src_reloc < src_addr || src_reloc >= src_addr + size) {
626 // Ignore entries that don't relocate addresses inside the source section.
627 continue;
628 }
629
630 ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta);
631
632 switch (rel_type) {
633 #ifdef __arm__
634 case R_ARM_RELATIVE:
635 *dst_ptr += map_delta;
636 break;
637 #endif // __arm__
638
639 #ifdef __i386__
640 case R_386_RELATIVE:
641 *dst_ptr += map_delta;
642 break;
643 #endif
644
645 #ifdef __mips__
646 case R_MIPS_REL32:
647 *dst_ptr += map_delta;
648 break;
649 #endif
650 default:
651 ;
652 }
653 }
654
655 #ifdef __mips__
656 // Only relocate local GOT entries.
657 ELF::Addr* got = plt_got_;
658 if (got) {
659 for (ELF::Addr n = 2; n < mips_local_got_count_; ++n) {
660 size_t got_addr = reinterpret_cast<size_t>(&got[n]);
661 if (got_addr < src_addr || got_addr >= src_addr + size)
662 continue;
663 ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(got_addr + dst_delta);
664 *dst_ptr += map_delta;
665 }
666 }
667 #endif
668
669 // Done
670 }
671
672 } // namespace crazy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698