Generalize compression tool

1. One binary for all architectures
 2. Generalize (and slightly improve) compression
 2.1 works on all relocation types (rela?.dyn section only so far)
 2.2 Uses same format to encode ElfW(Rel) as well as ElfW(Rela) tables

Bug: 18051137
Change-Id: I66c95d9076954ca115816fc577d0f5ef274e5e72
This commit is contained in:
Dmitriy Ivanov 2015-01-27 19:32:56 -08:00
parent 87a0617ebe
commit f8ff6b103b
28 changed files with 1627 additions and 2119 deletions

19
tools/Android.mk Normal file
View File

@ -0,0 +1,19 @@
#
# Copyright (C) 2015 The Android Open Source Project
#
# 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.
#
LOCAL_PATH := $(call my-dir)
include $(call all-subdir-makefiles)

View File

@ -0,0 +1,96 @@
#
# Copyright (C) 2015 The Android Open Source Project
#
# 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.
#
common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
src/debug.cc \
src/delta_encoder.cc \
src/elf_file.cc \
src/leb128.cc \
src/packer.cc \
src/sleb128.cc \
LOCAL_STATIC_LIBRARIES := libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf
LOCAL_MODULE := lib_relocation_packer
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := src/main.cc
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
LOCAL_MODULE := relocation_packer
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
src/debug_unittest.cc \
src/delta_encoder_unittest.cc \
src/elf_file_unittest.cc \
src/leb128_unittest.cc \
src/sleb128_unittest.cc \
src/packer_unittest.cc \
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_MODULE := relocation_packer_unit_tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_NATIVE_TEST)
# $(1) library name
define copy-test-library
include $(CLEAR_VARS)
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE := $(1)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES)
LOCAL_STRIP_MODULE := false
LOCAL_SRC_FILES := test_data/$(1)
include $(BUILD_PREBUILT)
endef
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))

View File

@ -1,148 +0,0 @@
# 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.
import("config.gni")
import("//testing/test.gni")
assert(relocation_packing_supported)
if (target_arch == "arm") {
target_define = "TARGET_ARM"
} else if (target_arch == "arm64") {
target_define = "TARGET_ARM64"
}
if (current_toolchain == host_toolchain) {
# GYP: //tools/relocation_packer/relocation_packer.gyp:lib_relocation_packer
source_set("lib_relocation_packer") {
defines = [ target_define ]
deps = [
"//third_party/elfutils:libelf",
]
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ "//build/config/compiler:no_chromium_code" ]
sources = [
"src/debug.cc",
"src/delta_encoder.cc",
"src/elf_file.cc",
"src/leb128.cc",
"src/packer.cc",
"src/sleb128.cc",
"src/run_length_encoder.cc",
]
}
# GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer
executable("relocation_packer") {
defines = [ target_define ]
deps = [
":lib_relocation_packer",
"//third_party/elfutils:libelf",
]
sources = [
"src/main.cc",
]
}
# GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests
test("relocation_packer_unittests") {
sources = [
"src/debug_unittest.cc",
"src/delta_encoder_unittest.cc",
"src/elf_file_unittest.cc",
"src/leb128_unittest.cc",
"src/packer_unittest.cc",
"src/sleb128_unittest.cc",
"src/run_length_encoder_unittest.cc",
"src/run_all_unittests.cc",
]
rebased_test_data = rebase_path("test_data", root_build_dir)
data = [
"test_data/elf_file_unittest_relocs_arm32.so",
"test_data/elf_file_unittest_relocs_arm32_packed.so",
"test_data/elf_file_unittest_relocs_arm64.so",
"test_data/elf_file_unittest_relocs_arm64_packed.so",
]
defines = [
target_define,
"INTERMEDIATE_DIR=\"$rebased_test_data\"",
]
include_dirs = [ "//" ]
deps = [
":lib_relocation_packer",
":relocation_packer_test_data",
"//testing:gtest",
]
}
}
if (current_toolchain == default_toolchain &&
(target_arch == "arm" || target_arch == "arm64")) {
# Targets to build test data. These participate only in building test
# data for use with elf_file_unittest.cc, and are not part of the main
# relocation packer build. Unit test data files are checked in to the
# source tree as 'golden' data, and are not generated 'on the fly' by
# the build.
#
# See test_data/generate_elf_file_unittest_relocs.sh for instructions.
# GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_test_data
shared_library("relocation_packer_test_data") {
cflags = [
"-O0",
"-g0",
]
sources = [
"test_data/elf_file_unittest_relocs.cc",
]
}
# GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests_test_data
action("relocation_packer_unittests_test_data") {
script = "test_data/generate_elf_file_unittest_relocs.py"
test_file = "$root_build_dir/librelocation_packer_test_data.so"
if (target_arch == "arm") {
added_section = ".android.rel.dyn"
packed_output = "elf_file_unittest_relocs_arm32_packed.so"
unpacked_output = "elf_file_unittest_relocs_arm32.so"
} else if (target_arch == "arm64") {
added_section = ".android.rela.dyn"
packed_output = "elf_file_unittest_relocs_arm64_packed.so"
unpacked_output = "elf_file_unittest_relocs_arm64.so"
} else {
assert(false, "Unsupported target arch for relocation packer")
}
packed_output = "$root_build_dir/$packed_output"
unpacked_output = "$root_build_dir/$unpacked_output"
inputs = [
test_file,
]
deps = [
":relocation_packer_test_data",
":relocation_packer($host_toolchain)",
]
outputs = [
packed_output,
unpacked_output,
]
args = [
"--android-pack-relocations",
rebase_path(relocation_packer_exe, root_build_dir),
"--android-objcopy",
rebase_path(android_objcopy, root_build_dir),
"--added-section=$added_section",
"--test-file",
rebase_path(test_file, root_build_dir),
"--packed-output",
rebase_path(packed_output, root_build_dir),
"--unpacked-output",
rebase_path(unpacked_output, root_build_dir),
]
}
}

View File

@ -1,21 +0,0 @@
# 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.
relocation_packing_supported = target_arch == "arm" || target_arch == "arm64"
if (relocation_packing_supported) {
relocation_packer_target = "//tools/relocation_packer($host_toolchain)"
relocation_packer_dir =
get_label_info("$relocation_packer_target", "root_out_dir")
relocation_packer_exe = "${relocation_packer_dir}/relocation_packer"
if (target_arch == "arm") {
relocations_have_addends = 0
} else if (target_arch == "arm64") {
relocations_have_addends = 1
}
} else {
relocations_have_addends = 0
relocation_packer_exe = ""
}

View File

@ -1,161 +0,0 @@
# 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.
{
'variables': {
'target_define%': 'TARGET_UNSUPPORTED',
'conditions': [
[ 'target_arch == "arm"', {
'target_define': 'TARGET_ARM',
}],
[ 'target_arch == "arm64"', {
'target_define': 'TARGET_ARM64',
}],
],
},
'targets': [
{
# GN: //tools/relocation_packer:lib_relocation_packer
'target_name': 'lib_relocation_packer',
'toolsets': ['host'],
'type': 'static_library',
'defines': [
'<(target_define)',
],
'dependencies': [
'../../third_party/elfutils/elfutils.gyp:libelf',
],
'sources': [
'src/debug.cc',
'src/delta_encoder.cc',
'src/elf_file.cc',
'src/leb128.cc',
'src/packer.cc',
'src/sleb128.cc',
'src/run_length_encoder.cc',
],
},
{
# GN: //tools/relocation_packer:relocation_packer
'target_name': 'relocation_packer',
'toolsets': ['host'],
'type': 'executable',
'defines': [
'<(target_define)',
],
'dependencies': [
'../../third_party/elfutils/elfutils.gyp:libelf',
'lib_relocation_packer',
],
'sources': [
'src/main.cc',
],
},
{
# GN: //tools/relocation_packer:relocation_packer_unittests
'target_name': 'relocation_packer_unittests',
'toolsets': ['host'],
'type': 'executable',
'defines': [
'<(target_define)',
],
'cflags': [
'-DINTERMEDIATE_DIR="<(INTERMEDIATE_DIR)"',
],
'dependencies': [
'../../testing/gtest.gyp:gtest',
'lib_relocation_packer',
],
'include_dirs': [
'../..',
],
'sources': [
'src/debug_unittest.cc',
'src/delta_encoder_unittest.cc',
'src/elf_file_unittest.cc',
'src/leb128_unittest.cc',
'src/packer_unittest.cc',
'src/sleb128_unittest.cc',
'src/run_length_encoder_unittest.cc',
'src/run_all_unittests.cc',
],
'copies': [
{
'destination': '<(INTERMEDIATE_DIR)',
'files': [
'test_data/elf_file_unittest_relocs_arm32.so',
'test_data/elf_file_unittest_relocs_arm32_packed.so',
'test_data/elf_file_unittest_relocs_arm64.so',
'test_data/elf_file_unittest_relocs_arm64_packed.so',
],
},
],
},
# Targets to build test data. These participate only in building test
# data for use with elf_file_unittest.cc, and are not part of the main
# relocation packer build. Unit test data files are checked in to the
# source tree as 'golden' data, and are not generated 'on the fly' by
# the build.
#
# See test_data/generate_elf_file_unittest_relocs.sh for instructions.
{
# GN: //tools/relocation_packer:relocation_packer_test_data
'target_name': 'relocation_packer_test_data',
'toolsets': ['target'],
'type': 'shared_library',
'cflags': [
'-O0',
'-g0',
],
'sources': [
'test_data/elf_file_unittest_relocs.cc',
],
},
{
# GN: //tools/relocation_packer:relocation_packer_unittests_test_data
'target_name': 'relocation_packer_unittests_test_data',
'toolsets': ['target'],
'type': 'none',
'actions': [
{
'variables': {
'test_file': '<(SHARED_LIB_DIR)/librelocation_packer_test_data.so',
'conditions': [
[ 'target_arch == "arm"', {
'added_section': '.android.rel.dyn',
'unpacked_output': 'elf_file_unittest_relocs_arm32.so',
'packed_output': 'elf_file_unittest_relocs_arm32_packed.so',
}],
[ 'target_arch == "arm64"', {
'added_section': '.android.rela.dyn',
'unpacked_output': 'elf_file_unittest_relocs_arm64.so',
'packed_output': 'elf_file_unittest_relocs_arm64_packed.so',
}],
],
},
'action_name': 'generate_relocation_packer_test_data',
'inputs': [
'test_data/generate_elf_file_unittest_relocs.py',
'<(PRODUCT_DIR)/relocation_packer',
'<(test_file)',
],
'outputs': [
'<(INTERMEDIATE_DIR)/<(unpacked_output)',
'<(INTERMEDIATE_DIR)/<(packed_output)',
],
'action': [
'python', 'test_data/generate_elf_file_unittest_relocs.py',
'--android-pack-relocations=<(PRODUCT_DIR)/relocation_packer',
'--android-objcopy=<(android_objcopy)',
'--added-section=<(added_section)',
'--test-file=<(test_file)',
'--unpacked-output=<(INTERMEDIATE_DIR)/<(unpacked_output)',
'--packed-output=<(INTERMEDIATE_DIR)/<(packed_output)',
],
},
],
},
],
}

View File

@ -5,7 +5,7 @@
#include "debug.h"
#include <sstream>
#include "testing/gtest/include/gtest/gtest.h"
#include "gtest/gtest.h"
namespace relocation_packer {

View File

@ -7,66 +7,301 @@
#include <vector>
#include "debug.h"
#include "elf_traits.h"
static constexpr uint32_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
static constexpr uint32_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
static constexpr uint32_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
static constexpr uint32_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
static bool is_relocation_grouped_by_info(uint64_t flags) {
return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
}
static bool is_relocation_grouped_by_offset_delta(uint64_t flags) {
return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
}
static bool is_relocation_grouped_by_addend(uint64_t flags) {
return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
}
static bool is_relocation_group_has_addend(uint64_t flags) {
return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
}
namespace relocation_packer {
// Encode relative relocations with addends into a delta encoded (packed)
// representation. Represented as simple r_offset and r_addend delta pairs,
// with an implicit neutral element at the start.
void RelocationDeltaCodec::Encode(const std::vector<ELF::Rela>& relocations,
std::vector<ELF::Sxword>* packed) {
// One relocation is sufficient for delta encoding.
if (relocations.size() < 1)
// Encode relocations into a delta encoded (packed) representation.
template <typename ELF>
void RelocationDeltaCodec<ELF>::Encode(const std::vector<ElfRela>& relocations,
std::vector<ElfAddr>* packed) {
if (relocations.size() == 0)
return;
// Start with the element count, then append the delta pairs.
packed->push_back(relocations.size());
// Start with the relocation count, then append groups
// TODO(dimitry): we might want to move it to DT_ANDROID_RELCOUNT section
packed->push_back(static_cast<ElfAddr>(relocations.size()));
ELF::Addr offset = 0;
ELF::Sxword addend = 0;
// lets write starting offset (offset of the first reloc - first delta)
ElfAddr start_offset = relocations.size() > 1 ?
relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) :
relocations[0].r_offset;
for (size_t i = 0; i < relocations.size(); ++i) {
const ELF::Rela* relocation = &relocations[i];
CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode);
packed->push_back(start_offset);
packed->push_back(relocation->r_offset - offset);
offset = relocation->r_offset;
packed->push_back(relocation->r_addend - addend);
addend = relocation->r_addend;
// this one is used to calculate delta
ElfAddr previous_addend = 0;
ElfAddr previous_offset = start_offset;
for (size_t group_start = 0; group_start < relocations.size(); ) {
ElfAddr group_flags = 0;
ElfAddr group_offset_delta = 0;
ElfAddr group_addend = 0;
ElfAddr group_info = 0;
ElfAddr group_size = 0;
DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags,
&group_offset_delta, &group_info, &group_addend);
// write the group header
packed->push_back(group_size);
packed->push_back(group_flags);
if (is_relocation_grouped_by_offset_delta(group_flags)) {
packed->push_back(group_offset_delta);
}
if (is_relocation_grouped_by_info(group_flags)) {
packed->push_back(group_info);
}
if (is_relocation_group_has_addend(group_flags) &&
is_relocation_grouped_by_addend(group_flags)) {
packed->push_back(group_addend - previous_addend);
previous_addend = group_addend;
}
for (size_t i = 0; i < static_cast<size_t>(group_size); ++i) {
CHECK((group_start + i) < relocations.size());
const ElfRela* relocation = &relocations[group_start + i];
if (!is_relocation_grouped_by_offset_delta(group_flags)) {
packed->push_back(relocation->r_offset - previous_offset);
}
previous_offset = relocation->r_offset;
if (!is_relocation_grouped_by_info(group_flags)) {
packed->push_back(relocation->r_info);
}
if (is_relocation_group_has_addend(group_flags) &&
!is_relocation_grouped_by_addend(group_flags)) {
packed->push_back(relocation->r_addend - previous_addend);
previous_addend = relocation->r_addend;
}
}
// If the relocation group does not have an addend - reset it to 0
// to simplify addend computation for the group following this one.
if (!is_relocation_group_has_addend(group_flags)) {
previous_addend = 0;
}
group_start += group_size;
}
}
// Decode relative relocations with addends from a delta encoded (packed)
// representation.
void RelocationDeltaCodec::Decode(const std::vector<ELF::Sxword>& packed,
std::vector<ELF::Rela>* relocations) {
// We need at least one packed pair after the packed pair count to be
// able to unpack.
if (packed.size() < 3)
// Decode relocations from a delta encoded (packed) representation.
template <typename ELF>
void RelocationDeltaCodec<ELF>::Decode(const std::vector<ElfAddr>& packed,
std::vector<ElfRela>* relocations) {
if (packed.size() < 5) {
return;
}
// Ensure that the packed data offers enough pairs. There may be zero
// padding on it that we ignore.
CHECK(static_cast<size_t>(packed[0]) <= (packed.size() - 1) >> 1);
size_t ndx = 0;
ElfAddr current_count = 0;
ElfAddr total_count = packed[ndx++];
ELF::Addr offset = 0;
ELF::Sxword addend = 0;
ElfAddr offset = packed[ndx++];
ElfAddr info = 0;
ElfAddr addend = 0;
// The first packed vector element is the pairs count. Start uncondensing
// pairs at the second, and finish at the end of the pairs data.
const size_t pairs_count = packed[0];
for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) {
offset += packed[i];
addend += packed[i + 1];
while(current_count < total_count) {
// read group
ElfAddr group_size = packed[ndx++];
ElfAddr group_flags = packed[ndx++];
ElfAddr group_offset_delta = 0;
// Generate a relocation for this offset and addend pair.
ELF::Rela relocation;
relocation.r_offset = offset;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocation.r_addend = addend;
relocations->push_back(relocation);
if (is_relocation_grouped_by_offset_delta(group_flags)) {
group_offset_delta = packed[ndx++];
}
if (is_relocation_grouped_by_info(group_flags)) {
info = packed[ndx++];
}
if (is_relocation_group_has_addend(group_flags) &&
is_relocation_grouped_by_addend(group_flags)) {
addend += packed[ndx++];
}
// now read not grouped info
for (ElfAddr i = 0; i<group_size; ++i) {
if (is_relocation_grouped_by_offset_delta(group_flags)) {
offset += group_offset_delta;
} else {
offset += packed[ndx++];
}
if (!is_relocation_grouped_by_info(group_flags)) {
info = packed[ndx++];
}
if (is_relocation_group_has_addend(group_flags) &&
!is_relocation_grouped_by_addend(group_flags)) {
addend += packed[ndx++];
}
ElfRela reloc;
reloc.r_offset = offset;
reloc.r_info = info;
reloc.r_addend = is_relocation_group_has_addend(group_flags) ? addend : 0;
relocations->push_back(reloc);
}
if (!is_relocation_group_has_addend(group_flags)) {
addend = 0;
}
current_count += group_size;
}
}
// This function detects a way to group reloc_one and reloc_two, sets up group_flags
// and initializes values for corresponding group_ fields. For example if relocations
// can be grouped by r_info the function will set group_info variable.
template <typename ELF>
void RelocationDeltaCodec<ELF>::DetectGroupFields(const ElfRela& reloc_one,
const ElfRela& reloc_two,
ElfAddr current_offset_delta,
ElfAddr* group_flags,
ElfAddr* group_offset_delta,
ElfAddr* group_info,
ElfAddr* group_addend) {
*group_flags = 0;
const ElfAddr offset_delta = static_cast<ElfAddr>(reloc_two.r_offset) -
static_cast<ElfAddr>(reloc_one.r_offset);
if (offset_delta == current_offset_delta) {
*group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG;
if (group_offset_delta != nullptr) {
*group_offset_delta = current_offset_delta;
}
}
if (reloc_one.r_info == reloc_two.r_info) {
*group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG;
if (group_info != nullptr) {
*group_info = reloc_one.r_info;
}
}
if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) {
*group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG;
if (reloc_one.r_addend == reloc_two.r_addend) {
*group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG;
if (group_addend != nullptr) {
*group_addend = reloc_one.r_addend;
}
}
}
}
// This function is used to detect if there is better group available
// during RelocationDeltaCodec<ELF>::DetectGroup processing.
// Current implementation prefers having groups without addend (== zero addend)
// to any other groups field with the ratio 3:1. This is because addend tends
// to be more unevenly distributed than other fields.
static uint32_t group_weight(uint64_t flags) {
uint32_t weight = 0;
if (!is_relocation_group_has_addend(flags)) {
weight += 3;
} else if (is_relocation_grouped_by_addend(flags)) {
weight += 1;
}
if (is_relocation_grouped_by_offset_delta(flags)) {
weight += 1;
}
if (is_relocation_grouped_by_info(flags)) {
weight += 1;
}
return weight;
}
template <typename ELF>
void RelocationDeltaCodec<ELF>::DetectGroup(const std::vector<ElfRela>& relocations,
size_t group_starts_with, ElfAddr previous_offset,
ElfAddr* group_size, ElfAddr* group_flags,
ElfAddr* group_offset_delta, ElfAddr* group_info,
ElfAddr* group_addend) {
CHECK(group_starts_with < relocations.size());
CHECK(group_flags != nullptr);
const ElfRela& reloc_one = relocations[group_starts_with++];
if (group_starts_with == relocations.size()) {
*group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
*group_size = 1;
return;
}
const ElfAddr offset_delta = reloc_one.r_offset - previous_offset;
// detect group_flags
DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags,
group_offset_delta, group_info, group_addend);
if (group_starts_with + 1 == relocations.size()) {
*group_size = 2;
return;
}
ElfAddr cnt = 1;
for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) {
ElfAddr candidate_flags;
// check if next group (reloc_current; reloc_next) has better grouped_by flags
DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags,
nullptr, nullptr, nullptr);
if (group_weight(*group_flags) < group_weight(candidate_flags)) {
break;
}
cnt++;
if (candidate_flags != *group_flags) {
break;
}
if (i + 1 == relocations.size() - 1) { // last one
cnt++;
}
}
// if as a result of checking candidates we ended up with cnt == 1
// reset flags to the default state
if (cnt == 1) {
*group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
}
*group_size = cnt;
}
template class RelocationDeltaCodec<ELF32_traits>;
template class RelocationDeltaCodec<ELF64_traits>;
} // namespace relocation_packer

View File

@ -2,50 +2,86 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Delta encode and decode relative relocations with addends.
// Delta encode and decode REL/RELA section of elf file.
//
// Relative relocations are the bulk of dynamic relocations (the
// .rel.dyn or .rela.dyn sections) in libchrome.<version>.so, and the ELF
// standard representation of them is wasteful. .rel.dyn contains
// relocations without addends, .rela.dyn relocations with addends.
// The encoded data format is sequence of elements of ElfAddr type (unsigned long):
//
// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes
// on 64 bit plaforms. It is split into offset, info, and addend fields.
// Offsets strictly increase, and each is commonly a few bytes different
// from its predecessor. Addends are less well behaved. The info field is
// constant. Example, from 'readelf -x4 libchrome.<version>.so' 64 bit:
// [00] relocation_count - the total count of relocations
// [01] initial r_offset - this is initial r_offset for the
// relocation table.
// followed by group structures:
// [02] group
// ...
// [nn] group
// the generalized format of the group is (! - always present ? - depends on group_flags):
// --------------
// ! group_size
// ! group_flags
// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set
// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set
// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND
// flag is set
//
// offset info
// 80949303 00000000 03040000 00000000 ................
// addend offset
// fc015b00 00000000 88949303 00000000 ..[.............
// info addend
// 03040000 00000000 24025b00 00000000 ........$.[.....
// offset info
// 90949303 00000000 03040000 00000000 ................
// addend offset
// 3c025b00 00000000 98949303 00000000 <.[.............
// info addend
// 03040000 00000000 50025b00 00000000 ........P.[.....
// The group description is followed by individual relocations.
// please note that there is a case when individual relocation
// section could be empty - that is if every field ends up grouped.
//
// The offset strictly increases, but the addend is unpredictable, so run
// length encoding will not work well with this data. We can however pack
// with delta encoding. The upper four bytes of the eight byte offset and
// addend are invariably zeroes. The difference between adjacent offsets
// is almost always small, and between adjacent addends is often small. And
// info is constant and can be eliminated.
// The format for individual relocations section is:
// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set
// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set
// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set
//
// Delta encoding reduces the size of the data modestly, so that the first
// three relocations above can be represented as:
// For example lets pack the following relocations:
//
// initial offset initial addend offset delta addend delta
// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028
// offset delta addend delta ...
// 00000000 00000008 00000000 0000009f
// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries:
// Offset Info Type Symbol's Value Symbol's Name + Addend
// 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8
// 00000000000a2180 0000000000000403 R_AARCH64_RELATIVE 177cc
// 00000000000a2188 0000000000000403 R_AARCH64_RELATIVE 177e0
// 00000000000a2190 0000000000000403 R_AARCH64_RELATIVE 177f4
// 00000000000a2198 0000000000000403 R_AARCH64_RELATIVE 17804
// 00000000000a21a0 0000000000000403 R_AARCH64_RELATIVE 17818
// 00000000000a21a8 0000000000000403 R_AARCH64_RELATIVE 1782c
// 00000000000a21b0 0000000000000403 R_AARCH64_RELATIVE 17840
// 00000000000a21b8 0000000000000403 R_AARCH64_RELATIVE 17854
// 00000000000a21c0 0000000000000403 R_AARCH64_RELATIVE 17868
// 00000000000a21c8 0000000000000403 R_AARCH64_RELATIVE 1787c
// 00000000000a21d0 0000000000000403 R_AARCH64_RELATIVE 17890
// 00000000000a21d8 0000000000000403 R_AARCH64_RELATIVE 178a4
// 00000000000a21e8 0000000000000403 R_AARCH64_RELATIVE 178b8
//
// The addend delta can be negative as well as positive, but overall the
// deltas have a much smaller range than the input data. When encoded as
// signed LEB128 the total data reduction becomes useful.
// The header is going to be
// [00] 14 <- count
// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta,
// the delta is 8 in this case)
// -- starting the first and only group
// [03] 14 <- group size
// [03] 0xb <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA
// | RELOCATION_GROUPED_BY_INFO
// [04] 8 <- offset delta
// [05] 0x403 <- r_info
// -- end of group definition, starting list of r_addend deltas
// [06] 0x177a8
// [07] 0x24 = 177cc - 177a8
// [08] 0x14 = 177e0 - 177cc
// [09] 0x14 = 177f4 - 177e0
// [10] 0x10 = 17804 - 177f4
// [11] 0x14 = 17818 - 17804
// [12] 0x14 = 1782c - 17818
// [13] 0x14 = 17840 - 1782c
// [14] 0x14 = 17854 - 17840
// [15] 0x14 = 17868 - 17854
// [16] 0x14 = 1787c - 17868
// [17] 0x14 = 17890 - 1787c
// [18] 0x14 = 178a4 - 17890
// [19] 0x14 = 178b8 - 178a4
// -- the end.
// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can
// save us more bytes...
// The input ends when sum(group_size) == relocation_count
#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
@ -60,19 +96,35 @@ namespace relocation_packer {
// A RelocationDeltaCodec packs vectors of relative relocations with
// addends into more compact forms, and unpacks them to reproduce the
// pre-packed data.
template <typename ELF>
class RelocationDeltaCodec {
public:
// Encode relative relocations with addends into a more compact form.
typedef typename ELF::Addr ElfAddr;
typedef typename ELF::Rela ElfRela;
// Encode relocations with addends into a more compact form.
// |relocations| is a vector of relative relocation with addend structs.
// |packed| is the vector of packed words into which relocations are packed.
static void Encode(const std::vector<ELF::Rela>& relocations,
std::vector<ELF::Sxword>* packed);
static void Encode(const std::vector<ElfRela>& relocations,
std::vector<ElfAddr>* packed);
// Decode relative relocations with addends from their more compact form.
// |packed| is the vector of packed relocations.
// |relocations| is a vector of unpacked relative relocations.
static void Decode(const std::vector<ELF::Sxword>& packed,
std::vector<ELF::Rela>* relocations);
static void Decode(const std::vector<ElfAddr>& packed,
std::vector<ElfRela>* relocations);
private:
static void DetectGroup(const std::vector<ElfRela>& relocations,
size_t group_starts_with, ElfAddr previous_offset,
ElfAddr* group_size, ElfAddr* group_flags,
ElfAddr* group_offset_delta, ElfAddr* group_info,
ElfAddr* group_addend);
static void DetectGroupFields(const ElfRela& reloc_one, const ElfRela& reloc_two,
ElfAddr current_offset_delta, ElfAddr* group_flags,
ElfAddr* group_offset_delta, ElfAddr* group_info,
ElfAddr* group_addend);
};
} // namespace relocation_packer

View File

@ -6,27 +6,29 @@
#include <vector>
#include "elf.h"
#include "elf_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "gtest/gtest.h"
namespace {
void AddRelocation(ELF::Addr addr,
ELF::Sxword addend,
std::vector<ELF::Rela>* relocations) {
ELF::Rela relocation;
template <typename T>
void AddRelocation(uint32_t addr,
uint32_t info,
int32_t addend,
std::vector<T>* relocations) {
T relocation;
relocation.r_offset = addr;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocation.r_info = info;
relocation.r_addend = addend;
relocations->push_back(relocation);
}
bool CheckRelocation(ELF::Addr addr,
ELF::Sxword addend,
const ELF::Rela& relocation) {
template <typename T>
bool CheckRelocation(uint32_t addr,
uint32_t info,
int32_t addend,
const T& relocation) {
return relocation.r_offset == addr &&
ELF_R_SYM(relocation.r_info) == 0 &&
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
relocation.r_info == info &&
relocation.r_addend == addend;
}
@ -34,117 +36,188 @@ bool CheckRelocation(ELF::Addr addr,
namespace relocation_packer {
TEST(Delta, Encode) {
std::vector<ELF::Rela> relocations;
std::vector<ELF::Sxword> packed;
template <typename ELF>
static void encode() {
std::vector<typename ELF::Rela> relocations;
std::vector<typename ELF::Addr> packed;
RelocationDeltaCodec codec;
RelocationDeltaCodec<ELF> codec;
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(0, packed.size());
ASSERT_EQ(0U, packed.size());
// Initial relocation.
AddRelocation(0xf00d0000, 10000, &relocations);
AddRelocation(0xf00d0000, 11U, 10000, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(3, packed.size());
// size of reloc table, size of group, flags, 3 fields, zero
EXPECT_EQ(7U, packed.size());
// One pair present.
EXPECT_EQ(1, packed[0]);
// Delta from the neutral element is the initial relocation.
EXPECT_EQ(0xf00d0000, packed[1]);
EXPECT_EQ(10000, packed[2]);
size_t ndx = 0;
EXPECT_EQ(1U, packed[ndx++]);
EXPECT_EQ(0xf00d0000, packed[ndx++]);
EXPECT_EQ(1U, packed[ndx++]); // group_size
EXPECT_EQ(8U, packed[ndx++]); // flags
// Delta from the neutral element is zero
EXPECT_EQ(0U, packed[ndx++]); // offset_delta
EXPECT_EQ(11U, packed[ndx++]); // info
EXPECT_EQ(10000U, packed[ndx++]); // addend_delta
// Add a second relocation, 4 byte offset delta, 12 byte addend delta.
AddRelocation(0xf00d0004, 10012, &relocations);
// same info
AddRelocation(0xf00d0004, 11U, 10012, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(5, packed.size());
// Two pairs present.
EXPECT_EQ(2, packed[0]);
// Delta from the neutral element is the initial relocation.
EXPECT_EQ(0xf00d0000, packed[1]);
EXPECT_EQ(10000, packed[2]);
// 4 byte offset delta, 12 byte addend delta.
EXPECT_EQ(4, packed[3]);
EXPECT_EQ(12, packed[4]);
ndx = 0;
EXPECT_EQ(8U, packed.size());
EXPECT_EQ(2U, packed[ndx++]); // relocs count
EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset
EXPECT_EQ(2U, packed[ndx++]); // group count
EXPECT_EQ(11U, packed[ndx++]); // flags
EXPECT_EQ(4U, packed[ndx++]); // group offset delta
EXPECT_EQ(11U, packed[ndx++]); // info
EXPECT_EQ(10000U, packed[ndx++]); // addend delta
EXPECT_EQ(12U, packed[ndx++]); // addend delta
// Add a third relocation, 4 byte offset delta, 12 byte addend delta.
AddRelocation(0xf00d0008, 10024, &relocations);
// different info
AddRelocation(0xf00d0008, 41U, 10024, &relocations);
// Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
AddRelocation(0xf00d0010, 10000, &relocations);
AddRelocation(0xf00d0018, 9976, &relocations);
AddRelocation(0xf00d0020, 9952, &relocations);
AddRelocation(0xf00d0010, 42U, 10000, &relocations);
AddRelocation(0xf00d0018, 42U, 9976, &relocations);
AddRelocation(0xf00d0020, 42U, 9952, &relocations);
AddRelocation(0xf00d2028, 1042U, 0, &relocations);
AddRelocation(0xf00d2030, 3442U, 0, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(13, packed.size());
// Six pairs present.
EXPECT_EQ(6, packed[0]);
ndx = 0;
EXPECT_EQ(26U, packed.size());
// Total number of relocs
EXPECT_EQ(8U, packed[ndx++]);
EXPECT_EQ(0xf00cfffc, packed[ndx++]);
// 2 in first group
EXPECT_EQ(2U, packed[ndx++]);
EXPECT_EQ(11U, packed[ndx++]); //flags
EXPECT_EQ(4U, packed[ndx++]); // group offset delta
EXPECT_EQ(11U, packed[ndx++]); // info
// Initial relocation.
EXPECT_EQ(0xf00d0000, packed[1]);
EXPECT_EQ(10000, packed[2]);
EXPECT_EQ(10000U, packed[ndx++]); // addend delta
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_EQ(4, packed[3]);
EXPECT_EQ(12, packed[4]);
EXPECT_EQ(4, packed[5]);
EXPECT_EQ(12, packed[6]);
EXPECT_EQ(12U, packed[ndx++]); // addend delta
// second group has only one reloc
EXPECT_EQ(1U, packed[ndx++]); // count
EXPECT_EQ(8U, packed[ndx++]); // flags
EXPECT_EQ(4U, packed[ndx++]); // offset delta
EXPECT_EQ(41U, packed[ndx++]); // info
EXPECT_EQ(12U, packed[ndx++]); // addend delta
// next - 3 relocs grouped by info
EXPECT_EQ(3U, packed[ndx++]); // count
EXPECT_EQ(11U, packed[ndx++]); // flags
EXPECT_EQ(8U, packed[ndx++]); // group offset delta
EXPECT_EQ(42U, packed[ndx++]); // info
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_EQ(8, packed[7]);
EXPECT_EQ(-24, packed[8]);
EXPECT_EQ(8, packed[9]);
EXPECT_EQ(-24, packed[10]);
EXPECT_EQ(8, packed[11]);
EXPECT_EQ(-24, packed[12]);
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
// and last - 2 relocations without addend
EXPECT_EQ(2U, packed[ndx++]);
EXPECT_EQ(0U, packed[ndx++]); // flags
// offset_deltas and r_infos for next 2 relocations
EXPECT_EQ(0x2008U, packed[ndx++]); // offset delta
EXPECT_EQ(1042U, packed[ndx++]); // r_info
EXPECT_EQ(0x8U, packed[ndx++]); // offset delta
EXPECT_EQ(3442U, packed[ndx++]); // r_info
EXPECT_EQ(packed.size(), ndx);
}
TEST(Delta, Decode) {
std::vector<ELF::Sxword> packed;
std::vector<ELF::Rela> relocations;
TEST(Delta, Encode32) {
encode<ELF32_traits>();
}
RelocationDeltaCodec codec;
TEST(Delta, Encode64) {
encode<ELF64_traits>();
}
template <typename ELF>
static void decode() {
std::vector<typename ELF::Addr> packed;
std::vector<typename ELF::Rela> relocations;
RelocationDeltaCodec<ELF> codec;
codec.Decode(packed, &relocations);
EXPECT_EQ(0, relocations.size());
EXPECT_EQ(0U, relocations.size());
// Six pairs.
packed.push_back(6);
packed.push_back(6U); // count
packed.push_back(0xc0ddfffc); // base offset
packed.push_back(3U); // group count
packed.push_back(11U); // flags
packed.push_back(4U); // offset delta
packed.push_back(11U); // info
// Initial relocation.
packed.push_back(0xc0de0000);
packed.push_back(10000);
packed.push_back(10000U);
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
packed.push_back(4);
packed.push_back(12);
packed.push_back(4);
packed.push_back(12);
packed.push_back(12U); // addend
packed.push_back(12U); // addend
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
packed.push_back(8);
packed.push_back(-24);
packed.push_back(8);
packed.push_back(-24);
packed.push_back(8);
packed.push_back(-24);
packed.push_back(1U); // group count
packed.push_back(9U); // flags
packed.push_back(11U); // info
packed.push_back(8U);
packed.push_back(static_cast<typename ELF::Addr>(-24));
// next group with 2 relocs
packed.push_back(2U); // group count
packed.push_back(11U); // flags
packed.push_back(8U); // offset
packed.push_back(42U); // info
packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
relocations.clear();
codec.Decode(packed, &relocations);
EXPECT_EQ(6, relocations.size());
EXPECT_EQ(6U, relocations.size());
// Initial relocation.
EXPECT_TRUE(CheckRelocation(0xc0de0000, 10000, relocations[0]));
EXPECT_TRUE(CheckRelocation(0xc0de0000, 11U, 10000, relocations[0]));
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0004, 10012, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xc0de0008, 10024, relocations[2]));
EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2]));
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0010, 10000, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xc0de0018, 9976, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xc0de0020, 9952, relocations[5]));
EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xc0de0020, 42U, 9952, relocations[5]));
}
TEST(Delta, Decode32) {
decode<ELF32_traits>();
}
TEST(Delta, Decode64) {
decode<ELF64_traits>();
}
// TODO (dimitry): add more tests (fix by 19 January 2038 03:14:07 UTC)
// TODO (dimtiry): 1. Incorrect packed array for decode
// TODO (dimtiry): 2. Try to catch situation where it is likely to get series of groups with size 1
} // namespace relocation_packer

File diff suppressed because it is too large Load Diff

View File

@ -67,12 +67,13 @@ namespace relocation_packer {
// An ElfFile reads shared objects, and shuttles relative relocations
// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn
// sections.
template <typename ELF>
class ElfFile {
public:
explicit ElfFile(int fd)
: fd_(fd), is_padding_relocations_(false), elf_(NULL),
relocations_section_(NULL), dynamic_section_(NULL),
android_relocations_section_(NULL), relocations_type_(NONE) {}
relocations_type_(NONE) {}
~ElfFile() {}
// Set padding mode. When padding, PackRelocations() will not shrink
@ -92,6 +93,10 @@ class ElfFile {
bool UnpackRelocations();
private:
enum relocations_type_t {
NONE = 0, REL, RELA
};
// Load a new ElfFile from a filedescriptor. If flushing, the file must
// be open for read/write. Returns true on successful ELF file load.
// |fd| is an open file descriptor for the shared object.
@ -99,17 +104,34 @@ class ElfFile {
// Templated packer, helper for PackRelocations(). Rel type is one of
// ELF::Rel or ELF::Rela.
template <typename Rel>
bool PackTypedRelocations(const std::vector<Rel>& relocations);
bool PackTypedRelocations(std::vector<typename ELF::Rela>* relocations);
// Templated unpacker, helper for UnpackRelocations(). Rel type is one of
// ELF::Rel or ELF::Rela.
template <typename Rel>
bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
// Write ELF file changes.
void Flush();
void AdjustRelativeRelocationTargets(typename ELF::Off hole_start,
ssize_t hole_size,
std::vector<typename ELF::Rela>* relocations);
static void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
typename ELF::Word new_sh_type, relocations_type_t relocations_type);
static void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
typename ELF::Off hole_start,
ssize_t hole_size,
relocations_type_t relocations_type);
static void ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, size_t rel_array_size,
std::vector<typename ELF::Rela>* rela_vector);
static void ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
std::vector<typename ELF::Rel>* rel_vector);
// File descriptor opened on the shared object.
int fd_;
@ -123,10 +145,9 @@ class ElfFile {
// Sections that we manipulate, assigned by Load().
Elf_Scn* relocations_section_;
Elf_Scn* dynamic_section_;
Elf_Scn* android_relocations_section_;
// Relocation type found, assigned by Load().
enum { NONE = 0, REL, RELA } relocations_type_;
relocations_type_t relocations_type_;
};
} // namespace relocation_packer

View File

@ -11,12 +11,7 @@
#include <vector>
#include "debug.h"
#include "elf_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
// Macro stringification.
// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html
#define XSTR(S) STR(S)
#define STR(S) #S
#include "gtest/gtest.h"
namespace {
@ -27,8 +22,6 @@ void GetDataFilePath(const char* name, std::string* path) {
if (bindir) {
data_dir = std::string(bindir);
} else {
// Test data is in the gyp INTERMEDIATE_DIR subdirectory of the directory
// that contains the current binary.
char path[PATH_MAX];
memset(path, 0, sizeof(path));
ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1));
@ -37,8 +30,7 @@ void GetDataFilePath(const char* name, std::string* path) {
size_t pos = data_dir.rfind('/');
ASSERT_NE(std::string::npos, pos);
data_dir.erase(pos + 1);
data_dir += std::string(XSTR(INTERMEDIATE_DIR));
data_dir.erase(pos);
}
*path = data_dir + "/" + name;
@ -49,7 +41,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) {
GetDataFilePath(name, &path);
FILE* testfile = fopen(path.c_str(), "rb");
ASSERT_FALSE(testfile == NULL);
ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'";
FILE* temporary = tmpfile();
ASSERT_FALSE(temporary == NULL);
@ -70,15 +62,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) {
*stream = temporary;
}
void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) {
const char* arch = NULL;
if (ELF::kMachine == EM_ARM) {
arch = "arm32";
} else if (ELF::kMachine == EM_AARCH64) {
arch = "arm64";
}
ASSERT_FALSE(arch == NULL);
void OpenRelocsTestFiles(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) {
const std::string base = std::string("elf_file_unittest_relocs_") + arch;
const std::string relocs = base + ".so";
const std::string packed_relocs = base + "_packed.so";
@ -115,41 +99,9 @@ void CheckFileContentsEqual(FILE* first, FILE* second) {
EXPECT_TRUE(feof(first) && feof(second));
}
} // namespace
namespace relocation_packer {
TEST(ElfFile, PackRelocations) {
ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
if (HasFatalFailure())
return;
ElfFile elf_file(fileno(relocs_so));
// Ensure unpacking fails (not packed).
EXPECT_FALSE(elf_file.UnpackRelocations());
// Pack relocations, and check files are now identical.
EXPECT_TRUE(elf_file.PackRelocations());
CheckFileContentsEqual(relocs_so, packed_relocs_so);
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
}
TEST(ElfFile, UnpackRelocations) {
ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
if (HasFatalFailure())
return;
ElfFile elf_file(fileno(packed_relocs_so));
template <typename ELF>
static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
relocation_packer::ElfFile<ELF> elf_file(fileno(packed_relocs_so));
// Ensure packing fails (already packed).
EXPECT_FALSE(elf_file.PackRelocations());
@ -161,4 +113,76 @@ TEST(ElfFile, UnpackRelocations) {
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
}
static void RunUnpackRelocationsTestFor(const std::string& arch) {
ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
if (relocs_so != NULL && packed_relocs_so != NULL) {
// lets detect elf class
ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET))
<< "Invalid file length: " << strerror(errno);
uint8_t elf_class = 0;
ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so));
ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET));
if (elf_class == ELFCLASS32) {
ProcessUnpack<ELF32_traits>(relocs_so, packed_relocs_so);
} else {
ProcessUnpack<ELF64_traits>(relocs_so, packed_relocs_so);
}
}
}
template <typename ELF>
static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) {
relocation_packer::ElfFile<ELF> elf_file(fileno(relocs_so));
// Ensure unpacking fails (not packed).
EXPECT_FALSE(elf_file.UnpackRelocations());
// Pack relocations, and check files are now identical.
EXPECT_TRUE(elf_file.PackRelocations());
CheckFileContentsEqual(relocs_so, packed_relocs_so);
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
}
static void RunPackRelocationsTestFor(const std::string& arch) {
ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
if (relocs_so != NULL && packed_relocs_so != NULL) {
// lets detect elf class
ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET))
<< "Invalid file length: " << strerror(errno);
uint8_t elf_class = 0;
ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so));
fseek(packed_relocs_so, 0, SEEK_SET);
if (elf_class == ELFCLASS32) {
ProcessPack<ELF32_traits>(relocs_so, packed_relocs_so);
} else {
ProcessPack<ELF64_traits>(relocs_so, packed_relocs_so);
}
}
}
} // namespace
namespace relocation_packer {
TEST(ElfFile, PackRelocations) {
RunPackRelocationsTestFor("arm32");
RunPackRelocationsTestFor("arm64");
}
TEST(ElfFile, UnpackRelocations) {
RunUnpackRelocationsTestFor("arm32");
RunUnpackRelocationsTestFor("arm64");
}
} // namespace relocation_packer

View File

@ -10,31 +10,10 @@
#include "elf.h"
#include "libelf.h"
// The TARGET_ macro controls which Elf types we expect and handle.
// Either TARGET_ARM or TARGET_ARM64 must be defined, but not both.
#if !defined(TARGET_ARM) && !defined(TARGET_ARM64)
# error "Unsupported target, define one of TARGET_ARM or TARGET_ARM64"
#elif defined(TARGET_ARM) && defined(TARGET_ARM64)
# error "Define one of TARGET_ARM or TARGET_ARM64, but not both"
#endif
// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h.
#ifndef EM_AARCH64
#define EM_AARCH64 183
#endif
#ifndef R_AARCH64_RELATIVE
#define R_AARCH64_RELATIVE 1027
#endif
#ifndef R_AARCH64_NONE
#define R_AARCH64_NONE 0
#endif
// ELF is a traits structure used to provide convenient aliases for
// 32/64 bit Elf types and functions, depending on the target specified.
// 32/64 bit Elf types and functions, depending on the target file.
#if defined(TARGET_ARM)
struct ELF {
struct ELF32_traits {
typedef Elf32_Addr Addr;
typedef Elf32_Dyn Dyn;
typedef Elf32_Ehdr Ehdr;
@ -48,27 +27,17 @@ struct ELF {
typedef Elf32_Sym Sym;
typedef Elf32_Word Word;
typedef Elf32_Xword Xword;
typedef Elf32_Half Half;
static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
enum { kMachine = EM_ARM };
enum { kFileClass = ELFCLASS32 };
enum { kRelativeRelocationCode = R_ARM_RELATIVE };
enum { kNoRelocationCode = R_ARM_NONE };
enum { kGnuStackSegmentAlignment = 0 };
static inline const char* Machine() { return "ARM"; }
# define ELF_R_SYM(val) ELF32_R_SYM(val)
# define ELF_R_TYPE(val) ELF32_R_TYPE(val)
# define ELF_R_INFO(sym, type) ELF32_R_INFO(sym, type)
# define ELF_ST_TYPE(val) ELF32_ST_TYPE(val)
static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); }
static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); }
static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); }
};
#elif defined(TARGET_ARM64)
struct ELF {
struct ELF64_traits {
typedef Elf64_Addr Addr;
typedef Elf64_Dyn Dyn;
typedef Elf64_Ehdr Ehdr;
@ -82,24 +51,14 @@ struct ELF {
typedef Elf64_Sym Sym;
typedef Elf64_Word Word;
typedef Elf64_Xword Xword;
typedef Elf64_Half Half;
static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
enum { kMachine = EM_AARCH64 };
enum { kFileClass = ELFCLASS64 };
enum { kRelativeRelocationCode = R_AARCH64_RELATIVE };
enum { kNoRelocationCode = R_AARCH64_NONE };
enum { kGnuStackSegmentAlignment = 16 };
static inline const char* Machine() { return "ARM64"; }
# define ELF_R_SYM(val) ELF64_R_SYM(val)
# define ELF_R_TYPE(val) ELF64_R_TYPE(val)
# define ELF_R_INFO(sym, type) ELF64_R_INFO(sym, type)
# define ELF_ST_TYPE(val) ELF64_ST_TYPE(val)
static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); }
static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); }
static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); }
};
#endif
#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_

View File

@ -12,40 +12,50 @@
namespace relocation_packer {
// Empty constructor and destructor to silence chromium-style.
Leb128Encoder::Leb128Encoder() { }
Leb128Encoder::~Leb128Encoder() { }
template <typename uint_t>
Leb128Encoder<uint_t>::Leb128Encoder() { }
template <typename uint_t>
Leb128Encoder<uint_t>::~Leb128Encoder() { }
// Add a single value to the encoding. Values are encoded with variable
// length. The least significant 7 bits of each byte hold 7 bits of data,
// and the most significant bit is set on each byte except the last.
void Leb128Encoder::Enqueue(ELF::Xword value) {
template <typename uint_t>
void Leb128Encoder<uint_t>::Enqueue(uint_t value) {
uint_t uvalue = static_cast<uint_t>(value);
do {
const uint8_t byte = value & 127;
value >>= 7;
encoding_.push_back((value ? 128 : 0) | byte);
} while (value);
const uint8_t byte = uvalue & 127;
uvalue >>= 7;
encoding_.push_back((uvalue ? 128 : 0) | byte);
} while (uvalue);
}
// Add a vector of values to the encoding.
void Leb128Encoder::EnqueueAll(const std::vector<ELF::Xword>& values) {
for (size_t i = 0; i < values.size(); ++i)
template <typename uint_t>
void Leb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
for (size_t i = 0; i < values.size(); ++i) {
Enqueue(values[i]);
}
}
// Create a new decoder for the given encoded stream.
Leb128Decoder::Leb128Decoder(const std::vector<uint8_t>& encoding) {
template <typename uint_t>
Leb128Decoder<uint_t>::Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
encoding_ = encoding;
cursor_ = 0;
cursor_ = start_with;
}
// Empty destructor to silence chromium-style.
Leb128Decoder::~Leb128Decoder() { }
template <typename uint_t>
Leb128Decoder<uint_t>::~Leb128Decoder() { }
// Decode and retrieve a single value from the encoding. Read forwards until
// a byte without its most significant bit is found, then read the 7 bit
// fields of the bytes spanned to re-form the value.
ELF::Xword Leb128Decoder::Dequeue() {
ELF::Xword value = 0;
template <typename uint_t>
uint_t Leb128Decoder<uint_t>::Dequeue() {
uint_t value = 0;
size_t shift = 0;
uint8_t byte;
@ -53,7 +63,7 @@ ELF::Xword Leb128Decoder::Dequeue() {
// Loop until we reach a byte with its high order bit clear.
do {
byte = encoding_[cursor_++];
value |= static_cast<ELF::Xword>(byte & 127) << shift;
value |= static_cast<uint_t>(byte & 127) << shift;
shift += 7;
} while (byte & 128);
@ -61,9 +71,17 @@ ELF::Xword Leb128Decoder::Dequeue() {
}
// Decode and retrieve all remaining values from the encoding.
void Leb128Decoder::DequeueAll(std::vector<ELF::Xword>* values) {
while (cursor_ < encoding_.size())
template <typename uint_t>
void Leb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
while (cursor_ < encoding_.size()) {
values->push_back(Dequeue());
}
}
template class Leb128Encoder<uint32_t>;
template class Leb128Encoder<uint64_t>;
template class Leb128Decoder<uint32_t>;
template class Leb128Decoder<uint64_t>;
} // namespace relocation_packer

View File

@ -21,6 +21,7 @@
namespace relocation_packer {
// Encode packed words as a LEB128 byte stream.
template <typename uint_t>
class Leb128Encoder {
public:
// Explicit (but empty) constructor and destructor, for chromium-style.
@ -29,11 +30,11 @@ class Leb128Encoder {
// Add a value to the encoding stream.
// |value| is the unsigned int to add.
void Enqueue(ELF::Xword value);
void Enqueue(uint_t value);
// Add a vector of values to the encoding stream.
// |values| is the vector of unsigned ints to add.
void EnqueueAll(const std::vector<ELF::Xword>& values);
void EnqueueAll(const std::vector<uint_t>& values);
// Retrieve the encoded representation of the values.
// |encoding| is the returned vector of encoded data.
@ -45,21 +46,22 @@ class Leb128Encoder {
};
// Decode a LEB128 byte stream to produce packed words.
template <typename uint_t>
class Leb128Decoder {
public:
// Create a new decoder for the given encoded stream.
// |encoding| is the vector of encoded data.
explicit Leb128Decoder(const std::vector<uint8_t>& encoding);
explicit Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
// Explicit (but empty) destructor, for chromium-style.
~Leb128Decoder();
// Retrieve the next value from the encoded stream.
ELF::Xword Dequeue();
uint_t Dequeue();
// Retrieve all remaining values from the encoded stream.
// |values| is the vector of decoded data.
void DequeueAll(std::vector<ELF::Xword>* values);
void DequeueAll(std::vector<uint_t>* values);
private:
// Encoded LEB128 stream.

View File

@ -5,19 +5,19 @@
#include "leb128.h"
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "gtest/gtest.h"
namespace relocation_packer {
TEST(Leb128, Encoder) {
std::vector<ELF::Xword> values;
TEST(Leb128, Encoder64) {
std::vector<uint64_t> values;
values.push_back(624485);
values.push_back(0);
values.push_back(1);
values.push_back(127);
values.push_back(128);
Leb128Encoder encoder;
Leb128Encoder<uint64_t> encoder;
encoder.EnqueueAll(values);
encoder.Enqueue(4294967295);
@ -26,7 +26,7 @@ TEST(Leb128, Encoder) {
std::vector<uint8_t> encoding;
encoder.GetEncoding(&encoding);
EXPECT_EQ(23, encoding.size());
EXPECT_EQ(23U, encoding.size());
// 624485
EXPECT_EQ(0xe5, encoding[0]);
EXPECT_EQ(0x8e, encoding[1]);
@ -59,7 +59,7 @@ TEST(Leb128, Encoder) {
EXPECT_EQ(0x01, encoding[22]);
}
TEST(Leb128, Decoder) {
TEST(Leb128, Decoder64) {
std::vector<uint8_t> encoding;
// 624485
encoding.push_back(0xe5);
@ -92,20 +92,20 @@ TEST(Leb128, Decoder) {
encoding.push_back(0xff);
encoding.push_back(0x01);
Leb128Decoder decoder(encoding);
Leb128Decoder<uint64_t> decoder(encoding, 0);
EXPECT_EQ(624485, decoder.Dequeue());
EXPECT_EQ(624485U, decoder.Dequeue());
std::vector<ELF::Xword> dequeued;
std::vector<uint64_t> dequeued;
decoder.DequeueAll(&dequeued);
EXPECT_EQ(6, dequeued.size());
EXPECT_EQ(0, dequeued[0]);
EXPECT_EQ(1, dequeued[1]);
EXPECT_EQ(127, dequeued[2]);
EXPECT_EQ(128, dequeued[3]);
EXPECT_EQ(4294967295, dequeued[4]);
EXPECT_EQ(18446744073709551615ul, dequeued[5]);
EXPECT_EQ(6U, dequeued.size());
EXPECT_EQ(0U, dequeued[0]);
EXPECT_EQ(1U, dequeued[1]);
EXPECT_EQ(127U, dequeued[2]);
EXPECT_EQ(128U, dequeued[3]);
EXPECT_EQ(4294967295U, dequeued[4]);
EXPECT_EQ(18446744073709551615UL, dequeued[5]);
}
} // namespace relocation_packer

View File

@ -25,11 +25,12 @@
#include "debug.h"
#include "elf_file.h"
#include "elf_traits.h"
#include "libelf.h"
namespace {
#include "nativehelper/ScopedFd.h"
void PrintUsage(const char* argv0) {
static void PrintUsage(const char* argv0) {
std::string temporary = argv0;
const size_t last_slash = temporary.find_last_of("/");
if (last_slash != temporary.npos) {
@ -45,59 +46,11 @@ void PrintUsage(const char* argv0) {
" -p, --pad do not shrink relocations, but pad (for debugging)\n\n",
basename);
if (ELF::kMachine == EM_ARM) {
printf(
"Extracts relative relocations from the .rel.dyn section, packs them\n"
"into a more compact format, and stores the packed relocations in\n"
".android.rel.dyn. Expands .android.rel.dyn to hold the packed\n"
"data, and shrinks .rel.dyn by the amount of unpacked data removed\n"
"from it.\n\n"
"Before being packed, a shared library needs to be prepared by adding\n"
"a null .android.rel.dyn section.\n\n"
"To pack relocations in a shared library:\n\n"
" echo -n 'NULL' >/tmp/small\n"
" arm-linux-androideabi-objcopy \\\n"
" --add-section .android.rel.dyn=/tmp/small \\\n"
" libchrome.<version>.so\n"
" rm /tmp/small\n"
" %s libchrome.<version>.so\n\n"
"To unpack and restore the shared library to its original state:\n\n"
" %s -u libchrome.<version>.so\n"
" arm-linux-androideabi-objcopy \\\n"
" --remove-section=.android.rel.dyn libchrome.<version>.so\n\n",
basename, basename);
} else if (ELF::kMachine == EM_AARCH64) {
printf(
"Extracts relative relocations from the .rela.dyn section, packs them\n"
"into a more compact format, and stores the packed relocations in\n"
".android.rela.dyn. Expands .android.rela.dyn to hold the packed\n"
"data, and shrinks .rela.dyn by the amount of unpacked data removed\n"
"from it.\n\n"
"Before being packed, a shared library needs to be prepared by adding\n"
"a null .android.rela.dyn section.\n\n"
"To pack relocations in a shared library:\n\n"
" echo -n 'NULL' >/tmp/small\n"
" aarch64-linux-android-objcopy \\\n"
" --add-section .android.rela.dyn=/tmp/small \\\n"
" libchrome.<version>.so\n"
" rm /tmp/small\n"
" %s libchrome.<version>.so\n\n"
"To unpack and restore the shared library to its original state:\n\n"
" %s -u libchrome.<version>.so\n"
" aarch64-linux-android-objcopy \\\n"
" --remove-section=.android.rela.dyn libchrome.<version>.so\n\n",
basename, basename);
} else {
NOTREACHED();
}
printf(
"Debug sections are not handled, so packing should not be used on\n"
"shared libraries compiled for debugging or otherwise unstripped.\n");
}
} // namespace
int main(int argc, char* argv[]) {
bool is_unpacking = false;
bool is_verbose = false;
@ -143,11 +96,9 @@ int main(int argc, char* argv[]) {
LOG(WARNING) << "Elf Library is out of date!";
}
LOG(INFO) << "Configured for " << ELF::Machine();
const char* file = argv[argc - 1];
const int fd = open(file, O_RDWR);
if (fd == -1) {
ScopedFd fd(open(file, O_RDWR));
if (fd.get() == -1) {
LOG(ERROR) << file << ": " << strerror(errno);
return 1;
}
@ -155,16 +106,43 @@ int main(int argc, char* argv[]) {
if (is_verbose)
relocation_packer::Logger::SetVerbose(1);
relocation_packer::ElfFile elf_file(fd);
elf_file.SetPadding(is_padding);
// We need to detect elf class in order to create
// correct implementation
uint8_t e_ident[EI_NIDENT];
if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) {
LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno);
return 1;
}
bool status;
if (is_unpacking)
status = elf_file.UnpackRelocations();
else
status = elf_file.PackRelocations();
if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
return 1;
}
close(fd);
bool status = false;
if (e_ident[EI_CLASS] == ELFCLASS32) {
relocation_packer::ElfFile<ELF32_traits> elf_file(fd.get());
elf_file.SetPadding(is_padding);
if (is_unpacking) {
status = elf_file.UnpackRelocations();
} else {
status = elf_file.PackRelocations();
}
} else if (e_ident[EI_CLASS] == ELFCLASS64) {
relocation_packer::ElfFile<ELF64_traits> elf_file(fd.get());
elf_file.SetPadding(is_padding);
if (is_unpacking) {
status = elf_file.UnpackRelocations();
} else {
status = elf_file.PackRelocations();
}
} else {
LOG(ERROR) << file << ": unknown ELFCLASS: " << e_ident[EI_CLASS];
return 1;
}
if (!status) {
LOG(ERROR) << file << ": failed to pack/unpack file";

View File

@ -10,115 +10,79 @@
#include "delta_encoder.h"
#include "elf_traits.h"
#include "leb128.h"
#include "run_length_encoder.h"
#include "sleb128.h"
namespace relocation_packer {
// Pack relative relocations into a run-length encoded packed
// representation.
void RelocationPacker::PackRelativeRelocations(
const std::vector<ELF::Rel>& relocations,
std::vector<uint8_t>* packed) {
// Pack relocations into a group encoded packed representation.
template <typename ELF>
void RelocationPacker<ELF>::PackRelocations(const std::vector<typename ELF::Rela>& relocations,
std::vector<uint8_t>* packed) {
// Run-length encode.
std::vector<ELF::Xword> packed_words;
RelocationRunLengthCodec codec;
std::vector<typename ELF::Addr> packed_words;
RelocationDeltaCodec<ELF> codec;
codec.Encode(relocations, &packed_words);
// If insufficient data to run-length encode, do nothing.
// If insufficient data do nothing.
if (packed_words.empty())
return;
// LEB128 encode, with "APR1" prefix.
Leb128Encoder encoder;
encoder.Enqueue('A');
encoder.Enqueue('P');
encoder.Enqueue('R');
encoder.Enqueue('1');
encoder.EnqueueAll(packed_words);
Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
Leb128Encoder<typename ELF::Addr> leb128_encoder;
encoder.GetEncoding(packed);
std::vector<uint8_t> leb128_packed;
std::vector<uint8_t> sleb128_packed;
// Pad packed to a whole number of words. This padding will decode as
// LEB128 zeroes. Run-length decoding ignores it because encoding
// embeds the pairs count in the stream itself.
while (packed->size() % sizeof(ELF::Word))
packed->push_back(0);
leb128_encoder.EnqueueAll(packed_words);
leb128_encoder.GetEncoding(&leb128_packed);
sleb128_encoder.EnqueueAll(packed_words);
sleb128_encoder.GetEncoding(&sleb128_packed);
// TODO (simonb): Estimate savings on current android system image and consider using
// one encoder for all packed relocations to reduce complexity.
if (leb128_packed.size() <= sleb128_packed.size()) {
packed->push_back('A');
packed->push_back('P');
packed->push_back('U');
packed->push_back('2');
packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end());
} else {
packed->push_back('A');
packed->push_back('P');
packed->push_back('S');
packed->push_back('2');
packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end());
}
}
// Unpack relative relocations from a run-length encoded packed
// representation.
void RelocationPacker::UnpackRelativeRelocations(
template <typename ELF>
void RelocationPacker<ELF>::UnpackRelocations(
const std::vector<uint8_t>& packed,
std::vector<ELF::Rel>* relocations) {
// LEB128 decode, after checking and stripping "APR1" prefix.
std::vector<ELF::Xword> packed_words;
Leb128Decoder decoder(packed);
CHECK(decoder.Dequeue() == 'A' &&
decoder.Dequeue() == 'P' &&
decoder.Dequeue() == 'R' &&
decoder.Dequeue() == '1');
decoder.DequeueAll(&packed_words);
std::vector<typename ELF::Rela>* relocations) {
// Run-length decode.
RelocationRunLengthCodec codec;
std::vector<typename ELF::Addr> packed_words;
CHECK(packed.size() > 4 &&
packed[0] == 'A' &&
packed[1] == 'P' &&
(packed[2] == 'U' || packed[2] == 'S') &&
packed[3] == '2');
if (packed[2] == 'U') {
Leb128Decoder<typename ELF::Addr> decoder(packed, 4);
decoder.DequeueAll(&packed_words);
} else {
Sleb128Decoder<typename ELF::Addr> decoder(packed, 4);
decoder.DequeueAll(&packed_words);
}
RelocationDeltaCodec<ELF> codec;
codec.Decode(packed_words, relocations);
}
// Pack relative relocations with addends into a delta encoded packed
// representation.
void RelocationPacker::PackRelativeRelocations(
const std::vector<ELF::Rela>& relocations,
std::vector<uint8_t>* packed) {
// Delta encode.
std::vector<ELF::Sxword> packed_words;
RelocationDeltaCodec codec;
codec.Encode(relocations, &packed_words);
// If insufficient data to delta encode, do nothing.
if (packed_words.empty())
return;
// Signed LEB128 encode, with "APA1" prefix. ASCII does not encode as
// itself under signed LEB128, so we have to treat it specially.
Sleb128Encoder encoder;
encoder.EnqueueAll(packed_words);
std::vector<uint8_t> encoded;
encoder.GetEncoding(&encoded);
packed->push_back('A');
packed->push_back('P');
packed->push_back('A');
packed->push_back('1');
packed->insert(packed->end(), encoded.begin(), encoded.end());
// Pad packed to a whole number of words. This padding will decode as
// signed LEB128 zeroes. Delta decoding ignores it because encoding
// embeds the pairs count in the stream itself.
while (packed->size() % sizeof(ELF::Word))
packed->push_back(0);
}
// Unpack relative relocations with addends from a delta encoded
// packed representation.
void RelocationPacker::UnpackRelativeRelocations(
const std::vector<uint8_t>& packed,
std::vector<ELF::Rela>* relocations) {
// Check "APA1" prefix.
CHECK(packed.at(0) == 'A' &&
packed.at(1) == 'P' &&
packed.at(2) == 'A' &&
packed.at(3) == '1');
// Signed LEB128 decode, after stripping "APA1" prefix.
std::vector<ELF::Sxword> packed_words;
std::vector<uint8_t> stripped(packed.begin() + 4, packed.end());
Sleb128Decoder decoder(stripped);
decoder.DequeueAll(&packed_words);
// Delta decode.
RelocationDeltaCodec codec;
codec.Decode(packed_words, relocations);
}
template class RelocationPacker<ELF32_traits>;
template class RelocationPacker<ELF64_traits>;
} // namespace relocation_packer

View File

@ -48,29 +48,25 @@
#include <vector>
#include "elf.h"
#include "elf_traits.h"
namespace relocation_packer {
// A RelocationPacker packs vectors of relative relocations into more
// A RelocationPacker packs vectors of relocations into more
// compact forms, and unpacks them to reproduce the pre-packed data.
template <typename ELF>
class RelocationPacker {
public:
// Pack relative relocations into a more compact form.
// |relocations| is a vector of relative relocation structs.
// Pack relocations into a more compact form.
// |relocations| is a vector of relocation structs.
// |packed| is the vector of packed bytes into which relocations are packed.
static void PackRelativeRelocations(const std::vector<ELF::Rel>& relocations,
std::vector<uint8_t>* packed);
static void PackRelativeRelocations(const std::vector<ELF::Rela>& relocations,
std::vector<uint8_t>* packed);
static void PackRelocations(const std::vector<typename ELF::Rela>& relocations,
std::vector<uint8_t>* packed);
// Unpack relative relocations from their more compact form.
// Unpack relocations from their more compact form.
// |packed| is the vector of packed relocations.
// |relocations| is a vector of unpacked relative relocation structs.
static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
std::vector<ELF::Rel>* relocations);
static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
std::vector<ELF::Rela>* relocations);
// |relocations| is a vector of unpacked relocation structs.
static void UnpackRelocations(const std::vector<uint8_t>& packed,
std::vector<typename ELF::Rela>* relocations);
};
} // namespace relocation_packer

View File

@ -7,244 +7,286 @@
#include <vector>
#include "elf.h"
#include "elf_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "gtest/gtest.h"
namespace {
void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
ELF::Rel relocation;
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 = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocations->push_back(relocation);
}
bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
return relocation.r_offset == addr &&
ELF_R_SYM(relocation.r_info) == 0 &&
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
}
void AddRelocation(ELF::Addr addr,
ELF::Sxword addend,
std::vector<ELF::Rela>* relocations) {
ELF::Rela relocation;
relocation.r_offset = addr;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocation.r_info = info;
relocation.r_addend = addend;
relocations->push_back(relocation);
}
bool CheckRelocation(ELF::Addr addr,
ELF::Sxword addend,
const ELF::Rela& 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 &&
ELF_R_SYM(relocation.r_info) == 0 &&
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
relocation.r_info == info &&
relocation.r_addend == addend;
}
} // namespace
namespace relocation_packer {
TEST(Packer, PackRel) {
std::vector<ELF::Rel> relocations;
template <typename ELF>
static void DoPackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed;
RelocationPacker packer;
// Initial relocation.
AddRelocation(0xd1ce0000, &relocations);
AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
// Two more relocations, 4 byte deltas.
AddRelocation(0xd1ce0004, &relocations);
AddRelocation(0xd1ce0008, &relocations);
AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
// Three more relocations, 8 byte deltas.
AddRelocation(0xd1ce0010, &relocations);
AddRelocation(0xd1ce0018, &relocations);
AddRelocation(0xd1ce0020, &relocations);
AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
RelocationPacker<ELF> packer;
packed.clear();
packer.PackRelativeRelocations(relocations, &packed);
packer.PackRelocations(relocations, &packed);
EXPECT_EQ(16, packed.size());
ASSERT_EQ(18U, packed.size());
// Identifier.
EXPECT_EQ('A', packed[0]);
EXPECT_EQ('P', packed[1]);
EXPECT_EQ('R', packed[2]);
EXPECT_EQ('1', packed[3]);
// Count-delta pairs count.
EXPECT_EQ(2, packed[4]);
// 0xd1ce0000
EXPECT_EQ(128, packed[5]);
EXPECT_EQ(128, packed[6]);
EXPECT_EQ(184, packed[7]);
EXPECT_EQ(142, packed[8]);
EXPECT_EQ(13, packed[9]);
// Run of two relocations, 4 byte deltas.
EXPECT_EQ(2, packed[10]);
EXPECT_EQ(4, packed[11]);
// Run of three relocations, 8 byte deltas.
EXPECT_EQ(3, packed[12]);
EXPECT_EQ(8, packed[13]);
// Padding.
EXPECT_EQ(0, packed[14]);
EXPECT_EQ(0, packed[15]);
size_t ndx = 0;
EXPECT_EQ('A', packed[ndx++]);
EXPECT_EQ('P', packed[ndx++]);
EXPECT_EQ('U', packed[ndx++]);
EXPECT_EQ('2', packed[ndx++]);
// relocation count
EXPECT_EQ(6, packed[ndx++]);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
EXPECT_EQ(0xfc, packed[ndx++]);
EXPECT_EQ(0xff, packed[ndx++]);
EXPECT_EQ(0xb7, packed[ndx++]);
EXPECT_EQ(0x8e, packed[ndx++]);
EXPECT_EQ(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, UnpackRel) {
TEST(Packer, PackNoAddend) {
DoPackNoAddend<ELF32_traits>();
DoPackNoAddend<ELF64_traits>();
}
template <typename ELF>
static void DoUnpackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed;
std::vector<ELF::Rel> relocations;
RelocationPacker packer;
// Identifier.
packed.push_back('A');
packed.push_back('P');
packed.push_back('R');
packed.push_back('1');
// Count-delta pairs count.
packed.push_back(2);
// 0xd1ce0000
packed.push_back(128);
packed.push_back(128);
packed.push_back(184);
packed.push_back(142);
packed.push_back(13);
// Run of two relocations, 4 byte deltas.
packed.push_back(2);
packed.push_back(4);
// Run of three relocations, 8 byte deltas.
packed.push_back(3);
packed.push_back(8);
// Padding.
packed.push_back(0);
packed.push_back(0);
relocations.clear();
packer.UnpackRelativeRelocations(packed, &relocations);
EXPECT_EQ(6, relocations.size());
// Initial relocation.
EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0]));
// Two relocations, 4 byte deltas.
EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2]));
// Three relocations, 8 byte deltas.
EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5]));
}
TEST(Packer, PackRela) {
std::vector<ELF::Rela> relocations;
std::vector<uint8_t> packed;
RelocationPacker packer;
// Initial relocation.
AddRelocation(0xd1ce0000, 10000, &relocations);
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
AddRelocation(0xd1ce0004, 10012, &relocations);
AddRelocation(0xd1ce0008, 10024, &relocations);
// Three more relocations, 8 byte deltas, -24 byte addend deltas.
AddRelocation(0xd1ce0010, 10000, &relocations);
AddRelocation(0xd1ce0018, 9976, &relocations);
AddRelocation(0xd1ce0020, 9952, &relocations);
packed.clear();
packer.PackRelativeRelocations(relocations, &packed);
EXPECT_EQ(24, packed.size());
// Identifier.
EXPECT_EQ('A', packed[0]);
EXPECT_EQ('P', packed[1]);
EXPECT_EQ('A', packed[2]);
EXPECT_EQ('1', packed[3]);
// Delta pairs count.
EXPECT_EQ(6, packed[4]);
// 0xd1ce0000
EXPECT_EQ(128, packed[5]);
EXPECT_EQ(128, packed[6]);
EXPECT_EQ(184, packed[7]);
EXPECT_EQ(142, packed[8]);
EXPECT_EQ(13, packed[9]);
// 10000
EXPECT_EQ(144, packed[10]);
EXPECT_EQ(206, packed[11]);
EXPECT_EQ(0, packed[12]);
// 4, 12
EXPECT_EQ(4, packed[13]);
EXPECT_EQ(12, packed[14]);
// 4, 12
EXPECT_EQ(4, packed[15]);
EXPECT_EQ(12, packed[16]);
// 8, -24
EXPECT_EQ(8, packed[17]);
EXPECT_EQ(104, packed[18]);
// 8, -24
EXPECT_EQ(8, packed[19]);
EXPECT_EQ(104, packed[20]);
// 8, -24
EXPECT_EQ(8, packed[21]);
EXPECT_EQ(104, packed[22]);
// Padding.
EXPECT_EQ(0, packed[23]);
}
TEST(Packer, UnpackRela) {
std::vector<uint8_t> packed;
std::vector<ELF::Rela> relocations;
RelocationPacker packer;
// Identifier.
packed.push_back('A');
packed.push_back('P');
packed.push_back('A');
packed.push_back('1');
// Delta pairs count.
packed.push_back('U');
packed.push_back('2');
// relocation count
packed.push_back(6);
// 0xd1ce0000
packed.push_back(128);
packed.push_back(128);
packed.push_back(184);
packed.push_back(142);
packed.push_back(13);
// 10000
packed.push_back(144);
packed.push_back(206);
packed.push_back(0);
// 4, 12
packed.push_back(4);
packed.push_back(12);
// 4, 12
packed.push_back(4);
packed.push_back(12);
// 8, -24
packed.push_back(8);
packed.push_back(104);
// 8, -24
packed.push_back(8);
packed.push_back(104);
// 8, -24
packed.push_back(8);
packed.push_back(104);
// Padding.
packed.push_back(0);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
packed.push_back(0xfc);
packed.push_back(0xff);
packed.push_back(0xb7);
packed.push_back(0x8e);
packed.push_back(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, UnpackNoAddend) {
DoUnpackNoAddend<ELF32_traits>();
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.UnpackRelativeRelocations(packed, &relocations);
packer.UnpackRelocations(packed, &relocations);
EXPECT_EQ(6, relocations.size());
EXPECT_EQ(6U, relocations.size());
size_t ndx = 0;
// Initial relocation.
EXPECT_TRUE(CheckRelocation(0xd1ce0000, 10000, relocations[0]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x01, 10024, relocations[ndx++]));
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xd1ce0004, 10012, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xd1ce0008, 10024, relocations[2]));
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(0xd1ce0010, 10000, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xd1ce0018, 9976, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xd1ce0020, 9952, relocations[5]));
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

View File

@ -1,10 +0,0 @@
// 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 "testing/gtest/include/gtest/gtest.h"
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,144 +0,0 @@
// 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 "run_length_encoder.h"
#include <vector>
#include "debug.h"
#include "elf_traits.h"
namespace relocation_packer {
namespace {
// Generate a vector of deltas between the r_offset fields of adjacent
// relative relocations.
void GetDeltas(const std::vector<ELF::Rel>& relocations,
std::vector<ELF::Addr>* deltas) {
CHECK(relocations.size() >= 2);
for (size_t i = 0; i < relocations.size() - 1; ++i) {
const ELF::Rel* first = &relocations[i];
CHECK(ELF_R_TYPE(first->r_info) == ELF::kRelativeRelocationCode);
const ELF::Rel* second = &relocations[i + 1];
CHECK(ELF_R_TYPE(second->r_info) == ELF::kRelativeRelocationCode);
// Requires that offsets are 'strictly increasing'. The packing
// algorithm fails if this does not hold.
CHECK(second->r_offset > first->r_offset);
deltas->push_back(second->r_offset - first->r_offset);
}
}
// Condense a set of r_offset deltas into a run-length encoded packing.
// Represented as count-delta pairs, where count is the run length and
// delta the common difference between adjacent r_offsets.
void Condense(const std::vector<ELF::Addr>& deltas,
std::vector<ELF::Xword>* packed) {
CHECK(!deltas.empty());
size_t count = 0;
ELF::Addr current = deltas[0];
// Identify spans of identically valued deltas.
for (size_t i = 0; i < deltas.size(); ++i) {
const ELF::Addr delta = deltas[i];
if (delta == current) {
count++;
} else {
// We reached the end of a span of identically valued deltas.
packed->push_back(count);
packed->push_back(current);
current = delta;
count = 1;
}
}
// Write the final span.
packed->push_back(count);
packed->push_back(current);
}
// Uncondense a set of r_offset deltas from a run-length encoded packing.
// The initial address for uncondensing, the start index for the first
// condensed slot in packed, and the count of pairs are provided.
void Uncondense(ELF::Addr addr,
const std::vector<ELF::Xword>& packed,
size_t start_index,
size_t end_index,
std::vector<ELF::Rel>* relocations) {
// The first relocation is just one created from the initial address.
ELF::Rel initial;
initial.r_offset = addr;
initial.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocations->push_back(initial);
// Read each count and delta pair, beginning at the start index and
// finishing at the end index.
for (size_t i = start_index; i < end_index; i += 2) {
size_t count = packed[i];
const ELF::Addr delta = packed[i + 1];
CHECK(count > 0 && delta > 0);
// Generate relocations for this count and delta pair.
while (count) {
addr += delta;
ELF::Rel relocation;
relocation.r_offset = addr;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocations->push_back(relocation);
count--;
}
}
}
} // namespace
// Encode relative relocations into a run-length encoded (packed)
// representation.
void RelocationRunLengthCodec::Encode(const std::vector<ELF::Rel>& relocations,
std::vector<ELF::Xword>* packed) {
// If we have zero or one relocation only then there is no packing
// possible; a run-length encoding needs a run.
if (relocations.size() < 2)
return;
std::vector<ELF::Addr> deltas;
GetDeltas(relocations, &deltas);
// Reserve space for the element count.
packed->push_back(0);
// Initialize the packed data with the first offset, then follow up with
// the condensed deltas vector.
packed->push_back(relocations[0].r_offset);
Condense(deltas, packed);
// Fill in the packed pair count.
packed->at(0) = (packed->size() - 2) >> 1;
}
// Decode relative relocations from a run-length encoded (packed)
// representation.
void RelocationRunLengthCodec::Decode(const std::vector<ELF::Xword>& packed,
std::vector<ELF::Rel>* relocations) {
// We need at least one packed pair after the packed pair count and start
// address to be able to unpack.
if (packed.size() < 4)
return;
// Ensure that the packed data offers enough pairs. There may be zero
// padding on it that we ignore.
CHECK(packed[0] <= (packed.size() - 2) >> 1);
// The first packed vector element is the pairs count and the second the
// initial address. Start uncondensing pairs at the third, and finish
// at the end of the pairs data.
const size_t pairs_count = packed[0];
const ELF::Addr addr = packed[1];
Uncondense(addr, packed, 2, 2 + (pairs_count << 1), relocations);
}
} // namespace relocation_packer

View File

@ -1,124 +0,0 @@
// 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 "run_length_encoder.h"
#include <vector>
#include "elf.h"
#include "elf_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
ELF::Rel relocation;
relocation.r_offset = addr;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocations->push_back(relocation);
}
bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
return relocation.r_offset == addr &&
ELF_R_SYM(relocation.r_info) == 0 &&
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
}
} // namespace
namespace relocation_packer {
TEST(RunLength, Encode) {
std::vector<ELF::Rel> relocations;
std::vector<ELF::Xword> packed;
RelocationRunLengthCodec codec;
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(0, packed.size());
// Add one relocation (insufficient data to encode).
AddRelocation(0xf00d0000, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(0, packed.size());
// Add a second relocation, 4 byte delta (minimum data to encode).
AddRelocation(0xf00d0004, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(4, packed.size());
// One count-delta pair present.
EXPECT_EQ(1, packed[0]);
// Initial relocation.
EXPECT_EQ(0xf00d0000, packed[1]);
// Run of a single relocation, 4 byte delta.
EXPECT_EQ(1, packed[2]);
EXPECT_EQ(4, packed[3]);
// Add a third relocation, 4 byte delta.
AddRelocation(0xf00d0008, &relocations);
// Add three more relocations, 8 byte deltas.
AddRelocation(0xf00d0010, &relocations);
AddRelocation(0xf00d0018, &relocations);
AddRelocation(0xf00d0020, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
EXPECT_EQ(6, packed.size());
// Two count-delta pairs present.
EXPECT_EQ(2, packed[0]);
// Initial relocation.
EXPECT_EQ(0xf00d0000, packed[1]);
// Run of two relocations, 4 byte deltas.
EXPECT_EQ(2, packed[2]);
EXPECT_EQ(4, packed[3]);
// Run of three relocations, 8 byte deltas.
EXPECT_EQ(3, packed[4]);
EXPECT_EQ(8, packed[5]);
}
TEST(RunLength, Decode) {
std::vector<ELF::Xword> packed;
std::vector<ELF::Rel> relocations;
RelocationRunLengthCodec codec;
codec.Decode(packed, &relocations);
EXPECT_EQ(0, relocations.size());
// Two count-delta pairs.
packed.push_back(2);
// Initial relocation.
packed.push_back(0xc0de0000);
// Run of two relocations, 4 byte deltas.
packed.push_back(2);
packed.push_back(4);
// Run of three relocations, 8 byte deltas.
packed.push_back(3);
packed.push_back(8);
relocations.clear();
codec.Decode(packed, &relocations);
EXPECT_EQ(6, relocations.size());
// Initial relocation.
EXPECT_TRUE(CheckRelocation(0xc0de0000, relocations[0]));
// Two relocations, 4 byte deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0004, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xc0de0008, relocations[2]));
// Three relocations, 8 byte deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0010, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xc0de0018, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xc0de0020, relocations[5]));
}
} // namespace relocation_packer

View File

@ -10,11 +10,33 @@
#include "elf_traits.h"
namespace {
template <typename T>
class uint_traits {};
template <>
class uint_traits<uint64_t> {
public:
typedef int64_t int_t;
};
template <>
class uint_traits<uint32_t> {
public:
typedef int32_t int_t;
};
}
namespace relocation_packer {
// Empty constructor and destructor to silence chromium-style.
Sleb128Encoder::Sleb128Encoder() { }
Sleb128Encoder::~Sleb128Encoder() { }
template <typename uint_t>
Sleb128Encoder<uint_t>::Sleb128Encoder() { }
template <typename uint_t>
Sleb128Encoder<uint_t>::~Sleb128Encoder() { }
// Add a single value to the encoding. Values are encoded with variable
// length. The least significant 7 bits of each byte hold 7 bits of data,
@ -22,11 +44,13 @@ Sleb128Encoder::~Sleb128Encoder() { }
// value is sign extended up to a multiple of 7 bits (ensuring that the
// most significant bit is zero for a positive number and one for a
// negative number).
void Sleb128Encoder::Enqueue(ELF::Sxword value) {
template <typename uint_t>
void Sleb128Encoder<uint_t>::Enqueue(uint_t value) {
typedef typename uint_traits<uint_t>::int_t int_t;
static const size_t size = CHAR_BIT * sizeof(value);
bool more = true;
const bool negative = value < 0;
const bool negative = static_cast<int_t>(value) < 0;
while (more) {
uint8_t byte = value & 127;
@ -34,11 +58,11 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) {
// Sign extend if encoding a -ve value.
if (negative)
value |= -(static_cast<ELF::Sxword>(1) << (size - 7));
value |= -(static_cast<uint_t>(1) << (size - 7));
// The sign bit of byte is second high order bit.
const bool sign_bit = byte & 64;
if ((value == 0 && !sign_bit) || (value == -1 && sign_bit))
if ((value == 0 && !sign_bit) || (value == static_cast<uint_t>(-1) && sign_bit))
more = false;
else
byte |= 128;
@ -47,25 +71,30 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) {
}
// Add a vector of values to the encoding.
void Sleb128Encoder::EnqueueAll(const std::vector<ELF::Sxword>& values) {
for (size_t i = 0; i < values.size(); ++i)
template <typename uint_t>
void Sleb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
for (size_t i = 0; i < values.size(); ++i) {
Enqueue(values[i]);
}
}
// Create a new decoder for the given encoded stream.
Sleb128Decoder::Sleb128Decoder(const std::vector<uint8_t>& encoding) {
template <typename uint_t>
Sleb128Decoder<uint_t>::Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
encoding_ = encoding;
cursor_ = 0;
cursor_ = start_with;
}
// Empty destructor to silence chromium-style.
Sleb128Decoder::~Sleb128Decoder() { }
template <typename uint_t>
Sleb128Decoder<uint_t>::~Sleb128Decoder() { }
// Decode and retrieve a single value from the encoding. Consume bytes
// until one without its most significant bit is found, and re-form the
// value from the 7 bit fields of the bytes consumed.
ELF::Sxword Sleb128Decoder::Dequeue() {
ELF::Sxword value = 0;
template <typename uint_t>
uint_t Sleb128Decoder<uint_t>::Dequeue() {
uint_t value = 0;
static const size_t size = CHAR_BIT * sizeof(value);
size_t shift = 0;
@ -74,22 +103,29 @@ ELF::Sxword Sleb128Decoder::Dequeue() {
// Loop until we reach a byte with its high order bit clear.
do {
byte = encoding_[cursor_++];
value |= (static_cast<ELF::Sxword>(byte & 127) << shift);
value |= (static_cast<uint_t>(byte & 127) << shift);
shift += 7;
} while (byte & 128);
// The sign bit is second high order bit of the final byte decoded.
// Sign extend if value is -ve and we did not shift all of it.
if (shift < size && (byte & 64))
value |= -(static_cast<ELF::Sxword>(1) << shift);
value |= -(static_cast<uint_t>(1) << shift);
return value;
return static_cast<uint_t>(value);
}
// Decode and retrieve all remaining values from the encoding.
void Sleb128Decoder::DequeueAll(std::vector<ELF::Sxword>* values) {
while (cursor_ < encoding_.size())
template <typename uint_t>
void Sleb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
while (cursor_ < encoding_.size()) {
values->push_back(Dequeue());
}
}
template class Sleb128Encoder<uint32_t>;
template class Sleb128Encoder<uint64_t>;
template class Sleb128Decoder<uint32_t>;
template class Sleb128Decoder<uint64_t>;
} // namespace relocation_packer

View File

@ -22,6 +22,7 @@
namespace relocation_packer {
// Encode packed words as a signed LEB128 byte stream.
template<typename int_t>
class Sleb128Encoder {
public:
// Explicit (but empty) constructor and destructor, for chromium-style.
@ -30,11 +31,11 @@ class Sleb128Encoder {
// Add a value to the encoding stream.
// |value| is the signed int to add.
void Enqueue(ELF::Sxword value);
void Enqueue(int_t value);
// Add a vector of values to the encoding stream.
// |values| is the vector of signed ints to add.
void EnqueueAll(const std::vector<ELF::Sxword>& values);
void EnqueueAll(const std::vector<int_t>& values);
// Retrieve the encoded representation of the values.
// |encoding| is the returned vector of encoded data.
@ -46,21 +47,22 @@ class Sleb128Encoder {
};
// Decode a LEB128 byte stream to produce packed words.
template <typename int_t>
class Sleb128Decoder {
public:
// Create a new decoder for the given encoded stream.
// |encoding| is the vector of encoded data.
explicit Sleb128Decoder(const std::vector<uint8_t>& encoding);
explicit Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
// Explicit (but empty) destructor, for chromium-style.
~Sleb128Decoder();
// Retrieve the next value from the encoded stream.
ELF::Sxword Dequeue();
int_t Dequeue();
// Retrieve all remaining values from the encoded stream.
// |values| is the vector of decoded data.
void DequeueAll(std::vector<ELF::Sxword>* values);
void DequeueAll(std::vector<int_t>* values);
private:
// Encoded LEB128 stream.

View File

@ -6,27 +6,27 @@
#include <vector>
#include "elf_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "gtest/gtest.h"
namespace relocation_packer {
TEST(Sleb128, Encoder) {
std::vector<ELF::Sxword> values;
values.push_back(624485);
values.push_back(0);
values.push_back(1);
values.push_back(63);
values.push_back(64);
values.push_back(-1);
values.push_back(-624485);
TEST(Sleb128, Encoder64) {
std::vector<uint64_t> values;
values.push_back(624485U);
values.push_back(0U);
values.push_back(1U);
values.push_back(63U);
values.push_back(64U);
values.push_back(static_cast<uint64_t>(-1));
values.push_back(static_cast<uint64_t>(-624485));
Sleb128Encoder encoder;
Sleb128Encoder<uint64_t> encoder;
encoder.EnqueueAll(values);
encoder.Enqueue(2147483647);
encoder.Enqueue(-2147483648);
encoder.Enqueue(9223372036854775807ll);
encoder.Enqueue(-9223372036854775807ll - 1);
encoder.Enqueue(2147483647U);
encoder.Enqueue(static_cast<uint64_t>(-2147483648));
encoder.Enqueue(9223372036854775807ULL);
encoder.Enqueue(static_cast<uint64_t>(-9223372036854775807LL - 1));
std::vector<uint8_t> encoding;
encoder.GetEncoding(&encoding);
@ -143,24 +143,24 @@ TEST(Sleb128, Decoder) {
encoding.push_back(0x80);
encoding.push_back(0x7f);
Sleb128Decoder decoder(encoding);
Sleb128Decoder<uint64_t> decoder(encoding, 0);
EXPECT_EQ(624485, decoder.Dequeue());
EXPECT_EQ(624485U, decoder.Dequeue());
std::vector<ELF::Sxword> dequeued;
std::vector<uint64_t> dequeued;
decoder.DequeueAll(&dequeued);
EXPECT_EQ(10u, dequeued.size());
EXPECT_EQ(0, dequeued[0]);
EXPECT_EQ(1, dequeued[1]);
EXPECT_EQ(63, dequeued[2]);
EXPECT_EQ(64, dequeued[3]);
EXPECT_EQ(-1, dequeued[4]);
EXPECT_EQ(-624485, dequeued[5]);
EXPECT_EQ(2147483647, dequeued[6]);
EXPECT_EQ(-2147483648, dequeued[7]);
EXPECT_EQ(9223372036854775807ll, dequeued[8]);
EXPECT_EQ(-9223372036854775807ll - 1, dequeued[9]);
EXPECT_EQ(10U, dequeued.size());
EXPECT_EQ(0U, dequeued[0]);
EXPECT_EQ(1U, dequeued[1]);
EXPECT_EQ(63U, dequeued[2]);
EXPECT_EQ(64U, dequeued[3]);
EXPECT_EQ(static_cast<uint64_t>(-1), dequeued[4]);
EXPECT_EQ(static_cast<uint64_t>(-624485), dequeued[5]);
EXPECT_EQ(2147483647U, dequeued[6]);
EXPECT_EQ(static_cast<uint64_t>(-2147483648), dequeued[7]);
EXPECT_EQ(9223372036854775807ULL, dequeued[8]);
EXPECT_EQ(static_cast<uint64_t>(-9223372036854775807LL - 1), dequeued[9]);
}
} // namespace relocation_packer