blob: 20010e50e66c5e1080761133bff7c4126d488a1c [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/scoped_ptr.h"
#include "starboard/elf_loader/elf.h"
#include "starboard/elf_loader/file_impl.h"
#include "starboard/string.h"
#include "testing/gtest/include/gtest/gtest.h"
#if SB_API_VERSION >= 12 && (SB_API_VERSION >= 12 || SB_HAS(MMAP)) && \
SB_CAN(MAP_EXECUTABLE_MEMORY)
namespace starboard {
namespace elf_loader {
namespace {
// Test constants used as sample data.
const Addr kTestAddress = 34;
const Sword kTestAddend = 5;
class RelocationsTest : public ::testing::Test {
protected:
RelocationsTest() {
SbMemorySet(buf_, 'A', sizeof(buf_));
base_addr_ = reinterpret_cast<Addr>(&buf_);
dynamic_table_[0].d_tag = DT_REL;
dynamic_section_.reset(
new DynamicSection(base_addr_, dynamic_table_, 1, 0));
dynamic_section_->InitDynamicSection();
exported_symbols_.reset(new ExportedSymbols());
relocations_.reset(new Relocations(base_addr_, dynamic_section_.get(),
exported_symbols_.get()));
}
~RelocationsTest() {}
void VerifySymAddress(const rel_t* rel, Addr sym_addr) {
Addr target_addr = base_addr_ + rel->r_offset;
Addr target_value = sym_addr;
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
#ifdef USE_RELA
void VerifySymAddressPlusAddend(const rel_t* rel, Addr sym_addr) {
Addr target_addr = base_addr_ + rel->r_offset;
Addr target_value = sym_addr + rel->r_addend;
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
void VerifyBaseAddressPlusAddend(const rel_t* rel) {
Addr target_addr = base_addr_ + rel->r_offset;
Addr target_value = base_addr_ + rel->r_addend;
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
void VerifySymAddressPlusAddendDelta(const rel_t* rel, Addr sym_addr) {
Addr target_addr = base_addr_ + rel->r_offset;
Addr offset_rel = rel->r_offset + base_addr_;
Addr target_value = sym_addr + (rel->r_addend - offset_rel);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
#endif
protected:
scoped_ptr<Relocations> relocations_;
Addr base_addr_;
private:
char buf_[128];
Dyn dynamic_table_[10];
scoped_ptr<DynamicSection> dynamic_section_;
scoped_ptr<ExportedSymbols> exported_symbols_;
};
#if SB_IS(ARCH_ARM)
TEST_F(RelocationsTest, R_ARM_JUMP_SLOT) {
rel_t rel;
rel.r_offset = 0;
rel.r_info = R_ARM_JUMP_SLOT;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddress(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_ARM_GLOB_DAT) {
rel_t rel;
rel.r_offset = 1;
rel.r_info = R_ARM_GLOB_DAT;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddress(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_ARM_ABS32) {
rel_t rel;
rel.r_offset = 2;
rel.r_info = R_ARM_ABS32;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
target_value += sym_addr;
// Expected relocation calculation:
// *target += sym_addr;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
TEST_F(RelocationsTest, R_ARM_REL32) {
rel_t rel;
rel.r_offset = 3;
rel.r_info = R_ARM_REL32;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
target_value += sym_addr - rel.r_offset;
// Expected relocation calculation:
// *target += sym_addr - rel->r_offset;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
TEST_F(RelocationsTest, R_ARM_RELATIVE) {
rel_t rel;
rel.r_offset = 4;
rel.r_info = R_ARM_RELATIVE;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
target_value += base_addr_;
// Expected relocation calculation:
// *target += base_memory_address_;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
#endif // SB_IS(ARCH_ARM)
#if SB_IS(ARCH_ARM64) && defined(USE_RELA)
TEST_F(RelocationsTest, R_AARCH64_JUMP_SLOT) {
rel_t rel;
rel.r_offset = 0;
rel.r_info = R_AARCH64_JUMP_SLOT;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddressPlusAddend(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_AARCH64_GLOB_DAT) {
rel_t rel;
rel.r_offset = 1;
rel.r_info = R_AARCH64_GLOB_DAT;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddressPlusAddend(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_AARCH64_ABS64) {
rel_t rel;
rel.r_offset = 2;
rel.r_info = R_AARCH64_ABS64;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
target_value += sym_addr + rel.r_addend;
// Expected relocation calculation:
// *target += sym_addr + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
TEST_F(RelocationsTest, R_AARCH64_RELATIVE) {
rel_t rel;
rel.r_offset = 3;
rel.r_info = R_AARCH64_RELATIVE;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = base_memory_address_ + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifyBaseAddressPlusAddend(&rel);
}
#endif // SB_IS(ARCH_ARM64) && defined(USE_RELA)
#if SB_IS(ARCH_X86)
TEST_F(RelocationsTest, R_386_JMP_SLOT) {
rel_t rel;
rel.r_offset = 0;
rel.r_info = R_386_JMP_SLOT;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddress(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_386_GLOB_DAT) {
rel_t rel;
rel.r_offset = 1;
rel.r_info = R_386_GLOB_DAT;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddress(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_386_RELATIVE) {
rel_t rel;
rel.r_offset = 2;
rel.r_info = R_386_RELATIVE;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
target_value += base_addr_;
// Expected relocation calculation:
// *target += base_memory_address_;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
TEST_F(RelocationsTest, R_386_32) {
rel_t rel;
rel.r_offset = 3;
rel.r_info = R_386_32;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
target_value += sym_addr;
// Expected relocation calculation:
// *target += sym_addr;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
TEST_F(RelocationsTest, R_386_PC32) {
rel_t rel;
rel.r_offset = 4;
rel.r_info = R_386_PC32;
Addr sym_addr = kTestAddress;
Addr target_addr = base_addr_ + rel.r_offset;
Addr target_value = *reinterpret_cast<Addr*>(target_addr);
Addr reloc = static_cast<Addr>(rel.r_offset + base_addr_);
target_value += (sym_addr - reloc);
// Expected relocation calculation:
// *target += (sym_addr - reloc);
relocations_->ApplyResolvedReloc(&rel, sym_addr);
EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
}
#endif // SB_IS(ARCH_X86)
#if SB_IS(ARCH_X64) && defined(USE_RELA)
TEST_F(RelocationsTest, R_X86_64_JMP_SLOT) {
rel_t rel;
rel.r_offset = 0;
rel.r_info = R_X86_64_JMP_SLOT;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddressPlusAddend(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_X86_64_GLOB_DAT) {
rel_t rel;
rel.r_offset = 1;
rel.r_info = R_X86_64_GLOB_DAT;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddressPlusAddend(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_X86_64_RELATIVE) {
rel_t rel;
rel.r_offset = 2;
rel.r_info = R_X86_64_RELATIVE;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = base_memory_address_ + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifyBaseAddressPlusAddend(&rel);
}
TEST_F(RelocationsTest, R_X86_64_64) {
rel_t rel;
rel.r_offset = 3;
rel.r_info = R_X86_64_64;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
// Expected relocation calculation:
// *target = sym_addr + addend;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
VerifySymAddressPlusAddend(&rel, sym_addr);
}
TEST_F(RelocationsTest, R_X86_64_PC32) {
rel_t rel;
rel.r_offset = 4;
rel.r_info = R_X86_64_PC32;
rel.r_addend = kTestAddend;
Addr sym_addr = kTestAddress;
relocations_->ApplyResolvedReloc(&rel, sym_addr);
// Expected relocation calculation:
// *target = sym_addr + (addend - reloc);
VerifySymAddressPlusAddendDelta(&rel, sym_addr);
}
#endif // SB_IS(ARCH_X64) && defined(USE_RELA)
} // namespace
} // namespace elf_loader
} // namespace starboard
#endif // SB_API_VERSION >= 12 && (SB_API_VERSION >= 12
// || SB_HAS(MMAP)) && SB_CAN(MAP_EXECUTABLE_MEMORY)