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 "debug.h"
#include <sstream> #include <sstream>
#include "testing/gtest/include/gtest/gtest.h" #include "gtest/gtest.h"
namespace relocation_packer { namespace relocation_packer {

View File

@ -7,66 +7,301 @@
#include <vector> #include <vector>
#include "debug.h" #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 { namespace relocation_packer {
// Encode relative relocations with addends into a delta encoded (packed) // Encode relocations into a delta encoded (packed) representation.
// representation. Represented as simple r_offset and r_addend delta pairs, template <typename ELF>
// with an implicit neutral element at the start. void RelocationDeltaCodec<ELF>::Encode(const std::vector<ElfRela>& relocations,
void RelocationDeltaCodec::Encode(const std::vector<ELF::Rela>& relocations, std::vector<ElfAddr>* packed) {
std::vector<ELF::Sxword>* packed) { if (relocations.size() == 0)
// One relocation is sufficient for delta encoding.
if (relocations.size() < 1)
return; return;
// Start with the element count, then append the delta pairs. // Start with the relocation count, then append groups
packed->push_back(relocations.size()); // 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; // lets write starting offset (offset of the first reloc - first delta)
ELF::Sxword addend = 0; 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) { packed->push_back(start_offset);
const ELF::Rela* relocation = &relocations[i];
CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode);
packed->push_back(relocation->r_offset - offset); // this one is used to calculate delta
offset = relocation->r_offset; ElfAddr previous_addend = 0;
packed->push_back(relocation->r_addend - addend); ElfAddr previous_offset = start_offset;
addend = relocation->r_addend;
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) // Decode relocations from a delta encoded (packed) representation.
// representation. template <typename ELF>
void RelocationDeltaCodec::Decode(const std::vector<ELF::Sxword>& packed, void RelocationDeltaCodec<ELF>::Decode(const std::vector<ElfAddr>& packed,
std::vector<ELF::Rela>* relocations) { std::vector<ElfRela>* relocations) {
// We need at least one packed pair after the packed pair count to be if (packed.size() < 5) {
// able to unpack.
if (packed.size() < 3)
return; return;
}
// Ensure that the packed data offers enough pairs. There may be zero size_t ndx = 0;
// padding on it that we ignore. ElfAddr current_count = 0;
CHECK(static_cast<size_t>(packed[0]) <= (packed.size() - 1) >> 1); ElfAddr total_count = packed[ndx++];
ELF::Addr offset = 0; ElfAddr offset = packed[ndx++];
ELF::Sxword addend = 0; ElfAddr info = 0;
ElfAddr addend = 0;
// The first packed vector element is the pairs count. Start uncondensing while(current_count < total_count) {
// pairs at the second, and finish at the end of the pairs data. // read group
const size_t pairs_count = packed[0]; ElfAddr group_size = packed[ndx++];
for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) { ElfAddr group_flags = packed[ndx++];
offset += packed[i]; ElfAddr group_offset_delta = 0;
addend += packed[i + 1];
// Generate a relocation for this offset and addend pair. if (is_relocation_grouped_by_offset_delta(group_flags)) {
ELF::Rela relocation; group_offset_delta = packed[ndx++];
relocation.r_offset = offset; }
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
relocation.r_addend = addend; if (is_relocation_grouped_by_info(group_flags)) {
relocations->push_back(relocation); 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 } // namespace relocation_packer

View File

@ -2,50 +2,86 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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 // The encoded data format is sequence of elements of ElfAddr type (unsigned long):
// .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.
// //
// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes // [00] relocation_count - the total count of relocations
// on 64 bit plaforms. It is split into offset, info, and addend fields. // [01] initial r_offset - this is initial r_offset for the
// Offsets strictly increase, and each is commonly a few bytes different // relocation table.
// from its predecessor. Addends are less well behaved. The info field is // followed by group structures:
// constant. Example, from 'readelf -x4 libchrome.<version>.so' 64 bit: // [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 // The group description is followed by individual relocations.
// 80949303 00000000 03040000 00000000 ................ // please note that there is a case when individual relocation
// addend offset // section could be empty - that is if every field ends up grouped.
// 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 offset strictly increases, but the addend is unpredictable, so run // The format for individual relocations section is:
// length encoding will not work well with this data. We can however pack // ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set
// with delta encoding. The upper four bytes of the eight byte offset and // ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set
// addend are invariably zeroes. The difference between adjacent offsets // ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set
// is almost always small, and between adjacent addends is often small. And
// info is constant and can be eliminated.
// //
// Delta encoding reduces the size of the data modestly, so that the first // For example lets pack the following relocations:
// three relocations above can be represented as:
// //
// initial offset initial addend offset delta addend delta // Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries:
// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028 // Offset Info Type Symbol's Value Symbol's Name + Addend
// offset delta addend delta ... // 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8
// 00000000 00000008 00000000 0000009f // 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 // The header is going to be
// deltas have a much smaller range than the input data. When encoded as // [00] 14 <- count
// signed LEB128 the total data reduction becomes useful. // [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_ #ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
#define 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 // A RelocationDeltaCodec packs vectors of relative relocations with
// addends into more compact forms, and unpacks them to reproduce the // addends into more compact forms, and unpacks them to reproduce the
// pre-packed data. // pre-packed data.
template <typename ELF>
class RelocationDeltaCodec { class RelocationDeltaCodec {
public: 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. // |relocations| is a vector of relative relocation with addend structs.
// |packed| is the vector of packed words into which relocations are packed. // |packed| is the vector of packed words into which relocations are packed.
static void Encode(const std::vector<ELF::Rela>& relocations, static void Encode(const std::vector<ElfRela>& relocations,
std::vector<ELF::Sxword>* packed); std::vector<ElfAddr>* packed);
// Decode relative relocations with addends from their more compact form. // Decode relative relocations with addends from their more compact form.
// |packed| is the vector of packed relocations. // |packed| is the vector of packed relocations.
// |relocations| is a vector of unpacked relative relocations. // |relocations| is a vector of unpacked relative relocations.
static void Decode(const std::vector<ELF::Sxword>& packed, static void Decode(const std::vector<ElfAddr>& packed,
std::vector<ELF::Rela>* relocations); 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 } // namespace relocation_packer

View File

@ -6,27 +6,29 @@
#include <vector> #include <vector>
#include "elf.h" #include "elf.h"
#include "elf_traits.h" #include "gtest/gtest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace { namespace {
void AddRelocation(ELF::Addr addr, template <typename T>
ELF::Sxword addend, void AddRelocation(uint32_t addr,
std::vector<ELF::Rela>* relocations) { uint32_t info,
ELF::Rela relocation; int32_t addend,
std::vector<T>* relocations) {
T relocation;
relocation.r_offset = addr; relocation.r_offset = addr;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); relocation.r_info = info;
relocation.r_addend = addend; relocation.r_addend = addend;
relocations->push_back(relocation); relocations->push_back(relocation);
} }
bool CheckRelocation(ELF::Addr addr, template <typename T>
ELF::Sxword addend, bool CheckRelocation(uint32_t addr,
const ELF::Rela& relocation) { uint32_t info,
int32_t addend,
const T& relocation) {
return relocation.r_offset == addr && return relocation.r_offset == addr &&
ELF_R_SYM(relocation.r_info) == 0 && relocation.r_info == info &&
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
relocation.r_addend == addend; relocation.r_addend == addend;
} }
@ -34,117 +36,188 @@ bool CheckRelocation(ELF::Addr addr,
namespace relocation_packer { namespace relocation_packer {
TEST(Delta, Encode) { template <typename ELF>
std::vector<ELF::Rela> relocations; static void encode() {
std::vector<ELF::Sxword> packed; std::vector<typename ELF::Rela> relocations;
std::vector<typename ELF::Addr> packed;
RelocationDeltaCodec codec; RelocationDeltaCodec<ELF> codec;
packed.clear();
codec.Encode(relocations, &packed); codec.Encode(relocations, &packed);
EXPECT_EQ(0, packed.size()); ASSERT_EQ(0U, packed.size());
// Initial relocation. // Initial relocation.
AddRelocation(0xf00d0000, 10000, &relocations); AddRelocation(0xf00d0000, 11U, 10000, &relocations);
packed.clear();
codec.Encode(relocations, &packed); 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. // One pair present.
EXPECT_EQ(1, packed[0]); size_t ndx = 0;
// Delta from the neutral element is the initial relocation. EXPECT_EQ(1U, packed[ndx++]);
EXPECT_EQ(0xf00d0000, packed[1]); EXPECT_EQ(0xf00d0000, packed[ndx++]);
EXPECT_EQ(10000, packed[2]); 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. // 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(); packed.clear();
codec.Encode(relocations, &packed); codec.Encode(relocations, &packed);
EXPECT_EQ(5, packed.size()); ndx = 0;
// Two pairs present. EXPECT_EQ(8U, packed.size());
EXPECT_EQ(2, packed[0]);
// Delta from the neutral element is the initial relocation. EXPECT_EQ(2U, packed[ndx++]); // relocs count
EXPECT_EQ(0xf00d0000, packed[1]); EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset
EXPECT_EQ(10000, packed[2]); EXPECT_EQ(2U, packed[ndx++]); // group count
// 4 byte offset delta, 12 byte addend delta. EXPECT_EQ(11U, packed[ndx++]); // flags
EXPECT_EQ(4, packed[3]); EXPECT_EQ(4U, packed[ndx++]); // group offset delta
EXPECT_EQ(12, packed[4]); 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. // 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. // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
AddRelocation(0xf00d0010, 10000, &relocations); AddRelocation(0xf00d0010, 42U, 10000, &relocations);
AddRelocation(0xf00d0018, 9976, &relocations); AddRelocation(0xf00d0018, 42U, 9976, &relocations);
AddRelocation(0xf00d0020, 9952, &relocations); AddRelocation(0xf00d0020, 42U, 9952, &relocations);
AddRelocation(0xf00d2028, 1042U, 0, &relocations);
AddRelocation(0xf00d2030, 3442U, 0, &relocations);
packed.clear(); packed.clear();
codec.Encode(relocations, &packed); codec.Encode(relocations, &packed);
EXPECT_EQ(13, packed.size()); ndx = 0;
// Six pairs present. EXPECT_EQ(26U, packed.size());
EXPECT_EQ(6, packed[0]); // 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. // Initial relocation.
EXPECT_EQ(0xf00d0000, packed[1]); EXPECT_EQ(10000U, packed[ndx++]); // addend delta
EXPECT_EQ(10000, packed[2]);
// Two relocations, 4 byte offset deltas, 12 byte addend deltas. // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_EQ(4, packed[3]); EXPECT_EQ(12U, packed[ndx++]); // addend delta
EXPECT_EQ(12, packed[4]);
EXPECT_EQ(4, packed[5]); // second group has only one reloc
EXPECT_EQ(12, packed[6]); 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. // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_EQ(8, packed[7]); EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(-24, packed[8]); EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(8, packed[9]); EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(-24, packed[10]);
EXPECT_EQ(8, packed[11]); // and last - 2 relocations without addend
EXPECT_EQ(-24, packed[12]); 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) { TEST(Delta, Encode32) {
std::vector<ELF::Sxword> packed; encode<ELF32_traits>();
std::vector<ELF::Rela> relocations; }
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); codec.Decode(packed, &relocations);
EXPECT_EQ(0, relocations.size()); EXPECT_EQ(0U, relocations.size());
// Six pairs. // 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. // Initial relocation.
packed.push_back(0xc0de0000); packed.push_back(10000U);
packed.push_back(10000);
// Two relocations, 4 byte offset deltas, 12 byte addend deltas. // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
packed.push_back(4); packed.push_back(12U); // addend
packed.push_back(12); packed.push_back(12U); // addend
packed.push_back(4);
packed.push_back(12);
// Three relocations, 8 byte offset deltas, -24 byte addend deltas. // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
packed.push_back(8); packed.push_back(1U); // group count
packed.push_back(-24); packed.push_back(9U); // flags
packed.push_back(8); packed.push_back(11U); // info
packed.push_back(-24);
packed.push_back(8); packed.push_back(8U);
packed.push_back(-24); 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(); relocations.clear();
codec.Decode(packed, &relocations); codec.Decode(packed, &relocations);
EXPECT_EQ(6, relocations.size()); EXPECT_EQ(6U, relocations.size());
// Initial relocation. // 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. // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0004, 10012, relocations[1])); EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xc0de0008, 10024, relocations[2])); EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2]));
// Three relocations, 8 byte offset deltas, -24 byte addend deltas. // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0010, 10000, relocations[3])); EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xc0de0018, 9976, relocations[4])); EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xc0de0020, 9952, relocations[5])); 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 } // 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 // An ElfFile reads shared objects, and shuttles relative relocations
// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn // between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn
// sections. // sections.
template <typename ELF>
class ElfFile { class ElfFile {
public: public:
explicit ElfFile(int fd) explicit ElfFile(int fd)
: fd_(fd), is_padding_relocations_(false), elf_(NULL), : fd_(fd), is_padding_relocations_(false), elf_(NULL),
relocations_section_(NULL), dynamic_section_(NULL), relocations_section_(NULL), dynamic_section_(NULL),
android_relocations_section_(NULL), relocations_type_(NONE) {} relocations_type_(NONE) {}
~ElfFile() {} ~ElfFile() {}
// Set padding mode. When padding, PackRelocations() will not shrink // Set padding mode. When padding, PackRelocations() will not shrink
@ -92,6 +93,10 @@ class ElfFile {
bool UnpackRelocations(); bool UnpackRelocations();
private: private:
enum relocations_type_t {
NONE = 0, REL, RELA
};
// Load a new ElfFile from a filedescriptor. If flushing, the file must // Load a new ElfFile from a filedescriptor. If flushing, the file must
// be open for read/write. Returns true on successful ELF file load. // be open for read/write. Returns true on successful ELF file load.
// |fd| is an open file descriptor for the shared object. // |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 // Templated packer, helper for PackRelocations(). Rel type is one of
// ELF::Rel or ELF::Rela. // ELF::Rel or ELF::Rela.
template <typename Rel> bool PackTypedRelocations(std::vector<typename ELF::Rela>* relocations);
bool PackTypedRelocations(const std::vector<Rel>& relocations);
// Templated unpacker, helper for UnpackRelocations(). Rel type is one of // Templated unpacker, helper for UnpackRelocations(). Rel type is one of
// ELF::Rel or ELF::Rela. // ELF::Rel or ELF::Rela.
template <typename Rel>
bool UnpackTypedRelocations(const std::vector<uint8_t>& packed); bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
// Write ELF file changes. // Write ELF file changes.
void Flush(); 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. // File descriptor opened on the shared object.
int fd_; int fd_;
@ -123,10 +145,9 @@ class ElfFile {
// Sections that we manipulate, assigned by Load(). // Sections that we manipulate, assigned by Load().
Elf_Scn* relocations_section_; Elf_Scn* relocations_section_;
Elf_Scn* dynamic_section_; Elf_Scn* dynamic_section_;
Elf_Scn* android_relocations_section_;
// Relocation type found, assigned by Load(). // Relocation type found, assigned by Load().
enum { NONE = 0, REL, RELA } relocations_type_; relocations_type_t relocations_type_;
}; };
} // namespace relocation_packer } // namespace relocation_packer

View File

@ -11,12 +11,7 @@
#include <vector> #include <vector>
#include "debug.h" #include "debug.h"
#include "elf_traits.h" #include "elf_traits.h"
#include "testing/gtest/include/gtest/gtest.h" #include "gtest/gtest.h"
// Macro stringification.
// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html
#define XSTR(S) STR(S)
#define STR(S) #S
namespace { namespace {
@ -27,8 +22,6 @@ void GetDataFilePath(const char* name, std::string* path) {
if (bindir) { if (bindir) {
data_dir = std::string(bindir); data_dir = std::string(bindir);
} else { } else {
// Test data is in the gyp INTERMEDIATE_DIR subdirectory of the directory
// that contains the current binary.
char path[PATH_MAX]; char path[PATH_MAX];
memset(path, 0, sizeof(path)); memset(path, 0, sizeof(path));
ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1)); 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('/'); size_t pos = data_dir.rfind('/');
ASSERT_NE(std::string::npos, pos); ASSERT_NE(std::string::npos, pos);
data_dir.erase(pos + 1); data_dir.erase(pos);
data_dir += std::string(XSTR(INTERMEDIATE_DIR));
} }
*path = data_dir + "/" + name; *path = data_dir + "/" + name;
@ -49,7 +41,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) {
GetDataFilePath(name, &path); GetDataFilePath(name, &path);
FILE* testfile = fopen(path.c_str(), "rb"); FILE* testfile = fopen(path.c_str(), "rb");
ASSERT_FALSE(testfile == NULL); ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'";
FILE* temporary = tmpfile(); FILE* temporary = tmpfile();
ASSERT_FALSE(temporary == NULL); ASSERT_FALSE(temporary == NULL);
@ -70,15 +62,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) {
*stream = temporary; *stream = temporary;
} }
void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) { void OpenRelocsTestFiles(const std::string& arch, 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);
const std::string base = std::string("elf_file_unittest_relocs_") + arch; const std::string base = std::string("elf_file_unittest_relocs_") + arch;
const std::string relocs = base + ".so"; const std::string relocs = base + ".so";
const std::string packed_relocs = base + "_packed.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)); EXPECT_TRUE(feof(first) && feof(second));
} }
} // namespace template <typename ELF>
static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
namespace relocation_packer { relocation_packer::ElfFile<ELF> elf_file(fileno(packed_relocs_so));
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));
// Ensure packing fails (already packed). // Ensure packing fails (already packed).
EXPECT_FALSE(elf_file.PackRelocations()); EXPECT_FALSE(elf_file.PackRelocations());
@ -161,4 +113,76 @@ TEST(ElfFile, UnpackRelocations) {
CloseRelocsTestFiles(relocs_so, packed_relocs_so); 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 } // namespace relocation_packer

View File

@ -10,31 +10,10 @@
#include "elf.h" #include "elf.h"
#include "libelf.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 // 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 ELF32_traits {
struct ELF {
typedef Elf32_Addr Addr; typedef Elf32_Addr Addr;
typedef Elf32_Dyn Dyn; typedef Elf32_Dyn Dyn;
typedef Elf32_Ehdr Ehdr; typedef Elf32_Ehdr Ehdr;
@ -48,27 +27,17 @@ struct ELF {
typedef Elf32_Sym Sym; typedef Elf32_Sym Sym;
typedef Elf32_Word Word; typedef Elf32_Word Word;
typedef Elf32_Xword Xword; typedef Elf32_Xword Xword;
typedef Elf32_Half Half;
static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); } static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); } static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); } static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); }
enum { kMachine = EM_ARM }; static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); }
enum { kFileClass = ELFCLASS32 }; static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); }
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)
}; };
#elif defined(TARGET_ARM64) struct ELF64_traits {
struct ELF {
typedef Elf64_Addr Addr; typedef Elf64_Addr Addr;
typedef Elf64_Dyn Dyn; typedef Elf64_Dyn Dyn;
typedef Elf64_Ehdr Ehdr; typedef Elf64_Ehdr Ehdr;
@ -82,24 +51,14 @@ struct ELF {
typedef Elf64_Sym Sym; typedef Elf64_Sym Sym;
typedef Elf64_Word Word; typedef Elf64_Word Word;
typedef Elf64_Xword Xword; typedef Elf64_Xword Xword;
typedef Elf64_Half Half;
static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); } static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); } static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); } static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); }
enum { kMachine = EM_AARCH64 }; static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); }
enum { kFileClass = ELFCLASS64 }; static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); }
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)
}; };
#endif
#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ #endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_

View File

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

View File

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

View File

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

View File

@ -25,11 +25,12 @@
#include "debug.h" #include "debug.h"
#include "elf_file.h" #include "elf_file.h"
#include "elf_traits.h"
#include "libelf.h" #include "libelf.h"
namespace { #include "nativehelper/ScopedFd.h"
void PrintUsage(const char* argv0) { static void PrintUsage(const char* argv0) {
std::string temporary = argv0; std::string temporary = argv0;
const size_t last_slash = temporary.find_last_of("/"); const size_t last_slash = temporary.find_last_of("/");
if (last_slash != temporary.npos) { 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", " -p, --pad do not shrink relocations, but pad (for debugging)\n\n",
basename); 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( printf(
"Debug sections are not handled, so packing should not be used on\n" "Debug sections are not handled, so packing should not be used on\n"
"shared libraries compiled for debugging or otherwise unstripped.\n"); "shared libraries compiled for debugging or otherwise unstripped.\n");
} }
} // namespace
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
bool is_unpacking = false; bool is_unpacking = false;
bool is_verbose = false; bool is_verbose = false;
@ -143,11 +96,9 @@ int main(int argc, char* argv[]) {
LOG(WARNING) << "Elf Library is out of date!"; LOG(WARNING) << "Elf Library is out of date!";
} }
LOG(INFO) << "Configured for " << ELF::Machine();
const char* file = argv[argc - 1]; const char* file = argv[argc - 1];
const int fd = open(file, O_RDWR); ScopedFd fd(open(file, O_RDWR));
if (fd == -1) { if (fd.get() == -1) {
LOG(ERROR) << file << ": " << strerror(errno); LOG(ERROR) << file << ": " << strerror(errno);
return 1; return 1;
} }
@ -155,16 +106,43 @@ int main(int argc, char* argv[]) {
if (is_verbose) if (is_verbose)
relocation_packer::Logger::SetVerbose(1); relocation_packer::Logger::SetVerbose(1);
relocation_packer::ElfFile elf_file(fd); // We need to detect elf class in order to create
elf_file.SetPadding(is_padding); // 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 (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
if (is_unpacking) LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
status = elf_file.UnpackRelocations(); return 1;
else }
status = elf_file.PackRelocations();
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) { if (!status) {
LOG(ERROR) << file << ": failed to pack/unpack file"; LOG(ERROR) << file << ": failed to pack/unpack file";

View File

@ -10,115 +10,79 @@
#include "delta_encoder.h" #include "delta_encoder.h"
#include "elf_traits.h" #include "elf_traits.h"
#include "leb128.h" #include "leb128.h"
#include "run_length_encoder.h"
#include "sleb128.h" #include "sleb128.h"
namespace relocation_packer { namespace relocation_packer {
// Pack relative relocations into a run-length encoded packed // Pack relocations into a group encoded packed representation.
// representation. template <typename ELF>
void RelocationPacker::PackRelativeRelocations( void RelocationPacker<ELF>::PackRelocations(const std::vector<typename ELF::Rela>& relocations,
const std::vector<ELF::Rel>& relocations, std::vector<uint8_t>* packed) {
std::vector<uint8_t>* packed) {
// Run-length encode. // Run-length encode.
std::vector<ELF::Xword> packed_words; std::vector<typename ELF::Addr> packed_words;
RelocationRunLengthCodec codec; RelocationDeltaCodec<ELF> codec;
codec.Encode(relocations, &packed_words); codec.Encode(relocations, &packed_words);
// If insufficient data to run-length encode, do nothing. // If insufficient data do nothing.
if (packed_words.empty()) if (packed_words.empty())
return; return;
// LEB128 encode, with "APR1" prefix. Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
Leb128Encoder encoder; Leb128Encoder<typename ELF::Addr> leb128_encoder;
encoder.Enqueue('A');
encoder.Enqueue('P');
encoder.Enqueue('R');
encoder.Enqueue('1');
encoder.EnqueueAll(packed_words);
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_encoder.EnqueueAll(packed_words);
// LEB128 zeroes. Run-length decoding ignores it because encoding leb128_encoder.GetEncoding(&leb128_packed);
// embeds the pairs count in the stream itself.
while (packed->size() % sizeof(ELF::Word)) sleb128_encoder.EnqueueAll(packed_words);
packed->push_back(0); 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 // Unpack relative relocations from a run-length encoded packed
// representation. // representation.
void RelocationPacker::UnpackRelativeRelocations( template <typename ELF>
void RelocationPacker<ELF>::UnpackRelocations(
const std::vector<uint8_t>& packed, const std::vector<uint8_t>& packed,
std::vector<ELF::Rel>* relocations) { std::vector<typename ELF::Rela>* 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);
// Run-length decode. std::vector<typename ELF::Addr> packed_words;
RelocationRunLengthCodec codec; 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); codec.Decode(packed_words, relocations);
} }
// Pack relative relocations with addends into a delta encoded packed template class RelocationPacker<ELF32_traits>;
// representation. template class RelocationPacker<ELF64_traits>;
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);
}
} // namespace relocation_packer } // namespace relocation_packer

View File

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

View File

@ -7,244 +7,286 @@
#include <vector> #include <vector>
#include "elf.h" #include "elf.h"
#include "elf_traits.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) { template <typename ELF>
ELF::Rel relocation; 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_offset = addr;
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); relocation.r_info = info;
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_addend = addend; relocation.r_addend = addend;
relocations->push_back(relocation); relocations->push_back(relocation);
} }
bool CheckRelocation(ELF::Addr addr, template <typename ELF>
ELF::Sxword addend, static bool CheckRelocation(typename ELF::Addr addr,
const ELF::Rela& relocation) { typename ELF::Xword info,
typename ELF::Sxword addend,
const typename ELF::Rela& relocation) {
return relocation.r_offset == addr && return relocation.r_offset == addr &&
ELF_R_SYM(relocation.r_info) == 0 && relocation.r_info == info &&
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
relocation.r_addend == addend; relocation.r_addend == addend;
} }
} // namespace
namespace relocation_packer { namespace relocation_packer {
TEST(Packer, PackRel) { template <typename ELF>
std::vector<ELF::Rel> relocations; static void DoPackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed; std::vector<uint8_t> packed;
RelocationPacker packer;
// Initial relocation. // Initial relocation.
AddRelocation(0xd1ce0000, &relocations); AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
// Two more relocations, 4 byte deltas. // Two more relocations, 4 byte deltas.
AddRelocation(0xd1ce0004, &relocations); AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
AddRelocation(0xd1ce0008, &relocations); AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
// Three more relocations, 8 byte deltas. // Three more relocations, 8 byte deltas.
AddRelocation(0xd1ce0010, &relocations); AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
AddRelocation(0xd1ce0018, &relocations); AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
AddRelocation(0xd1ce0020, &relocations); AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
RelocationPacker<ELF> packer;
packed.clear(); packed.clear();
packer.PackRelativeRelocations(relocations, &packed); packer.PackRelocations(relocations, &packed);
EXPECT_EQ(16, packed.size()); ASSERT_EQ(18U, packed.size());
// Identifier. // Identifier.
EXPECT_EQ('A', packed[0]); size_t ndx = 0;
EXPECT_EQ('P', packed[1]); EXPECT_EQ('A', packed[ndx++]);
EXPECT_EQ('R', packed[2]); EXPECT_EQ('P', packed[ndx++]);
EXPECT_EQ('1', packed[3]); EXPECT_EQ('U', packed[ndx++]);
// Count-delta pairs count. EXPECT_EQ('2', packed[ndx++]);
EXPECT_EQ(2, packed[4]); // relocation count
// 0xd1ce0000 EXPECT_EQ(6, packed[ndx++]);
EXPECT_EQ(128, packed[5]); // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
EXPECT_EQ(128, packed[6]); EXPECT_EQ(0xfc, packed[ndx++]);
EXPECT_EQ(184, packed[7]); EXPECT_EQ(0xff, packed[ndx++]);
EXPECT_EQ(142, packed[8]); EXPECT_EQ(0xb7, packed[ndx++]);
EXPECT_EQ(13, packed[9]); EXPECT_EQ(0x8e, packed[ndx++]);
// Run of two relocations, 4 byte deltas. EXPECT_EQ(0x0d, packed[ndx++]);
EXPECT_EQ(2, packed[10]); // first group
EXPECT_EQ(4, packed[11]); EXPECT_EQ(3, packed[ndx++]); // size
// Run of three relocations, 8 byte deltas. EXPECT_EQ(3, packed[ndx++]); // flags
EXPECT_EQ(3, packed[12]); EXPECT_EQ(4, packed[ndx++]); // r_offset_delta
EXPECT_EQ(8, packed[13]); EXPECT_EQ(0x11, packed[ndx++]); // r_info
// Padding. // second group
EXPECT_EQ(0, packed[14]); EXPECT_EQ(3, packed[ndx++]); // size
EXPECT_EQ(0, packed[15]); 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<uint8_t> packed;
std::vector<ELF::Rel> relocations;
RelocationPacker packer;
// Identifier.
packed.push_back('A'); packed.push_back('A');
packed.push_back('P'); packed.push_back('P');
packed.push_back('R'); packed.push_back('U');
packed.push_back('1'); packed.push_back('2');
// Count-delta pairs count. // relocation 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(6); packed.push_back(6);
// 0xd1ce0000 // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
packed.push_back(128); packed.push_back(0xfc);
packed.push_back(128); packed.push_back(0xff);
packed.push_back(184); packed.push_back(0xb7);
packed.push_back(142); packed.push_back(0x8e);
packed.push_back(13); packed.push_back(0x0d);
// 10000 // first group
packed.push_back(144); packed.push_back(3); // size
packed.push_back(206); packed.push_back(3); // flags
packed.push_back(0); packed.push_back(4); // r_offset_delta
// 4, 12 packed.push_back(0x11); // r_info
packed.push_back(4); // second group
packed.push_back(12); packed.push_back(3); // size
// 4, 12 packed.push_back(3); // flags
packed.push_back(4); packed.push_back(8); // r_offset_delta
packed.push_back(12); packed.push_back(0x11); // r_info
// 8, -24
packed.push_back(8); RelocationPacker<ELF> packer;
packed.push_back(104); packer.UnpackRelocations(packed, &relocations);
// 8, -24
packed.push_back(8); size_t ndx = 0;
packed.push_back(104); EXPECT_EQ(6U, relocations.size());
// 8, -24 EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x11, 0, relocations[ndx++]));
packed.push_back(8); EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x11, 0, relocations[ndx++]));
packed.push_back(104); EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x11, 0, relocations[ndx++]));
// Padding.
packed.push_back(0); 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(); 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. // 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. // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xd1ce0004, 10012, relocations[1])); EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x01, 10012, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation(0xd1ce0008, 10024, relocations[2])); EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x01, 10024, relocations[ndx++]));
// Three more relocations, 8 byte offset deltas, -24 byte addend deltas. // Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xd1ce0010, 10000, relocations[3])); EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x01, 10000, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation(0xd1ce0018, 9976, relocations[4])); EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x01, 9976, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation(0xd1ce0020, 9952, relocations[5])); 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 } // 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" #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 { namespace relocation_packer {
// Empty constructor and destructor to silence chromium-style. // Empty constructor and destructor to silence chromium-style.
Sleb128Encoder::Sleb128Encoder() { } template <typename uint_t>
Sleb128Encoder::~Sleb128Encoder() { } Sleb128Encoder<uint_t>::Sleb128Encoder() { }
template <typename uint_t>
Sleb128Encoder<uint_t>::~Sleb128Encoder() { }
// Add a single value to the encoding. Values are encoded with variable // 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, // 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 // 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 // most significant bit is zero for a positive number and one for a
// negative number). // 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); static const size_t size = CHAR_BIT * sizeof(value);
bool more = true; bool more = true;
const bool negative = value < 0; const bool negative = static_cast<int_t>(value) < 0;
while (more) { while (more) {
uint8_t byte = value & 127; uint8_t byte = value & 127;
@ -34,11 +58,11 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) {
// Sign extend if encoding a -ve value. // Sign extend if encoding a -ve value.
if (negative) 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. // The sign bit of byte is second high order bit.
const bool sign_bit = byte & 64; 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; more = false;
else else
byte |= 128; byte |= 128;
@ -47,25 +71,30 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) {
} }
// Add a vector of values to the encoding. // Add a vector of values to the encoding.
void Sleb128Encoder::EnqueueAll(const std::vector<ELF::Sxword>& values) { template <typename uint_t>
for (size_t i = 0; i < values.size(); ++i) void Sleb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
for (size_t i = 0; i < values.size(); ++i) {
Enqueue(values[i]); Enqueue(values[i]);
}
} }
// Create a new decoder for the given encoded stream. // 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; encoding_ = encoding;
cursor_ = 0; cursor_ = start_with;
} }
// Empty destructor to silence chromium-style. // 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 // Decode and retrieve a single value from the encoding. Consume bytes
// until one without its most significant bit is found, and re-form the // until one without its most significant bit is found, and re-form the
// value from the 7 bit fields of the bytes consumed. // value from the 7 bit fields of the bytes consumed.
ELF::Sxword Sleb128Decoder::Dequeue() { template <typename uint_t>
ELF::Sxword value = 0; uint_t Sleb128Decoder<uint_t>::Dequeue() {
uint_t value = 0;
static const size_t size = CHAR_BIT * sizeof(value); static const size_t size = CHAR_BIT * sizeof(value);
size_t shift = 0; size_t shift = 0;
@ -74,22 +103,29 @@ ELF::Sxword Sleb128Decoder::Dequeue() {
// Loop until we reach a byte with its high order bit clear. // Loop until we reach a byte with its high order bit clear.
do { do {
byte = encoding_[cursor_++]; byte = encoding_[cursor_++];
value |= (static_cast<ELF::Sxword>(byte & 127) << shift); value |= (static_cast<uint_t>(byte & 127) << shift);
shift += 7; shift += 7;
} while (byte & 128); } while (byte & 128);
// The sign bit is second high order bit of the final byte decoded. // 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. // Sign extend if value is -ve and we did not shift all of it.
if (shift < size && (byte & 64)) 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. // Decode and retrieve all remaining values from the encoding.
void Sleb128Decoder::DequeueAll(std::vector<ELF::Sxword>* values) { template <typename uint_t>
while (cursor_ < encoding_.size()) void Sleb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
while (cursor_ < encoding_.size()) {
values->push_back(Dequeue()); 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 } // namespace relocation_packer

View File

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

View File

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