Chromium Code Reviews| Index: third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp |
| diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp |
| index b2b80c1d1e93968be5283ea7cae52f6e993989ff..23859a95ee833585b9e155b26632442284d6db25 100644 |
| --- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp |
| +++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp |
| @@ -4,6 +4,7 @@ |
| #include "crazy_linker_elf_relocations.h" |
| +#include <assert.h> |
| #include <errno.h> |
| #include "crazy_linker_debug.h" |
| @@ -32,6 +33,23 @@ |
| #define DT_FLAGS 30 |
| #endif |
| +// Extension dynamic tags for Android packed relocations. |
| +#ifndef DT_LOOS |
| +#define DT_LOOS 0x6000000d |
| +#endif |
| +#ifndef DT_ANDROID_REL |
| +#define DT_ANDROID_REL (DT_LOOS + 2) |
| +#endif |
| +#ifndef DT_ANDROID_RELSZ |
| +#define DT_ANDROID_RELSZ (DT_LOOS + 3) |
| +#endif |
| +#ifndef DT_ANDROID_RELA |
| +#define DT_ANDROID_RELA (DT_LOOS + 4) |
| +#endif |
| +#ifndef DT_ANDROID_RELASZ |
| +#define DT_ANDROID_RELASZ (DT_LOOS + 5) |
| +#endif |
| + |
| // Processor-specific relocation types supported by the linker. |
| #ifdef __arm__ |
| @@ -228,6 +246,38 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) { |
| else |
| has_rel_relocations = true; |
| break; |
| + case DT_ANDROID_RELA: |
| + case DT_ANDROID_REL: |
| + RLOG(" %s addr=%p\n", |
| + (tag == DT_ANDROID_RELA) ? "DT_ANDROID_RELA" : "DT_ANDROID_REL", |
| + dyn_addr); |
| + if (android_relocations_) { |
| + *error = "Unsupported DT_ANDROID_RELA/DT_ANDROID_REL " |
| + "combination in dynamic section"; |
| + return false; |
| + } |
| + android_relocations_ = reinterpret_cast<uint8_t*>(dyn_addr); |
| + if (tag == DT_ANDROID_RELA) |
| + has_rela_relocations = true; |
| + else |
| + has_rel_relocations = true; |
| + break; |
| + case DT_ANDROID_RELASZ: |
| + case DT_ANDROID_RELSZ: |
| + RLOG(" %s size=%d\n", |
| + (tag == DT_ANDROID_RELASZ) |
| + ? "DT_ANDROID_RELASZ" : "DT_ANDROID_RELSZ", dyn_addr); |
| + if (android_relocations_size_) { |
| + *error = "Unsupported DT_ANDROID_RELASZ/DT_ANDROID_RELSZ " |
| + "combination in dyn section"; |
| + return false; |
| + } |
| + android_relocations_size_ = dyn_value; |
| + if (tag == DT_ANDROID_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. |
| @@ -247,7 +297,7 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) { |
| has_text_relocations_ = true; |
| if (dyn_value & DF_SYMBOLIC) |
| has_symbolic_ = true; |
| - RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n", |
| + RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n", |
| has_text_relocations_ ? "true" : "false", |
| has_symbolic_ ? "true" : "false"); |
| break; |
| @@ -273,7 +323,8 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) { |
| } |
| if (has_rel_relocations && has_rela_relocations) { |
| - *error = "Combining DT_REL and DT_RELA is not currently supported"; |
| + *error = "Combining relocations with and without addends is not " |
| + "currently supported"; |
| return false; |
| } |
| @@ -287,11 +338,13 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) { |
| } |
| if (relocations_type_ == DT_REL && has_rela_relocations) { |
| - *error = "Found DT_RELA in dyn section, but DT_PLTREL is DT_REL"; |
| + *error = "Found relocations with addends 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"; |
| + *error = "Found relocations without addends in dyn section, " |
| + "but DT_PLTREL is DT_RELA"; |
| return false; |
| } |
| @@ -310,10 +363,8 @@ bool ElfRelocations::ApplyAll(const ElfSymbols* symbols, |
| } |
| } |
| -#if defined(__arm__) || defined(__aarch64__) |
| - if (!ApplyPackedRelocations(error)) |
| + if (!ApplyAndroidRelocations(symbols, resolver, error)) |
| return false; |
| -#endif |
| if (relocations_type_ == DT_REL) { |
| if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(relocations_), |
| @@ -361,114 +412,198 @@ bool ElfRelocations::ApplyAll(const ElfSymbols* symbols, |
| return true; |
| } |
| -#if defined(__arm__) || defined(__aarch64__) |
| +// Helper class for Android packed relocations. Encapsulates the packing |
| +// flags used by Android for packed relocation groups. |
| +class AndroidPackedRelocationGroupFlags { |
| + public: |
| + explicit AndroidPackedRelocationGroupFlags(size_t flags) : flags_(flags) { } |
| -void ElfRelocations::RegisterPackedRelocations(uint8_t* packed_relocations) { |
| - packed_relocations_ = packed_relocations; |
| -} |
| + bool is_relocation_grouped_by_info() const { |
| + return hasFlag(RELOCATION_GROUPED_BY_INFO_FLAG); |
| + } |
| + bool is_relocation_grouped_by_offset_delta() const { |
| + return hasFlag(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG); |
| + } |
| + bool is_relocation_grouped_by_addend() const { |
| + return hasFlag(RELOCATION_GROUPED_BY_ADDEND_FLAG); |
| + } |
| + bool is_relocation_group_has_addend() const { |
| + return hasFlag(RELOCATION_GROUP_HAS_ADDEND_FLAG); |
| + } |
| -bool ElfRelocations::ApplyPackedRel(const uint8_t* packed_relocations, |
| - Error* error) { |
| - Leb128Decoder decoder(packed_relocations); |
| - |
| - // Find the count of pairs and the start address. |
| - size_t pairs = decoder.Dequeue(); |
| - const ELF::Addr start_address = decoder.Dequeue(); |
| - |
| - // Emit initial relative relocation. |
| - ELF::Rel relocation; |
| - relocation.r_offset = start_address; |
| - relocation.r_info = ELF_R_INFO(0, RELATIVE_RELOCATION_CODE); |
| - const ELF::Addr sym_addr = 0; |
| - const bool resolved = false; |
| - if (!ApplyRelReloc(&relocation, sym_addr, resolved, error)) |
| - return false; |
| + private: |
| + bool hasFlag(size_t flag) const { return (flags_ & flag) != 0; } |
| - size_t unpacked_count = 1; |
| + static const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1 << 0; |
| + static const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 1 << 1; |
| + static const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 1 << 2; |
| + static const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 1 << 3; |
| - // Emit relocations for each count-delta pair. |
| - while (pairs) { |
| - size_t count = decoder.Dequeue(); |
| - const size_t delta = decoder.Dequeue(); |
| + const size_t flags_; |
| +}; |
| - // Emit count relative relocations with delta offset. |
| - while (count) { |
| - relocation.r_offset += delta; |
| - if (!ApplyRelReloc(&relocation, sym_addr, resolved, error)) |
| +bool ElfRelocations::ForEachAndroidRelocation(RelocationHandler handler, |
| + void* opaque) { |
| + // Skip over the "APU2" or "APS2" signature. |
| + const uint8_t* data_start = android_relocations_ + 4; |
| + const size_t data_size = android_relocations_size_ - 4; |
| + |
| + // Create the appropriate type of LEB128 decoder, and wrap it inside a |
| + // ScopedPtr for convenience. |
| + Leb128Decoder* decoder; |
| + if (android_relocations_[2] == 'U') |
| + decoder = new Uleb128Decoder(data_start, data_size); |
| + else |
| + decoder = new Sleb128Decoder(data_start, data_size); |
|
rmcilroy
2015/04/13 10:36:10
nit - add a DCHECK that android_relocations_[2] ==
simonb (inactive)
2015/04/28 14:39:41
Gone -- the android packer no longer generates uns
|
| + ScopedPtr<Leb128Decoder> scoped_decoder(decoder); |
| + |
| + // Unpacking into a relocation with addend, both for REL and RELA, is |
| + // convenient at this point. If REL, the handler needs to take care of |
| + // any conversion before use. |
| + ELF::Rela relocation; |
| + memset(&relocation, 0, sizeof(relocation)); |
| + |
| + // Read the relocation count and initial offset. |
| + const size_t relocation_count = decoder->pop_front(); |
| + relocation.r_offset = decoder->pop_front(); |
| + |
| + LOG("%s: relocation_count=%d, initial r_offset=%p\n", |
| + __FUNCTION__, |
| + relocation_count, |
| + relocation.r_offset); |
| + |
| + size_t relocations_handled = 0; |
| + while (relocations_handled < relocation_count) { |
| + // Read the start of the group header to obtain its size and flags. |
| + const size_t group_size = decoder->pop_front(); |
| + AndroidPackedRelocationGroupFlags group_flags(decoder->pop_front()); |
|
rmcilroy
2015/04/13 10:36:10
Maybe we could just package up the different group
simonb (inactive)
2015/04/28 14:39:41
I'd like to keep this code close to that of the an
rmcilroy
2015/05/01 14:15:22
Ok, SGTM.
|
| + |
| + // Read other group header fields, depending on the flags read above. |
| + size_t group_r_offset_delta = 0; |
| + if (group_flags.is_relocation_grouped_by_offset_delta()) |
| + group_r_offset_delta = decoder->pop_front(); |
| + |
| + if (group_flags.is_relocation_grouped_by_info()) |
| + relocation.r_info = decoder->pop_front(); |
| + |
| + if (group_flags.is_relocation_group_has_addend() && |
| + group_flags.is_relocation_grouped_by_addend()) |
| + relocation.r_addend += decoder->pop_front(); |
| + else if (!group_flags.is_relocation_group_has_addend()) |
| + relocation.r_addend = 0; |
| + |
| + // Expand the group into individual relocations. |
| + for (size_t group_index = 0; group_index < group_size; group_index++) { |
| + if (group_flags.is_relocation_grouped_by_offset_delta()) |
| + relocation.r_offset += group_r_offset_delta; |
| + else |
| + relocation.r_offset += decoder->pop_front(); |
| + |
| + if (!group_flags.is_relocation_grouped_by_info()) |
| + relocation.r_info = decoder->pop_front(); |
| + |
| + if (group_flags.is_relocation_group_has_addend() && |
| + !group_flags.is_relocation_grouped_by_addend()) |
| + relocation.r_addend += decoder->pop_front(); |
|
rmcilroy
2015/04/13 10:36:10
If you do the suggestion above then the AndroidRel
simonb (inactive)
2015/04/28 14:39:41
Acknowledged.
|
| + |
| + // Pass the relocation to the supplied handler function. If the handler |
| + // returns false we view this as failure and return false to our caller. |
| + if (!handler(this, &relocation, opaque)) { |
| + LOG("%s: failed handling relocation %d\n", |
| + __FUNCTION__, |
| + relocations_handled); |
| return false; |
| - unpacked_count++; |
| - count--; |
| + } |
| + |
| + relocations_handled++; |
| } |
| - pairs--; |
| } |
| - RLOG("%s: unpacked_count=%d\n", __FUNCTION__, unpacked_count); |
| + LOG("%s: relocations_handled=%d\n", __FUNCTION__, relocations_handled); |
| return true; |
| } |
| -bool ElfRelocations::ApplyPackedRela(const uint8_t* packed_relocations, |
| - Error* error) { |
| - Sleb128Decoder decoder(packed_relocations); |
| +namespace { |
|
rmcilroy
2015/04/13 10:36:10
Could we just make these private static functions
simonb (inactive)
2015/04/28 14:39:41
We could. Not sure what it would buy us though.
rmcilroy
2015/05/01 14:15:21
It just seems strange to have a mix of private fun
|
| - // Find the count of pairs. |
| - size_t pairs = decoder.Dequeue(); |
| +// Validate the Android packed relocations signature. |
| +bool IsValidAndroidPackedRelocations(const uint8_t* android_relocations, |
| + size_t android_relocations_size) { |
| + if (android_relocations_size < 4) |
| + return false; |
| - ELF::Addr offset = 0; |
| - ELF::Sxword addend = 0; |
| + // Check for an initial APU2 or APS2 Android packed relocations header. |
| + return (android_relocations[0] == 'A' && |
| + android_relocations[1] == 'P' && |
| + (android_relocations[2] == 'U' || android_relocations[2] == 'S') && |
| + android_relocations[3] == '2'); |
| +} |
| - const ELF::Addr sym_addr = 0; |
| - const bool resolved = false; |
| +// Narrow a Rela to its equivalent Rel. The r_addend field in the input |
| +// Rela must be zero. |
| +void ConvertRelaToRel(const ELF::Rela* rela, ELF::Rel* rel) { |
| + assert(rela->r_addend == 0); |
| + rel->r_offset = rela->r_offset; |
| + rel->r_info = rela->r_info; |
| +} |
| - size_t unpacked_count = 0; |
| +} // namespace |
| - // Emit relocations for each deltas pair. |
| - while (pairs) { |
| - offset += decoder.Dequeue(); |
| - addend += decoder.Dequeue(); |
| +// Args for ApplyAndroidRelocation handler function. |
| +struct ApplyAndroidRelocationArgs { |
| + ELF::Addr relocations_type; |
| + const ElfSymbols* symbols; |
| + ElfRelocations::SymbolResolver* resolver; |
| + Error* error; |
| +}; |
| - ELF::Rela relocation; |
| - relocation.r_offset = offset; |
| - relocation.r_info = ELF_R_INFO(0, RELATIVE_RELOCATION_CODE); |
| - relocation.r_addend = addend; |
| - if (!ApplyRelaReloc(&relocation, sym_addr, resolved, error)) |
| - return false; |
| - unpacked_count++; |
| - pairs--; |
| +// Static ForEachAndroidRelocation() handler. |
| +bool ElfRelocations::ApplyAndroidRelocation(ElfRelocations* relocations, |
| + const ELF::Rela* relocation, |
| + void* opaque) { |
| + // Unpack args from opaque. |
| + ApplyAndroidRelocationArgs* args = |
| + reinterpret_cast<ApplyAndroidRelocationArgs*>(opaque); |
| + const ELF::Addr relocations_type = args->relocations_type; |
| + const ElfSymbols* symbols = args->symbols; |
| + ElfRelocations::SymbolResolver* resolver = args->resolver; |
| + Error* error = args->error; |
| + |
| + // For REL relocations, convert from RELA to REL and apply the conversion. |
| + // For RELA relocations, apply RELA directly. |
| + if (relocations_type == DT_REL) { |
| + ELF::Rel converted; |
| + ConvertRelaToRel(relocation, &converted); |
| + return relocations->ApplyRelReloc(&converted, symbols, resolver, error); |
| } |
| - RLOG("%s: unpacked_count=%d\n", __FUNCTION__, unpacked_count); |
| + if (relocations_type == DT_RELA) |
| + return relocations->ApplyRelaReloc(relocation, symbols, resolver, error); |
| + |
| return true; |
|
rmcilroy
2015/04/13 10:36:10
Are non DT_REL / DT_RELA relocations allowed to go
simonb (inactive)
2015/04/28 14:39:41
Relocations are either DT_REL or DT_RELA, but cann
rmcilroy
2015/05/01 14:15:22
I see, that's fine.
|
| } |
| -bool ElfRelocations::ApplyPackedRelocations(Error* error) { |
| - if (!packed_relocations_) |
| +bool ElfRelocations::ApplyAndroidRelocations(const ElfSymbols* symbols, |
| + SymbolResolver* resolver, |
| + Error* error) { |
| + if (!android_relocations_) |
| return true; |
| - // Check for an initial APR1 header, packed relocations. |
| - if (packed_relocations_[0] == 'A' && |
| - packed_relocations_[1] == 'P' && |
| - packed_relocations_[2] == 'R' && |
| - packed_relocations_[3] == '1') { |
| - return ApplyPackedRel(packed_relocations_ + 4, error); |
| - } |
| - |
| - // Check for an initial APA1 header, packed relocations with addend. |
| - if (packed_relocations_[0] == 'A' && |
| - packed_relocations_[1] == 'P' && |
| - packed_relocations_[2] == 'A' && |
| - packed_relocations_[3] == '1') { |
| - return ApplyPackedRela(packed_relocations_ + 4, error); |
| - } |
| + if (!IsValidAndroidPackedRelocations(android_relocations_, |
| + android_relocations_size_)) |
| + return false; |
| - error->Format("Bad packed relocations ident, expected APR1 or APA1"); |
| - return false; |
| + ApplyAndroidRelocationArgs args; |
| + args.relocations_type = relocations_type_; |
| + args.symbols = symbols; |
| + args.resolver = resolver; |
| + args.error = error; |
| + return ForEachAndroidRelocation(&ApplyAndroidRelocation, &args); |
| } |
| -#endif // __arm__ || __aarch64__ |
| -bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela, |
| - ELF::Addr sym_addr, |
| - bool resolved CRAZY_UNUSED, |
| - Error* error) { |
| +bool ElfRelocations::ApplyResolvedRelaReloc(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; |
| @@ -560,10 +695,10 @@ bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela, |
| return true; |
| } |
| -bool ElfRelocations::ApplyRelReloc(const ELF::Rel* rel, |
| - ELF::Addr sym_addr, |
| - bool resolved CRAZY_UNUSED, |
| - Error* error) { |
| +bool ElfRelocations::ApplyResolvedRelReloc(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); |
| @@ -721,6 +856,43 @@ bool ElfRelocations::ResolveSymbol(ELF::Word rel_type, |
| return false; |
| } |
| +bool ElfRelocations::ApplyRelReloc(const ELF::Rel* rel, |
| + const ElfSymbols* symbols, |
| + SymbolResolver* resolver, |
| + Error* error) { |
| + 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_); |
| + RLOG(" reloc=%p offset=%p type=%d symbol=%d\n", |
| + reloc, |
| + rel->r_offset, |
| + rel_type, |
| + rel_symbol); |
| + |
| + if (rel_type == 0) |
| + return true; |
| + |
| + bool resolved = false; |
| + |
| + // If this is a symbolic relocation, compute the symbol's address. |
| + if (__builtin_expect(rel_symbol != 0, 0)) { |
| + if (!ResolveSymbol(rel_type, |
| + rel_symbol, |
| + symbols, |
| + resolver, |
| + reloc, |
| + &sym_addr, |
| + error)) { |
| + return false; |
| + } |
| + resolved = true; |
| + } |
| + |
| + return ApplyResolvedRelReloc(rel, sym_addr, resolved, error); |
| +} |
| + |
| bool ElfRelocations::ApplyRelRelocs(const ELF::Rel* rel, |
| size_t rel_count, |
| const ElfSymbols* symbols, |
| @@ -732,43 +904,50 @@ bool ElfRelocations::ApplyRelRelocs(const ELF::Rel* rel, |
| return true; |
| for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) { |
| - const ELF::Word rel_type = ELF_R_TYPE(rel->r_info); |
| - const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info); |
| + RLOG(" Relocation %d of %d:\n", rel_n + 1, rel_count); |
| - 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; |
| + if (!ApplyRelReloc(rel, symbols, resolver, error)) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| - bool resolved = false; |
| +bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela, |
|
rmcilroy
2015/04/13 10:36:10
Could you template this with ApplyRelReloc? Not su
simonb (inactive)
2015/04/28 14:39:41
Possible, but the result doesn't aid readability m
rmcilroy
2015/05/01 14:15:22
Acknowledged.
|
| + const ElfSymbols* symbols, |
| + SymbolResolver* resolver, |
| + Error* error) { |
| + const ELF::Word rel_type = ELF_R_TYPE(rela->r_info); |
| + const ELF::Word rel_symbol = ELF_R_SYM(rela->r_info); |
| - // If this is a symbolic relocation, compute the symbol's address. |
| - if (__builtin_expect(rel_symbol != 0, 0)) { |
| - if (!ResolveSymbol(rel_type, |
| - rel_symbol, |
| - symbols, |
| - resolver, |
| - reloc, |
| - &sym_addr, |
| - error)) { |
| - return false; |
| - } |
| - resolved = true; |
| - } |
| + ELF::Addr sym_addr = 0; |
| + ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_); |
| + RLOG(" reloc=%p offset=%p type=%d symbol=%d\n", |
| + reloc, |
| + rela->r_offset, |
| + rel_type, |
| + rel_symbol); |
| + |
| + if (rel_type == 0) |
| + return true; |
| - if (!ApplyRelReloc(rel, sym_addr, resolved, error)) |
| + bool resolved = false; |
| + |
| + // If this is a symbolic relocation, compute the symbol's address. |
| + if (__builtin_expect(rel_symbol != 0, 0)) { |
| + if (!ResolveSymbol(rel_type, |
| + rel_symbol, |
| + symbols, |
| + resolver, |
| + reloc, |
| + &sym_addr, |
| + error)) { |
| return false; |
| + } |
| + resolved = true; |
| } |
| - return true; |
| + return ApplyResolvedRelaReloc(rela, sym_addr, resolved, error); |
| } |
| bool ElfRelocations::ApplyRelaRelocs(const ELF::Rela* rela, |
| @@ -782,39 +961,9 @@ bool ElfRelocations::ApplyRelaRelocs(const ELF::Rela* rela, |
| return true; |
| 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); |
| - |
| - 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); |
| - |
| - if (rel_type == 0) |
| - continue; |
| - |
| - bool resolved = false; |
| + RLOG(" Relocation %d of %d:\n", rel_n + 1, rela_count); |
| - // If this is a symbolic relocation, compute the symbol's address. |
| - if (__builtin_expect(rel_symbol != 0, 0)) { |
| - if (!ResolveSymbol(rel_type, |
| - rel_symbol, |
| - symbols, |
| - resolver, |
| - reloc, |
| - &sym_addr, |
| - error)) { |
| - return false; |
| - } |
| - resolved = true; |
| - } |
| - |
| - if (!ApplyRelaReloc(rela, sym_addr, resolved, error)) |
| + if (!ApplyRelaReloc(rela, symbols, resolver, error)) |
| return false; |
| } |
| @@ -910,6 +1059,82 @@ void ElfRelocations::AdjustRelocation(ELF::Word rel_type, |
| } |
| } |
| +// Args for ApplyAndroidRelocation handler function. |
| +struct RelocateAndroidRelocationArgs { |
|
rmcilroy
2015/04/13 10:36:10
nit - move down to just above RelocateAndroidReloc
simonb (inactive)
2015/04/28 14:39:41
Done.
|
| + size_t src_addr; |
| + size_t dst_addr; |
| + size_t map_addr; |
| + size_t size; |
| +}; |
| + |
| +void ElfRelocations::RelocateAndroidReloc(const ELF::Rela* relocation, |
| + 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; |
| + |
| + const ELF::Word rel_type = ELF_R_TYPE(relocation->r_info); |
| + const ELF::Word rel_symbol = ELF_R_SYM(relocation->r_info); |
| + ELF::Addr src_reloc = |
| + static_cast<ELF::Addr>(relocation->r_offset + load_bias_); |
| + |
| + if (rel_type == 0 || rel_symbol != 0) { |
| + // Ignore empty and symbolic relocations |
| + return; |
| + } |
| + |
| + if (src_reloc < src_addr || src_reloc >= src_addr + size) { |
| + // Ignore entries that don't relocate addresses inside the source section. |
| + return; |
| + } |
| + |
| + AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta); |
| +} |
| + |
| +// Static ForEachAndroidRelocation() handler. |
| +bool ElfRelocations::RelocateAndroidRelocation(ElfRelocations* relocations, |
| + const ELF::Rela* relocation, |
| + void* opaque) { |
| + // Unpack args from opaque, to obtain addrs and size; |
| + RelocateAndroidRelocationArgs* args = |
| + reinterpret_cast<RelocateAndroidRelocationArgs*>(opaque); |
| + const size_t src_addr = args->src_addr; |
| + const size_t dst_addr = args->dst_addr; |
| + const size_t map_addr = args->map_addr; |
| + const size_t size = args->size; |
| + |
| + // Relocate the given relocation. Because the r_addend field is ignored |
| + // in relocating RELA relocations we do not need to convert from REL to |
| + // RELA and supply alternative relocator functions; instead we can work |
| + // here directly on the RELA supplied by ForEachAndroidRelocation(), even |
| + // on REL architectures. |
| + relocations->RelocateAndroidReloc(relocation, |
| + src_addr, dst_addr, map_addr, size); |
| + return true; |
| +} |
| + |
| +void ElfRelocations::RelocateAndroidRelocations(size_t src_addr, |
| + size_t dst_addr, |
| + size_t map_addr, |
| + size_t size) { |
| + if (!android_relocations_) |
| + return; |
| + |
| + assert(IsValidAndroidPackedRelocations(android_relocations_, |
| + android_relocations_size_)); |
| + |
| + RelocateAndroidRelocationArgs args; |
| + args.src_addr = src_addr; |
| + args.dst_addr = dst_addr; |
| + args.map_addr = map_addr; |
| + args.size = size; |
| + ForEachAndroidRelocation(&RelocateAndroidRelocation, &args); |
| +} |
| + |
| void ElfRelocations::RelocateRela(size_t src_addr, |
| size_t dst_addr, |
| size_t map_addr, |
| @@ -985,6 +1210,9 @@ void ElfRelocations::CopyAndRelocate(size_t src_addr, |
| reinterpret_cast<void*>(src_addr), |
| size); |
| + // Relocate android relocations. |
| + RelocateAndroidRelocations(src_addr, dst_addr, map_addr, size); |
| + |
| // Relocate relocations. |
| if (relocations_type_ == DT_REL) |
| RelocateRel(src_addr, dst_addr, map_addr, size); |