diff options
author | Andrew Hsieh <andrewhsieh@google.com> | 2014-06-10 03:05:23 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-06-10 03:05:23 +0000 |
commit | ac7003e419ed3b02df456a73f707acd7e26f5383 (patch) | |
tree | 5668e1e71c3314288cf62bb145e347be321a6005 /sources/android/crazy_linker | |
parent | 48a0b8e28d69b73b53f1687e501005b9a346c098 (diff) | |
parent | ea353ff7717f48322409a1c4b7f4468085e20055 (diff) | |
download | ndk-ac7003e419ed3b02df456a73f707acd7e26f5383.tar.gz |
Merge "crazy_linker: Add support for arm64."
Diffstat (limited to 'sources/android/crazy_linker')
12 files changed, 580 insertions, 224 deletions
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp index bcd2bea11..79e58bf1c 100644 --- a/sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp +++ b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp @@ -33,6 +33,7 @@ // Processor-specific relocation types supported by the linker. #ifdef __arm__ +/* arm32 relocations */ #define R_ARM_ABS32 2 #define R_ARM_REL32 3 #define R_ARM_GLOB_DAT 21 @@ -42,6 +43,17 @@ #endif // __arm__ +#ifdef __aarch64__ + +/* arm64 relocations */ +#define R_AARCH64_ABS64 257 +#define R_AARCH64_COPY 1024 +#define R_AARCH64_GLOB_DAT 1025 +#define R_AARCH64_JUMP_SLOT 1026 +#define R_AARCH64_RELATIVE 1027 + +#endif // __aarch64__ + #ifdef __i386__ /* i386 relocations */ @@ -67,7 +79,7 @@ enum RelocationType { }; // Convert an ELF relocation type info a RelocationType value. -RelocationType GetRelocationType(unsigned r_type) { +RelocationType GetRelocationType(ELF::Word r_type) { switch (r_type) { #ifdef __arm__ case R_ARM_JUMP_SLOT: @@ -83,6 +95,19 @@ RelocationType GetRelocationType(unsigned r_type) { return RELOCATION_TYPE_COPY; #endif +#ifdef __aarch64__ + case R_AARCH64_JUMP_SLOT: + case R_AARCH64_GLOB_DAT: + case R_AARCH64_ABS64: + return RELOCATION_TYPE_ABSOLUTE; + + case R_AARCH64_RELATIVE: + return RELOCATION_TYPE_RELATIVE; + + case R_AARCH64_COPY: + return RELOCATION_TYPE_COPY; +#endif + #ifdef __i386__ case R_386_JMP_SLOT: case R_386_GLOB_DAT: @@ -114,49 +139,71 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) { phdr_count_ = view->phdr_count(); load_bias_ = view->load_bias(); + // We handle only Rel or Rela, but not both. If DT_RELA or DT_RELASZ + // then we require DT_PLTREL to agree. + bool has_rela_relocations = false; + bool has_rel_relocations = false; + // Parse the dynamic table. ElfView::DynamicIterator dyn(view); for (; dyn.HasNext(); dyn.GetNext()) { ELF::Addr dyn_value = dyn.GetValue(); uintptr_t dyn_addr = dyn.GetAddress(view->load_bias()); - switch (dyn.GetTag()) { + const ELF::Addr tag = dyn.GetTag(); + switch (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\n"); - if (dyn_value != DT_REL) { - *error = "Unsupported DT_RELA entry in dynamic section"; + RLOG(" DT_PLTREL value=%d\n", dyn_value); + if (dyn_value != DT_REL && dyn_value != DT_RELA) { + *error = "Invalid DT_PLTREL value in dynamic section"; return false; } + relocations_type_ = dyn_value; break; case DT_JMPREL: RLOG(" DT_JMPREL addr=%p\n", dyn_addr); - plt_relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr); + plt_relocations_ = dyn_addr; break; case DT_PLTRELSZ: - plt_relocations_count_ = dyn_value / sizeof(ELF::Rel); - RLOG(" DT_PLTRELSZ size=%d count=%d\n", - dyn_value, - plt_relocations_count_); + plt_relocations_size_ = dyn_value; + RLOG(" DT_PLTRELSZ size=%d\n", dyn_value); break; + case DT_RELA: case DT_REL: - RLOG(" DT_REL addr=%p\n", dyn_addr); - relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr); + RLOG(" %s addr=%p\n", + (tag == DT_RELA) ? "DT_RELA" : "DT_REL", + dyn_addr); + if (relocations_) { + *error = "Unsupported DT_RELA/DT_REL combination in dynamic section"; + return false; + } + relocations_ = dyn_addr; + if (tag == DT_RELA) + has_rela_relocations = true; + else + has_rel_relocations = true; break; + case DT_RELASZ: case DT_RELSZ: - relocations_count_ = dyn_value / sizeof(ELF::Rel); - RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, relocations_count_); + RLOG(" %s size=%d\n", + (tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ", + dyn_addr); + if (relocations_size_) { + *error = "Unsupported DT_RELASZ/DT_RELSZ combination in dyn section"; + return false; + } + relocations_size_ = dyn_value; + if (tag == DT_RELASZ) + has_rela_relocations = true; + else + has_rel_relocations = true; break; case DT_PLTGOT: // Only used on MIPS currently. Could also be used on other platforms // when lazy binding (i.e. RTLD_LAZY) is implemented. RLOG(" DT_PLTGOT addr=%p\n", dyn_addr); - plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr); + plt_got_ = reinterpret_cast<ELF::Addr*>(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; @@ -195,6 +242,20 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) { } } + if (relocations_type_ != DT_REL && relocations_type_ != DT_RELA) { + *error = "Unsupported or missing DT_PLTREL in dynamic section"; + return false; + } + + if (relocations_type_ == DT_REL && has_rela_relocations) { + *error = "Found DT_RELA in dyn section, but DT_PLTREL is DT_REL"; + return false; + } + if (relocations_type_ == DT_RELA && has_rel_relocations) { + *error = "Found DT_REL in dyn section, but DT_PLTREL is DT_RELA"; + return false; + } + return true; } @@ -210,14 +271,34 @@ bool ElfRelocations::ApplyAll(const ElfSymbols* symbols, } } - if (!ApplyRelocs(plt_relocations_, - plt_relocations_count_, - symbols, - resolver, - error) || - !ApplyRelocs( - relocations_, relocations_count_, symbols, resolver, error)) { - return false; + if (relocations_type_ == DT_REL) { + if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(plt_relocations_), + plt_relocations_size_ / sizeof(ELF::Rel), + symbols, + resolver, + error)) + return false; + if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(relocations_), + relocations_size_ / sizeof(ELF::Rel), + symbols, + resolver, + error)) + return false; + } + + else if (relocations_type_ == DT_RELA) { + if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(plt_relocations_), + plt_relocations_size_ / sizeof(ELF::Rela), + symbols, + resolver, + error)) + return false; + if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(relocations_), + relocations_size_ / sizeof(ELF::Rela), + symbols, + resolver, + error)) + return false; } #ifdef __mips__ @@ -236,19 +317,249 @@ bool ElfRelocations::ApplyAll(const ElfSymbols* symbols, return true; } -bool ElfRelocations::ApplyRelocs(const ELF::Rel* rel, - size_t rel_count, - const ElfSymbols* symbols, - SymbolResolver* resolver, - Error* error) { +bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela, + ELF::Addr sym_addr, + bool resolved CRAZY_UNUSED, + Error* error) { + const ELF::Word rela_type = ELF_R_TYPE(rela->r_info); + const ELF::Word CRAZY_UNUSED rela_symbol = ELF_R_SYM(rela->r_info); + const ELF::Sword CRAZY_UNUSED addend = rela->r_addend; + + const ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_); + + RLOG(" rela reloc=%p offset=%p type=%d addend=%p\n", + reloc, + rela->r_offset, + rela_type, + addend); + + // Apply the relocation. + ELF::Addr* CRAZY_UNUSED target = reinterpret_cast<ELF::Addr*>(reloc); + switch (rela_type) { +#ifdef __aarch64__ + case R_AARCH64_JUMP_SLOT: + RLOG(" R_AARCH64_JUMP_SLOT target=%p addr=%p\n", + target, + sym_addr + addend); + *target = sym_addr + addend; + break; + + case R_AARCH64_GLOB_DAT: + RLOG(" R_AARCH64_GLOB_DAT target=%p addr=%p\n", + target, + sym_addr + addend); + *target = sym_addr + addend; + break; + + case R_AARCH64_ABS64: + RLOG(" R_AARCH64_ABS64 target=%p (%p) addr=%p\n", + target, + *target, + sym_addr + addend); + *target += sym_addr + addend; + break; + + case R_AARCH64_RELATIVE: + RLOG(" R_AARCH64_RELATIVE target=%p (%p) bias=%p\n", + target, + *target, + load_bias_ + addend); + if (__builtin_expect(rela_symbol, 0)) { + *error = "Invalid relative relocation with symbol"; + return false; + } + *target = load_bias_ + addend; + break; + + case R_AARCH64_COPY: + // NOTE: These relocations are forbidden in shared libraries. + RLOG(" R_AARCH64_COPY\n"); + *error = "Invalid R_AARCH64_COPY relocation in shared library"; + return false; +#endif // __aarch64__ + + default: + error->Format("Invalid relocation type (%d)", rela_type); + return false; + } + + return true; +} + +bool ElfRelocations::ApplyRelReloc(const ELF::Rel* rel, + ELF::Addr sym_addr, + bool resolved CRAZY_UNUSED, + Error* error) { + const ELF::Word rel_type = ELF_R_TYPE(rel->r_info); + const ELF::Word CRAZY_UNUSED rel_symbol = ELF_R_SYM(rel->r_info); + + const ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_); + + RLOG(" rel reloc=%p offset=%p type=%d\n", reloc, rel->r_offset, rel_type); + + // Apply the relocation. + ELF::Addr* CRAZY_UNUSED 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; +} + +bool ElfRelocations::ResolveSymbol(ELF::Word rel_type, + ELF::Word rel_symbol, + const ElfSymbols* symbols, + SymbolResolver* resolver, + ELF::Addr reloc, + ELF::Addr* sym_addr, + Error* error) { + const char* sym_name = symbols->LookupNameById(rel_symbol); + 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); + *sym_addr = reinterpret_cast<ELF::Addr>(address); + return true; + } + + // The symbol was not found. Normally this is an error except + // if this is a weak reference. + if (!symbols->IsWeakById(rel_symbol)) { + error->Format("Could not find symbol '%s'", sym_name); + return false; + } + + 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; + return true; + } + + if (r == RELOCATION_TYPE_PC_RELATIVE) { + *sym_addr = reloc; + return true; + } + + error->Format( + "Invalid weak relocation type (%d) for unknown symbol '%s'", + r, + sym_name); + return false; +} + +bool ElfRelocations::ApplyRelRelocs(const ELF::Rel* rel, + size_t rel_count, + const ElfSymbols* symbols, + SymbolResolver* resolver, + 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); + const ELF::Word rel_type = ELF_R_TYPE(rel->r_info); + const ELF::Word 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_); @@ -263,147 +574,68 @@ bool ElfRelocations::ApplyRelocs(const ELF::Rel* rel, if (rel_type == 0) continue; - bool CRAZY_UNUSED resolved = false; + bool resolved = false; // If this is a symbolic relocation, compute the symbol's address. if (__builtin_expect(rel_symbol != 0, 0)) { - const char* sym_name = symbols->LookupNameById(rel_symbol); - 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 (!symbols->IsWeakById(rel_symbol)) { - 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; - } - } + resolved = ResolveSymbol(rel_type, + rel_symbol, + symbols, + resolver, + reloc, + &sym_addr, + error); } - // 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; + if (!ApplyRelReloc(rel, sym_addr, resolved, error)) + return false; + } - 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__ + return true; +} -#ifdef __i386__ - case R_386_JMP_SLOT: - *target = sym_addr; - break; +bool ElfRelocations::ApplyRelaRelocs(const ELF::Rela* rela, + size_t rela_count, + const ElfSymbols* symbols, + SymbolResolver* resolver, + Error* error) { + RLOG("%s: rela=%p rela_count=%d\n", __FUNCTION__, rela, rela_count); - case R_386_GLOB_DAT: - *target = sym_addr; - break; + if (!rela) + return true; - case R_386_RELATIVE: - if (rel_symbol) { - *error = "Invalid relative relocation with symbol"; - return false; - } - *target += load_bias_; - break; + for (size_t rel_n = 0; rel_n < rela_count; rela++, rel_n++) { + const ELF::Word rel_type = ELF_R_TYPE(rela->r_info); + const ELF::Word rel_symbol = ELF_R_SYM(rela->r_info); - case R_386_32: - *target += sym_addr; - break; + ELF::Addr sym_addr = 0; + ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_); + RLOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n", + rel_n + 1, + rela_count, + reloc, + rela->r_offset, + rel_type, + rel_symbol); - case R_386_PC32: - *target += (sym_addr - reloc); - break; -#endif // __i386__ + if (rel_type == 0) + continue; -#ifdef __mips__ - case R_MIPS_REL32: - if (resolved) - *target += sym_addr; - else - *target += load_bias_; - break; -#endif // __mips__ + bool resolved = false; - default: - error->Format("Invalid relocation type (%d)", rel_type); - return false; + // If this is a symbolic relocation, compute the symbol's address. + if (__builtin_expect(rel_symbol != 0, 0)) { + resolved = ResolveSymbol(rel_type, + rel_symbol, + symbols, + resolver, + reloc, + &sym_addr, + error); } + + if (!ApplyRelaReloc(rela, sym_addr, resolved, error)) + return false; } return true; @@ -457,27 +689,58 @@ bool ElfRelocations::RelocateMipsGot(const ElfSymbols* symbols, } #endif // __mips__ -void ElfRelocations::CopyAndRelocate(size_t src_addr, - size_t dst_addr, - size_t map_addr, - size_t size) { - // First, a straight copy. - ::memcpy(reinterpret_cast<void*>(dst_addr), - reinterpret_cast<void*>(src_addr), - size); +void ElfRelocations::AdjustRelocation(ELF::Word rel_type, + ELF::Addr src_reloc, + size_t dst_delta, + size_t map_delta) { + ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta); + + switch (rel_type) { +#ifdef __arm__ + case R_ARM_RELATIVE: + *dst_ptr += map_delta; + break; +#endif // __arm__ +#ifdef __aarch64__ + case R_AARCH64_RELATIVE: + *dst_ptr += map_delta; + break; +#endif // __aarch64__ + +#ifdef __i386__ + case R_386_RELATIVE: + *dst_ptr += map_delta; + break; +#endif + +#ifdef __mips__ + case R_MIPS_REL32: + *dst_ptr += map_delta; + break; +#endif + default: + ; + } +} + +void ElfRelocations::RelocateRela(size_t src_addr, + size_t dst_addr, + size_t map_addr, + size_t size) { // Add this value to each source address to get the corresponding // destination address. - size_t dst_delta = dst_addr - src_addr; - size_t map_delta = map_addr - src_addr; + const size_t dst_delta = dst_addr - src_addr; + const size_t map_delta = map_addr - src_addr; // Ignore PLT relocations, which all target symbols (ignored here). - const ELF::Rel* rel = relocations_; - const ELF::Rel* rel_limit = rel + relocations_count_; + const ELF::Rela* rel = reinterpret_cast<ELF::Rela*>(relocations_); + const size_t relocations_count = relocations_size_ / sizeof(ELF::Rela); + const ELF::Rela* rel_limit = rel + relocations_count; for (; rel < rel_limit; ++rel) { - unsigned rel_type = ELF_R_TYPE(rel->r_info); - unsigned rel_symbol = ELF_R_SYM(rel->r_info); + const ELF::Word rel_type = ELF_R_TYPE(rel->r_info); + const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info); ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_); if (rel_type == 0 || rel_symbol != 0) { @@ -490,32 +753,65 @@ void ElfRelocations::CopyAndRelocate(size_t src_addr, continue; } - ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta); + AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta); + } +} - switch (rel_type) { -#ifdef __arm__ - case R_ARM_RELATIVE: - *dst_ptr += map_delta; - break; -#endif // __arm__ +void ElfRelocations::RelocateRel(size_t src_addr, + size_t dst_addr, + size_t map_addr, + size_t size) { + // Add this value to each source address to get the corresponding + // destination address. + const size_t dst_delta = dst_addr - src_addr; + const size_t map_delta = map_addr - src_addr; -#ifdef __i386__ - case R_386_RELATIVE: - *dst_ptr += map_delta; - break; -#endif + // Ignore PLT relocations, which all target symbols (ignored here). + const ELF::Rel* rel = reinterpret_cast<ELF::Rel*>(relocations_); + const size_t relocations_count = relocations_size_ / sizeof(ELF::Rel); + const ELF::Rel* rel_limit = rel + relocations_count; -#ifdef __mips__ - case R_MIPS_REL32: - *dst_ptr += map_delta; - break; -#endif - default: - ; + for (; rel < rel_limit; ++rel) { + const ELF::Word rel_type = ELF_R_TYPE(rel->r_info); + const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info); + ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_); + + if (rel_type == 0 || rel_symbol != 0) { + // Ignore empty and symbolic relocations + continue; } + + if (src_reloc < src_addr || src_reloc >= src_addr + size) { + // Ignore entries that don't relocate addresses inside the source section. + continue; + } + + AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta); } +} + +void ElfRelocations::CopyAndRelocate(size_t src_addr, + size_t dst_addr, + size_t map_addr, + size_t size) { + // First, a straight copy. + ::memcpy(reinterpret_cast<void*>(dst_addr), + reinterpret_cast<void*>(src_addr), + size); + + // Relocate relocations. + if (relocations_type_ == DT_REL) + RelocateRel(src_addr, dst_addr, map_addr, size); + + else if (relocations_type_ == DT_RELA) + RelocateRela(src_addr, dst_addr, map_addr, size); #ifdef __mips__ + // Add this value to each source address to get the corresponding + // destination address. + const size_t dst_delta = dst_addr - src_addr; + const size_t map_delta = map_addr - src_addr; + // Only relocate local GOT entries. ELF::Addr* got = plt_got_; if (got) { @@ -528,8 +824,6 @@ void ElfRelocations::CopyAndRelocate(size_t src_addr, } } #endif - - // Done } } // namespace crazy diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h index 3973fc092..56220ce25 100644 --- a/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h +++ b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h @@ -56,11 +56,43 @@ class ElfRelocations { size_t size); private: - bool ApplyRelocs(const ELF::Rel* relocs, - size_t relocs_count, - const ElfSymbols* symbols, - SymbolResolver* resolver, - Error* error); + bool ResolveSymbol(unsigned rel_type, + unsigned rel_symbol, + const ElfSymbols* symbols, + SymbolResolver* resolver, + ELF::Addr reloc, + ELF::Addr* sym_addr, + Error* error); + bool ApplyRelaReloc(const ELF::Rela* rela, + ELF::Addr sym_addr, + bool resolved, + Error* error); + bool ApplyRelReloc(const ELF::Rel* rel, + ELF::Addr sym_addr, + bool resolved, + Error* error); + bool ApplyRelaRelocs(const ELF::Rela* relocs, + size_t relocs_count, + const ElfSymbols* symbols, + SymbolResolver* resolver, + Error* error); + bool ApplyRelRelocs(const ELF::Rel* relocs, + size_t relocs_count, + const ElfSymbols* symbols, + SymbolResolver* resolver, + Error* error); + void AdjustRelocation(ELF::Word rel_type, + ELF::Addr src_reloc, + size_t dst_delta, + size_t map_delta); + void RelocateRela(size_t src_addr, + size_t dst_addr, + size_t map_addr, + size_t size); + void RelocateRel(size_t src_addr, + size_t dst_addr, + size_t map_addr, + size_t size); #if defined(__mips__) bool RelocateMipsGot(const ElfSymbols* symbols, @@ -72,12 +104,13 @@ class ElfRelocations { size_t phdr_count_; size_t load_bias_; - const ELF::Rel* plt_relocations_; - size_t plt_relocations_count_; + ELF::Addr relocations_type_; + ELF::Addr plt_relocations_; + size_t plt_relocations_size_; ELF::Addr* plt_got_; - const ELF::Rel* relocations_; - size_t relocations_count_; + ELF::Addr relocations_; + size_t relocations_size_; #if defined(__mips__) // MIPS-specific relocation fields. diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp index 704076f18..cc2c7d21f 100644 --- a/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp +++ b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp @@ -35,7 +35,7 @@ bool ElfSymbols::Init(const ElfView* view) { case DT_HASH: LOG(" DT_HASH addr=%p\n", dyn_addr); { - ELF::Addr* data = reinterpret_cast<uintptr_t*>(dyn_addr); + ELF::Word* data = reinterpret_cast<ELF::Word*>(dyn_addr); hash_bucket_size_ = data[0]; hash_chain_size_ = data[1]; hash_bucket_ = data + 2; diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h index 9e79b22f7..a2172e96c 100644 --- a/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h +++ b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h @@ -66,9 +66,9 @@ class ElfSymbols { private: const ELF::Sym* symbol_table_; const char* string_table_; - ELF::Addr* hash_bucket_; + ELF::Word* hash_bucket_; size_t hash_bucket_size_; - ELF::Addr* hash_chain_; + ELF::Word* hash_chain_; size_t hash_chain_size_; }; diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp index 5fa16df6d..cc45931b1 100644 --- a/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp +++ b/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp @@ -8,12 +8,12 @@ namespace crazy { -bool ElfView::InitUnmapped(size_t load_address, +bool ElfView::InitUnmapped(ELF::Addr load_address, const ELF::Phdr* phdr, size_t phdr_count, Error* error) { // Compute load size and bias. - size_t min_vaddr = 0; + ELF::Addr min_vaddr = 0; load_size_ = phdr_table_get_load_size(phdr, phdr_count, &min_vaddr, NULL); if (load_size_ == 0) { *error = "Invalid program header table"; diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_view.h b/sources/android/crazy_linker/src/crazy_linker_elf_view.h index c60dd6ea4..7bb3f0a65 100644 --- a/sources/android/crazy_linker/src/crazy_linker_elf_view.h +++ b/sources/android/crazy_linker/src/crazy_linker_elf_view.h @@ -35,7 +35,7 @@ class ElfView { // appropriate values. Note that functions phdr() or dynamic() will always // return an address relative to |load_address|, even if the binary was // not loaded yet in the process. - bool InitUnmapped(size_t load_address, + bool InitUnmapped(ELF::Addr load_address, const ELF::Phdr* phdr, size_t phdr_count, Error* error); @@ -75,7 +75,7 @@ class ElfView { ELF::Addr GetValue() const { return dyn_->d_un.d_val; } ELF::Addr* GetValuePointer() const { - return const_cast<ELF::Addr*>(&dyn_->d_un.d_val); + return const_cast<ELF::Addr*>(&dyn_->d_un.d_ptr); } uintptr_t GetOffset() const { return dyn_->d_un.d_ptr; } @@ -98,8 +98,8 @@ class ElfView { size_t phdr_count_; const ELF::Dyn* dynamic_; size_t dynamic_count_; - size_t dynamic_flags_; - size_t load_address_; + ELF::Word dynamic_flags_; + ELF::Addr load_address_; size_t load_size_; size_t load_bias_; }; diff --git a/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp index 7ce869702..00f013d40 100644 --- a/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp +++ b/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp @@ -4,6 +4,7 @@ #include "crazy_linker_proc_maps.h" +#include <limits.h> #include <minitest/minitest.h> #include "crazy_linker_system_mock.h" diff --git a/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp b/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp index 406346e66..1011c39b4 100644 --- a/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp +++ b/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp @@ -8,7 +8,6 @@ #include <stdlib.h> #include <sys/mman.h> #include <elf.h> -#include <sys/exec_elf.h> #include "crazy_linker_ashmem.h" #include "crazy_linker_debug.h" diff --git a/sources/android/crazy_linker/src/crazy_linker_shared_library.h b/sources/android/crazy_linker/src/crazy_linker_shared_library.h index 8ce381e91..a8087c195 100644 --- a/sources/android/crazy_linker/src/crazy_linker_shared_library.h +++ b/sources/android/crazy_linker/src/crazy_linker_shared_library.h @@ -170,8 +170,8 @@ class SharedLibrary { ElfView view_; ElfSymbols symbols_; - size_t relro_start_; - size_t relro_size_; + ELF::Addr relro_start_; + ELF::Addr relro_size_; bool relro_used_; SharedLibrary* list_next_; @@ -205,4 +205,4 @@ class SharedLibrary { } // namespace crazy -#endif // CRAZY_LINKER_SHARED_LIBRARY_H
\ No newline at end of file +#endif // CRAZY_LINKER_SHARED_LIBRARY_H diff --git a/sources/android/crazy_linker/src/elf_traits.h b/sources/android/crazy_linker/src/elf_traits.h index 6fbe139f9..1d9c8e117 100644 --- a/sources/android/crazy_linker/src/elf_traits.h +++ b/sources/android/crazy_linker/src/elf_traits.h @@ -16,28 +16,48 @@ struct ELF { typedef Elf32_Ehdr Ehdr; typedef Elf32_Phdr Phdr; typedef Elf32_Word Word; + typedef Elf32_Sword Sword; typedef Elf32_Addr Addr; typedef Elf32_Dyn Dyn; typedef Elf32_Sym Sym; typedef Elf32_Rel Rel; + typedef Elf32_Rela Rela; typedef Elf32_auxv_t auxv_t; enum { kElfClass = ELFCLASS32 }; enum { kElfBits = 32 }; + +#ifndef ELF_R_TYPE +#define ELF_R_TYPE ELF32_R_TYPE +#endif + +#ifndef ELF_R_SYM +#define ELF_R_SYM ELF32_R_SYM +#endif }; #elif __SIZEOF_POINTER__ == 8 struct ELF { typedef Elf64_Ehdr Ehdr; typedef Elf64_Phdr Phdr; typedef Elf64_Word Word; + typedef Elf64_Sword Sword; typedef Elf64_Addr Addr; typedef Elf64_Dyn Dyn; typedef Elf64_Sym Sym; typedef Elf64_Rel Rel; + typedef Elf64_Rela Rela; typedef Elf64_auxv_t auxv_t; enum { kElfClass = ELFCLASS64 }; enum { kElfBits = 64 }; + +#ifndef ELF_R_TYPE +#define ELF_R_TYPE ELF64_R_TYPE +#endif + +#ifndef ELF_R_SYM +#define ELF_R_SYM ELF64_R_SYM +#endif }; #else #error "Unsupported target CPU bitness" @@ -49,6 +69,8 @@ struct ELF { #define ELF_MACHINE EM_386 #elif defined(__mips__) #define ELF_MACHINE EM_MIPS +#elif defined(__aarch64__) +#define ELF_MACHINE EM_AARCH64 #else #error "Unsupported target CPU architecture" #endif diff --git a/sources/android/crazy_linker/src/linker_phdr.cpp b/sources/android/crazy_linker/src/linker_phdr.cpp index c22f5a19c..1dd52fdd7 100644 --- a/sources/android/crazy_linker/src/linker_phdr.cpp +++ b/sources/android/crazy_linker/src/linker_phdr.cpp @@ -142,7 +142,7 @@ size_t phdr_table_get_load_size(const ELF::Phdr* phdr_table, size_t phdr_count, ELF::Addr* out_min_vaddr, ELF::Addr* out_max_vaddr) { - ELF::Addr min_vaddr = 0xFFFFFFFFU; + ELF::Addr min_vaddr = ~static_cast<ELF::Addr>(0); ELF::Addr max_vaddr = 0x00000000U; bool found_pt_load = false; diff --git a/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp b/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp index f9d291a98..737488114 100644 --- a/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp +++ b/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp @@ -7,9 +7,16 @@ // like __aeabi_atexit(), which are not normally returned by // a call to dlsym(). +// Libc is not required to copy strings passed to putenv(). If it does +// not then env pointers become invalid when rodata is unmapped on +// library unload. To guard against this, putenv() strings are first +// strdup()'ed. This is a mild memory leak. + #include <stdlib.h> +#ifdef __arm__ extern "C" void __aeabi_atexit(void*); +#endif class A { public: @@ -17,17 +24,17 @@ class A { x_ = rand(); const char* env = getenv("TEST_VAR"); if (!env || strcmp(env, "INIT")) - putenv("TEST_VAR=LOAD_ERROR"); + putenv(strdup("TEST_VAR=LOAD_ERROR")); else - putenv("TEST_VAR=LOADED"); + putenv(strdup("TEST_VAR=LOADED")); } ~A() { const char* env = getenv("TEST_VAR"); if (!env || strcmp(env, "LOADED")) - putenv("TEST_VAR=UNLOAD_ERROR"); + putenv(strdup("TEST_VAR=UNLOAD_ERROR")); else - putenv("TEST_VAR=UNLOADED"); + putenv(strdup("TEST_VAR=UNLOADED")); } int Get() const { return x_; } |