Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relocator.cpp |
diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relocator.cpp b/third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relocator.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0236653b113b7857ac29e0b51ab9ceb65dd9783b |
--- /dev/null |
+++ b/third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relocator.cpp |
@@ -0,0 +1,430 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "crazy_linker_elf_relocator.h" |
+ |
+#include <errno.h> |
+#include <string.h> |
+ |
+#include "crazy_linker_debug.h" |
+#include "crazy_linker_util.h" |
+#include "linker_phdr.h" |
+ |
+// Set to 1 to print debug traces during relocations. |
+// Default is 0 since there are _tons_ of them. |
+#define DEBUG_RELOCATIONS 0 |
+ |
+#define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__) |
+#define RLOG_ERRNO(...) LOG_IF_ERRNO(DEBUG_RELOCATIONS, __VA_ARGS__) |
+ |
+ |
+#ifndef DF_SYMBOLIC |
+#define DF_SYMBOLIC 2 |
+#endif |
+ |
+#ifndef DF_TEXTREL |
+#define DF_TEXTREL 4 |
+#endif |
+ |
+#ifndef DT_FLAGS |
+#define DT_FLAGS 30 |
+#endif |
+ |
+// Processor-specific relocation types supported by the linker. |
+#ifdef __arm__ |
+ |
+#define R_ARM_ABS32 2 |
+#define R_ARM_REL32 3 |
+#define R_ARM_GLOB_DAT 21 |
+#define R_ARM_JUMP_SLOT 22 |
+#define R_ARM_COPY 20 |
+#define R_ARM_RELATIVE 23 |
+ |
+#endif // __arm__ |
+ |
+#ifdef __i386__ |
+ |
+/* i386 relocations */ |
+#define R_386_32 1 |
+#define R_386_PC32 2 |
+#define R_386_GLOB_DAT 6 |
+#define R_386_JMP_SLOT 7 |
+#define R_386_RELATIVE 8 |
+ |
+#endif // __i386__ |
+ |
+namespace crazy { |
+ |
+namespace { |
+ |
+// List of known relocation types the relocator knows about. |
+enum RelocationType { |
+ RELOCATION_TYPE_UNKNOWN = 0, |
+ RELOCATION_TYPE_ABSOLUTE = 1, |
+ RELOCATION_TYPE_RELATIVE = 2, |
+ RELOCATION_TYPE_PC_RELATIVE = 3, |
+ RELOCATION_TYPE_COPY = 4, |
+}; |
+ |
+// Convert an ELF relocation type info a RelocationType value. |
+RelocationType GetRelocationType(unsigned r_type) { |
+ switch (r_type) { |
+#ifdef __arm__ |
+ case R_ARM_JUMP_SLOT: |
+ case R_ARM_GLOB_DAT: |
+ case R_ARM_ABS32: |
+ return RELOCATION_TYPE_ABSOLUTE; |
+ |
+ case R_ARM_REL32: |
+ case R_ARM_RELATIVE: |
+ return RELOCATION_TYPE_RELATIVE; |
+ |
+ case R_ARM_COPY: |
+ return RELOCATION_TYPE_COPY; |
+#endif |
+ |
+#ifdef __i386__ |
+ case R_386_JMP_SLOT: |
+ case R_386_GLOB_DAT: |
+ case R_386_32: |
+ return RELOCATION_TYPE_ABSOLUTE; |
+ |
+ case R_386_RELATIVE: |
+ return RELOCATION_TYPE_RELATIVE; |
+ |
+ case R_386_PC32: |
+ return RELOCATION_TYPE_PC_RELATIVE; |
+#endif |
+ |
+#ifdef __mips__ |
+ case R_MIPS_REL32: |
+ return RELOCATION_TYPE_RELATIVE; |
+#endif |
+ |
+ default: |
+ return RELOCATION_TYPE_UNKNOWN; |
+ } |
+} |
+ |
+} // namespace |
+ |
+ElfRelocator::ElfRelocator() { |
+} |
+ |
+bool ElfRelocator::Init(const ELF::Phdr* phdr, |
+ size_t phdr_count, |
+ size_t load_bias, |
+ const ELF::Dyn* dyn, |
+ size_t count, |
+ Error* error) { |
+ phdr_ = phdr; |
+ phdr_count_ = phdr_count; |
+ load_bias_ = load_bias; |
+ |
+ LOG("%s: phdr=%p phdr_count=%d load_bias=%x dyn_table=%p dyn_count=%d\n", |
+ __FUNCTION__, phdr, (int)phdr_count, (int)load_bias, dyn, (int)count); |
+ |
+ for ( ; count > 0; dyn++, count-- ) { |
+ // Be paranoid. |
+ if (dyn->d_tag == DT_NULL) |
+ break; |
+ |
+ ELF::Addr dyn_value = dyn->d_un.d_val; |
+ uintptr_t dyn_addr = load_bias_ + dyn->d_un.d_ptr; |
+ |
+ switch (dyn->d_tag) { |
+ case DT_PLTREL: |
+ // NOTE: Yes, there is nothing to record here, the content of |
+ // plt_rel_ will come from DT_JMPREL instead. |
+ RLOG(" DT_PLTREL"); |
+ if (dyn_value != DT_REL) { |
+ *error = "Unsupported DT_RELA entry in dynamic section"; |
+ return false; |
+ } |
+ break; |
+ case DT_JMPREL: |
+ RLOG(" DT_JMPREL addr=%p\n", dyn_addr); |
+ plt_rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr); |
+ break; |
+ case DT_PLTRELSZ: |
+ plt_rel_count_ = dyn_value / sizeof(ELF::Rel); |
+ RLOG(" DT_PLTRELSZ size=%d count=%d\n", dyn_value, plt_rel_count_); |
+ break; |
+ case DT_REL: |
+ RLOG(" DT_REL addr=%p\n", dyn_addr); |
+ rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr); |
+ break; |
+ case DT_RELSZ: |
+ rel_count_ = dyn_value / sizeof(ELF::Rel); |
+ RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, rel_count_); |
+ break; |
+ case DT_PLTGOT: |
+ // NOTE from original linker: |
+ // Save this in case we decide to do lazy binding. We don't yet. |
+ RLOG(" DT_PLTGOT addr=%p\n", dyn_addr); |
+ plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr); |
+ break; |
+ case DT_RELA: |
+ *error = "Unsupported DT_RELA entry in dynamic section"; |
+ return false; |
+ case DT_TEXTREL: |
+ RLOG(" DT_TEXTREL\n"); |
+ has_text_relocations_ = true; |
+ break; |
+ case DT_SYMBOLIC: |
+ RLOG(" DT_SYMBOLIC\n"); |
+ has_symbolic_ = true; |
+ break; |
+ case DT_FLAGS: |
+ if (dyn_value & DF_TEXTREL) |
+ has_text_relocations_ = true; |
+ if (dyn_value & DF_SYMBOLIC) |
+ has_symbolic_ = true; |
+ RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n", |
+ has_text_relocations_ ? "true" : "false", |
+ has_symbolic_ ? "true" : "false"); |
+ break; |
+#if defined(__mips__) |
+ case DT_MIPS_SYMTABNO: |
+ RLOG(" DT_MIPS_SYMTABNO value=%d\n", dyn_value); |
+ mips_symtabno_ = dyn_value; |
+ break; |
+ |
+ case DT_MIPS_LOCAL_GOTNO: |
+ RLOG(" DT_MIPS_LOCAL_GOTNO value=%d\n", dyn_value); |
+ mips_local_gotno_ = dyn_value; |
+ break; |
+ |
+ case DT_MIPS_GOTSYM: |
+ RLOG(" DT_MIPS_GOTSYM value=%d\n", dyn_value); |
+ mips_gotsym_ = dyn_value; |
+ break; |
+#endif |
+ default: |
+ RLOG(" UNKNOWN tag=%d value=%08x addr=%p\n", |
+ dyn->d_tag, dyn_value, (void*)dyn_addr); |
+ ; |
+ } |
+ } |
+ LOG("%s: Done!\n", __FUNCTION__); |
+ return true; |
+} |
+ |
+bool ElfRelocator::Apply(SymbolResolver* resolver, |
+ const char* string_table, |
+ const ELF::Sym* symbol_table, |
+ Error* error) { |
+ resolver_ = resolver; |
+ string_table_ = string_table; |
+ symbol_table_ = symbol_table; |
+ |
+ LOG("%s: string_table=%p sybol_table=%p\n", __FUNCTION__, |
+ string_table, symbol_table); |
+ |
+ if (has_text_relocations_) { |
+ if (phdr_table_unprotect_segments(phdr_, |
+ phdr_count_, |
+ load_bias_) < 0) { |
+ error->Format("Can't unprotect loadable segments: %s", |
+ strerror(errno)); |
+ return false; |
+ } |
+ } |
+ |
+ if (!ApplyRelocs(plt_rel_, plt_rel_count_, error) || |
+ !ApplyRelocs(rel_, rel_count_, error)) { |
+ return false; |
+ } |
+ |
+#ifdef __mips__ |
+ if (!RelocateMipsGot(error)) |
+ return false; |
+#endif |
+ |
+ if (has_text_relocations_) { |
+ if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) { |
+ error->Format("Can't protect loadable segments: %s", |
+ strerror(errno)); |
+ return false; |
+ } |
+ } |
+ |
+ // Turn on GNU RELRO protection now. |
+ LOG("%s: Enabling GNU RELRO protection\n", __FUNCTION__); |
+ |
+ if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) { |
+ error->Format("Can't enable GNU RELRO protection: %s", |
+ strerror(errno)); |
+ return false; |
+ } |
+ |
+ LOG("%s: Done\n", __FUNCTION__); |
+ return true; |
+} |
+ |
+bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel, |
+ size_t rel_count, |
+ Error* error) { |
+ RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count); |
+ |
+ if (!rel) |
+ return true; |
+ |
+ for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) { |
+ unsigned rel_type = ELF_R_TYPE(rel->r_info); |
+ unsigned rel_symbol = ELF_R_SYM(rel->r_info); |
+ |
+ ELF::Addr sym_addr = 0; |
+ ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_); |
+ RLOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n", |
+ rel_n + 1, rel_count, reloc, rel->r_offset, rel_type, rel_symbol); |
+ |
+ if (rel_type == 0) |
+ continue; |
+ |
+ bool CRAZY_UNUSED resolved = false; |
+ |
+ // If this is a symbolic relocation, compute the symbol's address. |
+ if (__builtin_expect(rel_symbol != 0, 0)) { |
+ const char* sym_name = |
+ string_table_ + symbol_table_[rel_symbol].st_name; |
+ RLOG(" symbol name='%s'\n", sym_name); |
+ void* address = resolver_->Lookup(sym_name); |
+ if (address) { |
+ // The symbol was found, so compute its address. |
+ RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, |
+ sym_name, address); |
+ resolved = true; |
+ sym_addr = reinterpret_cast<ELF::Addr>(address); |
+ } else { |
+ // The symbol was not found. Normally this is an error except |
+ // if this is a weak reference. |
+ if (ELF_ST_BIND(symbol_table_[rel_symbol].st_info) != STB_WEAK) { |
+ error->Format("Could not find symbol '%s'", sym_name); |
+ return false; |
+ } |
+ |
+ resolved = true; |
+ RLOG("%s: weak reference to unresolved symbol %s\n", |
+ __FUNCTION__, sym_name); |
+ |
+ // IHI0044C AAELF 4.5.1.1: |
+ // Libraries are not searched to resolve weak references. |
+ // It is not an error for a weak reference to remain |
+ // unsatisfied. |
+ // |
+ // During linking, the value of an undefined weak reference is: |
+ // - Zero if the relocation type is absolute |
+ // - The address of the place if the relocation is pc-relative |
+ // - The address of nominal base address if the relocation |
+ // type is base-relative. |
+ RelocationType r = GetRelocationType(rel_type); |
+ if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE) |
+ sym_addr = 0; |
+ else if (r == RELOCATION_TYPE_PC_RELATIVE) |
+ sym_addr = reloc; |
+ else { |
+ error->Format( |
+ "Invalid weak relocation type (%d) for unknown symbol '%s'", |
+ r, sym_name); |
+ return false; |
+ } |
+ } |
+ } |
+ |
+ // Apply the relocation. |
+ ELF::Addr* target = reinterpret_cast<ELF::Addr*>(reloc); |
+ switch (rel_type) { |
+#ifdef __arm__ |
+ case R_ARM_JUMP_SLOT: |
+ RLOG(" R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr); |
+ *target = sym_addr; |
+ break; |
+ |
+ case R_ARM_GLOB_DAT: |
+ RLOG(" R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr); |
+ *target = sym_addr; |
+ break; |
+ |
+ case R_ARM_ABS32: |
+ RLOG(" R_ARM_ABS32 target=%p (%p) addr=%p\n", target, *target, sym_addr); |
+ *target += sym_addr; |
+ break; |
+ |
+ case R_ARM_REL32: |
+ RLOG(" R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n", target, *target, sym_addr, rel->r_offset); |
+ *target += sym_addr - rel->r_offset; |
+ break; |
+ |
+ case R_ARM_RELATIVE: |
+ RLOG(" R_ARM_RELATIVE target=%p (%p) bias=%p\n", target, *target, load_bias_); |
+ if (__builtin_expect(rel_symbol, 0)) { |
+ *error = "Invalid relative relocation with symbol"; |
+ return false; |
+ } |
+ *target += load_bias_; |
+ break; |
+ |
+ case R_ARM_COPY: |
+ // NOTE: These relocations are forbidden in shared libraries. |
+ // The Android linker has special code to deal with this, which |
+ // is not needed here. |
+ RLOG(" R_ARM_COPY\n"); |
+ *error = "Invalid R_ARM_COPY relocation in shared library"; |
+ return false; |
+#endif // __arm__ |
+ |
+#ifdef __i386__ |
+ case R_386_JMP_SLOT: |
+ *target = sym_addr; |
+ break; |
+ |
+ case R_386_GLOB_DAT: |
+ *target = sym_addr; |
+ break; |
+ |
+ case R_386_RELATIVE: |
+ if (rel_symbol) { |
+ *error = "Invalid relative relocation with symbol"; |
+ return false; |
+ } |
+ *target += load_bias_; |
+ break; |
+ |
+ case R_386_32: |
+ *target += sym_addr; |
+ break; |
+ |
+ case R_386_PC32: |
+ *target += (sym_addr - reloc); |
+ break; |
+#endif // __i386__ |
+ |
+#ifdef __mips__ |
+ case R_MIPS_REL32: |
+ if (resolved) |
+ *target += sym_addr; |
+ else |
+ *target += load_bias_; |
+ break; |
+#endif // __mips__ |
+ |
+ default: |
+ error->Format("Invalid relocation type (%d)", rel_type); |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
+#ifdef __mips__ |
+bool ElfRelocator::RelocateMipsGot(Error* error) { |
+ // TODO(digit): Implement this. |
+ *error = "Relocating the MIPS GOT is not implemented"; |
+ return false; |
+} |
+#endif // __mips__ |
+ |
+} // namespace crazy |