blob: ba5d798ef63102e8a618d3540baba4218f401581 [file] [log] [blame]
// 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 <memory>
#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_system.h"
#include "crazy_linker_util.h"
#include "linker_phdr.h"
#include "linker_reloc_iterators.h"
#include "linker_sleb128.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
// Careful: the Android <elf.h> defines these with value corresponding to
// DT_ANDROID_RELRxx below, so we undefine them, just in case. The DT_RELRxx
// values corresponds to what lld generates by default with
// '--pack-dyn-relocs=relr'.
//
// For more details, see https://reviews.llvm.org/D48247
#undef DT_RELR
#define DT_RELR 0x24
#undef DT_RELRSZ
#define DT_RELRSZ 0x23
#undef DT_RELRENT
#define DT_RELRENT 0x25
// NOTE: The Android system linker only supports the DT_ANDROID_RELRxx entries
// but their content is exactly equivalent to the DT_RELRxx ones. One can tell
// lld to use the Android values with '--use-android-relr-tags'.
//
// The crazy linker supports both format, because it's essentially free :)
#ifndef DT_ANDROID_RELR
#define DT_ANDROID_RELR 0x6fffe000
#endif
#ifndef DT_ANDROID_RELRSZ
#define DT_ANDROID_RELRSZ 0x6fffe001
#endif
#ifndef DT_ANDROID_RELRENT
#define DT_ANDROID_RELRENT 0x6fffe003
#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
ElfRelocations::ElfRelocations() = default;
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();
// 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) {
#if defined(USE_RELA)
case DT_REL:
case DT_RELSZ:
case DT_ANDROID_REL:
case DT_ANDROID_RELSZ:
#else
case DT_RELA:
case DT_RELASZ:
case DT_ANDROID_RELA:
case DT_ANDROID_RELASZ:
#endif
LOG("unsupported relocation type");
*error = "Relocation for wrong architecture";
return false;
case DT_PLTREL:
RLOG(" DT_PLTREL value=%d\n", dyn_value);
#if defined(USE_RELA)
if (dyn_value != DT_RELA) {
RLOG("unsupported DT_PLTREL in \"%s\"; expected DT_RELA");
return false;
}
#else
if (dyn_value != DT_REL) {
RLOG("unsupported DT_PLTREL in \"%s\"; expected DT_REL");
return false;
}
#endif
break;
case DT_JMPREL:
RLOG(" DT_JMPREL addr=%p\n", dyn_addr - load_bias_);
plt_relocations_ = dyn_addr;
break;
case DT_PLTRELSZ:
plt_relocations_size_ = dyn_value;
RLOG(" DT_PLTRELSZ size=%d\n", dyn_value);
break;
#if defined(USE_RELA)
case DT_RELA:
#else
case DT_REL:
#endif
RLOG(" %s addr=%p\n", (tag == DT_RELA) ? "DT_RELA" : "DT_REL",
dyn_addr - load_bias_);
if (relocations_) {
*error = "Unsupported DT_RELA/DT_REL combination in dynamic section";
return false;
}
relocations_ = dyn_addr;
break;
#if defined(USE_RELA)
case DT_RELASZ:
#else
case DT_RELSZ:
#endif
RLOG(" %s size=%d\n", (tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ",
dyn_value);
relocations_size_ = dyn_value;
break;
#if defined(USE_RELA)
case DT_ANDROID_RELA:
#else
case DT_ANDROID_REL:
#endif
RLOG(" %s addr=%p\n",
(tag == DT_ANDROID_REL) ? "DT_ANDROID_REL" : "DT_ANDROID_RELA",
dyn_addr - load_bias_);
if (android_relocations_) {
*error = "Multiple DT_ANDROID_* sections defined.";
return false;
}
android_relocations_ = reinterpret_cast<uint8_t*>(dyn_addr);
break;
#if defined(USE_RELA)
case DT_ANDROID_RELASZ:
#else
case DT_ANDROID_RELSZ:
#endif
RLOG(" %s size=%d\n",
(tag == DT_ANDROID_RELASZ) ? "DT_ANDROID_RELASZ"
: "DT_ANDROID_RELSZ",
dyn_value);
android_relocations_size_ = dyn_value;
break;
case DT_RELR:
case DT_ANDROID_RELR:
RLOG(" DT_RELR\n");
relr_.SetAddress(dyn_addr);
break;
case DT_ANDROID_RELRSZ:
case DT_RELRSZ:
relr_.SetSize(dyn_value);
RLOG(" DT_RELSZ size=%d\n", dyn_value);
break;
case DT_RELRENT:
case DT_ANDROID_RELRENT:
if (dyn_value != sizeof(ELF::Relr)) {
RLOG("Invalid RELR entry size (%d, expected %d)",
static_cast<int>(dyn_value),
static_cast<int>(sizeof(ELF::Relr)));
*error = "Invalid DT_RELRENT value";
return false;
}
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 - load_bias_);
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:
;
}
}
return true;
}
bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
SymbolResolver* resolver,
Error* error) {
LOG("Enter");
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;
relr_.Apply(load_bias_);
if (!ApplyRelocs(reinterpret_cast<rel_t*>(relocations_),
relocations_size_ / sizeof(rel_t), symbols, resolver, error))
return false;
if (!ApplyRelocs(reinterpret_cast<rel_t*>(plt_relocations_),
plt_relocations_size_ / sizeof(rel_t), 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("Done");
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(kRelocationGroupedByInfoFlag);
}
bool is_relocation_grouped_by_offset_delta() const {
return hasFlag(kRelocationGroupedByOffsetDeltaFlag);
}
bool is_relocation_grouped_by_addend() const {
return hasFlag(kRelocationGroupedByAddendFlag);
}
bool is_relocation_group_has_addend() const {
return hasFlag(kRelocationGroupHasAddendFlag);
}
private:
bool hasFlag(size_t flag) const { return (flags_ & flag) != 0; }
static const size_t kRelocationGroupedByInfoFlag = 1 << 0;
static const size_t kRelocationGroupedByOffsetDeltaFlag = 1 << 1;
static const size_t kRelocationGroupedByAddendFlag = 1 << 2;
static const size_t kRelocationGroupHasAddendFlag = 1 << 3;
const size_t flags_;
};
template <typename ElfRelIteratorT>
bool ElfRelocations::ForEachAndroidRelocationHelper(
ElfRelIteratorT&& rel_iterator,
RelocationHandler handler,
void* opaque) {
size_t relocations_handled = 0;
while (rel_iterator.has_next()) {
const auto rel = rel_iterator.next();
if (rel == nullptr) {
LOG("failed to parse relocation %d", relocations_handled);
return false;
}
// 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, rel, opaque)) {
LOG("failed handling relocation %d", relocations_handled);
return false;
}
relocations_handled++;
}
LOG("relocations_handled=%d", relocations_handled);
return true;
}
bool ElfRelocations::ForEachAndroidRelocation(RelocationHandler handler,
void* opaque) {
// Skip over the "APS2" signature.
const uint8_t* packed_relocs = android_relocations_ + 4;
const size_t packed_relocs_size = android_relocations_size_ - 4;
return ForEachAndroidRelocationHelper(
packed_reloc_iterator<sleb128_decoder>(
sleb128_decoder(packed_relocs, packed_relocs_size)),
handler, opaque);
}
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');
}
} // namespace
// Args for ApplyAndroidRelocation handler function.
struct ApplyAndroidRelocationArgs {
const ElfSymbols* symbols;
ElfRelocations::SymbolResolver* resolver;
Error* error;
};
// Static ForEachAndroidRelocation() handler.
bool ElfRelocations::ApplyAndroidRelocation(ElfRelocations* relocations,
const rel_t* relocation,
void* opaque) {
// Unpack args from opaque.
ApplyAndroidRelocationArgs* args =
reinterpret_cast<ApplyAndroidRelocationArgs*>(opaque);
const ElfSymbols* symbols = args->symbols;
ElfRelocations::SymbolResolver* resolver = args->resolver;
Error* error = args->error;
return relocations->ApplyReloc(relocation, symbols, resolver, error);
}
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.symbols = symbols;
args.resolver = resolver;
args.error = error;
return ForEachAndroidRelocation(&ApplyAndroidRelocation, &args);
}
#if defined(USE_RELA)
bool ElfRelocations::ApplyResolvedReloc(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;
}
#else
bool ElfRelocations::ApplyResolvedReloc(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;
}
#endif // defined(USE_RELA)
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("symbol %s resolved to %p", 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("weak reference to unresolved symbol %s", 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::ApplyReloc(const rel_t* 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(" offset=%p type=%d reloc=%p symbol=%d\n", rel->r_offset, rel_type,
reloc, 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 ApplyResolvedReloc(rel, sym_addr, resolved, error);
}
bool ElfRelocations::ApplyRelocs(const rel_t* rel,
size_t rel_count,
const ElfSymbols* symbols,
SymbolResolver* resolver,
Error* error) {
RLOG("rel=%p rel_count=%d", 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 (!ApplyReloc(rel, 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 rel_t* 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 rel_t* 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;
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);
}
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_t* rel = reinterpret_cast<rel_t*>(relocations_);
const size_t relocations_count = relocations_size_ / sizeof(rel_t);
const rel_t* 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);
}
}
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.
RelocateRelocations(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