// 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 "packer.h"
#include <vector>
#include "elf.h"
#include "elf_traits.h"
#include "gtest/gtest.h"
template <typename ELF>
static void AddRelocation(typename ELF::Addr addr,
typename ELF::Xword info,
typename ELF::Sxword addend,
std::vector<typename ELF::Rela>* relocations) {
typename ELF::Rela relocation;
relocation.r_offset = addr;
relocation.r_info = info;
relocation.r_addend = addend;
relocations->push_back(relocation);
}
template <typename ELF>
static bool CheckRelocation(typename ELF::Addr addr,
typename ELF::Xword info,
typename ELF::Sxword addend,
const typename ELF::Rela& relocation) {
return relocation.r_offset == addr &&
relocation.r_info == info &&
relocation.r_addend == addend;
}
namespace relocation_packer {
template <typename ELF>
static void DoPackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed;
bool is_32 = sizeof(typename ELF::Addr) == 4;
// Initial relocation.
AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
// Two more relocations, 4 byte deltas.
AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
// Three more relocations, 8 byte deltas.
AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
RelocationPacker<ELF> packer;
packed.clear();
packer.PackRelocations(relocations, &packed);
ASSERT_EQ(18U, packed.size());
// Identifier.
size_t ndx = 0;
EXPECT_EQ('A', packed[ndx++]);
EXPECT_EQ('P', packed[ndx++]);
EXPECT_EQ('S', packed[ndx++]);
EXPECT_EQ('2', packed[ndx++]);
// relocation count
EXPECT_EQ(6, packed[ndx++]);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 7d/0d (32/64bit)
EXPECT_EQ(0xfc, packed[ndx++]);
EXPECT_EQ(0xff, packed[ndx++]);
EXPECT_EQ(0xb7, packed[ndx++]);
EXPECT_EQ(0x8e, packed[ndx++]);
EXPECT_EQ(is_32 ? 0x7d : 0x0d, packed[ndx++]);
// first group
EXPECT_EQ(3, packed[ndx++]); // size
EXPECT_EQ(3, packed[ndx++]); // flags
EXPECT_EQ(4, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x11, packed[ndx++]); // r_info
// second group
EXPECT_EQ(3, packed[ndx++]); // size
EXPECT_EQ(3, packed[ndx++]); // flags
EXPECT_EQ(8, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x11, packed[ndx++]); // r_info
EXPECT_EQ(ndx, packed.size());
}
TEST(Packer, PackNoAddend32) {
DoPackNoAddend<ELF32_traits>();
}
TEST(Packer, PackNoAddend64) {
DoPackNoAddend<ELF64_traits>();
}
template <typename ELF>
static void DoUnpackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed;
bool is_32 = sizeof(typename ELF::Addr) == 4;
packed.push_back('A');
packed.push_back('P');
packed.push_back('S');
packed.push_back('2');
// relocation count
packed.push_back(6);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 7d/0d (32/64bit)
packed.push_back(0xfc);
packed.push_back(0xff);
packed.push_back(0xb7);
packed.push_back(0x8e);
packed.push_back(is_32 ? 0x7d : 0x0d);
// first group
packed.push_back(3); // size
packed.push_back(3); // flags
packed.push_back(4); // r_offset_delta
packed.push_back(0x11); // r_info
// second group
packed.push_back(3); // size
packed.push_back(3); // flags
packed.push_back(8); // r_offset_delta
packed.push_back(0x11); // r_info
RelocationPacker<ELF> packer;
packer.UnpackRelocations(packed, &relocations);
size_t ndx = 0;
EXPECT_EQ(6U, relocations.size());
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x11, 0, relocations[ndx++]));
EXPECT_EQ(ndx, relocations.size());
}
TEST(Packer, UnpackNoAddend32) {
DoUnpackNoAddend<ELF32_traits>();
}
TEST(Packer, UnpackNoAddend64) {
DoUnpackNoAddend<ELF64_traits>();
}
template <typename ELF>
static void DoPackWithAddend() {
std::vector<typename ELF::Rela> relocations;
// Initial relocation.
AddRelocation<ELF>(0xd1ce0000, 0x01, 10024, &relocations);
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
AddRelocation<ELF>(0xd1ce0004, 0x01, 10012, &relocations);
AddRelocation<ELF>(0xd1ce0008, 0x01, 10024, &relocations);
// Three more relocations, 8 byte deltas, -24 byte addend deltas.
AddRelocation<ELF>(0xd1ce0010, 0x01, 10000, &relocations);
AddRelocation<ELF>(0xd1ce0018, 0x01, 9976, &relocations);
AddRelocation<ELF>(0xd1ce0020, 0x01, 9952, &relocations);
std::vector<uint8_t> packed;
RelocationPacker<ELF> packer;
packed.clear();
packer.PackRelocations(relocations, &packed);
EXPECT_EQ(26U, packed.size());
size_t ndx = 0;
// Identifier.
EXPECT_EQ('A', packed[ndx++]);
EXPECT_EQ('P', packed[ndx++]);
EXPECT_EQ('S', packed[ndx++]);
EXPECT_EQ('2', packed[ndx++]);
// Relocation count
EXPECT_EQ(6U, packed[ndx++]);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr)
EXPECT_EQ(0xfc, packed[ndx++]);
EXPECT_EQ(0xff, packed[ndx++]);
EXPECT_EQ(0xb7, packed[ndx++]);
EXPECT_EQ(0x8e, packed[ndx++]);
if (sizeof(typename ELF::Addr) == 8) {
// positive for uint64_t
EXPECT_EQ(0x0d, packed[ndx++]);
} else {
// negative for uint32_t
EXPECT_EQ(0x7d, packed[ndx++]);
}
// group 1
EXPECT_EQ(0x03, packed[ndx++]); // size
EXPECT_EQ(0x0b, packed[ndx++]); // flags
EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x01, packed[ndx++]); // r_info
// group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
EXPECT_EQ(0xa8, packed[ndx++]);
EXPECT_EQ(0xce, packed[ndx++]);
EXPECT_EQ(0x00, packed[ndx++]);
// group 1 - addend 2: -12 = 0x74
EXPECT_EQ(0x74, packed[ndx++]);
// group 1 - addend 3: +12 = 0x0c
EXPECT_EQ(0x0c, packed[ndx++]);
// group 2
EXPECT_EQ(0x03, packed[ndx++]); // size
EXPECT_EQ(0x0b, packed[ndx++]); // flags
EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x01, packed[ndx++]); // r_info
// group 2 - addend 1: -24 = 0x68
EXPECT_EQ(0x68, packed[ndx++]);
// group 2 - addend 2: -24 = 0x68
EXPECT_EQ(0x68, packed[ndx++]);
// group 2 - addend 3: -24 = 0x68
EXPECT_EQ(0x68, packed[ndx++]);
EXPECT_EQ(ndx, packed.size());
}
TEST(Packer, PackWithAddend) {
DoPackWithAddend<ELF32_traits>();
DoPackWithAddend<ELF64_traits>();
}
template <typename ELF>
static void DoUnpackWithAddend() {
std::vector<uint8_t> packed;
// Identifier.
packed.push_back('A');
packed.push_back('P');
packed.push_back('S');
packed.push_back('2');
// Relocation count
packed.push_back(6U);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
packed.push_back(0xfc);
packed.push_back(0xff);
packed.push_back(0xb7);
packed.push_back(0x8e);
if (sizeof(typename ELF::Addr) == 8) {
// positive for uint64_t
packed.push_back(0x0d);
} else {
// negative for uint32_t
packed.push_back(0x7d);
}
// group 1
packed.push_back(0x03); // size
packed.push_back(0x0b); // flags
packed.push_back(0x04); // r_offset_delta
packed.push_back(0x01); // r_info
// group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
packed.push_back(0xa8);
packed.push_back(0xce);
packed.push_back(0x00);
// group 1 - addend 2: -12 = 0x74
packed.push_back(0x74);
// group 1 - addend 3: +12 = 0x0c
packed.push_back(0x0c);
// group 2
packed.push_back(0x03); // size
packed.push_back(0x0b); // flags
packed.push_back(0x08); // r_offset_delta
packed.push_back(0x01); // r_info
// group 2 - addend 1: -24 = 0x68
packed.push_back(0x68);
// group 2 - addend 2: -24 = 0x68
packed.push_back(0x68);
// group 2 - addend 3: -24 = 0x68
packed.push_back(0x68);
std::vector<typename ELF::Rela> relocations;
RelocationPacker<ELF> packer;
relocations.clear();
packer.UnpackRelocations(packed, &relocations);
EXPECT_EQ(6U, relocations.size());
size_t ndx = 0;
// Initial relocation.
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x01, 10024, relocations[ndx++]));
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x01, 10012, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x01, 10024, relocations[ndx++]));
// Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x01, 10000, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x01, 9976, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x01, 9952, relocations[ndx++]));
EXPECT_EQ(ndx, relocations.size());
}
TEST(Packer, UnpackWithAddend) {
DoUnpackWithAddend<ELF32_traits>();
DoUnpackWithAddend<ELF64_traits>();
}
} // namespace relocation_packer