| 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
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5691ed383a76db34d118f6285eb82468fb85a154
|
| --- /dev/null
|
| +++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp
|
| @@ -0,0 +1,1211 @@
|
| +// Copyright 2014 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_relocations.h"
|
| +
|
| +#include <assert.h>
|
| +#include <errno.h>
|
| +
|
| +#include "crazy_linker_debug.h"
|
| +#include "crazy_linker_elf_symbols.h"
|
| +#include "crazy_linker_elf_view.h"
|
| +#include "crazy_linker_error.h"
|
| +#include "crazy_linker_leb128.h"
|
| +#include "crazy_linker_system.h"
|
| +#include "crazy_linker_util.h"
|
| +#include "linker_phdr.h"
|
| +
|
| +#define DEBUG_RELOCATIONS 0
|
| +
|
| +#define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
|
| +#define RLOG_ERRNO(...) LOG_ERRNO_IF(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
|
| +
|
| +// 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__
|
| +
|
| +/* arm32 relocations */
|
| +#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
|
| +
|
| +#define RELATIVE_RELOCATION_CODE R_ARM_RELATIVE
|
| +
|
| +#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
|
| +
|
| +#define RELATIVE_RELOCATION_CODE R_AARCH64_RELATIVE
|
| +
|
| +#endif // __aarch64__
|
| +
|
| +#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__
|
| +
|
| +#ifdef __x86_64__
|
| +
|
| +/* x86_64 relocations */
|
| +#define R_X86_64_64 1
|
| +#define R_X86_64_PC32 2
|
| +#define R_X86_64_GLOB_DAT 6
|
| +#define R_X86_64_JMP_SLOT 7
|
| +#define R_X86_64_RELATIVE 8
|
| +
|
| +#endif // __x86_64__
|
| +
|
| +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(ELF::Word 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 __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:
|
| + 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 __x86_64__
|
| + case R_X86_64_JMP_SLOT:
|
| + case R_X86_64_GLOB_DAT:
|
| + case R_X86_64_64:
|
| + return RELOCATION_TYPE_ABSOLUTE;
|
| +
|
| + case R_X86_64_RELATIVE:
|
| + return RELOCATION_TYPE_RELATIVE;
|
| +
|
| + case R_X86_64_PC32:
|
| + return RELOCATION_TYPE_PC_RELATIVE;
|
| +#endif
|
| +
|
| +#ifdef __mips__
|
| + case R_MIPS_REL32:
|
| + return RELOCATION_TYPE_RELATIVE;
|
| +#endif
|
| +
|
| + default:
|
| + return RELOCATION_TYPE_UNKNOWN;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +bool ElfRelocations::Init(const ElfView* view, Error* error) {
|
| + // Save these for later.
|
| + phdr_ = view->phdr();
|
| + 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());
|
| +
|
| + const ELF::Addr tag = dyn.GetTag();
|
| + switch (tag) {
|
| + case DT_PLTREL:
|
| + 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_ = dyn_addr;
|
| + break;
|
| + case DT_PLTRELSZ:
|
| + plt_relocations_size_ = dyn_value;
|
| + RLOG(" DT_PLTRELSZ size=%d\n", dyn_value);
|
| + break;
|
| + case DT_RELA:
|
| + case DT_REL:
|
| + 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:
|
| + 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_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.
|
| + RLOG(" DT_PLTGOT addr=%p\n", dyn_addr);
|
| + plt_got_ = reinterpret_cast<ELF::Addr*>(dyn_addr);
|
| + break;
|
| + 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_symtab_count_ = dyn_value;
|
| + break;
|
| +
|
| + case DT_MIPS_LOCAL_GOTNO:
|
| + RLOG(" DT_MIPS_LOCAL_GOTNO value=%d\n", dyn_value);
|
| + mips_local_got_count_ = dyn_value;
|
| + break;
|
| +
|
| + case DT_MIPS_GOTSYM:
|
| + RLOG(" DT_MIPS_GOTSYM value=%d\n", dyn_value);
|
| + mips_gotsym_ = dyn_value;
|
| + break;
|
| +#endif
|
| + default:
|
| + ;
|
| + }
|
| + }
|
| +
|
| + if (has_rel_relocations && has_rela_relocations) {
|
| + *error = "Combining relocations with and without addends is not "
|
| + "currently supported";
|
| + return false;
|
| + }
|
| +
|
| + // If DT_PLTREL did not explicitly assign relocations_type_, set it
|
| + // here based on the type of relocations found.
|
| + if (relocations_type_ != DT_REL && relocations_type_ != DT_RELA) {
|
| + if (has_rel_relocations)
|
| + relocations_type_ = DT_REL;
|
| + else if (has_rela_relocations)
|
| + relocations_type_ = DT_RELA;
|
| + }
|
| +
|
| + if (relocations_type_ == DT_REL && has_rela_relocations) {
|
| + *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 relocations without addends in dyn section, "
|
| + "but DT_PLTREL is DT_RELA";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
|
| + SymbolResolver* resolver,
|
| + Error* error) {
|
| + LOG("%s: Enter\n", __FUNCTION__);
|
| +
|
| + 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 (!ApplyAndroidRelocations(symbols, resolver, error))
|
| + return false;
|
| +
|
| + if (relocations_type_ == DT_REL) {
|
| + if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(relocations_),
|
| + relocations_size_ / sizeof(ELF::Rel),
|
| + symbols,
|
| + resolver,
|
| + error))
|
| + return false;
|
| + if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(plt_relocations_),
|
| + plt_relocations_size_ / sizeof(ELF::Rel),
|
| + symbols,
|
| + resolver,
|
| + error))
|
| + return false;
|
| + }
|
| +
|
| + if (relocations_type_ == DT_RELA) {
|
| + if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(relocations_),
|
| + relocations_size_ / sizeof(ELF::Rela),
|
| + symbols,
|
| + resolver,
|
| + error))
|
| + return false;
|
| + if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(plt_relocations_),
|
| + plt_relocations_size_ / sizeof(ELF::Rela),
|
| + symbols,
|
| + resolver,
|
| + error))
|
| + return false;
|
| + }
|
| +
|
| +#ifdef __mips__
|
| + if (!RelocateMipsGot(symbols, resolver, error))
|
| + return false;
|
| +#endif
|
| +
|
| + if (has_text_relocations_) {
|
| + if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) {
|
| + error->Format("Can't reprotect loadable segments: %s", strerror(errno));
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + LOG("%s: Done\n", __FUNCTION__);
|
| + return true;
|
| +}
|
| +
|
| +// 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) { }
|
| +
|
| + 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);
|
| + }
|
| +
|
| + private:
|
| + bool hasFlag(size_t flag) const { return (flags_ & flag) != 0; }
|
| +
|
| + 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;
|
| +
|
| + const size_t flags_;
|
| +};
|
| +
|
| +bool ElfRelocations::ForEachAndroidRelocation(RelocationHandler handler,
|
| + void* opaque) {
|
| + // Skip over the "APS2" signature.
|
| + Sleb128Decoder decoder(android_relocations_ + 4,
|
| + android_relocations_size_ - 4);
|
| +
|
| + // 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());
|
| +
|
| + // 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();
|
| +
|
| + // 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;
|
| + }
|
| +
|
| + relocations_handled++;
|
| + }
|
| + }
|
| +
|
| + LOG("%s: relocations_handled=%d\n", __FUNCTION__, relocations_handled);
|
| + return true;
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +// 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;
|
| +
|
| + // Check for an initial APS2 Android packed relocations header.
|
| + return (android_relocations[0] == 'A' &&
|
| + android_relocations[1] == 'P' &&
|
| + android_relocations[2] == 'S' &&
|
| + android_relocations[3] == '2');
|
| +}
|
| +
|
| +// 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;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// Args for ApplyAndroidRelocation handler function.
|
| +struct ApplyAndroidRelocationArgs {
|
| + ELF::Addr relocations_type;
|
| + const ElfSymbols* symbols;
|
| + ElfRelocations::SymbolResolver* resolver;
|
| + Error* error;
|
| +};
|
| +
|
| +// 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);
|
| + }
|
| +
|
| + if (relocations_type == DT_RELA)
|
| + return relocations->ApplyRelaReloc(relocation, symbols, resolver, error);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ElfRelocations::ApplyAndroidRelocations(const ElfSymbols* symbols,
|
| + SymbolResolver* resolver,
|
| + Error* error) {
|
| + if (!android_relocations_)
|
| + return true;
|
| +
|
| + if (!IsValidAndroidPackedRelocations(android_relocations_,
|
| + android_relocations_size_))
|
| + return false;
|
| +
|
| + ApplyAndroidRelocationArgs args;
|
| + args.relocations_type = relocations_type_;
|
| + args.symbols = symbols;
|
| + args.resolver = resolver;
|
| + args.error = error;
|
| + return ForEachAndroidRelocation(&ApplyAndroidRelocation, &args);
|
| +}
|
| +
|
| +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;
|
| +
|
| + 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__
|
| +
|
| +#ifdef __x86_64__
|
| + case R_X86_64_JMP_SLOT:
|
| + *target = sym_addr + addend;
|
| + break;
|
| +
|
| + case R_X86_64_GLOB_DAT:
|
| + *target = sym_addr + addend;
|
| + break;
|
| +
|
| + case R_X86_64_RELATIVE:
|
| + if (rela_symbol) {
|
| + *error = "Invalid relative relocation with symbol";
|
| + return false;
|
| + }
|
| + *target = load_bias_ + addend;
|
| + break;
|
| +
|
| + case R_X86_64_64:
|
| + *target = sym_addr + addend;
|
| + break;
|
| +
|
| + case R_X86_64_PC32:
|
| + *target = sym_addr + (addend - reloc);
|
| + break;
|
| +#endif // __x86_64__
|
| +
|
| + default:
|
| + error->Format("Invalid relocation type (%d)", rela_type);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +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);
|
| +
|
| + 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::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,
|
| + 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++) {
|
| + RLOG(" Relocation %d of %d:\n", rel_n + 1, rel_count);
|
| +
|
| + if (!ApplyRelReloc(rel, symbols, resolver, error))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela,
|
| + 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);
|
| +
|
| + 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;
|
| +
|
| + 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 ApplyResolvedRelaReloc(rela, sym_addr, resolved, error);
|
| +}
|
| +
|
| +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);
|
| +
|
| + if (!rela)
|
| + return true;
|
| +
|
| + for (size_t rel_n = 0; rel_n < rela_count; rela++, rel_n++) {
|
| + RLOG(" Relocation %d of %d:\n", rel_n + 1, rela_count);
|
| +
|
| + if (!ApplyRelaReloc(rela, symbols, resolver, error))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +#ifdef __mips__
|
| +bool ElfRelocations::RelocateMipsGot(const ElfSymbols* symbols,
|
| + SymbolResolver* resolver,
|
| + Error* error) {
|
| + if (!plt_got_)
|
| + return true;
|
| +
|
| + // Handle the local GOT entries.
|
| + // This mimics what the system linker does.
|
| + // Note from the system linker:
|
| + // got[0]: lazy resolver function address.
|
| + // got[1]: may be used for a GNU extension.
|
| + // Set it to a recognizable address in case someone calls it
|
| + // (should be _rtld_bind_start).
|
| + ELF::Addr* got = plt_got_;
|
| + got[0] = 0xdeadbeef;
|
| + if (got[1] & 0x80000000)
|
| + got[1] = 0xdeadbeef;
|
| +
|
| + for (ELF::Addr n = 2; n < mips_local_got_count_; ++n)
|
| + got[n] += load_bias_;
|
| +
|
| + // Handle the global GOT entries.
|
| + got += mips_local_got_count_;
|
| + for (size_t idx = mips_gotsym_; idx < mips_symtab_count_; idx++, got++) {
|
| + const char* sym_name = symbols->LookupNameById(idx);
|
| + void* sym_addr = resolver->Lookup(sym_name);
|
| + if (sym_addr) {
|
| + // Found symbol, update GOT entry.
|
| + *got = reinterpret_cast<ELF::Addr>(sym_addr);
|
| + continue;
|
| + }
|
| +
|
| + if (symbols->IsWeakById(idx)) {
|
| + // Undefined symbols are only ok if this is a weak reference.
|
| + // Update GOT entry to 0 though.
|
| + *got = 0;
|
| + continue;
|
| + }
|
| +
|
| + error->Format("Cannot locate symbol %s", sym_name);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +#endif // __mips__
|
| +
|
| +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 __x86_64__
|
| + case R_X86_64_RELATIVE:
|
| + *dst_ptr += map_delta;
|
| + break;
|
| +#endif
|
| +
|
| +#ifdef __mips__
|
| + case R_MIPS_REL32:
|
| + *dst_ptr += map_delta;
|
| + break;
|
| +#endif
|
| + default:
|
| + ;
|
| + }
|
| +}
|
| +
|
| +void ElfRelocations::AdjustAndroidRelocation(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);
|
| +}
|
| +
|
| +// Args for ApplyAndroidRelocation handler function.
|
| +struct RelocateAndroidRelocationArgs {
|
| + size_t src_addr;
|
| + size_t dst_addr;
|
| + size_t map_addr;
|
| + size_t size;
|
| +};
|
| +
|
| +// 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->AdjustAndroidRelocation(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);
|
| +}
|
| +
|
| +template<typename Rel>
|
| +void ElfRelocations::RelocateRelocations(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;
|
| +
|
| + // Ignore PLT relocations, which all target symbols (ignored here).
|
| + const Rel* rel = reinterpret_cast<Rel*>(relocations_);
|
| + const size_t relocations_count = relocations_size_ / sizeof(Rel);
|
| + const Rel* rel_limit = rel + relocations_count;
|
| +
|
| + 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);
|
| + }
|
| +}
|
| +
|
| +template void ElfRelocations::RelocateRelocations<ELF::Rel>(
|
| + size_t src_addr, size_t dst_addr, size_t map_addr, size_t size);
|
| +
|
| +template void ElfRelocations::RelocateRelocations<ELF::Rela>(
|
| + size_t src_addr, size_t dst_addr, size_t map_addr, size_t size);
|
| +
|
| +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 android relocations.
|
| + RelocateAndroidRelocations(src_addr, dst_addr, map_addr, size);
|
| +
|
| + // Relocate relocations.
|
| + if (relocations_type_ == DT_REL)
|
| + RelocateRelocations<ELF::Rel>(src_addr, dst_addr, map_addr, size);
|
| +
|
| + if (relocations_type_ == DT_RELA)
|
| + RelocateRelocations<ELF::Rela>(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) {
|
| + for (ELF::Addr n = 2; n < mips_local_got_count_; ++n) {
|
| + size_t got_addr = reinterpret_cast<size_t>(&got[n]);
|
| + if (got_addr < src_addr || got_addr >= src_addr + size)
|
| + continue;
|
| + ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(got_addr + dst_delta);
|
| + *dst_ptr += map_delta;
|
| + }
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +} // namespace crazy
|
|
|