blob: 1d95058439dd488b8fe777b9f1c1d5fdb71c76ef [file] [log] [blame]
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/elf_loader/relocations.h"
#include "starboard/common/log.h"
#include "starboard/elf_loader/log.h"
namespace starboard {
namespace elf_loader {
Relocations::Relocations(Addr base_memory_address,
DynamicSection* dynamic_section,
ExportedSymbols* exported_symbols)
: base_memory_address_(base_memory_address),
dynamic_section_(dynamic_section),
plt_relocations_(0),
plt_relocations_size_(0),
plt_got_(NULL),
relocations_(0),
relocations_size_(0),
has_text_relocations_(false),
has_symbolic_(false),
exported_symbols_(exported_symbols) {}
bool Relocations::HasTextRelocations() {
return has_text_relocations_;
}
bool Relocations::InitRelocations() {
SB_DLOG(INFO) << "InitRelocations: dynamic_count="
<< dynamic_section_->GetDynamicTableSize();
const Dyn* dynamic = dynamic_section_->GetDynamicTable();
for (int i = 0; i < dynamic_section_->GetDynamicTableSize(); i++) {
Addr dyn_value = dynamic[i].d_un.d_val;
uintptr_t dyn_addr = base_memory_address_ + dynamic[i].d_un.d_ptr;
Addr tag = dynamic[i].d_tag;
SB_DLOG(INFO) << "InitRelocations: tag=" << tag;
switch (tag) {
#if defined(USE_RELA)
case DT_REL:
case DT_RELSZ:
#else
case DT_RELA:
case DT_RELASZ:
#endif
SB_LOG(ERROR) << "unsupported relocation type";
return false;
case DT_PLTREL:
SB_DLOG(INFO) << " DT_PLTREL value=" << dyn_value;
#if defined(USE_RELA)
if (dyn_value != DT_RELA) {
SB_LOG(ERROR) << "unsupported DT_PLTREL expected DT_RELA";
return false;
}
#else
if (dyn_value != DT_REL) {
SB_LOG(ERROR) << "unsupported DT_PLTREL expected DT_REL";
return false;
}
#endif
break;
case DT_JMPREL:
SB_DLOG(INFO) << " DT_JMPREL addr=0x" << std::hex
<< (dyn_addr - base_memory_address_);
plt_relocations_ = dyn_addr;
break;
case DT_PLTRELSZ:
plt_relocations_size_ = dyn_value;
SB_DLOG(INFO) << " DT_PLTRELSZ size=" << dyn_value;
break;
#if defined(USE_RELA)
case DT_RELA:
#else
case DT_REL:
#endif
SB_DLOG(INFO) << " " << ((tag == DT_RELA) ? "DT_RELA" : "DT_REL")
<< " addr=" << std::hex
<< (dyn_addr - base_memory_address_);
if (relocations_) {
SB_LOG(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
SB_DLOG(INFO) << " " << ((tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ")
<< " size=" << dyn_value;
relocations_size_ = dyn_value;
break;
case DT_RELR:
case DT_RELRSZ:
SB_LOG(ERROR) << " DT_RELRSZ NOT IMPLEMENTED";
break;
case DT_RELRENT:
case DT_PLTGOT:
SB_DLOG(INFO) << "DT_PLTGOT addr=0x" << std::hex
<< (dyn_addr - base_memory_address_);
plt_got_ = reinterpret_cast<Addr*>(dyn_addr);
break;
case DT_TEXTREL:
SB_DLOG(INFO) << " DT_TEXTREL";
has_text_relocations_ = true;
break;
case DT_SYMBOLIC:
SB_DLOG(INFO) << " DT_SYMBOLIC";
has_symbolic_ = true;
break;
case DT_FLAGS:
if (dyn_value & DF_TEXTREL)
has_text_relocations_ = true;
if (dyn_value & DF_SYMBOLIC)
has_symbolic_ = true;
SB_DLOG(INFO) << " DT_FLAGS has_text_relocations="
<< has_text_relocations_
<< " has_symbolic=" << has_symbolic_;
break;
default:
break;
}
}
return true;
}
bool Relocations::ApplyAllRelocations() {
SB_DLOG(INFO) << "Applying regular relocations";
if (!ApplyRelocations(reinterpret_cast<rel_t*>(relocations_),
relocations_size_ / sizeof(rel_t))) {
SB_LOG(ERROR) << "regular relocations failed";
return false;
}
SB_DLOG(INFO) << "Applying PLT relocations";
if (!ApplyRelocations(reinterpret_cast<rel_t*>(plt_relocations_),
plt_relocations_size_ / sizeof(rel_t))) {
SB_LOG(ERROR) << "PLT relocations failed";
return false;
}
return true;
}
bool Relocations::ApplyRelocations(const rel_t* rel, size_t rel_count) {
SB_DLOG(INFO) << "rel=" << std::hex << rel << std::dec
<< " rel_count=" << rel_count;
if (!rel)
return true;
for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
SB_DLOG(INFO) << " Relocation " << rel_n + 1 << " of " << rel_count;
if (!ApplyRelocation(rel))
return false;
}
return true;
}
bool Relocations::ApplyRelocation(const rel_t* rel) {
const Word rel_type = ELF_R_TYPE(rel->r_info);
const Word rel_symbol = ELF_R_SYM(rel->r_info);
Addr sym_addr = 0;
Addr reloc = static_cast<Addr>(rel->r_offset + base_memory_address_);
SB_DLOG(INFO) << " offset=0x" << std::hex << rel->r_offset
<< " type=" << std::dec << rel_type << " reloc=0x" << std::hex
<< reloc << " symbol=" << rel_symbol;
if (rel_type == 0)
return true;
if (rel_symbol != 0) {
if (!ResolveSymbol(rel_type, rel_symbol, reloc, &sym_addr)) {
SB_LOG(ERROR) << "Failed to resolve symbol: " << rel_symbol;
return false;
}
}
return ApplyResolvedReloc(rel, sym_addr);
}
#if defined(USE_RELA)
bool Relocations::ApplyResolvedReloc(const Rela* rela, Addr sym_addr) {
const Word rela_type = ELF_R_TYPE(rela->r_info);
const Sword addend = rela->r_addend;
const Addr reloc = static_cast<Addr>(rela->r_offset + base_memory_address_);
SB_DLOG(INFO) << " rela reloc=0x" << std::hex << reloc << " offset=0x"
<< rela->r_offset << " type=" << std::dec << rela_type
<< " addend=0x" << std::hex << addend;
Addr* target = reinterpret_cast<Addr*>(reloc);
switch (rela_type) {
#if SB_IS(ARCH_ARM64)
case R_AARCH64_JUMP_SLOT:
SB_DLOG(INFO) << " R_AARCH64_JUMP_SLOT target=" << std::hex << target
<< " addr=" << (sym_addr + addend);
*target = sym_addr + addend;
break;
case R_AARCH64_GLOB_DAT:
SB_DLOG(INFO) << " R_AARCH64_GLOB_DAT target=" << std::hex << target
<< " addr=" << (sym_addr + addend);
*target = sym_addr + addend;
break;
case R_AARCH64_ABS64:
SB_DLOG(INFO) << " R_AARCH64_ABS64 target=" << std::hex << target << " "
<< *target << " addr=" << sym_addr + addend;
*target += sym_addr + addend;
break;
case R_AARCH64_RELATIVE:
SB_DLOG(INFO) << " R_AARCH64_RELATIVE target=" << std::hex << target
<< " " << *target
<< " bias=" << base_memory_address_ + addend;
*target = base_memory_address_ + addend;
break;
#endif
#if SB_IS(ARCH_X64)
case R_X86_64_JMP_SLOT:
SB_DLOG(INFO) << " R_X86_64_JMP_SLOT target=" << std::hex << target
<< " addr=" << (sym_addr + addend);
*target = sym_addr + addend;
break;
case R_X86_64_GLOB_DAT:
SB_DLOG(INFO) << " R_X86_64_GLOB_DAT target=" << std::hex << target
<< " addr=" << (sym_addr + addend);
*target = sym_addr + addend;
break;
case R_X86_64_RELATIVE:
SB_DLOG(INFO) << " R_X86_64_RELATIVE target=" << std::hex << target
<< " " << *target
<< " bias=" << base_memory_address_ + addend;
*target = base_memory_address_ + addend;
break;
case R_X86_64_64:
*target = sym_addr + addend;
break;
case R_X86_64_PC32:
*target = sym_addr + (addend - reloc);
break;
#endif
default:
SB_LOG(ERROR) << "Invalid relocation type: " << rela_type;
return false;
}
return true;
}
#else
bool Relocations::ApplyResolvedReloc(const Rel* rel, Addr sym_addr) {
const Word rel_type = ELF_R_TYPE(rel->r_info);
const Addr reloc = static_cast<Addr>(rel->r_offset + base_memory_address_);
SB_DLOG(INFO) << " rel reloc=0x" << std::hex << reloc << " offset=0x"
<< rel->r_offset << " type=" << std::dec << rel_type;
Addr* target = reinterpret_cast<Addr*>(reloc);
switch (rel_type) {
#if SB_IS(ARCH_ARM)
case R_ARM_JUMP_SLOT:
SB_DLOG(INFO) << " R_ARM_JUMP_SLOT target=" << std::hex << target
<< " addr=" << sym_addr;
*target = sym_addr;
break;
case R_ARM_GLOB_DAT:
SB_DLOG(INFO) << " R_ARM_GLOB_DAT target=" << std::hex << target
<< " addr=" << sym_addr;
*target = sym_addr;
break;
case R_ARM_ABS32:
SB_DLOG(INFO) << " R_ARM_ABS32 target=" << std::hex << target << " "
<< *target << " addr=" << sym_addr;
*target += sym_addr;
break;
case R_ARM_REL32:
SB_DLOG(INFO) << " R_ARM_REL32 target=" << std::hex << target << " "
<< *target << " addr=" << sym_addr
<< " offset=" << rel->r_offset;
*target += sym_addr - rel->r_offset;
break;
case R_ARM_RELATIVE:
SB_DLOG(INFO) << " RR_ARM_RELATIVE target=" << std::hex << target << " "
<< *target << " bias=" << base_memory_address_;
*target += base_memory_address_;
break;
#endif
#if SB_IS(ARCH_X86)
case R_386_JMP_SLOT:
SB_DLOG(INFO) << " R_386_JMP_SLOT target=" << std::hex << target
<< " addr=" << sym_addr;
*target = sym_addr;
break;
case R_386_GLOB_DAT:
SB_DLOG(INFO) << " R_386_GLOB_DAT target=" << std::hex << target
<< " addr=" << sym_addr;
*target = sym_addr;
break;
case R_386_RELATIVE:
SB_DLOG(INFO) << " R_386_RELATIVE target=" << std::hex << target << " "
<< *target << " bias=" << base_memory_address_;
*target += base_memory_address_;
break;
case R_386_32:
SB_DLOG(INFO) << " R_386_32 target=" << std::hex << target << " "
<< *target << " addr=" << sym_addr;
*target += sym_addr;
break;
case R_386_PC32:
SB_DLOG(INFO) << " R_386_PC32 target=" << std::hex << target << " "
<< *target << " addr=" << sym_addr << " reloc=" << reloc;
*target += (sym_addr - reloc);
break;
#endif
default:
SB_LOG(ERROR) << "Invalid relocation type: " << rel_type;
return false;
}
return true;
}
#endif
RelocationType Relocations::GetRelocationType(Word r_type) {
switch (r_type) {
#if SB_IS(ARCH_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
#if SB_IS(ARCH_ARM64)
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
#if SB_IS(ARCH_X86)
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
#if SB_IS(ARCH_X64)
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
default:
return RELOCATION_TYPE_UNKNOWN;
}
}
bool Relocations::ResolveSymbol(Word rel_type,
Word rel_symbol,
Addr reloc,
Addr* sym_addr) {
const char* sym_name = dynamic_section_->LookupNameById(rel_symbol);
SB_DLOG(INFO) << "Resolve: " << sym_name;
const void* address = NULL;
const Sym* sym = dynamic_section_->LookupByName(sym_name);
if (sym) {
address = reinterpret_cast<void*>(base_memory_address_ + sym->st_value);
} else {
address = exported_symbols_->Lookup(sym_name);
}
SB_DLOG(INFO) << "Resolve: address=0x" << std::hex << address;
if (address) {
// The symbol was found, so compute its address.
*sym_addr = reinterpret_cast<Addr>(address);
return true;
}
// The symbol was not found. Normally this is an error except
// if this is a weak reference.
if (!dynamic_section_->IsWeakById(rel_symbol)) {
SB_LOG(ERROR) << "Could not find symbol: " << sym_name;
return false;
}
// 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;
}
SB_LOG(ERROR) << "Invalid weak relocation type (" << r
<< ") for unknown symbol '" << sym_name << "'";
return false;
}
} // namespace elf_loader
} // namespace starboard