--- /dev/null
+#
+# 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)
--- /dev/null
+#
+# 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))
+++ /dev/null
-# 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),
- ]
- }
-}
+++ /dev/null
-# 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 = ""
-}
+++ /dev/null
-# 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)',
- ],
- },
- ],
- },
- ],
-}
#include "debug.h"
#include <sstream>
-#include "testing/gtest/include/gtest/gtest.h"
+#include "gtest/gtest.h"
namespace relocation_packer {
#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()));
+
+ // 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;
+
+ packed->push_back(start_offset);
+
+ // 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 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;
+ }
+
+ size_t ndx = 0;
+ ElfAddr current_count = 0;
+ ElfAddr total_count = packed[ndx++];
+
+ ElfAddr offset = packed[ndx++];
+ ElfAddr info = 0;
+ ElfAddr addend = 0;
+
+ while(current_count < total_count) {
+ // read group
+ ElfAddr group_size = packed[ndx++];
+ ElfAddr group_flags = packed[ndx++];
+ ElfAddr group_offset_delta = 0;
+
+ 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++];
+ }
- ELF::Addr offset = 0;
- ELF::Sxword addend = 0;
+ if (is_relocation_group_has_addend(group_flags) &&
+ is_relocation_grouped_by_addend(group_flags)) {
+ addend += packed[ndx++];
+ }
- for (size_t i = 0; i < relocations.size(); ++i) {
- const ELF::Rela* relocation = &relocations[i];
- CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode);
+ // 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++];
+ }
- packed->push_back(relocation->r_offset - offset);
- offset = relocation->r_offset;
- packed->push_back(relocation->r_addend - addend);
- addend = relocation->r_addend;
+ 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;
+ }
+ }
}
}
-// 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)
+// 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;
+ }
- // 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);
-
- ELF::Addr offset = 0;
- ELF::Sxword 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];
-
- // 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);
+ 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
// 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_
// 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
#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;
}
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
// Implementation notes:
//
// We need to remove a piece from the ELF shared library. However, we also
-// want to ensure that code and data loads at the same addresses as before
-// packing, so that tools like breakpad can still match up addresses found
-// in any crash dumps with data extracted from the pre-packed version of
-// the shared library.
-//
-// Arranging this means that we have to split one of the LOAD segments into
-// two. Unfortunately, the program headers are located at the very start
-// of the shared library file, so expanding the program header section
-// would cause a lot of consequent changes to files offsets that we don't
-// really want to have to handle.
-//
-// Luckily, though, there is a segment that is always present and always
-// unused on Android; the GNU_STACK segment. What we do is to steal that
-// and repurpose it to be one of the split LOAD segments. We then have to
-// sort LOAD segments by offset to keep the crazy linker happy.
-//
-// All of this takes place in SplitProgramHeadersForHole(), used on packing,
-// and is unraveled on unpacking in CoalesceProgramHeadersForHole(). See
-// commentary on those functions for an example of this segment stealing
-// in action.
+// want to avoid fixing DWARF cfi data and relative relocation addresses.
+// So after packing we shift offets and starting address of the RX segment
+// while preserving code/data vaddrs location.
+// This requires some fixups for symtab/hash/gnu_hash dynamic section addresses.
#include "elf_file.h"
namespace relocation_packer {
-// Stub identifier written to 'null out' packed data, "NULL".
-static const uint32_t kStubIdentifier = 0x4c4c554eu;
-
// Out-of-band dynamic tags used to indicate the offset and size of the
// android packed relocations section.
-static const ELF::Sword DT_ANDROID_REL_OFFSET = DT_LOOS;
-static const ELF::Sword DT_ANDROID_REL_SIZE = DT_LOOS + 1;
+static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2;
+static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3;
+
+static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4;
+static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5;
+
+static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1;
+static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2;
// Alignment to preserve, in bytes. This must be at least as large as the
// largest d_align and sh_addralign values found in the loaded file.
// Out of caution for RELRO page alignment, we preserve to a complete target
// page. See http://www.airs.com/blog/archives/189.
-static const size_t kPreserveAlignment = 4096;
-
-namespace {
+static constexpr size_t kPreserveAlignment = 4096;
// Get section data. Checks that the section has exactly one data entry,
// so that the section size and the data size are the same. True in
// practice for all sections we resize when packing or unpacking. Done
// by ensuring that a call to elf_getdata(section, data) returns NULL as
// the next data entry.
-Elf_Data* GetSectionData(Elf_Scn* section) {
+static Elf_Data* GetSectionData(Elf_Scn* section) {
Elf_Data* data = elf_getdata(section, NULL);
CHECK(data && elf_getdata(section, data) == NULL);
return data;
// Rewrite section data. Allocates new data and makes it the data element's
// buffer. Relies on program exit to free allocated data.
-void RewriteSectionData(Elf_Scn* section,
- const void* section_data,
- size_t size) {
+static void RewriteSectionData(Elf_Scn* section,
+ const void* section_data,
+ size_t size) {
Elf_Data* data = GetSectionData(section);
CHECK(size == data->d_size);
uint8_t* area = new uint8_t[size];
}
// Verbose ELF header logging.
-void VerboseLogElfHeader(const ELF::Ehdr* elf_header) {
+template <typename Ehdr>
+static void VerboseLogElfHeader(const Ehdr* elf_header) {
VLOG(1) << "e_phoff = " << elf_header->e_phoff;
VLOG(1) << "e_shoff = " << elf_header->e_shoff;
VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
}
// Verbose ELF program header logging.
-void VerboseLogProgramHeader(size_t program_header_index,
- const ELF::Phdr* program_header) {
+template <typename Phdr>
+static void VerboseLogProgramHeader(size_t program_header_index,
+ const Phdr* program_header) {
std::string type;
switch (program_header->p_type) {
case PT_NULL: type = "NULL"; break;
}
// Verbose ELF section header logging.
-void VerboseLogSectionHeader(const std::string& section_name,
- const ELF::Shdr* section_header) {
+template <typename Shdr>
+static void VerboseLogSectionHeader(const std::string& section_name,
+ const Shdr* section_header) {
VLOG(1) << "section " << section_name;
VLOG(1) << " sh_addr = " << section_header->sh_addr;
VLOG(1) << " sh_offset = " << section_header->sh_offset;
VLOG(1) << " sh_size = " << section_header->sh_size;
+ VLOG(1) << " sh_entsize = " << section_header->sh_entsize;
VLOG(1) << " sh_addralign = " << section_header->sh_addralign;
}
// Verbose ELF section data logging.
-void VerboseLogSectionData(const Elf_Data* data) {
+static void VerboseLogSectionData(const Elf_Data* data) {
VLOG(1) << " data";
VLOG(1) << " d_buf = " << data->d_buf;
VLOG(1) << " d_off = " << data->d_off;
VLOG(1) << " d_align = " << data->d_align;
}
-} // namespace
-
// Load the complete ELF file into a memory image in libelf, and identify
// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or
// .android.rela.dyn sections. No-op if the ELF file has already been loaded.
-bool ElfFile::Load() {
+template <typename ELF>
+bool ElfFile<ELF>::Load() {
if (elf_)
return true;
return false;
}
- ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ auto elf_header = ELF::getehdr(elf);
if (!elf_header) {
LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno());
return false;
}
- if (elf_header->e_machine != ELF::kMachine) {
- LOG(ERROR) << "ELF file architecture is not " << ELF::Machine();
- return false;
- }
+
if (elf_header->e_type != ET_DYN) {
LOG(ERROR) << "ELF file is not a shared object";
return false;
CHECK(endian == ELFDATA2LSB);
CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
- // Also require that the file class is as expected.
const int file_class = elf_header->e_ident[EI_CLASS];
- CHECK(file_class == ELF::kFileClass);
-
VLOG(1) << "endian = " << endian << ", file class = " << file_class;
VerboseLogElfHeader(elf_header);
- const ELF::Phdr* elf_program_header = ELF::getphdr(elf);
- CHECK(elf_program_header);
+ auto elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header != nullptr);
- const ELF::Phdr* dynamic_program_header = NULL;
+ const typename ELF::Phdr* dynamic_program_header = NULL;
for (size_t i = 0; i < elf_header->e_phnum; ++i) {
- const ELF::Phdr* program_header = &elf_program_header[i];
+ auto program_header = &elf_program_header[i];
VerboseLogProgramHeader(i, program_header);
if (program_header->p_type == PT_DYNAMIC) {
dynamic_program_header = program_header;
}
}
- CHECK(dynamic_program_header != NULL);
+ CHECK(dynamic_program_header != nullptr);
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
// Notes of the dynamic relocations, packed relocations, and .dynamic
// sections. Found while iterating sections, and later stored in class
// attributes.
- Elf_Scn* found_relocations_section = NULL;
- Elf_Scn* found_android_relocations_section = NULL;
- Elf_Scn* found_dynamic_section = NULL;
+ Elf_Scn* found_relocations_section = nullptr;
+ Elf_Scn* found_dynamic_section = nullptr;
// Notes of relocation section types seen. We require one or the other of
// these; both is unsupported.
bool has_rela_relocations = false;
Elf_Scn* section = NULL;
- while ((section = elf_nextscn(elf, section)) != NULL) {
- const ELF::Shdr* section_header = ELF::getshdr(section);
+ while ((section = elf_nextscn(elf, section)) != nullptr) {
+ auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
VerboseLogSectionHeader(name, section_header);
// Note relocation section types.
- if (section_header->sh_type == SHT_REL) {
+ if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) {
has_rel_relocations = true;
}
- if (section_header->sh_type == SHT_RELA) {
+ if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) {
has_rela_relocations = true;
}
section_header->sh_size > 0) {
found_relocations_section = section;
}
- if ((name == ".android.rel.dyn" || name == ".android.rela.dyn") &&
- section_header->sh_size > 0) {
- found_android_relocations_section = section;
- }
+
if (section_header->sh_offset == dynamic_program_header->p_offset) {
found_dynamic_section = section;
}
LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section";
return false;
}
- if (!found_android_relocations_section) {
- LOG(ERROR) << "Missing or empty .android.rel.dyn or .android.rela.dyn "
- << "section (to fix, run with --help and follow the "
- << "pre-packing instructions)";
- return false;
- }
if (!found_dynamic_section) {
LOG(ERROR) << "Missing .dynamic section";
return false;
elf_ = elf;
relocations_section_ = found_relocations_section;
dynamic_section_ = found_dynamic_section;
- android_relocations_section_ = found_android_relocations_section;
relocations_type_ = has_rel_relocations ? REL : RELA;
return true;
}
-namespace {
-
// Helper for ResizeSection(). Adjust the main ELF header for the hole.
-void AdjustElfHeaderForHole(ELF::Ehdr* elf_header,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
if (elf_header->e_phoff > hole_start) {
elf_header->e_phoff += hole_size;
VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
}
// Helper for ResizeSection(). Adjust all section headers for the hole.
-void AdjustSectionHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+static void AdjustSectionHeadersForHole(Elf* elf,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != NULL) {
- ELF::Shdr* section_header = ELF::getshdr(section);
+ auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
if (section_header->sh_offset > hole_start) {
section_header->sh_offset += hole_size;
VLOG(1) << "section " << name
<< " sh_offset adjusted to " << section_header->sh_offset;
+ } else {
+ section_header->sh_addr -= hole_size;
+ VLOG(1) << "section " << name
+ << " sh_addr adjusted to " << section_header->sh_addr;
}
}
}
// Helper for ResizeSection(). Adjust the offsets of any program headers
// that have offsets currently beyond the hole start.
-void AdjustProgramHeaderOffsets(ELF::Phdr* program_headers,
- size_t count,
- ELF::Phdr* ignored_1,
- ELF::Phdr* ignored_2,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header == ignored_1 || program_header == ignored_2)
- continue;
+ typename ELF::Phdr* program_header = &program_headers[i];
if (program_header->p_offset > hole_start) {
// The hole start is past this segment, so adjust offset.
program_header->p_offset += hole_size;
VLOG(1) << "phdr[" << i
<< "] p_offset adjusted to "<< program_header->p_offset;
+ } else {
+ program_header->p_vaddr -= hole_size;
+ program_header->p_paddr -= hole_size;
+ VLOG(1) << "phdr[" << i
+ << "] p_vaddr adjusted to "<< program_header->p_vaddr
+ << "; p_paddr adjusted to "<< program_header->p_paddr;
}
}
}
// Helper for ResizeSection(). Find the first loadable segment in the
// file. We expect it to map from file offset zero.
-ELF::Phdr* FindFirstLoadSegment(ELF::Phdr* program_headers,
- size_t count) {
- ELF::Phdr* first_loadable_segment = NULL;
-
- for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header->p_type == PT_LOAD &&
- program_header->p_offset == 0 &&
- program_header->p_vaddr == 0 &&
- program_header->p_paddr == 0) {
- first_loadable_segment = program_header;
- }
- }
- LOG_IF(FATAL, !first_loadable_segment)
- << "Cannot locate a LOAD segment with address and offset zero";
-
- return first_loadable_segment;
-}
-
-// Helper for ResizeSection(). Find the PT_GNU_STACK segment, and check
-// that it contains what we expect so we can restore it on unpack if needed.
-ELF::Phdr* FindUnusedGnuStackSegment(ELF::Phdr* program_headers,
- size_t count) {
- ELF::Phdr* unused_segment = NULL;
-
- for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header->p_type == PT_GNU_STACK &&
- program_header->p_offset == 0 &&
- program_header->p_vaddr == 0 &&
- program_header->p_paddr == 0 &&
- program_header->p_filesz == 0 &&
- program_header->p_memsz == 0 &&
- program_header->p_flags == (PF_R | PF_W) &&
- program_header->p_align == ELF::kGnuStackSegmentAlignment) {
- unused_segment = program_header;
- }
- }
- LOG_IF(FATAL, !unused_segment)
- << "Cannot locate the expected GNU_STACK segment";
-
- return unused_segment;
-}
-
-// Helper for ResizeSection(). Find the segment that was the first loadable
-// one before we split it into two. This is the one into which we coalesce
-// the split segments on unpacking.
-ELF::Phdr* FindOriginalFirstLoadSegment(ELF::Phdr* program_headers,
- size_t count) {
- const ELF::Phdr* first_loadable_segment =
- FindFirstLoadSegment(program_headers, count);
-
- ELF::Phdr* original_first_loadable_segment = NULL;
-
+template <typename ELF>
+static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start) {
for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
+ typename ELF::Phdr* program_header = &program_headers[i];
- // The original first loadable segment is the one that follows on from
- // the one we wrote on split to be the current first loadable segment.
if (program_header->p_type == PT_LOAD &&
- program_header->p_offset == first_loadable_segment->p_filesz) {
- original_first_loadable_segment = program_header;
- }
- }
- LOG_IF(FATAL, !original_first_loadable_segment)
- << "Cannot locate the LOAD segment that follows a LOAD at offset zero";
-
- return original_first_loadable_segment;
-}
-
-// Helper for ResizeSection(). Find the segment that contains the hole.
-Elf_Scn* FindSectionContainingHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- Elf_Scn* section = NULL;
- Elf_Scn* last_unholed_section = NULL;
-
- while ((section = elf_nextscn(elf, section)) != NULL) {
- const ELF::Shdr* section_header = ELF::getshdr(section);
-
- // Because we get here after section headers have been adjusted for the
- // hole, we need to 'undo' that adjustment to give a view of the original
- // sections layout.
- ELF::Off offset = section_header->sh_offset;
- if (section_header->sh_offset >= hole_start) {
- offset -= hole_size;
- }
-
- if (offset <= hole_start) {
- last_unholed_section = section;
- }
- }
- LOG_IF(FATAL, !last_unholed_section)
- << "Cannot identify the section before the one containing the hole";
-
- // The section containing the hole is the one after the last one found
- // by the loop above.
- Elf_Scn* holed_section = elf_nextscn(elf, last_unholed_section);
- LOG_IF(FATAL, !holed_section)
- << "Cannot identify the section containing the hole";
-
- return holed_section;
-}
-
-// Helper for ResizeSection(). Find the last section contained in a segment.
-Elf_Scn* FindLastSectionInSegment(Elf* elf,
- ELF::Phdr* program_header,
- ELF::Off hole_start,
- ssize_t hole_size) {
- const ELF::Off segment_end =
- program_header->p_offset + program_header->p_filesz;
-
- Elf_Scn* section = NULL;
- Elf_Scn* last_section = NULL;
-
- while ((section = elf_nextscn(elf, section)) != NULL) {
- const ELF::Shdr* section_header = ELF::getshdr(section);
-
- // As above, 'undo' any section offset adjustment to give a view of the
- // original sections layout.
- ELF::Off offset = section_header->sh_offset;
- if (section_header->sh_offset >= hole_start) {
- offset -= hole_size;
- }
-
- if (offset < segment_end) {
- last_section = section;
- }
- }
- LOG_IF(FATAL, !last_section)
- << "Cannot identify the last section in the given segment";
-
- return last_section;
-}
-
-// Helper for ResizeSection(). Order loadable segments by their offsets.
-// The crazy linker contains assumptions about loadable segment ordering,
-// and it is better if we do not break them.
-void SortOrderSensitiveProgramHeaders(ELF::Phdr* program_headers,
- size_t count) {
- std::vector<ELF::Phdr*> orderable;
-
- // Collect together orderable program headers. These are all the LOAD
- // segments, and any GNU_STACK that may be present (removed on packing,
- // but replaced on unpacking).
- for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header->p_type == PT_LOAD ||
- program_header->p_type == PT_GNU_STACK) {
- orderable.push_back(program_header);
+ program_header->p_offset <= hole_start &&
+ (program_header->p_offset + program_header->p_filesz) >= hole_start ) {
+ return program_header;
}
}
+ LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start;
+ NOTREACHED();
- // Order these program headers so that any PT_GNU_STACK is last, and
- // the LOAD segments that precede it appear in offset order. Uses
- // insertion sort.
- for (size_t i = 1; i < orderable.size(); ++i) {
- for (size_t j = i; j > 0; --j) {
- ELF::Phdr* first = orderable[j - 1];
- ELF::Phdr* second = orderable[j];
-
- if (!(first->p_type == PT_GNU_STACK ||
- first->p_offset > second->p_offset)) {
- break;
- }
- std::swap(*first, *second);
- }
- }
+ return nullptr;
}
-// Helper for ResizeSection(). The GNU_STACK program header is unused in
-// Android, so we can repurpose it here. Before packing, the program header
-// table contains something like:
-//
-// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-// LOAD 0x000000 0x00000000 0x00000000 0x1efc818 0x1efc818 R E 0x1000
-// LOAD 0x1efd008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000
-// DYNAMIC 0x205ec50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4
-// GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
-//
-// The hole in the file is in the first of these. In order to preserve all
-// load addresses, what we do is to turn the GNU_STACK into a new LOAD entry
-// that maps segments up to where we created the hole, adjust the first LOAD
-// entry so that it maps segments after that, adjust any other program
-// headers whose offset is after the hole start, and finally order the LOAD
-// segments by offset, to give:
-//
-// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-// LOAD 0x000000 0x00000000 0x00000000 0x14ea4 0x14ea4 R E 0x1000
-// LOAD 0x014ea4 0x00212ea4 0x00212ea4 0x1cea164 0x1cea164 R E 0x1000
-// DYNAMIC 0x1e60c50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4
-// LOAD 0x1cff008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000
-//
-// We work out the split points by finding the .rel.dyn or .rela.dyn section
-// that contains the hole, and by finding the last section in a given segment.
-//
-// To unpack, we reverse the above to leave the file as it was originally.
-void SplitProgramHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- CHECK(hole_size < 0);
- const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+// Helper for ResizeSection(). Rewrite program headers.
+template <typename ELF>
+static void RewriteProgramHeadersForHole(Elf* elf,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
CHECK(elf_header);
- ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header);
const size_t program_header_count = elf_header->e_phnum;
// Locate the segment that we can overwrite to form the new LOAD entry,
// and the segment that we are going to split into two parts.
- ELF::Phdr* spliced_header =
- FindUnusedGnuStackSegment(elf_program_header, program_header_count);
- ELF::Phdr* split_header =
- FindFirstLoadSegment(elf_program_header, program_header_count);
-
- VLOG(1) << "phdr[" << split_header - elf_program_header << "] split";
- VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] new LOAD";
-
- // Find the section that contains the hole. We split on the section that
- // follows it.
- Elf_Scn* holed_section =
- FindSectionContainingHole(elf, hole_start, hole_size);
-
- size_t string_index;
- elf_getshdrstrndx(elf, &string_index);
-
- ELF::Shdr* section_header = ELF::getshdr(holed_section);
- std::string name = elf_strptr(elf, string_index, section_header->sh_name);
- VLOG(1) << "section " << name << " split after";
-
- // Find the last section in the segment we are splitting.
- Elf_Scn* last_section =
- FindLastSectionInSegment(elf, split_header, hole_start, hole_size);
-
- section_header = ELF::getshdr(last_section);
- name = elf_strptr(elf, string_index, section_header->sh_name);
- VLOG(1) << "section " << name << " split end";
-
- // Split on the section following the holed one, and up to (but not
- // including) the section following the last one in the split segment.
- Elf_Scn* split_section = elf_nextscn(elf, holed_section);
- LOG_IF(FATAL, !split_section)
- << "No section follows the section that contains the hole";
- Elf_Scn* end_section = elf_nextscn(elf, last_section);
- LOG_IF(FATAL, !end_section)
- << "No section follows the last section in the segment being split";
-
- // Split the first portion of split_header into spliced_header.
- const ELF::Shdr* split_section_header = ELF::getshdr(split_section);
- spliced_header->p_type = split_header->p_type;
- spliced_header->p_offset = split_header->p_offset;
- spliced_header->p_vaddr = split_header->p_vaddr;
- spliced_header->p_paddr = split_header->p_paddr;
- CHECK(split_header->p_filesz == split_header->p_memsz);
- spliced_header->p_filesz = split_section_header->sh_offset;
- spliced_header->p_memsz = split_section_header->sh_offset;
- spliced_header->p_flags = split_header->p_flags;
- spliced_header->p_align = split_header->p_align;
-
- // Now rewrite split_header to remove the part we spliced from it.
- const ELF::Shdr* end_section_header = ELF::getshdr(end_section);
- split_header->p_offset = spliced_header->p_filesz;
- CHECK(split_header->p_vaddr == split_header->p_paddr);
- split_header->p_vaddr = split_section_header->sh_addr;
- split_header->p_paddr = split_section_header->sh_addr;
- CHECK(split_header->p_filesz == split_header->p_memsz);
- split_header->p_filesz =
- end_section_header->sh_offset - spliced_header->p_filesz;
- split_header->p_memsz =
- end_section_header->sh_offset - spliced_header->p_filesz;
-
- // Adjust the offsets of all program headers that are not one of the pair
- // we just created by splitting.
- AdjustProgramHeaderOffsets(elf_program_header,
- program_header_count,
- spliced_header,
- split_header,
- hole_start,
- hole_size);
-
- // Finally, order loadable segments by offset/address. The crazy linker
- // contains assumptions about loadable segment ordering.
- SortOrderSensitiveProgramHeaders(elf_program_header,
- program_header_count);
-}
-
-// Helper for ResizeSection(). Undo the work of SplitProgramHeadersForHole().
-void CoalesceProgramHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- CHECK(hole_size > 0);
- const ELF::Ehdr* elf_header = ELF::getehdr(elf);
- CHECK(elf_header);
-
- ELF::Phdr* elf_program_header = ELF::getphdr(elf);
- CHECK(elf_program_header);
-
- const size_t program_header_count = elf_header->e_phnum;
-
- // Locate the segment that we overwrote to form the new LOAD entry, and
- // the segment that we split into two parts on packing.
- ELF::Phdr* spliced_header =
- FindFirstLoadSegment(elf_program_header, program_header_count);
- ELF::Phdr* split_header =
- FindOriginalFirstLoadSegment(elf_program_header, program_header_count);
-
- VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] stack";
- VLOG(1) << "phdr[" << split_header - elf_program_header << "] coalesce";
-
- // Find the last section in the second segment we are coalescing.
- Elf_Scn* last_section =
- FindLastSectionInSegment(elf, split_header, hole_start, hole_size);
-
- size_t string_index;
- elf_getshdrstrndx(elf, &string_index);
+ typename ELF::Phdr* target_load_header =
+ FindLoadSegmentForHole<ELF>(elf_program_header, program_header_count, hole_start);
- const ELF::Shdr* section_header = ELF::getshdr(last_section);
- std::string name = elf_strptr(elf, string_index, section_header->sh_name);
- VLOG(1) << "section " << name << " coalesced";
-
- // Rewrite the coalesced segment into split_header.
- const ELF::Shdr* last_section_header = ELF::getshdr(last_section);
- split_header->p_offset = spliced_header->p_offset;
- CHECK(split_header->p_vaddr == split_header->p_paddr);
- split_header->p_vaddr = spliced_header->p_vaddr;
- split_header->p_paddr = spliced_header->p_vaddr;
- CHECK(split_header->p_filesz == split_header->p_memsz);
- split_header->p_filesz =
- last_section_header->sh_offset + last_section_header->sh_size;
- split_header->p_memsz =
- last_section_header->sh_offset + last_section_header->sh_size;
-
- // Reconstruct the original GNU_STACK segment into spliced_header.
- spliced_header->p_type = PT_GNU_STACK;
- spliced_header->p_offset = 0;
- spliced_header->p_vaddr = 0;
- spliced_header->p_paddr = 0;
- spliced_header->p_filesz = 0;
- spliced_header->p_memsz = 0;
- spliced_header->p_flags = PF_R | PF_W;
- spliced_header->p_align = ELF::kGnuStackSegmentAlignment;
-
- // Adjust the offsets of all program headers that are not one of the pair
- // we just coalesced.
- AdjustProgramHeaderOffsets(elf_program_header,
- program_header_count,
- spliced_header,
- split_header,
- hole_start,
- hole_size);
-
- // Finally, order loadable segments by offset/address. The crazy linker
- // contains assumptions about loadable segment ordering.
- SortOrderSensitiveProgramHeaders(elf_program_header,
- program_header_count);
-}
+ VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust";
+ // Adjust PT_LOAD program header memsz and filesz
+ target_load_header->p_filesz += hole_size;
+ target_load_header->p_memsz += hole_size;
-// Helper for ResizeSection(). Rewrite program headers.
-void RewriteProgramHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- // If hole_size is negative then we are removing a piece of the file, and
- // we want to split program headers so that we keep the same addresses
- // for text and data. If positive, then we are putting that piece of the
- // file back in, so we coalesce the previously split program headers.
- if (hole_size < 0)
- SplitProgramHeadersForHole(elf, hole_start, hole_size);
- else if (hole_size > 0)
- CoalesceProgramHeadersForHole(elf, hole_start, hole_size);
+ // Adjust the offsets and p_vaddrs
+ AdjustProgramHeaderOffsets<ELF>(elf_program_header,
+ program_header_count,
+ hole_start,
+ hole_size);
}
// Helper for ResizeSection(). Locate and return the dynamic section.
-Elf_Scn* GetDynamicSection(Elf* elf) {
- const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+template <typename ELF>
+static Elf_Scn* GetDynamicSection(Elf* elf) {
+ const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
CHECK(elf_header);
- const ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header);
// Find the program header that describes the dynamic section.
- const ELF::Phdr* dynamic_program_header = NULL;
+ const typename ELF::Phdr* dynamic_program_header = NULL;
for (size_t i = 0; i < elf_header->e_phnum; ++i) {
- const ELF::Phdr* program_header = &elf_program_header[i];
+ const typename ELF::Phdr* program_header = &elf_program_header[i];
if (program_header->p_type == PT_DYNAMIC) {
dynamic_program_header = program_header;
Elf_Scn* dynamic_section = NULL;
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != NULL) {
- ELF::Shdr* section_header = ELF::getshdr(section);
+ typename ELF::Shdr* section_header = ELF::getshdr(section);
if (section_header->sh_offset == dynamic_program_header->p_offset) {
dynamic_section = section;
}
// Helper for ResizeSection(). Adjust the .dynamic section for the hole.
-template <typename Rel>
-void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+void ElfFile<ELF>::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+ typename ELF::Off hole_start,
+ ssize_t hole_size,
+ relocations_type_t relocations_type) {
+ CHECK(relocations_type != NONE);
Elf_Data* data = GetSectionData(dynamic_section);
- const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
- std::vector<ELF::Dyn> dynamics(
+ auto dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
+ if (hole_size > 0) { // expanding
+ hole_start += hole_size;
+ }
+
for (size_t i = 0; i < dynamics.size(); ++i) {
- ELF::Dyn* dynamic = &dynamics[i];
- const ELF::Sword tag = dynamic->d_tag;
+ typename ELF::Dyn* dynamic = &dynamics[i];
+ const typename ELF::Sword tag = dynamic->d_tag;
+
+ // Any tags that hold offsets are adjustment candidates.
+ const bool is_adjustable = (tag == DT_PLTGOT ||
+ tag == DT_HASH ||
+ tag == DT_GNU_HASH ||
+ tag == DT_STRTAB ||
+ tag == DT_SYMTAB ||
+ tag == DT_RELA ||
+ tag == DT_INIT ||
+ tag == DT_FINI ||
+ tag == DT_REL ||
+ tag == DT_JMPREL ||
+ tag == DT_INIT_ARRAY ||
+ tag == DT_FINI_ARRAY ||
+ tag == DT_ANDROID_REL||
+ tag == DT_ANDROID_RELA);
+
+ if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) {
+ dynamic->d_un.d_ptr -= hole_size;
+ VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+ << " d_ptr adjusted to " << dynamic->d_un.d_ptr;
+ }
// DT_RELSZ or DT_RELASZ indicate the overall size of relocations.
// Only one will be present. Adjust by hole size.
- if (tag == DT_RELSZ || tag == DT_RELASZ) {
+ if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) {
dynamic->d_un.d_val += hole_size;
VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
<< " d_val adjusted to " << dynamic->d_un.d_val;
}
- // DT_RELCOUNT or DT_RELACOUNT hold the count of relative relocations.
- // Only one will be present. Packing reduces it to the alignment
- // padding, if any; unpacking restores it to its former value. The
- // crazy linker does not use it, but we update it anyway.
- if (tag == DT_RELCOUNT || tag == DT_RELACOUNT) {
- // Cast sizeof to a signed type to avoid the division result being
- // promoted into an unsigned size_t.
- const ssize_t sizeof_rel = static_cast<ssize_t>(sizeof(Rel));
- dynamic->d_un.d_val += hole_size / sizeof_rel;
- VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
- << " d_val adjusted to " << dynamic->d_un.d_val;
- }
+ // Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and
+ // technically (2) the relative relocation count is not changed.
- // DT_RELENT and DT_RELAENT do not change, but make sure they are what
- // we expect. Only one will be present.
- if (tag == DT_RELENT || tag == DT_RELAENT) {
- CHECK(dynamic->d_un.d_val == sizeof(Rel));
- }
+ // DT_RELENT and DT_RELAENT don't change, ignore them as well.
}
void* section_data = &dynamics[0];
// Resize a section. If the new size is larger than the current size, open
// up a hole by increasing file offsets that come after the hole. If smaller
// than the current size, remove the hole by decreasing those offsets.
-template <typename Rel>
-void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) {
- ELF::Shdr* section_header = ELF::getshdr(section);
- if (section_header->sh_size == new_size)
- return;
+template <typename ELF>
+void ElfFile<ELF>::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
+ typename ELF::Word new_sh_type,
+ relocations_type_t relocations_type) {
- // Note if we are resizing the real dyn relocations.
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
- const std::string section_name =
- elf_strptr(elf, string_index, section_header->sh_name);
- const bool is_relocations_resize =
- (section_name == ".rel.dyn" || section_name == ".rela.dyn");
+ auto section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+ if (section_header->sh_size == new_size) {
+ return;
+ }
// Require that the section size and the data size are the same. True
// in practice for all sections we resize when packing or unpacking.
// data that we can validly expand).
CHECK(data->d_size && data->d_buf);
- const ELF::Off hole_start = section_header->sh_offset;
+ const auto hole_start = section_header->sh_offset;
const ssize_t hole_size = new_size - data->d_size;
- VLOG_IF(1, (hole_size > 0)) << "expand section size = " << data->d_size;
- VLOG_IF(1, (hole_size < 0)) << "shrink section size = " << data->d_size;
+ VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " <<
+ data->d_size << " -> " << (data->d_size + hole_size);
+ VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " <<
+ data->d_size << " -> " << (data->d_size + hole_size);
+
+ // libelf overrides sh_entsize for known sh_types, so it does not matter what we set
+ // for SHT_REL/SHT_RELA.
+ typename ELF::Xword new_entsize =
+ (new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0;
+
+ VLOG(1) << "Update section (" << name << ") entry size: " <<
+ section_header->sh_entsize << " -> " << new_entsize;
// Resize the data and the section header.
data->d_size += hole_size;
section_header->sh_size += hole_size;
+ section_header->sh_entsize = new_entsize;
+ section_header->sh_type = new_sh_type;
// Add the hole size to all offsets in the ELF file that are after the
// start of the hole. If the hole size is positive we are expanding the
// section to create a new hole; if negative, we are closing up a hole.
// Start with the main ELF header.
- ELF::Ehdr* elf_header = ELF::getehdr(elf);
- AdjustElfHeaderForHole(elf_header, hole_start, hole_size);
+ typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ AdjustElfHeaderForHole<ELF>(elf_header, hole_start, hole_size);
// Adjust all section headers.
- AdjustSectionHeadersForHole(elf, hole_start, hole_size);
+ AdjustSectionHeadersForHole<ELF>(elf, hole_start, hole_size);
- // If resizing the dynamic relocations, rewrite the program headers to
- // either split or coalesce segments, and adjust dynamic entries to match.
- if (is_relocations_resize) {
- RewriteProgramHeadersForHole(elf, hole_start, hole_size);
+ // Rewrite the program headers to either split or coalesce segments,
+ // and adjust dynamic entries to match.
+ RewriteProgramHeadersForHole<ELF>(elf, hole_start, hole_size);
- Elf_Scn* dynamic_section = GetDynamicSection(elf);
- AdjustDynamicSectionForHole<Rel>(dynamic_section, hole_start, hole_size);
- }
+ Elf_Scn* dynamic_section = GetDynamicSection<ELF>(elf);
+ AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type);
}
// Find the first slot in a dynamics array with the given tag. The array
// always ends with a free (unused) element, and which we exclude from the
// search. Returns dynamics->size() if not found.
-size_t FindDynamicEntry(ELF::Sword tag,
- std::vector<ELF::Dyn>* dynamics) {
+template <typename ELF>
+static size_t FindDynamicEntry(typename ELF::Sword tag,
+ std::vector<typename ELF::Dyn>* dynamics) {
// Loop until the penultimate entry. We exclude the end sentinel.
for (size_t i = 0; i < dynamics->size() - 1; ++i) {
- if (dynamics->at(i).d_tag == tag)
+ if (dynamics->at(i).d_tag == tag) {
return i;
+ }
}
// The tag was not found.
return dynamics->size();
}
-// Replace the first free (unused) slot in a dynamics vector with the given
-// value. The vector always ends with a free (unused) element, so the slot
-// found cannot be the last one in the vector.
-void AddDynamicEntry(const ELF::Dyn& dyn,
- std::vector<ELF::Dyn>* dynamics) {
- const size_t slot = FindDynamicEntry(DT_NULL, dynamics);
+// Replace dynamic entry.
+template <typename ELF>
+static void ReplaceDynamicEntry(typename ELF::Sword tag,
+ const typename ELF::Dyn& dyn,
+ std::vector<typename ELF::Dyn>* dynamics) {
+ const size_t slot = FindDynamicEntry<ELF>(tag, dynamics);
if (slot == dynamics->size()) {
- LOG(FATAL) << "No spare dynamic array slots found "
- << "(to fix, increase gold's --spare-dynamic-tags value)";
+ LOG(FATAL) << "Dynamic slot is not found for tag=" << tag;
}
// Replace this entry with the one supplied.
VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag;
}
-// Remove the element in the dynamics vector that matches the given tag with
-// unused slot data. Shuffle the following elements up, and ensure that the
-// last is the null sentinel.
-void RemoveDynamicEntry(ELF::Sword tag,
- std::vector<ELF::Dyn>* dynamics) {
- const size_t slot = FindDynamicEntry(tag, dynamics);
- CHECK(slot != dynamics->size());
-
- // Remove this entry by shuffling up everything that follows.
- for (size_t i = slot; i < dynamics->size() - 1; ++i) {
- dynamics->at(i) = dynamics->at(i + 1);
- VLOG(1) << "dynamic[" << i
- << "] overwritten with dynamic[" << i + 1 << "]";
- }
-
- // Ensure that the end sentinel is still present.
- CHECK(dynamics->at(dynamics->size() - 1).d_tag == DT_NULL);
-}
-
-// Construct a null relocation without addend.
-void NullRelocation(ELF::Rel* relocation) {
- relocation->r_offset = 0;
- relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
-}
-
-// Construct a null relocation with addend.
-void NullRelocation(ELF::Rela* relocation) {
- relocation->r_offset = 0;
- relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
- relocation->r_addend = 0;
-}
-
-// Pad relocations with the given number of null entries. Generates its
-// null entry with the appropriate NullRelocation() invocation.
-template <typename Rel>
-void PadRelocations(size_t count, std::vector<Rel>* relocations) {
- Rel null_relocation;
- NullRelocation(&null_relocation);
- std::vector<Rel> padding(count, null_relocation);
- relocations->insert(relocations->end(), padding.begin(), padding.end());
-}
-
-} // namespace
-
// Remove relative entries from dynamic relocations and write as packed
// data into android packed relocations.
-bool ElfFile::PackRelocations() {
+template <typename ELF>
+bool ElfFile<ELF>::PackRelocations() {
// Load the ELF file into libelf.
if (!Load()) {
LOG(ERROR) << "Failed to load as ELF";
// Retrieve the current dynamic relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
+ // we always pack rela, because packed format is pretty much the same
+ std::vector<typename ELF::Rela> relocations;
if (relocations_type_ == REL) {
// Convert data to a vector of relocations.
- const ELF::Rel* relocations_base = reinterpret_cast<ELF::Rel*>(data->d_buf);
- std::vector<ELF::Rel> relocations(
- relocations_base,
- relocations_base + data->d_size / sizeof(relocations[0]));
-
+ const typename ELF::Rel* relocations_base = reinterpret_cast<typename ELF::Rel*>(data->d_buf);
+ ConvertRelArrayToRelaVector(relocations_base,
+ data->d_size / sizeof(typename ELF::Rel), &relocations);
LOG(INFO) << "Relocations : REL";
- return PackTypedRelocations<ELF::Rel>(relocations);
- }
-
- if (relocations_type_ == RELA) {
+ } else if (relocations_type_ == RELA) {
// Convert data to a vector of relocations with addends.
- const ELF::Rela* relocations_base =
- reinterpret_cast<ELF::Rela*>(data->d_buf);
- std::vector<ELF::Rela> relocations(
+ const typename ELF::Rela* relocations_base = reinterpret_cast<typename ELF::Rela*>(data->d_buf);
+ relocations = std::vector<typename ELF::Rela>(
relocations_base,
relocations_base + data->d_size / sizeof(relocations[0]));
LOG(INFO) << "Relocations : RELA";
- return PackTypedRelocations<ELF::Rela>(relocations);
+ } else {
+ NOTREACHED();
}
- NOTREACHED();
- return false;
+ return PackTypedRelocations(&relocations);
}
// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
-template <typename Rel>
-bool ElfFile::PackTypedRelocations(const std::vector<Rel>& relocations) {
- // Filter relocations into those that are relative and others.
- std::vector<Rel> relative_relocations;
- std::vector<Rel> other_relocations;
-
- for (size_t i = 0; i < relocations.size(); ++i) {
- const Rel& relocation = relocations[i];
- if (ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode) {
- CHECK(ELF_R_SYM(relocation.r_info) == 0);
- relative_relocations.push_back(relocation);
- } else {
- other_relocations.push_back(relocation);
- }
- }
- LOG(INFO) << "Relative : " << relative_relocations.size() << " entries";
- LOG(INFO) << "Other : " << other_relocations.size() << " entries";
- LOG(INFO) << "Total : " << relocations.size() << " entries";
+template <typename ELF>
+bool ElfFile<ELF>::PackTypedRelocations(std::vector<typename ELF::Rela>* relocations) {
+ typedef typename ELF::Rela Rela;
- // If no relative relocations then we have nothing packable. Perhaps
+ // If no relocations then we have nothing packable. Perhaps
// the shared object has already been packed?
- if (relative_relocations.empty()) {
- LOG(ERROR) << "No relative relocations found (already packed?)";
+ if (relocations->empty()) {
+ LOG(ERROR) << "No relocations found (already packed?)";
return false;
}
- // If not padding fully, apply only enough padding to preserve alignment.
- // Otherwise, pad so that we do not shrink the relocations section at all.
- if (!is_padding_relocations_) {
- // Calculate the size of the hole we will close up when we rewrite
- // dynamic relocations.
- ssize_t hole_size =
- relative_relocations.size() * sizeof(relative_relocations[0]);
- const ssize_t unaligned_hole_size = hole_size;
-
- // Adjust the actual hole size to preserve alignment. We always adjust
- // by a whole number of NONE-type relocations.
- while (hole_size % kPreserveAlignment)
- hole_size -= sizeof(relative_relocations[0]);
- LOG(INFO) << "Compaction : " << hole_size << " bytes";
-
- // Adjusting for alignment may have removed any packing benefit.
- if (hole_size == 0) {
- LOG(INFO) << "Too few relative relocations to pack after alignment";
- return false;
- }
+ const size_t rel_size =
+ relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel);
+ const size_t initial_bytes = relocations->size() * rel_size;
- // Find the padding needed in other_relocations to preserve alignment.
- // Ensure that we never completely empty the real relocations section.
- size_t padding_bytes = unaligned_hole_size - hole_size;
- if (padding_bytes == 0 && other_relocations.size() == 0) {
- do {
- padding_bytes += sizeof(relative_relocations[0]);
- } while (padding_bytes % kPreserveAlignment);
- }
- CHECK(padding_bytes % sizeof(other_relocations[0]) == 0);
- const size_t padding = padding_bytes / sizeof(other_relocations[0]);
+ LOG(INFO) << "Unpacked : " << initial_bytes << " bytes";
+ std::vector<uint8_t> packed;
+ RelocationPacker<ELF> packer;
- // Padding may have removed any packing benefit.
- if (padding >= relative_relocations.size()) {
- LOG(INFO) << "Too few relative relocations to pack after padding";
- return false;
- }
+ // Pack relocations: dry run to estimate memory savings.
+ packer.PackRelocations(*relocations, &packed);
+ const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]);
+ LOG(INFO) << "Packed (no padding): " << packed_bytes_estimate << " bytes";
- // Add null relocations to other_relocations to preserve alignment.
- PadRelocations<Rel>(padding, &other_relocations);
- LOG(INFO) << "Alignment pad : " << padding << " relocations";
- } else {
- // If padding, add NONE-type relocations to other_relocations to make it
- // the same size as the the original relocations we read in. This makes
- // the ResizeSection() below a no-op.
- const size_t padding = relocations.size() - other_relocations.size();
- PadRelocations<Rel>(padding, &other_relocations);
+ if (packed.empty()) {
+ LOG(INFO) << "Too few relocations to pack";
+ return false;
}
- // Pack relative relocations.
- const size_t initial_bytes =
- relative_relocations.size() * sizeof(relative_relocations[0]);
- LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes";
- std::vector<uint8_t> packed;
- RelocationPacker packer;
- packer.PackRelativeRelocations(relative_relocations, &packed);
- const void* packed_data = &packed[0];
- const size_t packed_bytes = packed.size() * sizeof(packed[0]);
- LOG(INFO) << "Packed relative: " << packed_bytes << " bytes";
+ // Pre-calculate the size of the hole we will close up when we rewrite
+ // dynamic relocations. We have to adjust relocation addresses to
+ // account for this.
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+ ssize_t hole_size = initial_bytes - packed_bytes_estimate;
- // If we have insufficient relative relocations to form a run then
- // packing fails.
- if (packed.empty()) {
- LOG(INFO) << "Too few relative relocations to pack";
+ // hole_size needs to be page_aligned.
+ hole_size -= hole_size % kPreserveAlignment;
+
+ LOG(INFO) << "Compaction : " << hole_size << " bytes";
+
+ // Adjusting for alignment may have removed any packing benefit.
+ if (hole_size == 0) {
+ LOG(INFO) << "Too few relocations to pack after alignment";
return false;
}
+ if (hole_size <= 0) {
+ LOG(INFO) << "Packing relocations saves no space";
+ return false;
+ }
+
+ size_t data_padding_bytes = is_padding_relocations_ ?
+ initial_bytes - packed_bytes_estimate :
+ initial_bytes - hole_size - packed_bytes_estimate;
+
+ // pad data
+ std::vector<uint8_t> padding(data_padding_bytes, 0);
+ packed.insert(packed.end(), padding.begin(), padding.end());
+
+ const void* packed_data = &packed[0];
+
// Run a loopback self-test as a check that packing is lossless.
- std::vector<Rel> unpacked;
- packer.UnpackRelativeRelocations(packed, &unpacked);
- CHECK(unpacked.size() == relative_relocations.size());
+ std::vector<Rela> unpacked;
+ packer.UnpackRelocations(packed, &unpacked);
+ CHECK(unpacked.size() == relocations->size());
CHECK(!memcmp(&unpacked[0],
- &relative_relocations[0],
+ &relocations->at(0),
unpacked.size() * sizeof(unpacked[0])));
- // Make sure packing saved some space.
- if (packed_bytes >= initial_bytes) {
- LOG(INFO) << "Packing relative relocations saves no space";
- return false;
- }
-
- // Rewrite the current dynamic relocations section to be only the ARM
- // non-relative relocations, then shrink it to size.
- const void* section_data = &other_relocations[0];
- const size_t bytes = other_relocations.size() * sizeof(other_relocations[0]);
- ResizeSection<Rel>(elf_, relocations_section_, bytes);
- RewriteSectionData(relocations_section_, section_data, bytes);
+ // Rewrite the current dynamic relocations section with packed one then shrink it to size.
+ const size_t bytes = packed.size() * sizeof(packed[0]);
+ ResizeSection(elf_, relocations_section_, bytes,
+ relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_);
+ RewriteSectionData(relocations_section_, packed_data, bytes);
- // Rewrite the current packed android relocations section to hold the packed
- // relative relocations.
- ResizeSection<Rel>(elf_, android_relocations_section_, packed_bytes);
- RewriteSectionData(android_relocations_section_, packed_data, packed_bytes);
+ // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt
- // Rewrite .dynamic to include two new tags describing the packed android
+ // Rewrite .dynamic and rename relocation tags describing the packed android
// relocations.
Elf_Data* data = GetSectionData(dynamic_section_);
- const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
- std::vector<ELF::Dyn> dynamics(
+ const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
- // Use two of the spare slots to describe the packed section.
- ELF::Shdr* section_header = ELF::getshdr(android_relocations_section_);
+ section_header = ELF::getshdr(relocations_section_);
{
- ELF::Dyn dyn;
- dyn.d_tag = DT_ANDROID_REL_OFFSET;
- dyn.d_un.d_ptr = section_header->sh_offset;
- AddDynamicEntry(dyn, &dynamics);
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA;
+ dyn.d_un.d_ptr = section_header->sh_addr;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics);
}
{
- ELF::Dyn dyn;
- dyn.d_tag = DT_ANDROID_REL_SIZE;
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ;
dyn.d_un.d_val = section_header->sh_size;
- AddDynamicEntry(dyn, &dynamics);
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics);
}
+
const void* dynamics_data = &dynamics[0];
const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
// Find packed relative relocations in the packed android relocations
// section, unpack them, and rewrite the dynamic relocations section to
// contain unpacked data.
-bool ElfFile::UnpackRelocations() {
+template <typename ELF>
+bool ElfFile<ELF>::UnpackRelocations() {
// Load the ELF file into libelf.
if (!Load()) {
LOG(ERROR) << "Failed to load as ELF";
return false;
}
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
// Retrieve the current packed android relocations section data.
- Elf_Data* data = GetSectionData(android_relocations_section_);
+ Elf_Data* data = GetSectionData(relocations_section_);
// Convert data to a vector of bytes.
const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf);
packed_base,
packed_base + data->d_size / sizeof(packed[0]));
- if (packed.size() > 3 &&
+ if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) &&
+ packed.size() > 3 &&
packed[0] == 'A' &&
packed[1] == 'P' &&
- packed[2] == 'R' &&
- packed[3] == '1') {
- // Signature is APR1, unpack relocations.
- CHECK(relocations_type_ == REL);
- LOG(INFO) << "Relocations : REL";
- return UnpackTypedRelocations<ELF::Rel>(packed);
- }
-
- if (packed.size() > 3 &&
- packed[0] == 'A' &&
- packed[1] == 'P' &&
- packed[2] == 'A' &&
- packed[3] == '1') {
- // Signature is APA1, unpack relocations with addends.
- CHECK(relocations_type_ == RELA);
- LOG(INFO) << "Relocations : RELA";
- return UnpackTypedRelocations<ELF::Rela>(packed);
+ (packed[2] == 'U' || packed[2] == 'S') &&
+ packed[3] == '2') {
+ LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA");
+ } else {
+ LOG(ERROR) << "Packed relocations not found (not packed?)";
+ return false;
}
- LOG(ERROR) << "Packed relative relocations not found (not packed?)";
- return false;
+ return UnpackTypedRelocations(packed);
}
// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
-template <typename Rel>
-bool ElfFile::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
+template <typename ELF>
+bool ElfFile<ELF>::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
// Unpack the data to re-materialize the relative relocations.
const size_t packed_bytes = packed.size() * sizeof(packed[0]);
- LOG(INFO) << "Packed relative: " << packed_bytes << " bytes";
- std::vector<Rel> relative_relocations;
- RelocationPacker packer;
- packer.UnpackRelativeRelocations(packed, &relative_relocations);
- const size_t unpacked_bytes =
- relative_relocations.size() * sizeof(relative_relocations[0]);
- LOG(INFO) << "Unpacked relative: " << unpacked_bytes << " bytes";
+ LOG(INFO) << "Packed : " << packed_bytes << " bytes";
+ std::vector<typename ELF::Rela> unpacked_relocations;
+ RelocationPacker<ELF> packer;
+ packer.UnpackRelocations(packed, &unpacked_relocations);
+
+ const size_t relocation_entry_size =
+ relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela);
+ const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size;
+ LOG(INFO) << "Unpacked : " << unpacked_bytes << " bytes";
// Retrieve the current dynamic relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
- // Interpret data as relocations.
- const Rel* relocations_base = reinterpret_cast<Rel*>(data->d_buf);
- std::vector<Rel> relocations(
- relocations_base,
- relocations_base + data->d_size / sizeof(relocations[0]));
-
- std::vector<Rel> other_relocations;
- size_t padding = 0;
-
- // Filter relocations to locate any that are NONE-type. These will occur
- // if padding was turned on for packing.
- for (size_t i = 0; i < relocations.size(); ++i) {
- const Rel& relocation = relocations[i];
- if (ELF_R_TYPE(relocation.r_info) != ELF::kNoRelocationCode) {
- other_relocations.push_back(relocation);
- } else {
- ++padding;
- }
- }
- LOG(INFO) << "Relative : " << relative_relocations.size() << " entries";
- LOG(INFO) << "Other : " << other_relocations.size() << " entries";
+ LOG(INFO) << "Relocations : " << unpacked_relocations.size() << " entries";
// If we found the same number of null relocation entries in the dynamic
// relocations section as we hold as unpacked relative relocations, then
// this is a padded file.
- const bool is_padded = padding == relative_relocations.size();
- // Unless padded, report by how much we expand the file.
+ const bool is_padded = packed_bytes == unpacked_bytes;
+
+ // Unless padded, pre-apply relative relocations to account for the
+ // hole, and pre-adjust all relocation offsets accordingly.
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+
if (!is_padded) {
- // Calculate the size of the hole we will open up when we rewrite
- // dynamic relocations.
- ssize_t hole_size =
- relative_relocations.size() * sizeof(relative_relocations[0]);
-
- // Adjust the hole size for the padding added to preserve alignment.
- hole_size -= padding * sizeof(other_relocations[0]);
- LOG(INFO) << "Expansion : " << hole_size << " bytes";
+ LOG(INFO) << "Expansion : " << unpacked_bytes - packed_bytes << " bytes";
}
- // Rewrite the current dynamic relocations section to be the relative
- // relocations followed by other relocations. This is the usual order in
- // which we find them after linking, so this action will normally put the
- // entire dynamic relocations section back to its pre-split-and-packed state.
- relocations.assign(relative_relocations.begin(), relative_relocations.end());
- relocations.insert(relocations.end(),
- other_relocations.begin(), other_relocations.end());
- const void* section_data = &relocations[0];
- const size_t bytes = relocations.size() * sizeof(relocations[0]);
- LOG(INFO) << "Total : " << relocations.size() << " entries";
- ResizeSection<Rel>(elf_, relocations_section_, bytes);
- RewriteSectionData(relocations_section_, section_data, bytes);
-
- // Nearly empty the current packed android relocations section. Leaves a
- // four-byte stub so that some data remains allocated to the section.
- // This is a convenience which allows us to re-pack this file again without
- // having to remove the section and then add a new small one with objcopy.
- // The way we resize sections relies on there being some data in a section.
- ResizeSection<Rel>(
- elf_, android_relocations_section_, sizeof(kStubIdentifier));
- RewriteSectionData(
- android_relocations_section_, &kStubIdentifier, sizeof(kStubIdentifier));
+ // Rewrite the current dynamic relocations section with unpacked version of
+ // relocations.
+ const void* section_data = nullptr;
+ std::vector<typename ELF::Rel> unpacked_rel_relocations;
+ if (relocations_type_ == RELA) {
+ section_data = &unpacked_relocations[0];
+ } else if (relocations_type_ == REL) {
+ ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations);
+ section_data = &unpacked_rel_relocations[0];
+ } else {
+ NOTREACHED();
+ }
+
+ ResizeSection(elf_, relocations_section_, unpacked_bytes,
+ relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_);
+ RewriteSectionData(relocations_section_, section_data, unpacked_bytes);
// Rewrite .dynamic to remove two tags describing packed android relocations.
data = GetSectionData(dynamic_section_);
- const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
- std::vector<ELF::Dyn> dynamics(
+ const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
- RemoveDynamicEntry(DT_ANDROID_REL_OFFSET, &dynamics);
- RemoveDynamicEntry(DT_ANDROID_REL_SIZE, &dynamics);
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA;
+ dyn.d_un.d_ptr = section_header->sh_addr;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA,
+ dyn, &dynamics);
+ }
+
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ;
+ dyn.d_un.d_val = section_header->sh_size;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ,
+ dyn, &dynamics);
+ }
+
const void* dynamics_data = &dynamics[0];
const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
}
// Flush rewritten shared object file data.
-void ElfFile::Flush() {
+template <typename ELF>
+void ElfFile<ELF>::Flush() {
// Flag all ELF data held in memory as needing to be written back to the
// file, and tell libelf that we have controlled the file layout.
elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY);
// Write ELF data back to disk.
const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
+ if (file_bytes == -1) {
+ LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno());
+ }
+
CHECK(file_bytes > 0);
VLOG(1) << "elf_update returned: " << file_bytes;
CHECK(truncate == 0);
}
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array,
+ size_t rel_array_size,
+ std::vector<typename ELF::Rela>* rela_vector) {
+ for (size_t i = 0; i<rel_array_size; ++i) {
+ typename ELF::Rela rela;
+ rela.r_offset = rel_array[i].r_offset;
+ rela.r_info = rel_array[i].r_info;
+ rela.r_addend = 0;
+ rela_vector->push_back(rela);
+ }
+}
+
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
+ std::vector<typename ELF::Rel>* rel_vector) {
+ for (auto rela : rela_vector) {
+ typename ELF::Rel rel;
+ rel.r_offset = rela.r_offset;
+ rel.r_info = rela.r_info;
+ CHECK(rela.r_addend == 0);
+ rel_vector->push_back(rel);
+ }
+}
+
+template class ElfFile<ELF32_traits>;
+template class ElfFile<ELF64_traits>;
+
} // 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
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.
// 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_;
// 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
#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 {
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));
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;
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);
*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";
EXPECT_TRUE(feof(first) && feof(second));
}
-} // namespace
+template <typename ELF>
+static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
+ relocation_packer::ElfFile<ELF> elf_file(fileno(packed_relocs_so));
-namespace relocation_packer {
+ // Ensure packing fails (already packed).
+ EXPECT_FALSE(elf_file.PackRelocations());
-TEST(ElfFile, PackRelocations) {
- ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
+ // Unpack golden relocations, and check files are now identical.
+ EXPECT_TRUE(elf_file.UnpackRelocations());
+ CheckFileContentsEqual(packed_relocs_so, relocs_so);
+
+ CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+static void RunUnpackRelocationsTestFor(const std::string& arch) {
+ ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
- OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
- if (HasFatalFailure())
- return;
+ 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);
+ }
+ }
+}
- ElfFile elf_file(fileno(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());
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
}
-TEST(ElfFile, UnpackRelocations) {
- ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
+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(&relocs_so, &packed_relocs_so);
- if (HasFatalFailure())
- return;
+ 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);
+ }
+ }
+}
- ElfFile elf_file(fileno(packed_relocs_so));
+} // namespace
- // Ensure packing fails (already packed).
- EXPECT_FALSE(elf_file.PackRelocations());
+namespace relocation_packer {
- // Unpack golden relocations, and check files are now identical.
- EXPECT_TRUE(elf_file.UnpackRelocations());
- CheckFileContentsEqual(packed_relocs_so, relocs_so);
+TEST(ElfFile, PackRelocations) {
+ RunPackRelocationsTestFor("arm32");
+ RunPackRelocationsTestFor("arm64");
+}
- CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+TEST(ElfFile, UnpackRelocations) {
+ RunUnpackRelocationsTestFor("arm32");
+ RunUnpackRelocationsTestFor("arm64");
}
} // namespace relocation_packer
#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;
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;
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_
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;
// 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);
}
// 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
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.
// 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.
};
// 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.
#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);
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]);
EXPECT_EQ(0x01, encoding[22]);
}
-TEST(Leb128, Decoder) {
+TEST(Leb128, Decoder64) {
std::vector<uint8_t> encoding;
// 624485
encoding.push_back(0xe5);
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
#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) {
" -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;
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;
}
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;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
+ LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
+ return 1;
+ }
- bool status;
- if (is_unpacking)
- status = elf_file.UnpackRelocations();
- else
- status = elf_file.PackRelocations();
+ bool status = false;
- close(fd);
+ 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";
#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);
-
- encoder.GetEncoding(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);
+ Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
+ Leb128Encoder<typename ELF::Addr> leb128_encoder;
+
+ std::vector<uint8_t> leb128_packed;
+ std::vector<uint8_t> sleb128_packed;
+
+ 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);
-
- // Run-length decode.
- RelocationRunLengthCodec codec;
+ std::vector<typename ELF::Rela>* relocations) {
+
+ 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
#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
#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
-TEST(Packer, UnpackRel) {
- std::vector<uint8_t> packed;
- std::vector<ELF::Rel> relocations;
+ EXPECT_EQ(ndx, packed.size());
+}
- RelocationPacker packer;
+TEST(Packer, PackNoAddend) {
+ DoPackNoAddend<ELF32_traits>();
+ DoPackNoAddend<ELF64_traits>();
+}
- // Identifier.
+template <typename ELF>
+static void DoUnpackNoAddend() {
+ std::vector<typename ELF::Rela> relocations;
+ std::vector<uint8_t> packed;
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);
+ packed.push_back('U');
+ packed.push_back('2');
+ // relocation count
+ packed.push_back(6);
+ // 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
- relocations.clear();
- packer.UnpackRelativeRelocations(packed, &relocations);
+ RelocationPacker<ELF> packer;
+ packer.UnpackRelocations(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]));
+ 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, PackRela) {
- std::vector<ELF::Rela> relocations;
- std::vector<uint8_t> packed;
+TEST(Packer, UnpackNoAddend) {
+ DoUnpackNoAddend<ELF32_traits>();
+ DoUnpackNoAddend<ELF64_traits>();
+}
- RelocationPacker packer;
+template <typename ELF>
+static void DoPackWithAddend() {
+ std::vector<typename ELF::Rela> relocations;
// Initial relocation.
- AddRelocation(0xd1ce0000, 10000, &relocations);
+ AddRelocation<ELF>(0xd1ce0000, 0x01, 10024, &relocations);
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
- AddRelocation(0xd1ce0004, 10012, &relocations);
- AddRelocation(0xd1ce0008, 10024, &relocations);
+ AddRelocation<ELF>(0xd1ce0004, 0x01, 10012, &relocations);
+ AddRelocation<ELF>(0xd1ce0008, 0x01, 10024, &relocations);
// Three more relocations, 8 byte deltas, -24 byte addend deltas.
- AddRelocation(0xd1ce0010, 10000, &relocations);
- AddRelocation(0xd1ce0018, 9976, &relocations);
- AddRelocation(0xd1ce0020, 9952, &relocations);
+ 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.PackRelativeRelocations(relocations, &packed);
+ packer.PackRelocations(relocations, &packed);
- EXPECT_EQ(24, packed.size());
+ EXPECT_EQ(26U, packed.size());
+ size_t ndx = 0;
// 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]);
-}
+ 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++]);
-TEST(Packer, UnpackRela) {
- std::vector<uint8_t> packed;
- std::vector<ELF::Rela> relocations;
+ // 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());
+}
- RelocationPacker packer;
+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('A');
- packed.push_back('1');
- // Delta pairs 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);
+ 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
+++ /dev/null
-// 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();
-}
+++ /dev/null
-// 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
+++ /dev/null
-// 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
#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,
// 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;
// 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;
}
// 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;
// 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
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.
// 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.
};
// 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.
#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);
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