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:
parent
87a0617ebe
commit
f8ff6b103b
19
tools/Android.mk
Normal file
19
tools/Android.mk
Normal 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)
|
96
tools/relocation_packer/Android.mk
Normal file
96
tools/relocation_packer/Android.mk
Normal 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))
|
@ -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),
|
||||
]
|
||||
}
|
||||
}
|
@ -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 = ""
|
||||
}
|
@ -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)',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
|
@ -7,66 +7,301 @@
|
||||
#include <vector>
|
||||
|
||||
#include "debug.h"
|
||||
#include "elf_traits.h"
|
||||
|
||||
static constexpr uint32_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
|
||||
static constexpr uint32_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
|
||||
static constexpr uint32_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
|
||||
static constexpr uint32_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
|
||||
|
||||
static bool is_relocation_grouped_by_info(uint64_t flags) {
|
||||
return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
|
||||
}
|
||||
|
||||
static bool is_relocation_grouped_by_offset_delta(uint64_t flags) {
|
||||
return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
|
||||
}
|
||||
|
||||
static bool is_relocation_grouped_by_addend(uint64_t flags) {
|
||||
return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
|
||||
}
|
||||
|
||||
static bool is_relocation_group_has_addend(uint64_t flags) {
|
||||
return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
|
||||
}
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
// Encode relative relocations with addends into a delta encoded (packed)
|
||||
// representation. Represented as simple r_offset and r_addend delta pairs,
|
||||
// with an implicit neutral element at the start.
|
||||
void RelocationDeltaCodec::Encode(const std::vector<ELF::Rela>& relocations,
|
||||
std::vector<ELF::Sxword>* packed) {
|
||||
// One relocation is sufficient for delta encoding.
|
||||
if (relocations.size() < 1)
|
||||
// Encode relocations into a delta encoded (packed) representation.
|
||||
template <typename ELF>
|
||||
void RelocationDeltaCodec<ELF>::Encode(const std::vector<ElfRela>& relocations,
|
||||
std::vector<ElfAddr>* packed) {
|
||||
if (relocations.size() == 0)
|
||||
return;
|
||||
|
||||
// Start with the element count, then append the delta pairs.
|
||||
packed->push_back(relocations.size());
|
||||
// Start with the relocation count, then append groups
|
||||
// TODO(dimitry): we might want to move it to DT_ANDROID_RELCOUNT section
|
||||
packed->push_back(static_cast<ElfAddr>(relocations.size()));
|
||||
|
||||
ELF::Addr offset = 0;
|
||||
ELF::Sxword addend = 0;
|
||||
// lets write starting offset (offset of the first reloc - first delta)
|
||||
ElfAddr start_offset = relocations.size() > 1 ?
|
||||
relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) :
|
||||
relocations[0].r_offset;
|
||||
|
||||
for (size_t i = 0; i < relocations.size(); ++i) {
|
||||
const ELF::Rela* relocation = &relocations[i];
|
||||
CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode);
|
||||
packed->push_back(start_offset);
|
||||
|
||||
packed->push_back(relocation->r_offset - offset);
|
||||
offset = relocation->r_offset;
|
||||
packed->push_back(relocation->r_addend - addend);
|
||||
addend = relocation->r_addend;
|
||||
// this one is used to calculate delta
|
||||
ElfAddr previous_addend = 0;
|
||||
ElfAddr previous_offset = start_offset;
|
||||
|
||||
for (size_t group_start = 0; group_start < relocations.size(); ) {
|
||||
ElfAddr group_flags = 0;
|
||||
ElfAddr group_offset_delta = 0;
|
||||
ElfAddr group_addend = 0;
|
||||
ElfAddr group_info = 0;
|
||||
|
||||
ElfAddr group_size = 0;
|
||||
|
||||
DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags,
|
||||
&group_offset_delta, &group_info, &group_addend);
|
||||
|
||||
// write the group header
|
||||
packed->push_back(group_size);
|
||||
packed->push_back(group_flags);
|
||||
|
||||
if (is_relocation_grouped_by_offset_delta(group_flags)) {
|
||||
packed->push_back(group_offset_delta);
|
||||
}
|
||||
|
||||
if (is_relocation_grouped_by_info(group_flags)) {
|
||||
packed->push_back(group_info);
|
||||
}
|
||||
|
||||
if (is_relocation_group_has_addend(group_flags) &&
|
||||
is_relocation_grouped_by_addend(group_flags)) {
|
||||
packed->push_back(group_addend - previous_addend);
|
||||
previous_addend = group_addend;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(group_size); ++i) {
|
||||
CHECK((group_start + i) < relocations.size());
|
||||
const ElfRela* relocation = &relocations[group_start + i];
|
||||
|
||||
if (!is_relocation_grouped_by_offset_delta(group_flags)) {
|
||||
packed->push_back(relocation->r_offset - previous_offset);
|
||||
}
|
||||
previous_offset = relocation->r_offset;
|
||||
|
||||
if (!is_relocation_grouped_by_info(group_flags)) {
|
||||
packed->push_back(relocation->r_info);
|
||||
}
|
||||
|
||||
if (is_relocation_group_has_addend(group_flags) &&
|
||||
!is_relocation_grouped_by_addend(group_flags)) {
|
||||
packed->push_back(relocation->r_addend - previous_addend);
|
||||
previous_addend = relocation->r_addend;
|
||||
}
|
||||
}
|
||||
|
||||
// If the relocation group does not have an addend - reset it to 0
|
||||
// to simplify addend computation for the group following this one.
|
||||
if (!is_relocation_group_has_addend(group_flags)) {
|
||||
previous_addend = 0;
|
||||
}
|
||||
|
||||
group_start += group_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode relative relocations with addends from a delta encoded (packed)
|
||||
// representation.
|
||||
void RelocationDeltaCodec::Decode(const std::vector<ELF::Sxword>& packed,
|
||||
std::vector<ELF::Rela>* relocations) {
|
||||
// We need at least one packed pair after the packed pair count to be
|
||||
// able to unpack.
|
||||
if (packed.size() < 3)
|
||||
// Decode relocations from a delta encoded (packed) representation.
|
||||
template <typename ELF>
|
||||
void RelocationDeltaCodec<ELF>::Decode(const std::vector<ElfAddr>& packed,
|
||||
std::vector<ElfRela>* relocations) {
|
||||
if (packed.size() < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that the packed data offers enough pairs. There may be zero
|
||||
// padding on it that we ignore.
|
||||
CHECK(static_cast<size_t>(packed[0]) <= (packed.size() - 1) >> 1);
|
||||
size_t ndx = 0;
|
||||
ElfAddr current_count = 0;
|
||||
ElfAddr total_count = packed[ndx++];
|
||||
|
||||
ELF::Addr offset = 0;
|
||||
ELF::Sxword addend = 0;
|
||||
ElfAddr offset = packed[ndx++];
|
||||
ElfAddr info = 0;
|
||||
ElfAddr addend = 0;
|
||||
|
||||
// The first packed vector element is the pairs count. Start uncondensing
|
||||
// pairs at the second, and finish at the end of the pairs data.
|
||||
const size_t pairs_count = packed[0];
|
||||
for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) {
|
||||
offset += packed[i];
|
||||
addend += packed[i + 1];
|
||||
while(current_count < total_count) {
|
||||
// read group
|
||||
ElfAddr group_size = packed[ndx++];
|
||||
ElfAddr group_flags = packed[ndx++];
|
||||
ElfAddr group_offset_delta = 0;
|
||||
|
||||
// Generate a relocation for this offset and addend pair.
|
||||
ELF::Rela relocation;
|
||||
relocation.r_offset = offset;
|
||||
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
|
||||
relocation.r_addend = addend;
|
||||
relocations->push_back(relocation);
|
||||
if (is_relocation_grouped_by_offset_delta(group_flags)) {
|
||||
group_offset_delta = packed[ndx++];
|
||||
}
|
||||
|
||||
if (is_relocation_grouped_by_info(group_flags)) {
|
||||
info = packed[ndx++];
|
||||
}
|
||||
|
||||
if (is_relocation_group_has_addend(group_flags) &&
|
||||
is_relocation_grouped_by_addend(group_flags)) {
|
||||
addend += packed[ndx++];
|
||||
}
|
||||
|
||||
// now read not grouped info
|
||||
for (ElfAddr i = 0; i<group_size; ++i) {
|
||||
if (is_relocation_grouped_by_offset_delta(group_flags)) {
|
||||
offset += group_offset_delta;
|
||||
} else {
|
||||
offset += packed[ndx++];
|
||||
}
|
||||
|
||||
if (!is_relocation_grouped_by_info(group_flags)) {
|
||||
info = packed[ndx++];
|
||||
}
|
||||
|
||||
if (is_relocation_group_has_addend(group_flags) &&
|
||||
!is_relocation_grouped_by_addend(group_flags)) {
|
||||
addend += packed[ndx++];
|
||||
}
|
||||
|
||||
ElfRela reloc;
|
||||
reloc.r_offset = offset;
|
||||
reloc.r_info = info;
|
||||
reloc.r_addend = is_relocation_group_has_addend(group_flags) ? addend : 0;
|
||||
relocations->push_back(reloc);
|
||||
}
|
||||
|
||||
if (!is_relocation_group_has_addend(group_flags)) {
|
||||
addend = 0;
|
||||
}
|
||||
|
||||
current_count += group_size;
|
||||
}
|
||||
}
|
||||
|
||||
// This function detects a way to group reloc_one and reloc_two, sets up group_flags
|
||||
// and initializes values for corresponding group_ fields. For example if relocations
|
||||
// can be grouped by r_info the function will set group_info variable.
|
||||
template <typename ELF>
|
||||
void RelocationDeltaCodec<ELF>::DetectGroupFields(const ElfRela& reloc_one,
|
||||
const ElfRela& reloc_two,
|
||||
ElfAddr current_offset_delta,
|
||||
ElfAddr* group_flags,
|
||||
ElfAddr* group_offset_delta,
|
||||
ElfAddr* group_info,
|
||||
ElfAddr* group_addend) {
|
||||
*group_flags = 0;
|
||||
|
||||
const ElfAddr offset_delta = static_cast<ElfAddr>(reloc_two.r_offset) -
|
||||
static_cast<ElfAddr>(reloc_one.r_offset);
|
||||
|
||||
if (offset_delta == current_offset_delta) {
|
||||
*group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG;
|
||||
if (group_offset_delta != nullptr) {
|
||||
*group_offset_delta = current_offset_delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (reloc_one.r_info == reloc_two.r_info) {
|
||||
*group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG;
|
||||
if (group_info != nullptr) {
|
||||
*group_info = reloc_one.r_info;
|
||||
}
|
||||
}
|
||||
|
||||
if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) {
|
||||
*group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG;
|
||||
if (reloc_one.r_addend == reloc_two.r_addend) {
|
||||
*group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG;
|
||||
if (group_addend != nullptr) {
|
||||
*group_addend = reloc_one.r_addend;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used to detect if there is better group available
|
||||
// during RelocationDeltaCodec<ELF>::DetectGroup processing.
|
||||
// Current implementation prefers having groups without addend (== zero addend)
|
||||
// to any other groups field with the ratio 3:1. This is because addend tends
|
||||
// to be more unevenly distributed than other fields.
|
||||
static uint32_t group_weight(uint64_t flags) {
|
||||
uint32_t weight = 0;
|
||||
if (!is_relocation_group_has_addend(flags)) {
|
||||
weight += 3;
|
||||
} else if (is_relocation_grouped_by_addend(flags)) {
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
if (is_relocation_grouped_by_offset_delta(flags)) {
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
if (is_relocation_grouped_by_info(flags)) {
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
template <typename ELF>
|
||||
void RelocationDeltaCodec<ELF>::DetectGroup(const std::vector<ElfRela>& relocations,
|
||||
size_t group_starts_with, ElfAddr previous_offset,
|
||||
ElfAddr* group_size, ElfAddr* group_flags,
|
||||
ElfAddr* group_offset_delta, ElfAddr* group_info,
|
||||
ElfAddr* group_addend) {
|
||||
CHECK(group_starts_with < relocations.size());
|
||||
CHECK(group_flags != nullptr);
|
||||
|
||||
const ElfRela& reloc_one = relocations[group_starts_with++];
|
||||
if (group_starts_with == relocations.size()) {
|
||||
*group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
|
||||
*group_size = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const ElfAddr offset_delta = reloc_one.r_offset - previous_offset;
|
||||
|
||||
// detect group_flags
|
||||
DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags,
|
||||
group_offset_delta, group_info, group_addend);
|
||||
|
||||
if (group_starts_with + 1 == relocations.size()) {
|
||||
*group_size = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
ElfAddr cnt = 1;
|
||||
for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) {
|
||||
ElfAddr candidate_flags;
|
||||
// check if next group (reloc_current; reloc_next) has better grouped_by flags
|
||||
DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags,
|
||||
nullptr, nullptr, nullptr);
|
||||
|
||||
if (group_weight(*group_flags) < group_weight(candidate_flags)) {
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
|
||||
if (candidate_flags != *group_flags) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i + 1 == relocations.size() - 1) { // last one
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// if as a result of checking candidates we ended up with cnt == 1
|
||||
// reset flags to the default state
|
||||
if (cnt == 1) {
|
||||
*group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
|
||||
}
|
||||
|
||||
*group_size = cnt;
|
||||
}
|
||||
|
||||
template class RelocationDeltaCodec<ELF32_traits>;
|
||||
template class RelocationDeltaCodec<ELF64_traits>;
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -2,50 +2,86 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Delta encode and decode relative relocations with addends.
|
||||
// Delta encode and decode REL/RELA section of elf file.
|
||||
//
|
||||
// Relative relocations are the bulk of dynamic relocations (the
|
||||
// .rel.dyn or .rela.dyn sections) in libchrome.<version>.so, and the ELF
|
||||
// standard representation of them is wasteful. .rel.dyn contains
|
||||
// relocations without addends, .rela.dyn relocations with addends.
|
||||
// The encoded data format is sequence of elements of ElfAddr type (unsigned long):
|
||||
//
|
||||
// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes
|
||||
// on 64 bit plaforms. It is split into offset, info, and addend fields.
|
||||
// Offsets strictly increase, and each is commonly a few bytes different
|
||||
// from its predecessor. Addends are less well behaved. The info field is
|
||||
// constant. Example, from 'readelf -x4 libchrome.<version>.so' 64 bit:
|
||||
// [00] relocation_count - the total count of relocations
|
||||
// [01] initial r_offset - this is initial r_offset for the
|
||||
// relocation table.
|
||||
// followed by group structures:
|
||||
// [02] group
|
||||
// ...
|
||||
// [nn] group
|
||||
|
||||
// the generalized format of the group is (! - always present ? - depends on group_flags):
|
||||
// --------------
|
||||
// ! group_size
|
||||
// ! group_flags
|
||||
// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set
|
||||
// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set
|
||||
// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND
|
||||
// flag is set
|
||||
//
|
||||
// offset info
|
||||
// 80949303 00000000 03040000 00000000 ................
|
||||
// addend offset
|
||||
// fc015b00 00000000 88949303 00000000 ..[.............
|
||||
// info addend
|
||||
// 03040000 00000000 24025b00 00000000 ........$.[.....
|
||||
// offset info
|
||||
// 90949303 00000000 03040000 00000000 ................
|
||||
// addend offset
|
||||
// 3c025b00 00000000 98949303 00000000 <.[.............
|
||||
// info addend
|
||||
// 03040000 00000000 50025b00 00000000 ........P.[.....
|
||||
// The group description is followed by individual relocations.
|
||||
// please note that there is a case when individual relocation
|
||||
// section could be empty - that is if every field ends up grouped.
|
||||
//
|
||||
// The offset strictly increases, but the addend is unpredictable, so run
|
||||
// length encoding will not work well with this data. We can however pack
|
||||
// with delta encoding. The upper four bytes of the eight byte offset and
|
||||
// addend are invariably zeroes. The difference between adjacent offsets
|
||||
// is almost always small, and between adjacent addends is often small. And
|
||||
// info is constant and can be eliminated.
|
||||
// The format for individual relocations section is:
|
||||
// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set
|
||||
// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set
|
||||
// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set
|
||||
//
|
||||
// Delta encoding reduces the size of the data modestly, so that the first
|
||||
// three relocations above can be represented as:
|
||||
// For example lets pack the following relocations:
|
||||
//
|
||||
// initial offset initial addend offset delta addend delta
|
||||
// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028
|
||||
// offset delta addend delta ...
|
||||
// 00000000 00000008 00000000 0000009f
|
||||
// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries:
|
||||
// Offset Info Type Symbol's Value Symbol's Name + Addend
|
||||
// 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8
|
||||
// 00000000000a2180 0000000000000403 R_AARCH64_RELATIVE 177cc
|
||||
// 00000000000a2188 0000000000000403 R_AARCH64_RELATIVE 177e0
|
||||
// 00000000000a2190 0000000000000403 R_AARCH64_RELATIVE 177f4
|
||||
// 00000000000a2198 0000000000000403 R_AARCH64_RELATIVE 17804
|
||||
// 00000000000a21a0 0000000000000403 R_AARCH64_RELATIVE 17818
|
||||
// 00000000000a21a8 0000000000000403 R_AARCH64_RELATIVE 1782c
|
||||
// 00000000000a21b0 0000000000000403 R_AARCH64_RELATIVE 17840
|
||||
// 00000000000a21b8 0000000000000403 R_AARCH64_RELATIVE 17854
|
||||
// 00000000000a21c0 0000000000000403 R_AARCH64_RELATIVE 17868
|
||||
// 00000000000a21c8 0000000000000403 R_AARCH64_RELATIVE 1787c
|
||||
// 00000000000a21d0 0000000000000403 R_AARCH64_RELATIVE 17890
|
||||
// 00000000000a21d8 0000000000000403 R_AARCH64_RELATIVE 178a4
|
||||
// 00000000000a21e8 0000000000000403 R_AARCH64_RELATIVE 178b8
|
||||
//
|
||||
// The addend delta can be negative as well as positive, but overall the
|
||||
// deltas have a much smaller range than the input data. When encoded as
|
||||
// signed LEB128 the total data reduction becomes useful.
|
||||
// The header is going to be
|
||||
// [00] 14 <- count
|
||||
// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta,
|
||||
// the delta is 8 in this case)
|
||||
// -- starting the first and only group
|
||||
// [03] 14 <- group size
|
||||
// [03] 0xb <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA
|
||||
// | RELOCATION_GROUPED_BY_INFO
|
||||
// [04] 8 <- offset delta
|
||||
// [05] 0x403 <- r_info
|
||||
// -- end of group definition, starting list of r_addend deltas
|
||||
// [06] 0x177a8
|
||||
// [07] 0x24 = 177cc - 177a8
|
||||
// [08] 0x14 = 177e0 - 177cc
|
||||
// [09] 0x14 = 177f4 - 177e0
|
||||
// [10] 0x10 = 17804 - 177f4
|
||||
// [11] 0x14 = 17818 - 17804
|
||||
// [12] 0x14 = 1782c - 17818
|
||||
// [13] 0x14 = 17840 - 1782c
|
||||
// [14] 0x14 = 17854 - 17840
|
||||
// [15] 0x14 = 17868 - 17854
|
||||
// [16] 0x14 = 1787c - 17868
|
||||
// [17] 0x14 = 17890 - 1787c
|
||||
// [18] 0x14 = 178a4 - 17890
|
||||
// [19] 0x14 = 178b8 - 178a4
|
||||
// -- the end.
|
||||
|
||||
// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can
|
||||
// save us more bytes...
|
||||
|
||||
// The input ends when sum(group_size) == relocation_count
|
||||
|
||||
#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
|
||||
#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
|
||||
@ -60,19 +96,35 @@ namespace relocation_packer {
|
||||
// A RelocationDeltaCodec packs vectors of relative relocations with
|
||||
// addends into more compact forms, and unpacks them to reproduce the
|
||||
// pre-packed data.
|
||||
template <typename ELF>
|
||||
class RelocationDeltaCodec {
|
||||
public:
|
||||
// Encode relative relocations with addends into a more compact form.
|
||||
typedef typename ELF::Addr ElfAddr;
|
||||
typedef typename ELF::Rela ElfRela;
|
||||
|
||||
// Encode relocations with addends into a more compact form.
|
||||
// |relocations| is a vector of relative relocation with addend structs.
|
||||
// |packed| is the vector of packed words into which relocations are packed.
|
||||
static void Encode(const std::vector<ELF::Rela>& relocations,
|
||||
std::vector<ELF::Sxword>* packed);
|
||||
static void Encode(const std::vector<ElfRela>& relocations,
|
||||
std::vector<ElfAddr>* packed);
|
||||
|
||||
// Decode relative relocations with addends from their more compact form.
|
||||
// |packed| is the vector of packed relocations.
|
||||
// |relocations| is a vector of unpacked relative relocations.
|
||||
static void Decode(const std::vector<ELF::Sxword>& packed,
|
||||
std::vector<ELF::Rela>* relocations);
|
||||
static void Decode(const std::vector<ElfAddr>& packed,
|
||||
std::vector<ElfRela>* relocations);
|
||||
|
||||
private:
|
||||
static void DetectGroup(const std::vector<ElfRela>& relocations,
|
||||
size_t group_starts_with, ElfAddr previous_offset,
|
||||
ElfAddr* group_size, ElfAddr* group_flags,
|
||||
ElfAddr* group_offset_delta, ElfAddr* group_info,
|
||||
ElfAddr* group_addend);
|
||||
|
||||
static void DetectGroupFields(const ElfRela& reloc_one, const ElfRela& reloc_two,
|
||||
ElfAddr current_offset_delta, ElfAddr* group_flags,
|
||||
ElfAddr* group_offset_delta, ElfAddr* group_info,
|
||||
ElfAddr* group_addend);
|
||||
};
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -6,27 +6,29 @@
|
||||
|
||||
#include <vector>
|
||||
#include "elf.h"
|
||||
#include "elf_traits.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void AddRelocation(ELF::Addr addr,
|
||||
ELF::Sxword addend,
|
||||
std::vector<ELF::Rela>* relocations) {
|
||||
ELF::Rela relocation;
|
||||
template <typename T>
|
||||
void AddRelocation(uint32_t addr,
|
||||
uint32_t info,
|
||||
int32_t addend,
|
||||
std::vector<T>* relocations) {
|
||||
T relocation;
|
||||
relocation.r_offset = addr;
|
||||
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
|
||||
relocation.r_info = info;
|
||||
relocation.r_addend = addend;
|
||||
relocations->push_back(relocation);
|
||||
}
|
||||
|
||||
bool CheckRelocation(ELF::Addr addr,
|
||||
ELF::Sxword addend,
|
||||
const ELF::Rela& relocation) {
|
||||
template <typename T>
|
||||
bool CheckRelocation(uint32_t addr,
|
||||
uint32_t info,
|
||||
int32_t addend,
|
||||
const T& relocation) {
|
||||
return relocation.r_offset == addr &&
|
||||
ELF_R_SYM(relocation.r_info) == 0 &&
|
||||
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
|
||||
relocation.r_info == info &&
|
||||
relocation.r_addend == addend;
|
||||
}
|
||||
|
||||
@ -34,117 +36,188 @@ bool CheckRelocation(ELF::Addr addr,
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
TEST(Delta, Encode) {
|
||||
std::vector<ELF::Rela> relocations;
|
||||
std::vector<ELF::Sxword> packed;
|
||||
template <typename ELF>
|
||||
static void encode() {
|
||||
std::vector<typename ELF::Rela> relocations;
|
||||
std::vector<typename ELF::Addr> packed;
|
||||
|
||||
RelocationDeltaCodec codec;
|
||||
RelocationDeltaCodec<ELF> codec;
|
||||
|
||||
packed.clear();
|
||||
codec.Encode(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(0, packed.size());
|
||||
ASSERT_EQ(0U, packed.size());
|
||||
|
||||
// Initial relocation.
|
||||
AddRelocation(0xf00d0000, 10000, &relocations);
|
||||
AddRelocation(0xf00d0000, 11U, 10000, &relocations);
|
||||
|
||||
packed.clear();
|
||||
codec.Encode(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(3, packed.size());
|
||||
// size of reloc table, size of group, flags, 3 fields, zero
|
||||
EXPECT_EQ(7U, packed.size());
|
||||
// One pair present.
|
||||
EXPECT_EQ(1, packed[0]);
|
||||
// Delta from the neutral element is the initial relocation.
|
||||
EXPECT_EQ(0xf00d0000, packed[1]);
|
||||
EXPECT_EQ(10000, packed[2]);
|
||||
size_t ndx = 0;
|
||||
EXPECT_EQ(1U, packed[ndx++]);
|
||||
EXPECT_EQ(0xf00d0000, packed[ndx++]);
|
||||
EXPECT_EQ(1U, packed[ndx++]); // group_size
|
||||
EXPECT_EQ(8U, packed[ndx++]); // flags
|
||||
// Delta from the neutral element is zero
|
||||
EXPECT_EQ(0U, packed[ndx++]); // offset_delta
|
||||
EXPECT_EQ(11U, packed[ndx++]); // info
|
||||
EXPECT_EQ(10000U, packed[ndx++]); // addend_delta
|
||||
|
||||
// Add a second relocation, 4 byte offset delta, 12 byte addend delta.
|
||||
AddRelocation(0xf00d0004, 10012, &relocations);
|
||||
// same info
|
||||
AddRelocation(0xf00d0004, 11U, 10012, &relocations);
|
||||
|
||||
packed.clear();
|
||||
codec.Encode(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(5, packed.size());
|
||||
// Two pairs present.
|
||||
EXPECT_EQ(2, packed[0]);
|
||||
// Delta from the neutral element is the initial relocation.
|
||||
EXPECT_EQ(0xf00d0000, packed[1]);
|
||||
EXPECT_EQ(10000, packed[2]);
|
||||
// 4 byte offset delta, 12 byte addend delta.
|
||||
EXPECT_EQ(4, packed[3]);
|
||||
EXPECT_EQ(12, packed[4]);
|
||||
ndx = 0;
|
||||
EXPECT_EQ(8U, packed.size());
|
||||
|
||||
EXPECT_EQ(2U, packed[ndx++]); // relocs count
|
||||
EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset
|
||||
EXPECT_EQ(2U, packed[ndx++]); // group count
|
||||
EXPECT_EQ(11U, packed[ndx++]); // flags
|
||||
EXPECT_EQ(4U, packed[ndx++]); // group offset delta
|
||||
EXPECT_EQ(11U, packed[ndx++]); // info
|
||||
|
||||
EXPECT_EQ(10000U, packed[ndx++]); // addend delta
|
||||
EXPECT_EQ(12U, packed[ndx++]); // addend delta
|
||||
|
||||
// Add a third relocation, 4 byte offset delta, 12 byte addend delta.
|
||||
AddRelocation(0xf00d0008, 10024, &relocations);
|
||||
// different info
|
||||
AddRelocation(0xf00d0008, 41U, 10024, &relocations);
|
||||
|
||||
// Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
|
||||
AddRelocation(0xf00d0010, 10000, &relocations);
|
||||
AddRelocation(0xf00d0018, 9976, &relocations);
|
||||
AddRelocation(0xf00d0020, 9952, &relocations);
|
||||
AddRelocation(0xf00d0010, 42U, 10000, &relocations);
|
||||
AddRelocation(0xf00d0018, 42U, 9976, &relocations);
|
||||
AddRelocation(0xf00d0020, 42U, 9952, &relocations);
|
||||
|
||||
AddRelocation(0xf00d2028, 1042U, 0, &relocations);
|
||||
AddRelocation(0xf00d2030, 3442U, 0, &relocations);
|
||||
|
||||
packed.clear();
|
||||
codec.Encode(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(13, packed.size());
|
||||
// Six pairs present.
|
||||
EXPECT_EQ(6, packed[0]);
|
||||
ndx = 0;
|
||||
EXPECT_EQ(26U, packed.size());
|
||||
// Total number of relocs
|
||||
EXPECT_EQ(8U, packed[ndx++]);
|
||||
EXPECT_EQ(0xf00cfffc, packed[ndx++]);
|
||||
// 2 in first group
|
||||
EXPECT_EQ(2U, packed[ndx++]);
|
||||
EXPECT_EQ(11U, packed[ndx++]); //flags
|
||||
EXPECT_EQ(4U, packed[ndx++]); // group offset delta
|
||||
EXPECT_EQ(11U, packed[ndx++]); // info
|
||||
|
||||
// Initial relocation.
|
||||
EXPECT_EQ(0xf00d0000, packed[1]);
|
||||
EXPECT_EQ(10000, packed[2]);
|
||||
EXPECT_EQ(10000U, packed[ndx++]); // addend delta
|
||||
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
|
||||
EXPECT_EQ(4, packed[3]);
|
||||
EXPECT_EQ(12, packed[4]);
|
||||
EXPECT_EQ(4, packed[5]);
|
||||
EXPECT_EQ(12, packed[6]);
|
||||
EXPECT_EQ(12U, packed[ndx++]); // addend delta
|
||||
|
||||
// second group has only one reloc
|
||||
EXPECT_EQ(1U, packed[ndx++]); // count
|
||||
EXPECT_EQ(8U, packed[ndx++]); // flags
|
||||
|
||||
EXPECT_EQ(4U, packed[ndx++]); // offset delta
|
||||
EXPECT_EQ(41U, packed[ndx++]); // info
|
||||
EXPECT_EQ(12U, packed[ndx++]); // addend delta
|
||||
|
||||
// next - 3 relocs grouped by info
|
||||
EXPECT_EQ(3U, packed[ndx++]); // count
|
||||
EXPECT_EQ(11U, packed[ndx++]); // flags
|
||||
EXPECT_EQ(8U, packed[ndx++]); // group offset delta
|
||||
EXPECT_EQ(42U, packed[ndx++]); // info
|
||||
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
|
||||
EXPECT_EQ(8, packed[7]);
|
||||
EXPECT_EQ(-24, packed[8]);
|
||||
EXPECT_EQ(8, packed[9]);
|
||||
EXPECT_EQ(-24, packed[10]);
|
||||
EXPECT_EQ(8, packed[11]);
|
||||
EXPECT_EQ(-24, packed[12]);
|
||||
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
|
||||
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
|
||||
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
|
||||
|
||||
// and last - 2 relocations without addend
|
||||
EXPECT_EQ(2U, packed[ndx++]);
|
||||
EXPECT_EQ(0U, packed[ndx++]); // flags
|
||||
// offset_deltas and r_infos for next 2 relocations
|
||||
EXPECT_EQ(0x2008U, packed[ndx++]); // offset delta
|
||||
EXPECT_EQ(1042U, packed[ndx++]); // r_info
|
||||
EXPECT_EQ(0x8U, packed[ndx++]); // offset delta
|
||||
EXPECT_EQ(3442U, packed[ndx++]); // r_info
|
||||
|
||||
EXPECT_EQ(packed.size(), ndx);
|
||||
}
|
||||
|
||||
TEST(Delta, Decode) {
|
||||
std::vector<ELF::Sxword> packed;
|
||||
std::vector<ELF::Rela> relocations;
|
||||
TEST(Delta, Encode32) {
|
||||
encode<ELF32_traits>();
|
||||
}
|
||||
|
||||
RelocationDeltaCodec codec;
|
||||
TEST(Delta, Encode64) {
|
||||
encode<ELF64_traits>();
|
||||
}
|
||||
|
||||
template <typename ELF>
|
||||
static void decode() {
|
||||
std::vector<typename ELF::Addr> packed;
|
||||
std::vector<typename ELF::Rela> relocations;
|
||||
|
||||
RelocationDeltaCodec<ELF> codec;
|
||||
codec.Decode(packed, &relocations);
|
||||
|
||||
EXPECT_EQ(0, relocations.size());
|
||||
EXPECT_EQ(0U, relocations.size());
|
||||
|
||||
// Six pairs.
|
||||
packed.push_back(6);
|
||||
packed.push_back(6U); // count
|
||||
packed.push_back(0xc0ddfffc); // base offset
|
||||
packed.push_back(3U); // group count
|
||||
packed.push_back(11U); // flags
|
||||
packed.push_back(4U); // offset delta
|
||||
packed.push_back(11U); // info
|
||||
// Initial relocation.
|
||||
packed.push_back(0xc0de0000);
|
||||
packed.push_back(10000);
|
||||
packed.push_back(10000U);
|
||||
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
|
||||
packed.push_back(4);
|
||||
packed.push_back(12);
|
||||
packed.push_back(4);
|
||||
packed.push_back(12);
|
||||
packed.push_back(12U); // addend
|
||||
packed.push_back(12U); // addend
|
||||
|
||||
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
|
||||
packed.push_back(8);
|
||||
packed.push_back(-24);
|
||||
packed.push_back(8);
|
||||
packed.push_back(-24);
|
||||
packed.push_back(8);
|
||||
packed.push_back(-24);
|
||||
packed.push_back(1U); // group count
|
||||
packed.push_back(9U); // flags
|
||||
packed.push_back(11U); // info
|
||||
|
||||
packed.push_back(8U);
|
||||
packed.push_back(static_cast<typename ELF::Addr>(-24));
|
||||
// next group with 2 relocs
|
||||
packed.push_back(2U); // group count
|
||||
packed.push_back(11U); // flags
|
||||
packed.push_back(8U); // offset
|
||||
packed.push_back(42U); // info
|
||||
|
||||
packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
|
||||
packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
|
||||
|
||||
relocations.clear();
|
||||
codec.Decode(packed, &relocations);
|
||||
|
||||
EXPECT_EQ(6, relocations.size());
|
||||
EXPECT_EQ(6U, relocations.size());
|
||||
// Initial relocation.
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0000, 10000, relocations[0]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0000, 11U, 10000, relocations[0]));
|
||||
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0004, 10012, relocations[1]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0008, 10024, relocations[2]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2]));
|
||||
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0010, 10000, relocations[3]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0018, 9976, relocations[4]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0020, 9952, relocations[5]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4]));
|
||||
EXPECT_TRUE(CheckRelocation(0xc0de0020, 42U, 9952, relocations[5]));
|
||||
}
|
||||
|
||||
TEST(Delta, Decode32) {
|
||||
decode<ELF32_traits>();
|
||||
}
|
||||
|
||||
TEST(Delta, Decode64) {
|
||||
decode<ELF64_traits>();
|
||||
}
|
||||
|
||||
// TODO (dimitry): add more tests (fix by 19 January 2038 03:14:07 UTC)
|
||||
// TODO (dimtiry): 1. Incorrect packed array for decode
|
||||
// TODO (dimtiry): 2. Try to catch situation where it is likely to get series of groups with size 1
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -67,12 +67,13 @@ namespace relocation_packer {
|
||||
// An ElfFile reads shared objects, and shuttles relative relocations
|
||||
// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn
|
||||
// sections.
|
||||
template <typename ELF>
|
||||
class ElfFile {
|
||||
public:
|
||||
explicit ElfFile(int fd)
|
||||
: fd_(fd), is_padding_relocations_(false), elf_(NULL),
|
||||
relocations_section_(NULL), dynamic_section_(NULL),
|
||||
android_relocations_section_(NULL), relocations_type_(NONE) {}
|
||||
relocations_type_(NONE) {}
|
||||
~ElfFile() {}
|
||||
|
||||
// Set padding mode. When padding, PackRelocations() will not shrink
|
||||
@ -92,6 +93,10 @@ class ElfFile {
|
||||
bool UnpackRelocations();
|
||||
|
||||
private:
|
||||
enum relocations_type_t {
|
||||
NONE = 0, REL, RELA
|
||||
};
|
||||
|
||||
// Load a new ElfFile from a filedescriptor. If flushing, the file must
|
||||
// be open for read/write. Returns true on successful ELF file load.
|
||||
// |fd| is an open file descriptor for the shared object.
|
||||
@ -99,17 +104,34 @@ class ElfFile {
|
||||
|
||||
// Templated packer, helper for PackRelocations(). Rel type is one of
|
||||
// ELF::Rel or ELF::Rela.
|
||||
template <typename Rel>
|
||||
bool PackTypedRelocations(const std::vector<Rel>& relocations);
|
||||
bool PackTypedRelocations(std::vector<typename ELF::Rela>* relocations);
|
||||
|
||||
// Templated unpacker, helper for UnpackRelocations(). Rel type is one of
|
||||
// ELF::Rel or ELF::Rela.
|
||||
template <typename Rel>
|
||||
bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
|
||||
|
||||
// Write ELF file changes.
|
||||
void Flush();
|
||||
|
||||
void AdjustRelativeRelocationTargets(typename ELF::Off hole_start,
|
||||
ssize_t hole_size,
|
||||
std::vector<typename ELF::Rela>* relocations);
|
||||
|
||||
static void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
|
||||
typename ELF::Word new_sh_type, relocations_type_t relocations_type);
|
||||
|
||||
static void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
|
||||
typename ELF::Off hole_start,
|
||||
ssize_t hole_size,
|
||||
relocations_type_t relocations_type);
|
||||
|
||||
static void ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, size_t rel_array_size,
|
||||
std::vector<typename ELF::Rela>* rela_vector);
|
||||
|
||||
static void ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
|
||||
std::vector<typename ELF::Rel>* rel_vector);
|
||||
|
||||
|
||||
// File descriptor opened on the shared object.
|
||||
int fd_;
|
||||
|
||||
@ -123,10 +145,9 @@ class ElfFile {
|
||||
// Sections that we manipulate, assigned by Load().
|
||||
Elf_Scn* relocations_section_;
|
||||
Elf_Scn* dynamic_section_;
|
||||
Elf_Scn* android_relocations_section_;
|
||||
|
||||
// Relocation type found, assigned by Load().
|
||||
enum { NONE = 0, REL, RELA } relocations_type_;
|
||||
relocations_type_t relocations_type_;
|
||||
};
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -11,12 +11,7 @@
|
||||
#include <vector>
|
||||
#include "debug.h"
|
||||
#include "elf_traits.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
// Macro stringification.
|
||||
// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html
|
||||
#define XSTR(S) STR(S)
|
||||
#define STR(S) #S
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -27,8 +22,6 @@ void GetDataFilePath(const char* name, std::string* path) {
|
||||
if (bindir) {
|
||||
data_dir = std::string(bindir);
|
||||
} else {
|
||||
// Test data is in the gyp INTERMEDIATE_DIR subdirectory of the directory
|
||||
// that contains the current binary.
|
||||
char path[PATH_MAX];
|
||||
memset(path, 0, sizeof(path));
|
||||
ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1));
|
||||
@ -37,8 +30,7 @@ void GetDataFilePath(const char* name, std::string* path) {
|
||||
size_t pos = data_dir.rfind('/');
|
||||
ASSERT_NE(std::string::npos, pos);
|
||||
|
||||
data_dir.erase(pos + 1);
|
||||
data_dir += std::string(XSTR(INTERMEDIATE_DIR));
|
||||
data_dir.erase(pos);
|
||||
}
|
||||
|
||||
*path = data_dir + "/" + name;
|
||||
@ -49,7 +41,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) {
|
||||
GetDataFilePath(name, &path);
|
||||
|
||||
FILE* testfile = fopen(path.c_str(), "rb");
|
||||
ASSERT_FALSE(testfile == NULL);
|
||||
ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'";
|
||||
|
||||
FILE* temporary = tmpfile();
|
||||
ASSERT_FALSE(temporary == NULL);
|
||||
@ -70,15 +62,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) {
|
||||
*stream = temporary;
|
||||
}
|
||||
|
||||
void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) {
|
||||
const char* arch = NULL;
|
||||
if (ELF::kMachine == EM_ARM) {
|
||||
arch = "arm32";
|
||||
} else if (ELF::kMachine == EM_AARCH64) {
|
||||
arch = "arm64";
|
||||
}
|
||||
ASSERT_FALSE(arch == NULL);
|
||||
|
||||
void OpenRelocsTestFiles(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) {
|
||||
const std::string base = std::string("elf_file_unittest_relocs_") + arch;
|
||||
const std::string relocs = base + ".so";
|
||||
const std::string packed_relocs = base + "_packed.so";
|
||||
@ -115,41 +99,9 @@ void CheckFileContentsEqual(FILE* first, FILE* second) {
|
||||
EXPECT_TRUE(feof(first) && feof(second));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
TEST(ElfFile, PackRelocations) {
|
||||
ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
|
||||
|
||||
FILE* relocs_so = NULL;
|
||||
FILE* packed_relocs_so = NULL;
|
||||
OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
|
||||
if (HasFatalFailure())
|
||||
return;
|
||||
|
||||
ElfFile elf_file(fileno(relocs_so));
|
||||
|
||||
// Ensure unpacking fails (not packed).
|
||||
EXPECT_FALSE(elf_file.UnpackRelocations());
|
||||
|
||||
// Pack relocations, and check files are now identical.
|
||||
EXPECT_TRUE(elf_file.PackRelocations());
|
||||
CheckFileContentsEqual(relocs_so, packed_relocs_so);
|
||||
|
||||
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
|
||||
}
|
||||
|
||||
TEST(ElfFile, UnpackRelocations) {
|
||||
ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
|
||||
|
||||
FILE* relocs_so = NULL;
|
||||
FILE* packed_relocs_so = NULL;
|
||||
OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
|
||||
if (HasFatalFailure())
|
||||
return;
|
||||
|
||||
ElfFile elf_file(fileno(packed_relocs_so));
|
||||
template <typename ELF>
|
||||
static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
|
||||
relocation_packer::ElfFile<ELF> elf_file(fileno(packed_relocs_so));
|
||||
|
||||
// Ensure packing fails (already packed).
|
||||
EXPECT_FALSE(elf_file.PackRelocations());
|
||||
@ -161,4 +113,76 @@ TEST(ElfFile, UnpackRelocations) {
|
||||
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
|
||||
}
|
||||
|
||||
static void RunUnpackRelocationsTestFor(const std::string& arch) {
|
||||
ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
|
||||
|
||||
FILE* relocs_so = NULL;
|
||||
FILE* packed_relocs_so = NULL;
|
||||
OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
|
||||
|
||||
if (relocs_so != NULL && packed_relocs_so != NULL) {
|
||||
// lets detect elf class
|
||||
ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET))
|
||||
<< "Invalid file length: " << strerror(errno);
|
||||
uint8_t elf_class = 0;
|
||||
ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so));
|
||||
ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET));
|
||||
if (elf_class == ELFCLASS32) {
|
||||
ProcessUnpack<ELF32_traits>(relocs_so, packed_relocs_so);
|
||||
} else {
|
||||
ProcessUnpack<ELF64_traits>(relocs_so, packed_relocs_so);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ELF>
|
||||
static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) {
|
||||
relocation_packer::ElfFile<ELF> elf_file(fileno(relocs_so));
|
||||
|
||||
// Ensure unpacking fails (not packed).
|
||||
EXPECT_FALSE(elf_file.UnpackRelocations());
|
||||
|
||||
// Pack relocations, and check files are now identical.
|
||||
EXPECT_TRUE(elf_file.PackRelocations());
|
||||
CheckFileContentsEqual(relocs_so, packed_relocs_so);
|
||||
|
||||
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
|
||||
}
|
||||
|
||||
static void RunPackRelocationsTestFor(const std::string& arch) {
|
||||
ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
|
||||
|
||||
FILE* relocs_so = NULL;
|
||||
FILE* packed_relocs_so = NULL;
|
||||
OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
|
||||
|
||||
if (relocs_so != NULL && packed_relocs_so != NULL) {
|
||||
// lets detect elf class
|
||||
ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET))
|
||||
<< "Invalid file length: " << strerror(errno);
|
||||
uint8_t elf_class = 0;
|
||||
ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so));
|
||||
fseek(packed_relocs_so, 0, SEEK_SET);
|
||||
if (elf_class == ELFCLASS32) {
|
||||
ProcessPack<ELF32_traits>(relocs_so, packed_relocs_so);
|
||||
} else {
|
||||
ProcessPack<ELF64_traits>(relocs_so, packed_relocs_so);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
TEST(ElfFile, PackRelocations) {
|
||||
RunPackRelocationsTestFor("arm32");
|
||||
RunPackRelocationsTestFor("arm64");
|
||||
}
|
||||
|
||||
TEST(ElfFile, UnpackRelocations) {
|
||||
RunUnpackRelocationsTestFor("arm32");
|
||||
RunUnpackRelocationsTestFor("arm64");
|
||||
}
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -10,31 +10,10 @@
|
||||
#include "elf.h"
|
||||
#include "libelf.h"
|
||||
|
||||
// The TARGET_ macro controls which Elf types we expect and handle.
|
||||
// Either TARGET_ARM or TARGET_ARM64 must be defined, but not both.
|
||||
|
||||
#if !defined(TARGET_ARM) && !defined(TARGET_ARM64)
|
||||
# error "Unsupported target, define one of TARGET_ARM or TARGET_ARM64"
|
||||
#elif defined(TARGET_ARM) && defined(TARGET_ARM64)
|
||||
# error "Define one of TARGET_ARM or TARGET_ARM64, but not both"
|
||||
#endif
|
||||
|
||||
// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h.
|
||||
#ifndef EM_AARCH64
|
||||
#define EM_AARCH64 183
|
||||
#endif
|
||||
#ifndef R_AARCH64_RELATIVE
|
||||
#define R_AARCH64_RELATIVE 1027
|
||||
#endif
|
||||
#ifndef R_AARCH64_NONE
|
||||
#define R_AARCH64_NONE 0
|
||||
#endif
|
||||
|
||||
// ELF is a traits structure used to provide convenient aliases for
|
||||
// 32/64 bit Elf types and functions, depending on the target specified.
|
||||
// 32/64 bit Elf types and functions, depending on the target file.
|
||||
|
||||
#if defined(TARGET_ARM)
|
||||
struct ELF {
|
||||
struct ELF32_traits {
|
||||
typedef Elf32_Addr Addr;
|
||||
typedef Elf32_Dyn Dyn;
|
||||
typedef Elf32_Ehdr Ehdr;
|
||||
@ -48,27 +27,17 @@ struct ELF {
|
||||
typedef Elf32_Sym Sym;
|
||||
typedef Elf32_Word Word;
|
||||
typedef Elf32_Xword Xword;
|
||||
typedef Elf32_Half Half;
|
||||
|
||||
static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
|
||||
static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
|
||||
static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
|
||||
|
||||
enum { kMachine = EM_ARM };
|
||||
enum { kFileClass = ELFCLASS32 };
|
||||
enum { kRelativeRelocationCode = R_ARM_RELATIVE };
|
||||
enum { kNoRelocationCode = R_ARM_NONE };
|
||||
enum { kGnuStackSegmentAlignment = 0 };
|
||||
|
||||
static inline const char* Machine() { return "ARM"; }
|
||||
|
||||
# define ELF_R_SYM(val) ELF32_R_SYM(val)
|
||||
# define ELF_R_TYPE(val) ELF32_R_TYPE(val)
|
||||
# define ELF_R_INFO(sym, type) ELF32_R_INFO(sym, type)
|
||||
# define ELF_ST_TYPE(val) ELF32_ST_TYPE(val)
|
||||
static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); }
|
||||
static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); }
|
||||
static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); }
|
||||
};
|
||||
|
||||
#elif defined(TARGET_ARM64)
|
||||
struct ELF {
|
||||
struct ELF64_traits {
|
||||
typedef Elf64_Addr Addr;
|
||||
typedef Elf64_Dyn Dyn;
|
||||
typedef Elf64_Ehdr Ehdr;
|
||||
@ -82,24 +51,14 @@ struct ELF {
|
||||
typedef Elf64_Sym Sym;
|
||||
typedef Elf64_Word Word;
|
||||
typedef Elf64_Xword Xword;
|
||||
typedef Elf64_Half Half;
|
||||
|
||||
static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
|
||||
static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
|
||||
static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
|
||||
|
||||
enum { kMachine = EM_AARCH64 };
|
||||
enum { kFileClass = ELFCLASS64 };
|
||||
enum { kRelativeRelocationCode = R_AARCH64_RELATIVE };
|
||||
enum { kNoRelocationCode = R_AARCH64_NONE };
|
||||
enum { kGnuStackSegmentAlignment = 16 };
|
||||
|
||||
static inline const char* Machine() { return "ARM64"; }
|
||||
|
||||
# define ELF_R_SYM(val) ELF64_R_SYM(val)
|
||||
# define ELF_R_TYPE(val) ELF64_R_TYPE(val)
|
||||
# define ELF_R_INFO(sym, type) ELF64_R_INFO(sym, type)
|
||||
# define ELF_ST_TYPE(val) ELF64_ST_TYPE(val)
|
||||
static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); }
|
||||
static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); }
|
||||
static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); }
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
|
||||
|
@ -12,40 +12,50 @@
|
||||
namespace relocation_packer {
|
||||
|
||||
// Empty constructor and destructor to silence chromium-style.
|
||||
Leb128Encoder::Leb128Encoder() { }
|
||||
Leb128Encoder::~Leb128Encoder() { }
|
||||
template <typename uint_t>
|
||||
Leb128Encoder<uint_t>::Leb128Encoder() { }
|
||||
|
||||
template <typename uint_t>
|
||||
Leb128Encoder<uint_t>::~Leb128Encoder() { }
|
||||
|
||||
// Add a single value to the encoding. Values are encoded with variable
|
||||
// length. The least significant 7 bits of each byte hold 7 bits of data,
|
||||
// and the most significant bit is set on each byte except the last.
|
||||
void Leb128Encoder::Enqueue(ELF::Xword value) {
|
||||
template <typename uint_t>
|
||||
void Leb128Encoder<uint_t>::Enqueue(uint_t value) {
|
||||
uint_t uvalue = static_cast<uint_t>(value);
|
||||
do {
|
||||
const uint8_t byte = value & 127;
|
||||
value >>= 7;
|
||||
encoding_.push_back((value ? 128 : 0) | byte);
|
||||
} while (value);
|
||||
const uint8_t byte = uvalue & 127;
|
||||
uvalue >>= 7;
|
||||
encoding_.push_back((uvalue ? 128 : 0) | byte);
|
||||
} while (uvalue);
|
||||
}
|
||||
|
||||
// Add a vector of values to the encoding.
|
||||
void Leb128Encoder::EnqueueAll(const std::vector<ELF::Xword>& values) {
|
||||
for (size_t i = 0; i < values.size(); ++i)
|
||||
template <typename uint_t>
|
||||
void Leb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
Enqueue(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new decoder for the given encoded stream.
|
||||
Leb128Decoder::Leb128Decoder(const std::vector<uint8_t>& encoding) {
|
||||
template <typename uint_t>
|
||||
Leb128Decoder<uint_t>::Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
|
||||
encoding_ = encoding;
|
||||
cursor_ = 0;
|
||||
cursor_ = start_with;
|
||||
}
|
||||
|
||||
// Empty destructor to silence chromium-style.
|
||||
Leb128Decoder::~Leb128Decoder() { }
|
||||
template <typename uint_t>
|
||||
Leb128Decoder<uint_t>::~Leb128Decoder() { }
|
||||
|
||||
// Decode and retrieve a single value from the encoding. Read forwards until
|
||||
// a byte without its most significant bit is found, then read the 7 bit
|
||||
// fields of the bytes spanned to re-form the value.
|
||||
ELF::Xword Leb128Decoder::Dequeue() {
|
||||
ELF::Xword value = 0;
|
||||
template <typename uint_t>
|
||||
uint_t Leb128Decoder<uint_t>::Dequeue() {
|
||||
uint_t value = 0;
|
||||
|
||||
size_t shift = 0;
|
||||
uint8_t byte;
|
||||
@ -53,7 +63,7 @@ ELF::Xword Leb128Decoder::Dequeue() {
|
||||
// Loop until we reach a byte with its high order bit clear.
|
||||
do {
|
||||
byte = encoding_[cursor_++];
|
||||
value |= static_cast<ELF::Xword>(byte & 127) << shift;
|
||||
value |= static_cast<uint_t>(byte & 127) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 128);
|
||||
|
||||
@ -61,9 +71,17 @@ ELF::Xword Leb128Decoder::Dequeue() {
|
||||
}
|
||||
|
||||
// Decode and retrieve all remaining values from the encoding.
|
||||
void Leb128Decoder::DequeueAll(std::vector<ELF::Xword>* values) {
|
||||
while (cursor_ < encoding_.size())
|
||||
template <typename uint_t>
|
||||
void Leb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
|
||||
while (cursor_ < encoding_.size()) {
|
||||
values->push_back(Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
template class Leb128Encoder<uint32_t>;
|
||||
template class Leb128Encoder<uint64_t>;
|
||||
|
||||
template class Leb128Decoder<uint32_t>;
|
||||
template class Leb128Decoder<uint64_t>;
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -21,6 +21,7 @@
|
||||
namespace relocation_packer {
|
||||
|
||||
// Encode packed words as a LEB128 byte stream.
|
||||
template <typename uint_t>
|
||||
class Leb128Encoder {
|
||||
public:
|
||||
// Explicit (but empty) constructor and destructor, for chromium-style.
|
||||
@ -29,11 +30,11 @@ class Leb128Encoder {
|
||||
|
||||
// Add a value to the encoding stream.
|
||||
// |value| is the unsigned int to add.
|
||||
void Enqueue(ELF::Xword value);
|
||||
void Enqueue(uint_t value);
|
||||
|
||||
// Add a vector of values to the encoding stream.
|
||||
// |values| is the vector of unsigned ints to add.
|
||||
void EnqueueAll(const std::vector<ELF::Xword>& values);
|
||||
void EnqueueAll(const std::vector<uint_t>& values);
|
||||
|
||||
// Retrieve the encoded representation of the values.
|
||||
// |encoding| is the returned vector of encoded data.
|
||||
@ -45,21 +46,22 @@ class Leb128Encoder {
|
||||
};
|
||||
|
||||
// Decode a LEB128 byte stream to produce packed words.
|
||||
template <typename uint_t>
|
||||
class Leb128Decoder {
|
||||
public:
|
||||
// Create a new decoder for the given encoded stream.
|
||||
// |encoding| is the vector of encoded data.
|
||||
explicit Leb128Decoder(const std::vector<uint8_t>& encoding);
|
||||
explicit Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
|
||||
|
||||
// Explicit (but empty) destructor, for chromium-style.
|
||||
~Leb128Decoder();
|
||||
|
||||
// Retrieve the next value from the encoded stream.
|
||||
ELF::Xword Dequeue();
|
||||
uint_t Dequeue();
|
||||
|
||||
// Retrieve all remaining values from the encoded stream.
|
||||
// |values| is the vector of decoded data.
|
||||
void DequeueAll(std::vector<ELF::Xword>* values);
|
||||
void DequeueAll(std::vector<uint_t>* values);
|
||||
|
||||
private:
|
||||
// Encoded LEB128 stream.
|
||||
|
@ -5,19 +5,19 @@
|
||||
#include "leb128.h"
|
||||
|
||||
#include <vector>
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
TEST(Leb128, Encoder) {
|
||||
std::vector<ELF::Xword> values;
|
||||
TEST(Leb128, Encoder64) {
|
||||
std::vector<uint64_t> values;
|
||||
values.push_back(624485);
|
||||
values.push_back(0);
|
||||
values.push_back(1);
|
||||
values.push_back(127);
|
||||
values.push_back(128);
|
||||
|
||||
Leb128Encoder encoder;
|
||||
Leb128Encoder<uint64_t> encoder;
|
||||
encoder.EnqueueAll(values);
|
||||
|
||||
encoder.Enqueue(4294967295);
|
||||
@ -26,7 +26,7 @@ TEST(Leb128, Encoder) {
|
||||
std::vector<uint8_t> encoding;
|
||||
encoder.GetEncoding(&encoding);
|
||||
|
||||
EXPECT_EQ(23, encoding.size());
|
||||
EXPECT_EQ(23U, encoding.size());
|
||||
// 624485
|
||||
EXPECT_EQ(0xe5, encoding[0]);
|
||||
EXPECT_EQ(0x8e, encoding[1]);
|
||||
@ -59,7 +59,7 @@ TEST(Leb128, Encoder) {
|
||||
EXPECT_EQ(0x01, encoding[22]);
|
||||
}
|
||||
|
||||
TEST(Leb128, Decoder) {
|
||||
TEST(Leb128, Decoder64) {
|
||||
std::vector<uint8_t> encoding;
|
||||
// 624485
|
||||
encoding.push_back(0xe5);
|
||||
@ -92,20 +92,20 @@ TEST(Leb128, Decoder) {
|
||||
encoding.push_back(0xff);
|
||||
encoding.push_back(0x01);
|
||||
|
||||
Leb128Decoder decoder(encoding);
|
||||
Leb128Decoder<uint64_t> decoder(encoding, 0);
|
||||
|
||||
EXPECT_EQ(624485, decoder.Dequeue());
|
||||
EXPECT_EQ(624485U, decoder.Dequeue());
|
||||
|
||||
std::vector<ELF::Xword> dequeued;
|
||||
std::vector<uint64_t> dequeued;
|
||||
decoder.DequeueAll(&dequeued);
|
||||
|
||||
EXPECT_EQ(6, dequeued.size());
|
||||
EXPECT_EQ(0, dequeued[0]);
|
||||
EXPECT_EQ(1, dequeued[1]);
|
||||
EXPECT_EQ(127, dequeued[2]);
|
||||
EXPECT_EQ(128, dequeued[3]);
|
||||
EXPECT_EQ(4294967295, dequeued[4]);
|
||||
EXPECT_EQ(18446744073709551615ul, dequeued[5]);
|
||||
EXPECT_EQ(6U, dequeued.size());
|
||||
EXPECT_EQ(0U, dequeued[0]);
|
||||
EXPECT_EQ(1U, dequeued[1]);
|
||||
EXPECT_EQ(127U, dequeued[2]);
|
||||
EXPECT_EQ(128U, dequeued[3]);
|
||||
EXPECT_EQ(4294967295U, dequeued[4]);
|
||||
EXPECT_EQ(18446744073709551615UL, dequeued[5]);
|
||||
}
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -25,11 +25,12 @@
|
||||
|
||||
#include "debug.h"
|
||||
#include "elf_file.h"
|
||||
#include "elf_traits.h"
|
||||
#include "libelf.h"
|
||||
|
||||
namespace {
|
||||
#include "nativehelper/ScopedFd.h"
|
||||
|
||||
void PrintUsage(const char* argv0) {
|
||||
static void PrintUsage(const char* argv0) {
|
||||
std::string temporary = argv0;
|
||||
const size_t last_slash = temporary.find_last_of("/");
|
||||
if (last_slash != temporary.npos) {
|
||||
@ -45,59 +46,11 @@ void PrintUsage(const char* argv0) {
|
||||
" -p, --pad do not shrink relocations, but pad (for debugging)\n\n",
|
||||
basename);
|
||||
|
||||
if (ELF::kMachine == EM_ARM) {
|
||||
printf(
|
||||
"Extracts relative relocations from the .rel.dyn section, packs them\n"
|
||||
"into a more compact format, and stores the packed relocations in\n"
|
||||
".android.rel.dyn. Expands .android.rel.dyn to hold the packed\n"
|
||||
"data, and shrinks .rel.dyn by the amount of unpacked data removed\n"
|
||||
"from it.\n\n"
|
||||
"Before being packed, a shared library needs to be prepared by adding\n"
|
||||
"a null .android.rel.dyn section.\n\n"
|
||||
"To pack relocations in a shared library:\n\n"
|
||||
" echo -n 'NULL' >/tmp/small\n"
|
||||
" arm-linux-androideabi-objcopy \\\n"
|
||||
" --add-section .android.rel.dyn=/tmp/small \\\n"
|
||||
" libchrome.<version>.so\n"
|
||||
" rm /tmp/small\n"
|
||||
" %s libchrome.<version>.so\n\n"
|
||||
"To unpack and restore the shared library to its original state:\n\n"
|
||||
" %s -u libchrome.<version>.so\n"
|
||||
" arm-linux-androideabi-objcopy \\\n"
|
||||
" --remove-section=.android.rel.dyn libchrome.<version>.so\n\n",
|
||||
basename, basename);
|
||||
} else if (ELF::kMachine == EM_AARCH64) {
|
||||
printf(
|
||||
"Extracts relative relocations from the .rela.dyn section, packs them\n"
|
||||
"into a more compact format, and stores the packed relocations in\n"
|
||||
".android.rela.dyn. Expands .android.rela.dyn to hold the packed\n"
|
||||
"data, and shrinks .rela.dyn by the amount of unpacked data removed\n"
|
||||
"from it.\n\n"
|
||||
"Before being packed, a shared library needs to be prepared by adding\n"
|
||||
"a null .android.rela.dyn section.\n\n"
|
||||
"To pack relocations in a shared library:\n\n"
|
||||
" echo -n 'NULL' >/tmp/small\n"
|
||||
" aarch64-linux-android-objcopy \\\n"
|
||||
" --add-section .android.rela.dyn=/tmp/small \\\n"
|
||||
" libchrome.<version>.so\n"
|
||||
" rm /tmp/small\n"
|
||||
" %s libchrome.<version>.so\n\n"
|
||||
"To unpack and restore the shared library to its original state:\n\n"
|
||||
" %s -u libchrome.<version>.so\n"
|
||||
" aarch64-linux-android-objcopy \\\n"
|
||||
" --remove-section=.android.rela.dyn libchrome.<version>.so\n\n",
|
||||
basename, basename);
|
||||
} else {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
printf(
|
||||
"Debug sections are not handled, so packing should not be used on\n"
|
||||
"shared libraries compiled for debugging or otherwise unstripped.\n");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
bool is_unpacking = false;
|
||||
bool is_verbose = false;
|
||||
@ -143,11 +96,9 @@ int main(int argc, char* argv[]) {
|
||||
LOG(WARNING) << "Elf Library is out of date!";
|
||||
}
|
||||
|
||||
LOG(INFO) << "Configured for " << ELF::Machine();
|
||||
|
||||
const char* file = argv[argc - 1];
|
||||
const int fd = open(file, O_RDWR);
|
||||
if (fd == -1) {
|
||||
ScopedFd fd(open(file, O_RDWR));
|
||||
if (fd.get() == -1) {
|
||||
LOG(ERROR) << file << ": " << strerror(errno);
|
||||
return 1;
|
||||
}
|
||||
@ -155,16 +106,43 @@ int main(int argc, char* argv[]) {
|
||||
if (is_verbose)
|
||||
relocation_packer::Logger::SetVerbose(1);
|
||||
|
||||
relocation_packer::ElfFile elf_file(fd);
|
||||
elf_file.SetPadding(is_padding);
|
||||
// We need to detect elf class in order to create
|
||||
// correct implementation
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) {
|
||||
LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool status;
|
||||
if (is_unpacking)
|
||||
status = elf_file.UnpackRelocations();
|
||||
else
|
||||
status = elf_file.PackRelocations();
|
||||
if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
|
||||
LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
bool status = false;
|
||||
|
||||
if (e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
relocation_packer::ElfFile<ELF32_traits> elf_file(fd.get());
|
||||
elf_file.SetPadding(is_padding);
|
||||
|
||||
if (is_unpacking) {
|
||||
status = elf_file.UnpackRelocations();
|
||||
} else {
|
||||
status = elf_file.PackRelocations();
|
||||
}
|
||||
} else if (e_ident[EI_CLASS] == ELFCLASS64) {
|
||||
relocation_packer::ElfFile<ELF64_traits> elf_file(fd.get());
|
||||
elf_file.SetPadding(is_padding);
|
||||
|
||||
if (is_unpacking) {
|
||||
status = elf_file.UnpackRelocations();
|
||||
} else {
|
||||
status = elf_file.PackRelocations();
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << file << ": unknown ELFCLASS: " << e_ident[EI_CLASS];
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
LOG(ERROR) << file << ": failed to pack/unpack file";
|
||||
|
@ -10,115 +10,79 @@
|
||||
#include "delta_encoder.h"
|
||||
#include "elf_traits.h"
|
||||
#include "leb128.h"
|
||||
#include "run_length_encoder.h"
|
||||
#include "sleb128.h"
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
// Pack relative relocations into a run-length encoded packed
|
||||
// representation.
|
||||
void RelocationPacker::PackRelativeRelocations(
|
||||
const std::vector<ELF::Rel>& relocations,
|
||||
std::vector<uint8_t>* packed) {
|
||||
// Pack relocations into a group encoded packed representation.
|
||||
template <typename ELF>
|
||||
void RelocationPacker<ELF>::PackRelocations(const std::vector<typename ELF::Rela>& relocations,
|
||||
std::vector<uint8_t>* packed) {
|
||||
// Run-length encode.
|
||||
std::vector<ELF::Xword> packed_words;
|
||||
RelocationRunLengthCodec codec;
|
||||
std::vector<typename ELF::Addr> packed_words;
|
||||
RelocationDeltaCodec<ELF> codec;
|
||||
codec.Encode(relocations, &packed_words);
|
||||
|
||||
// If insufficient data to run-length encode, do nothing.
|
||||
// If insufficient data do nothing.
|
||||
if (packed_words.empty())
|
||||
return;
|
||||
|
||||
// LEB128 encode, with "APR1" prefix.
|
||||
Leb128Encoder encoder;
|
||||
encoder.Enqueue('A');
|
||||
encoder.Enqueue('P');
|
||||
encoder.Enqueue('R');
|
||||
encoder.Enqueue('1');
|
||||
encoder.EnqueueAll(packed_words);
|
||||
Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
|
||||
Leb128Encoder<typename ELF::Addr> leb128_encoder;
|
||||
|
||||
encoder.GetEncoding(packed);
|
||||
std::vector<uint8_t> leb128_packed;
|
||||
std::vector<uint8_t> sleb128_packed;
|
||||
|
||||
// Pad packed to a whole number of words. This padding will decode as
|
||||
// LEB128 zeroes. Run-length decoding ignores it because encoding
|
||||
// embeds the pairs count in the stream itself.
|
||||
while (packed->size() % sizeof(ELF::Word))
|
||||
packed->push_back(0);
|
||||
leb128_encoder.EnqueueAll(packed_words);
|
||||
leb128_encoder.GetEncoding(&leb128_packed);
|
||||
|
||||
sleb128_encoder.EnqueueAll(packed_words);
|
||||
sleb128_encoder.GetEncoding(&sleb128_packed);
|
||||
|
||||
// TODO (simonb): Estimate savings on current android system image and consider using
|
||||
// one encoder for all packed relocations to reduce complexity.
|
||||
if (leb128_packed.size() <= sleb128_packed.size()) {
|
||||
packed->push_back('A');
|
||||
packed->push_back('P');
|
||||
packed->push_back('U');
|
||||
packed->push_back('2');
|
||||
packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end());
|
||||
} else {
|
||||
packed->push_back('A');
|
||||
packed->push_back('P');
|
||||
packed->push_back('S');
|
||||
packed->push_back('2');
|
||||
packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack relative relocations from a run-length encoded packed
|
||||
// representation.
|
||||
void RelocationPacker::UnpackRelativeRelocations(
|
||||
template <typename ELF>
|
||||
void RelocationPacker<ELF>::UnpackRelocations(
|
||||
const std::vector<uint8_t>& packed,
|
||||
std::vector<ELF::Rel>* relocations) {
|
||||
// LEB128 decode, after checking and stripping "APR1" prefix.
|
||||
std::vector<ELF::Xword> packed_words;
|
||||
Leb128Decoder decoder(packed);
|
||||
CHECK(decoder.Dequeue() == 'A' &&
|
||||
decoder.Dequeue() == 'P' &&
|
||||
decoder.Dequeue() == 'R' &&
|
||||
decoder.Dequeue() == '1');
|
||||
decoder.DequeueAll(&packed_words);
|
||||
std::vector<typename ELF::Rela>* relocations) {
|
||||
|
||||
// Run-length decode.
|
||||
RelocationRunLengthCodec codec;
|
||||
std::vector<typename ELF::Addr> packed_words;
|
||||
CHECK(packed.size() > 4 &&
|
||||
packed[0] == 'A' &&
|
||||
packed[1] == 'P' &&
|
||||
(packed[2] == 'U' || packed[2] == 'S') &&
|
||||
packed[3] == '2');
|
||||
|
||||
if (packed[2] == 'U') {
|
||||
Leb128Decoder<typename ELF::Addr> decoder(packed, 4);
|
||||
decoder.DequeueAll(&packed_words);
|
||||
} else {
|
||||
Sleb128Decoder<typename ELF::Addr> decoder(packed, 4);
|
||||
decoder.DequeueAll(&packed_words);
|
||||
}
|
||||
|
||||
RelocationDeltaCodec<ELF> codec;
|
||||
codec.Decode(packed_words, relocations);
|
||||
}
|
||||
|
||||
// Pack relative relocations with addends into a delta encoded packed
|
||||
// representation.
|
||||
void RelocationPacker::PackRelativeRelocations(
|
||||
const std::vector<ELF::Rela>& relocations,
|
||||
std::vector<uint8_t>* packed) {
|
||||
// Delta encode.
|
||||
std::vector<ELF::Sxword> packed_words;
|
||||
RelocationDeltaCodec codec;
|
||||
codec.Encode(relocations, &packed_words);
|
||||
|
||||
// If insufficient data to delta encode, do nothing.
|
||||
if (packed_words.empty())
|
||||
return;
|
||||
|
||||
// Signed LEB128 encode, with "APA1" prefix. ASCII does not encode as
|
||||
// itself under signed LEB128, so we have to treat it specially.
|
||||
Sleb128Encoder encoder;
|
||||
encoder.EnqueueAll(packed_words);
|
||||
std::vector<uint8_t> encoded;
|
||||
encoder.GetEncoding(&encoded);
|
||||
|
||||
packed->push_back('A');
|
||||
packed->push_back('P');
|
||||
packed->push_back('A');
|
||||
packed->push_back('1');
|
||||
packed->insert(packed->end(), encoded.begin(), encoded.end());
|
||||
|
||||
// Pad packed to a whole number of words. This padding will decode as
|
||||
// signed LEB128 zeroes. Delta decoding ignores it because encoding
|
||||
// embeds the pairs count in the stream itself.
|
||||
while (packed->size() % sizeof(ELF::Word))
|
||||
packed->push_back(0);
|
||||
}
|
||||
|
||||
// Unpack relative relocations with addends from a delta encoded
|
||||
// packed representation.
|
||||
void RelocationPacker::UnpackRelativeRelocations(
|
||||
const std::vector<uint8_t>& packed,
|
||||
std::vector<ELF::Rela>* relocations) {
|
||||
// Check "APA1" prefix.
|
||||
CHECK(packed.at(0) == 'A' &&
|
||||
packed.at(1) == 'P' &&
|
||||
packed.at(2) == 'A' &&
|
||||
packed.at(3) == '1');
|
||||
|
||||
// Signed LEB128 decode, after stripping "APA1" prefix.
|
||||
std::vector<ELF::Sxword> packed_words;
|
||||
std::vector<uint8_t> stripped(packed.begin() + 4, packed.end());
|
||||
Sleb128Decoder decoder(stripped);
|
||||
decoder.DequeueAll(&packed_words);
|
||||
|
||||
// Delta decode.
|
||||
RelocationDeltaCodec codec;
|
||||
codec.Decode(packed_words, relocations);
|
||||
}
|
||||
template class RelocationPacker<ELF32_traits>;
|
||||
template class RelocationPacker<ELF64_traits>;
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -48,29 +48,25 @@
|
||||
#include <vector>
|
||||
|
||||
#include "elf.h"
|
||||
#include "elf_traits.h"
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
// A RelocationPacker packs vectors of relative relocations into more
|
||||
// A RelocationPacker packs vectors of relocations into more
|
||||
// compact forms, and unpacks them to reproduce the pre-packed data.
|
||||
template <typename ELF>
|
||||
class RelocationPacker {
|
||||
public:
|
||||
// Pack relative relocations into a more compact form.
|
||||
// |relocations| is a vector of relative relocation structs.
|
||||
// Pack relocations into a more compact form.
|
||||
// |relocations| is a vector of relocation structs.
|
||||
// |packed| is the vector of packed bytes into which relocations are packed.
|
||||
static void PackRelativeRelocations(const std::vector<ELF::Rel>& relocations,
|
||||
std::vector<uint8_t>* packed);
|
||||
static void PackRelativeRelocations(const std::vector<ELF::Rela>& relocations,
|
||||
std::vector<uint8_t>* packed);
|
||||
static void PackRelocations(const std::vector<typename ELF::Rela>& relocations,
|
||||
std::vector<uint8_t>* packed);
|
||||
|
||||
// Unpack relative relocations from their more compact form.
|
||||
// Unpack relocations from their more compact form.
|
||||
// |packed| is the vector of packed relocations.
|
||||
// |relocations| is a vector of unpacked relative relocation structs.
|
||||
static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
|
||||
std::vector<ELF::Rel>* relocations);
|
||||
static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
|
||||
std::vector<ELF::Rela>* relocations);
|
||||
// |relocations| is a vector of unpacked relocation structs.
|
||||
static void UnpackRelocations(const std::vector<uint8_t>& packed,
|
||||
std::vector<typename ELF::Rela>* relocations);
|
||||
};
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -7,244 +7,286 @@
|
||||
#include <vector>
|
||||
#include "elf.h"
|
||||
#include "elf_traits.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
|
||||
ELF::Rel relocation;
|
||||
template <typename ELF>
|
||||
static void AddRelocation(typename ELF::Addr addr,
|
||||
typename ELF::Xword info,
|
||||
typename ELF::Sxword addend,
|
||||
std::vector<typename ELF::Rela>* relocations) {
|
||||
typename ELF::Rela relocation;
|
||||
relocation.r_offset = addr;
|
||||
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
|
||||
relocations->push_back(relocation);
|
||||
}
|
||||
|
||||
bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
|
||||
return relocation.r_offset == addr &&
|
||||
ELF_R_SYM(relocation.r_info) == 0 &&
|
||||
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
|
||||
}
|
||||
|
||||
void AddRelocation(ELF::Addr addr,
|
||||
ELF::Sxword addend,
|
||||
std::vector<ELF::Rela>* relocations) {
|
||||
ELF::Rela relocation;
|
||||
relocation.r_offset = addr;
|
||||
relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
|
||||
relocation.r_info = info;
|
||||
relocation.r_addend = addend;
|
||||
|
||||
relocations->push_back(relocation);
|
||||
}
|
||||
|
||||
bool CheckRelocation(ELF::Addr addr,
|
||||
ELF::Sxword addend,
|
||||
const ELF::Rela& relocation) {
|
||||
template <typename ELF>
|
||||
static bool CheckRelocation(typename ELF::Addr addr,
|
||||
typename ELF::Xword info,
|
||||
typename ELF::Sxword addend,
|
||||
const typename ELF::Rela& relocation) {
|
||||
return relocation.r_offset == addr &&
|
||||
ELF_R_SYM(relocation.r_info) == 0 &&
|
||||
ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
|
||||
relocation.r_info == info &&
|
||||
relocation.r_addend == addend;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
TEST(Packer, PackRel) {
|
||||
std::vector<ELF::Rel> relocations;
|
||||
template <typename ELF>
|
||||
static void DoPackNoAddend() {
|
||||
std::vector<typename ELF::Rela> relocations;
|
||||
std::vector<uint8_t> packed;
|
||||
|
||||
RelocationPacker packer;
|
||||
|
||||
// Initial relocation.
|
||||
AddRelocation(0xd1ce0000, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
|
||||
// Two more relocations, 4 byte deltas.
|
||||
AddRelocation(0xd1ce0004, &relocations);
|
||||
AddRelocation(0xd1ce0008, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
|
||||
// Three more relocations, 8 byte deltas.
|
||||
AddRelocation(0xd1ce0010, &relocations);
|
||||
AddRelocation(0xd1ce0018, &relocations);
|
||||
AddRelocation(0xd1ce0020, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
|
||||
|
||||
RelocationPacker<ELF> packer;
|
||||
|
||||
packed.clear();
|
||||
packer.PackRelativeRelocations(relocations, &packed);
|
||||
packer.PackRelocations(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(16, packed.size());
|
||||
ASSERT_EQ(18U, packed.size());
|
||||
// Identifier.
|
||||
EXPECT_EQ('A', packed[0]);
|
||||
EXPECT_EQ('P', packed[1]);
|
||||
EXPECT_EQ('R', packed[2]);
|
||||
EXPECT_EQ('1', packed[3]);
|
||||
// Count-delta pairs count.
|
||||
EXPECT_EQ(2, packed[4]);
|
||||
// 0xd1ce0000
|
||||
EXPECT_EQ(128, packed[5]);
|
||||
EXPECT_EQ(128, packed[6]);
|
||||
EXPECT_EQ(184, packed[7]);
|
||||
EXPECT_EQ(142, packed[8]);
|
||||
EXPECT_EQ(13, packed[9]);
|
||||
// Run of two relocations, 4 byte deltas.
|
||||
EXPECT_EQ(2, packed[10]);
|
||||
EXPECT_EQ(4, packed[11]);
|
||||
// Run of three relocations, 8 byte deltas.
|
||||
EXPECT_EQ(3, packed[12]);
|
||||
EXPECT_EQ(8, packed[13]);
|
||||
// Padding.
|
||||
EXPECT_EQ(0, packed[14]);
|
||||
EXPECT_EQ(0, packed[15]);
|
||||
size_t ndx = 0;
|
||||
EXPECT_EQ('A', packed[ndx++]);
|
||||
EXPECT_EQ('P', packed[ndx++]);
|
||||
EXPECT_EQ('U', packed[ndx++]);
|
||||
EXPECT_EQ('2', packed[ndx++]);
|
||||
// relocation count
|
||||
EXPECT_EQ(6, packed[ndx++]);
|
||||
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
|
||||
EXPECT_EQ(0xfc, packed[ndx++]);
|
||||
EXPECT_EQ(0xff, packed[ndx++]);
|
||||
EXPECT_EQ(0xb7, packed[ndx++]);
|
||||
EXPECT_EQ(0x8e, packed[ndx++]);
|
||||
EXPECT_EQ(0x0d, packed[ndx++]);
|
||||
// first group
|
||||
EXPECT_EQ(3, packed[ndx++]); // size
|
||||
EXPECT_EQ(3, packed[ndx++]); // flags
|
||||
EXPECT_EQ(4, packed[ndx++]); // r_offset_delta
|
||||
EXPECT_EQ(0x11, packed[ndx++]); // r_info
|
||||
// second group
|
||||
EXPECT_EQ(3, packed[ndx++]); // size
|
||||
EXPECT_EQ(3, packed[ndx++]); // flags
|
||||
EXPECT_EQ(8, packed[ndx++]); // r_offset_delta
|
||||
EXPECT_EQ(0x11, packed[ndx++]); // r_info
|
||||
|
||||
EXPECT_EQ(ndx, packed.size());
|
||||
}
|
||||
|
||||
TEST(Packer, UnpackRel) {
|
||||
TEST(Packer, PackNoAddend) {
|
||||
DoPackNoAddend<ELF32_traits>();
|
||||
DoPackNoAddend<ELF64_traits>();
|
||||
}
|
||||
|
||||
template <typename ELF>
|
||||
static void DoUnpackNoAddend() {
|
||||
std::vector<typename ELF::Rela> relocations;
|
||||
std::vector<uint8_t> packed;
|
||||
std::vector<ELF::Rel> relocations;
|
||||
|
||||
RelocationPacker packer;
|
||||
|
||||
// Identifier.
|
||||
packed.push_back('A');
|
||||
packed.push_back('P');
|
||||
packed.push_back('R');
|
||||
packed.push_back('1');
|
||||
// Count-delta pairs count.
|
||||
packed.push_back(2);
|
||||
// 0xd1ce0000
|
||||
packed.push_back(128);
|
||||
packed.push_back(128);
|
||||
packed.push_back(184);
|
||||
packed.push_back(142);
|
||||
packed.push_back(13);
|
||||
// Run of two relocations, 4 byte deltas.
|
||||
packed.push_back(2);
|
||||
packed.push_back(4);
|
||||
// Run of three relocations, 8 byte deltas.
|
||||
packed.push_back(3);
|
||||
packed.push_back(8);
|
||||
// Padding.
|
||||
packed.push_back(0);
|
||||
packed.push_back(0);
|
||||
|
||||
relocations.clear();
|
||||
packer.UnpackRelativeRelocations(packed, &relocations);
|
||||
|
||||
EXPECT_EQ(6, relocations.size());
|
||||
// Initial relocation.
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0]));
|
||||
// Two relocations, 4 byte deltas.
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1]));
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2]));
|
||||
// Three relocations, 8 byte deltas.
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3]));
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4]));
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5]));
|
||||
}
|
||||
|
||||
TEST(Packer, PackRela) {
|
||||
std::vector<ELF::Rela> relocations;
|
||||
std::vector<uint8_t> packed;
|
||||
|
||||
RelocationPacker packer;
|
||||
|
||||
// Initial relocation.
|
||||
AddRelocation(0xd1ce0000, 10000, &relocations);
|
||||
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
|
||||
AddRelocation(0xd1ce0004, 10012, &relocations);
|
||||
AddRelocation(0xd1ce0008, 10024, &relocations);
|
||||
// Three more relocations, 8 byte deltas, -24 byte addend deltas.
|
||||
AddRelocation(0xd1ce0010, 10000, &relocations);
|
||||
AddRelocation(0xd1ce0018, 9976, &relocations);
|
||||
AddRelocation(0xd1ce0020, 9952, &relocations);
|
||||
|
||||
packed.clear();
|
||||
packer.PackRelativeRelocations(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(24, packed.size());
|
||||
// Identifier.
|
||||
EXPECT_EQ('A', packed[0]);
|
||||
EXPECT_EQ('P', packed[1]);
|
||||
EXPECT_EQ('A', packed[2]);
|
||||
EXPECT_EQ('1', packed[3]);
|
||||
// Delta pairs count.
|
||||
EXPECT_EQ(6, packed[4]);
|
||||
// 0xd1ce0000
|
||||
EXPECT_EQ(128, packed[5]);
|
||||
EXPECT_EQ(128, packed[6]);
|
||||
EXPECT_EQ(184, packed[7]);
|
||||
EXPECT_EQ(142, packed[8]);
|
||||
EXPECT_EQ(13, packed[9]);
|
||||
// 10000
|
||||
EXPECT_EQ(144, packed[10]);
|
||||
EXPECT_EQ(206, packed[11]);
|
||||
EXPECT_EQ(0, packed[12]);
|
||||
// 4, 12
|
||||
EXPECT_EQ(4, packed[13]);
|
||||
EXPECT_EQ(12, packed[14]);
|
||||
// 4, 12
|
||||
EXPECT_EQ(4, packed[15]);
|
||||
EXPECT_EQ(12, packed[16]);
|
||||
// 8, -24
|
||||
EXPECT_EQ(8, packed[17]);
|
||||
EXPECT_EQ(104, packed[18]);
|
||||
// 8, -24
|
||||
EXPECT_EQ(8, packed[19]);
|
||||
EXPECT_EQ(104, packed[20]);
|
||||
// 8, -24
|
||||
EXPECT_EQ(8, packed[21]);
|
||||
EXPECT_EQ(104, packed[22]);
|
||||
// Padding.
|
||||
EXPECT_EQ(0, packed[23]);
|
||||
}
|
||||
|
||||
TEST(Packer, UnpackRela) {
|
||||
std::vector<uint8_t> packed;
|
||||
std::vector<ELF::Rela> relocations;
|
||||
|
||||
RelocationPacker packer;
|
||||
|
||||
// Identifier.
|
||||
packed.push_back('A');
|
||||
packed.push_back('P');
|
||||
packed.push_back('A');
|
||||
packed.push_back('1');
|
||||
// Delta pairs count.
|
||||
packed.push_back('U');
|
||||
packed.push_back('2');
|
||||
// relocation count
|
||||
packed.push_back(6);
|
||||
// 0xd1ce0000
|
||||
packed.push_back(128);
|
||||
packed.push_back(128);
|
||||
packed.push_back(184);
|
||||
packed.push_back(142);
|
||||
packed.push_back(13);
|
||||
// 10000
|
||||
packed.push_back(144);
|
||||
packed.push_back(206);
|
||||
packed.push_back(0);
|
||||
// 4, 12
|
||||
packed.push_back(4);
|
||||
packed.push_back(12);
|
||||
// 4, 12
|
||||
packed.push_back(4);
|
||||
packed.push_back(12);
|
||||
// 8, -24
|
||||
packed.push_back(8);
|
||||
packed.push_back(104);
|
||||
// 8, -24
|
||||
packed.push_back(8);
|
||||
packed.push_back(104);
|
||||
// 8, -24
|
||||
packed.push_back(8);
|
||||
packed.push_back(104);
|
||||
// Padding.
|
||||
packed.push_back(0);
|
||||
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
|
||||
packed.push_back(0xfc);
|
||||
packed.push_back(0xff);
|
||||
packed.push_back(0xb7);
|
||||
packed.push_back(0x8e);
|
||||
packed.push_back(0x0d);
|
||||
// first group
|
||||
packed.push_back(3); // size
|
||||
packed.push_back(3); // flags
|
||||
packed.push_back(4); // r_offset_delta
|
||||
packed.push_back(0x11); // r_info
|
||||
// second group
|
||||
packed.push_back(3); // size
|
||||
packed.push_back(3); // flags
|
||||
packed.push_back(8); // r_offset_delta
|
||||
packed.push_back(0x11); // r_info
|
||||
|
||||
RelocationPacker<ELF> packer;
|
||||
packer.UnpackRelocations(packed, &relocations);
|
||||
|
||||
size_t ndx = 0;
|
||||
EXPECT_EQ(6U, relocations.size());
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x11, 0, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x11, 0, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x11, 0, relocations[ndx++]));
|
||||
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x11, 0, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x11, 0, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x11, 0, relocations[ndx++]));
|
||||
|
||||
EXPECT_EQ(ndx, relocations.size());
|
||||
}
|
||||
|
||||
TEST(Packer, UnpackNoAddend) {
|
||||
DoUnpackNoAddend<ELF32_traits>();
|
||||
DoUnpackNoAddend<ELF64_traits>();
|
||||
}
|
||||
|
||||
template <typename ELF>
|
||||
static void DoPackWithAddend() {
|
||||
std::vector<typename ELF::Rela> relocations;
|
||||
|
||||
// Initial relocation.
|
||||
AddRelocation<ELF>(0xd1ce0000, 0x01, 10024, &relocations);
|
||||
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
|
||||
AddRelocation<ELF>(0xd1ce0004, 0x01, 10012, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0008, 0x01, 10024, &relocations);
|
||||
// Three more relocations, 8 byte deltas, -24 byte addend deltas.
|
||||
AddRelocation<ELF>(0xd1ce0010, 0x01, 10000, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0018, 0x01, 9976, &relocations);
|
||||
AddRelocation<ELF>(0xd1ce0020, 0x01, 9952, &relocations);
|
||||
|
||||
std::vector<uint8_t> packed;
|
||||
|
||||
RelocationPacker<ELF> packer;
|
||||
|
||||
packed.clear();
|
||||
packer.PackRelocations(relocations, &packed);
|
||||
|
||||
EXPECT_EQ(26U, packed.size());
|
||||
size_t ndx = 0;
|
||||
// Identifier.
|
||||
EXPECT_EQ('A', packed[ndx++]);
|
||||
EXPECT_EQ('P', packed[ndx++]);
|
||||
EXPECT_EQ('S', packed[ndx++]);
|
||||
EXPECT_EQ('2', packed[ndx++]);
|
||||
// Relocation count
|
||||
EXPECT_EQ(6U, packed[ndx++]);
|
||||
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr)
|
||||
EXPECT_EQ(0xfc, packed[ndx++]);
|
||||
EXPECT_EQ(0xff, packed[ndx++]);
|
||||
EXPECT_EQ(0xb7, packed[ndx++]);
|
||||
EXPECT_EQ(0x8e, packed[ndx++]);
|
||||
if (sizeof(typename ELF::Addr) == 8) {
|
||||
// positive for uint64_t
|
||||
EXPECT_EQ(0x0d, packed[ndx++]);
|
||||
} else {
|
||||
// negative for uint32_t
|
||||
EXPECT_EQ(0x7d, packed[ndx++]);
|
||||
}
|
||||
// group 1
|
||||
EXPECT_EQ(0x03, packed[ndx++]); // size
|
||||
EXPECT_EQ(0x0b, packed[ndx++]); // flags
|
||||
EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta
|
||||
EXPECT_EQ(0x01, packed[ndx++]); // r_info
|
||||
// group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
|
||||
EXPECT_EQ(0xa8, packed[ndx++]);
|
||||
EXPECT_EQ(0xce, packed[ndx++]);
|
||||
EXPECT_EQ(0x00, packed[ndx++]);
|
||||
// group 1 - addend 2: -12 = 0x74
|
||||
EXPECT_EQ(0x74, packed[ndx++]);
|
||||
// group 1 - addend 3: +12 = 0x0c
|
||||
EXPECT_EQ(0x0c, packed[ndx++]);
|
||||
|
||||
// group 2
|
||||
EXPECT_EQ(0x03, packed[ndx++]); // size
|
||||
EXPECT_EQ(0x0b, packed[ndx++]); // flags
|
||||
EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta
|
||||
EXPECT_EQ(0x01, packed[ndx++]); // r_info
|
||||
|
||||
// group 2 - addend 1: -24 = 0x68
|
||||
EXPECT_EQ(0x68, packed[ndx++]);
|
||||
// group 2 - addend 2: -24 = 0x68
|
||||
EXPECT_EQ(0x68, packed[ndx++]);
|
||||
// group 2 - addend 3: -24 = 0x68
|
||||
EXPECT_EQ(0x68, packed[ndx++]);
|
||||
|
||||
EXPECT_EQ(ndx, packed.size());
|
||||
}
|
||||
|
||||
TEST(Packer, PackWithAddend) {
|
||||
DoPackWithAddend<ELF32_traits>();
|
||||
DoPackWithAddend<ELF64_traits>();
|
||||
}
|
||||
|
||||
template <typename ELF>
|
||||
static void DoUnpackWithAddend() {
|
||||
std::vector<uint8_t> packed;
|
||||
// Identifier.
|
||||
packed.push_back('A');
|
||||
packed.push_back('P');
|
||||
packed.push_back('S');
|
||||
packed.push_back('2');
|
||||
// Relocation count
|
||||
packed.push_back(6U);
|
||||
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
|
||||
packed.push_back(0xfc);
|
||||
packed.push_back(0xff);
|
||||
packed.push_back(0xb7);
|
||||
packed.push_back(0x8e);
|
||||
if (sizeof(typename ELF::Addr) == 8) {
|
||||
// positive for uint64_t
|
||||
packed.push_back(0x0d);
|
||||
} else {
|
||||
// negative for uint32_t
|
||||
packed.push_back(0x7d);
|
||||
}
|
||||
// group 1
|
||||
packed.push_back(0x03); // size
|
||||
packed.push_back(0x0b); // flags
|
||||
packed.push_back(0x04); // r_offset_delta
|
||||
packed.push_back(0x01); // r_info
|
||||
// group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
|
||||
packed.push_back(0xa8);
|
||||
packed.push_back(0xce);
|
||||
packed.push_back(0x00);
|
||||
// group 1 - addend 2: -12 = 0x74
|
||||
packed.push_back(0x74);
|
||||
// group 1 - addend 3: +12 = 0x0c
|
||||
packed.push_back(0x0c);
|
||||
|
||||
// group 2
|
||||
packed.push_back(0x03); // size
|
||||
packed.push_back(0x0b); // flags
|
||||
packed.push_back(0x08); // r_offset_delta
|
||||
packed.push_back(0x01); // r_info
|
||||
|
||||
// group 2 - addend 1: -24 = 0x68
|
||||
packed.push_back(0x68);
|
||||
// group 2 - addend 2: -24 = 0x68
|
||||
packed.push_back(0x68);
|
||||
// group 2 - addend 3: -24 = 0x68
|
||||
packed.push_back(0x68);
|
||||
|
||||
std::vector<typename ELF::Rela> relocations;
|
||||
|
||||
RelocationPacker<ELF> packer;
|
||||
|
||||
relocations.clear();
|
||||
packer.UnpackRelativeRelocations(packed, &relocations);
|
||||
packer.UnpackRelocations(packed, &relocations);
|
||||
|
||||
EXPECT_EQ(6, relocations.size());
|
||||
EXPECT_EQ(6U, relocations.size());
|
||||
size_t ndx = 0;
|
||||
// Initial relocation.
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0000, 10000, relocations[0]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x01, 10024, relocations[ndx++]));
|
||||
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0004, 10012, relocations[1]));
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0008, 10024, relocations[2]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x01, 10012, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x01, 10024, relocations[ndx++]));
|
||||
// Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0010, 10000, relocations[3]));
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0018, 9976, relocations[4]));
|
||||
EXPECT_TRUE(CheckRelocation(0xd1ce0020, 9952, relocations[5]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x01, 10000, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x01, 9976, relocations[ndx++]));
|
||||
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x01, 9952, relocations[ndx++]));
|
||||
|
||||
EXPECT_EQ(ndx, relocations.size());
|
||||
}
|
||||
|
||||
TEST(Packer, UnpackWithAddend) {
|
||||
DoUnpackWithAddend<ELF32_traits>();
|
||||
DoUnpackWithAddend<ELF64_traits>();
|
||||
}
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
@ -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
|
@ -10,11 +10,33 @@
|
||||
|
||||
#include "elf_traits.h"
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
class uint_traits {};
|
||||
|
||||
template <>
|
||||
class uint_traits<uint64_t> {
|
||||
public:
|
||||
typedef int64_t int_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
class uint_traits<uint32_t> {
|
||||
public:
|
||||
typedef int32_t int_t;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
// Empty constructor and destructor to silence chromium-style.
|
||||
Sleb128Encoder::Sleb128Encoder() { }
|
||||
Sleb128Encoder::~Sleb128Encoder() { }
|
||||
template <typename uint_t>
|
||||
Sleb128Encoder<uint_t>::Sleb128Encoder() { }
|
||||
|
||||
template <typename uint_t>
|
||||
Sleb128Encoder<uint_t>::~Sleb128Encoder() { }
|
||||
|
||||
// Add a single value to the encoding. Values are encoded with variable
|
||||
// length. The least significant 7 bits of each byte hold 7 bits of data,
|
||||
@ -22,11 +44,13 @@ Sleb128Encoder::~Sleb128Encoder() { }
|
||||
// value is sign extended up to a multiple of 7 bits (ensuring that the
|
||||
// most significant bit is zero for a positive number and one for a
|
||||
// negative number).
|
||||
void Sleb128Encoder::Enqueue(ELF::Sxword value) {
|
||||
template <typename uint_t>
|
||||
void Sleb128Encoder<uint_t>::Enqueue(uint_t value) {
|
||||
typedef typename uint_traits<uint_t>::int_t int_t;
|
||||
static const size_t size = CHAR_BIT * sizeof(value);
|
||||
|
||||
bool more = true;
|
||||
const bool negative = value < 0;
|
||||
const bool negative = static_cast<int_t>(value) < 0;
|
||||
|
||||
while (more) {
|
||||
uint8_t byte = value & 127;
|
||||
@ -34,11 +58,11 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) {
|
||||
|
||||
// Sign extend if encoding a -ve value.
|
||||
if (negative)
|
||||
value |= -(static_cast<ELF::Sxword>(1) << (size - 7));
|
||||
value |= -(static_cast<uint_t>(1) << (size - 7));
|
||||
|
||||
// The sign bit of byte is second high order bit.
|
||||
const bool sign_bit = byte & 64;
|
||||
if ((value == 0 && !sign_bit) || (value == -1 && sign_bit))
|
||||
if ((value == 0 && !sign_bit) || (value == static_cast<uint_t>(-1) && sign_bit))
|
||||
more = false;
|
||||
else
|
||||
byte |= 128;
|
||||
@ -47,25 +71,30 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) {
|
||||
}
|
||||
|
||||
// Add a vector of values to the encoding.
|
||||
void Sleb128Encoder::EnqueueAll(const std::vector<ELF::Sxword>& values) {
|
||||
for (size_t i = 0; i < values.size(); ++i)
|
||||
template <typename uint_t>
|
||||
void Sleb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
Enqueue(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new decoder for the given encoded stream.
|
||||
Sleb128Decoder::Sleb128Decoder(const std::vector<uint8_t>& encoding) {
|
||||
template <typename uint_t>
|
||||
Sleb128Decoder<uint_t>::Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
|
||||
encoding_ = encoding;
|
||||
cursor_ = 0;
|
||||
cursor_ = start_with;
|
||||
}
|
||||
|
||||
// Empty destructor to silence chromium-style.
|
||||
Sleb128Decoder::~Sleb128Decoder() { }
|
||||
template <typename uint_t>
|
||||
Sleb128Decoder<uint_t>::~Sleb128Decoder() { }
|
||||
|
||||
// Decode and retrieve a single value from the encoding. Consume bytes
|
||||
// until one without its most significant bit is found, and re-form the
|
||||
// value from the 7 bit fields of the bytes consumed.
|
||||
ELF::Sxword Sleb128Decoder::Dequeue() {
|
||||
ELF::Sxword value = 0;
|
||||
template <typename uint_t>
|
||||
uint_t Sleb128Decoder<uint_t>::Dequeue() {
|
||||
uint_t value = 0;
|
||||
static const size_t size = CHAR_BIT * sizeof(value);
|
||||
|
||||
size_t shift = 0;
|
||||
@ -74,22 +103,29 @@ ELF::Sxword Sleb128Decoder::Dequeue() {
|
||||
// Loop until we reach a byte with its high order bit clear.
|
||||
do {
|
||||
byte = encoding_[cursor_++];
|
||||
value |= (static_cast<ELF::Sxword>(byte & 127) << shift);
|
||||
value |= (static_cast<uint_t>(byte & 127) << shift);
|
||||
shift += 7;
|
||||
} while (byte & 128);
|
||||
|
||||
// The sign bit is second high order bit of the final byte decoded.
|
||||
// Sign extend if value is -ve and we did not shift all of it.
|
||||
if (shift < size && (byte & 64))
|
||||
value |= -(static_cast<ELF::Sxword>(1) << shift);
|
||||
value |= -(static_cast<uint_t>(1) << shift);
|
||||
|
||||
return value;
|
||||
return static_cast<uint_t>(value);
|
||||
}
|
||||
|
||||
// Decode and retrieve all remaining values from the encoding.
|
||||
void Sleb128Decoder::DequeueAll(std::vector<ELF::Sxword>* values) {
|
||||
while (cursor_ < encoding_.size())
|
||||
template <typename uint_t>
|
||||
void Sleb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
|
||||
while (cursor_ < encoding_.size()) {
|
||||
values->push_back(Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
template class Sleb128Encoder<uint32_t>;
|
||||
template class Sleb128Encoder<uint64_t>;
|
||||
template class Sleb128Decoder<uint32_t>;
|
||||
template class Sleb128Decoder<uint64_t>;
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace relocation_packer {
|
||||
|
||||
// Encode packed words as a signed LEB128 byte stream.
|
||||
template<typename int_t>
|
||||
class Sleb128Encoder {
|
||||
public:
|
||||
// Explicit (but empty) constructor and destructor, for chromium-style.
|
||||
@ -30,11 +31,11 @@ class Sleb128Encoder {
|
||||
|
||||
// Add a value to the encoding stream.
|
||||
// |value| is the signed int to add.
|
||||
void Enqueue(ELF::Sxword value);
|
||||
void Enqueue(int_t value);
|
||||
|
||||
// Add a vector of values to the encoding stream.
|
||||
// |values| is the vector of signed ints to add.
|
||||
void EnqueueAll(const std::vector<ELF::Sxword>& values);
|
||||
void EnqueueAll(const std::vector<int_t>& values);
|
||||
|
||||
// Retrieve the encoded representation of the values.
|
||||
// |encoding| is the returned vector of encoded data.
|
||||
@ -46,21 +47,22 @@ class Sleb128Encoder {
|
||||
};
|
||||
|
||||
// Decode a LEB128 byte stream to produce packed words.
|
||||
template <typename int_t>
|
||||
class Sleb128Decoder {
|
||||
public:
|
||||
// Create a new decoder for the given encoded stream.
|
||||
// |encoding| is the vector of encoded data.
|
||||
explicit Sleb128Decoder(const std::vector<uint8_t>& encoding);
|
||||
explicit Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
|
||||
|
||||
// Explicit (but empty) destructor, for chromium-style.
|
||||
~Sleb128Decoder();
|
||||
|
||||
// Retrieve the next value from the encoded stream.
|
||||
ELF::Sxword Dequeue();
|
||||
int_t Dequeue();
|
||||
|
||||
// Retrieve all remaining values from the encoded stream.
|
||||
// |values| is the vector of decoded data.
|
||||
void DequeueAll(std::vector<ELF::Sxword>* values);
|
||||
void DequeueAll(std::vector<int_t>* values);
|
||||
|
||||
private:
|
||||
// Encoded LEB128 stream.
|
||||
|
@ -6,27 +6,27 @@
|
||||
|
||||
#include <vector>
|
||||
#include "elf_traits.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
TEST(Sleb128, Encoder) {
|
||||
std::vector<ELF::Sxword> values;
|
||||
values.push_back(624485);
|
||||
values.push_back(0);
|
||||
values.push_back(1);
|
||||
values.push_back(63);
|
||||
values.push_back(64);
|
||||
values.push_back(-1);
|
||||
values.push_back(-624485);
|
||||
TEST(Sleb128, Encoder64) {
|
||||
std::vector<uint64_t> values;
|
||||
values.push_back(624485U);
|
||||
values.push_back(0U);
|
||||
values.push_back(1U);
|
||||
values.push_back(63U);
|
||||
values.push_back(64U);
|
||||
values.push_back(static_cast<uint64_t>(-1));
|
||||
values.push_back(static_cast<uint64_t>(-624485));
|
||||
|
||||
Sleb128Encoder encoder;
|
||||
Sleb128Encoder<uint64_t> encoder;
|
||||
encoder.EnqueueAll(values);
|
||||
|
||||
encoder.Enqueue(2147483647);
|
||||
encoder.Enqueue(-2147483648);
|
||||
encoder.Enqueue(9223372036854775807ll);
|
||||
encoder.Enqueue(-9223372036854775807ll - 1);
|
||||
encoder.Enqueue(2147483647U);
|
||||
encoder.Enqueue(static_cast<uint64_t>(-2147483648));
|
||||
encoder.Enqueue(9223372036854775807ULL);
|
||||
encoder.Enqueue(static_cast<uint64_t>(-9223372036854775807LL - 1));
|
||||
|
||||
std::vector<uint8_t> encoding;
|
||||
encoder.GetEncoding(&encoding);
|
||||
@ -143,24 +143,24 @@ TEST(Sleb128, Decoder) {
|
||||
encoding.push_back(0x80);
|
||||
encoding.push_back(0x7f);
|
||||
|
||||
Sleb128Decoder decoder(encoding);
|
||||
Sleb128Decoder<uint64_t> decoder(encoding, 0);
|
||||
|
||||
EXPECT_EQ(624485, decoder.Dequeue());
|
||||
EXPECT_EQ(624485U, decoder.Dequeue());
|
||||
|
||||
std::vector<ELF::Sxword> dequeued;
|
||||
std::vector<uint64_t> dequeued;
|
||||
decoder.DequeueAll(&dequeued);
|
||||
|
||||
EXPECT_EQ(10u, dequeued.size());
|
||||
EXPECT_EQ(0, dequeued[0]);
|
||||
EXPECT_EQ(1, dequeued[1]);
|
||||
EXPECT_EQ(63, dequeued[2]);
|
||||
EXPECT_EQ(64, dequeued[3]);
|
||||
EXPECT_EQ(-1, dequeued[4]);
|
||||
EXPECT_EQ(-624485, dequeued[5]);
|
||||
EXPECT_EQ(2147483647, dequeued[6]);
|
||||
EXPECT_EQ(-2147483648, dequeued[7]);
|
||||
EXPECT_EQ(9223372036854775807ll, dequeued[8]);
|
||||
EXPECT_EQ(-9223372036854775807ll - 1, dequeued[9]);
|
||||
EXPECT_EQ(10U, dequeued.size());
|
||||
EXPECT_EQ(0U, dequeued[0]);
|
||||
EXPECT_EQ(1U, dequeued[1]);
|
||||
EXPECT_EQ(63U, dequeued[2]);
|
||||
EXPECT_EQ(64U, dequeued[3]);
|
||||
EXPECT_EQ(static_cast<uint64_t>(-1), dequeued[4]);
|
||||
EXPECT_EQ(static_cast<uint64_t>(-624485), dequeued[5]);
|
||||
EXPECT_EQ(2147483647U, dequeued[6]);
|
||||
EXPECT_EQ(static_cast<uint64_t>(-2147483648), dequeued[7]);
|
||||
EXPECT_EQ(9223372036854775807ULL, dequeued[8]);
|
||||
EXPECT_EQ(static_cast<uint64_t>(-9223372036854775807LL - 1), dequeued[9]);
|
||||
}
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user