From 87a0617ebe7561bf28d3a19fbe192372598969b8 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 6 Feb 2015 10:56:28 -0800 Subject: [PATCH 1/4] Import relocation packer from chromium repo Bug: 18051137 Change-Id: Ia67fa11da8247e3f86f70a8ce99e6695f2c05423 --- tools/relocation_packer/BUILD.gn | 148 ++ tools/relocation_packer/LICENSE | 27 + tools/relocation_packer/README.TXT | 135 ++ tools/relocation_packer/config.gni | 21 + tools/relocation_packer/relocation_packer.gyp | 161 +++ tools/relocation_packer/src/debug.cc | 55 + tools/relocation_packer/src/debug.h | 115 ++ tools/relocation_packer/src/debug_unittest.cc | 122 ++ tools/relocation_packer/src/delta_encoder.cc | 72 + tools/relocation_packer/src/delta_encoder.h | 80 + .../src/delta_encoder_unittest.cc | 150 ++ tools/relocation_packer/src/elf_file.cc | 1283 +++++++++++++++++ tools/relocation_packer/src/elf_file.h | 134 ++ .../src/elf_file_unittest.cc | 164 +++ tools/relocation_packer/src/elf_traits.h | 105 ++ tools/relocation_packer/src/leb128.cc | 69 + tools/relocation_packer/src/leb128.h | 74 + .../relocation_packer/src/leb128_unittest.cc | 111 ++ tools/relocation_packer/src/main.cc | 175 +++ tools/relocation_packer/src/packer.cc | 124 ++ tools/relocation_packer/src/packer.h | 78 + .../relocation_packer/src/packer_unittest.cc | 250 ++++ .../src/run_all_unittests.cc | 10 + .../src/run_length_encoder.cc | 144 ++ .../src/run_length_encoder.h | 81 ++ .../src/run_length_encoder_unittest.cc | 124 ++ tools/relocation_packer/src/sleb128.cc | 95 ++ tools/relocation_packer/src/sleb128.h | 75 + .../relocation_packer/src/sleb128_unittest.cc | 166 +++ .../test_data/elf_file_unittest_relocs.cc | 1014 +++++++++++++ .../elf_file_unittest_relocs_arm32.so | Bin 0 -> 93210 bytes .../elf_file_unittest_relocs_arm32_packed.so | Bin 0 -> 89126 bytes .../elf_file_unittest_relocs_arm64.so | Bin 0 -> 134131 bytes .../elf_file_unittest_relocs_arm64_packed.so | Bin 0 -> 123855 bytes .../generate_elf_file_unittest_relocs.py | 88 ++ .../generate_elf_file_unittest_relocs.sh | 35 + 36 files changed, 5485 insertions(+) create mode 100644 tools/relocation_packer/BUILD.gn create mode 100644 tools/relocation_packer/LICENSE create mode 100644 tools/relocation_packer/README.TXT create mode 100644 tools/relocation_packer/config.gni create mode 100644 tools/relocation_packer/relocation_packer.gyp create mode 100644 tools/relocation_packer/src/debug.cc create mode 100644 tools/relocation_packer/src/debug.h create mode 100644 tools/relocation_packer/src/debug_unittest.cc create mode 100644 tools/relocation_packer/src/delta_encoder.cc create mode 100644 tools/relocation_packer/src/delta_encoder.h create mode 100644 tools/relocation_packer/src/delta_encoder_unittest.cc create mode 100644 tools/relocation_packer/src/elf_file.cc create mode 100644 tools/relocation_packer/src/elf_file.h create mode 100644 tools/relocation_packer/src/elf_file_unittest.cc create mode 100644 tools/relocation_packer/src/elf_traits.h create mode 100644 tools/relocation_packer/src/leb128.cc create mode 100644 tools/relocation_packer/src/leb128.h create mode 100644 tools/relocation_packer/src/leb128_unittest.cc create mode 100644 tools/relocation_packer/src/main.cc create mode 100644 tools/relocation_packer/src/packer.cc create mode 100644 tools/relocation_packer/src/packer.h create mode 100644 tools/relocation_packer/src/packer_unittest.cc create mode 100644 tools/relocation_packer/src/run_all_unittests.cc create mode 100644 tools/relocation_packer/src/run_length_encoder.cc create mode 100644 tools/relocation_packer/src/run_length_encoder.h create mode 100644 tools/relocation_packer/src/run_length_encoder_unittest.cc create mode 100644 tools/relocation_packer/src/sleb128.cc create mode 100644 tools/relocation_packer/src/sleb128.h create mode 100644 tools/relocation_packer/src/sleb128_unittest.cc create mode 100644 tools/relocation_packer/test_data/elf_file_unittest_relocs.cc create mode 100755 tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so create mode 100755 tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so create mode 100755 tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so create mode 100755 tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so create mode 100755 tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py create mode 100755 tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh diff --git a/tools/relocation_packer/BUILD.gn b/tools/relocation_packer/BUILD.gn new file mode 100644 index 000000000..0b29c9162 --- /dev/null +++ b/tools/relocation_packer/BUILD.gn @@ -0,0 +1,148 @@ +# 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), + ] + } +} diff --git a/tools/relocation_packer/LICENSE b/tools/relocation_packer/LICENSE new file mode 100644 index 000000000..972bb2edb --- /dev/null +++ b/tools/relocation_packer/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tools/relocation_packer/README.TXT b/tools/relocation_packer/README.TXT new file mode 100644 index 000000000..071ab5df2 --- /dev/null +++ b/tools/relocation_packer/README.TXT @@ -0,0 +1,135 @@ +Introduction: +------------- + +Relative relocations are the bulk of dynamic relocations (the .rel.dyn +or .rela.dyn sections) in libchrome..so. The ELF standard +representation of them is wasteful. + +Packing uses a combination of run length encoding, delta encoding, and LEB128 +encoding to store them more efficiently. Packed relocations are placed in +a new .android.rel.dyn or .android.rela.dyn section. Packing reduces +the footprint of libchrome..so in the filesystem, in APK downloads, +and in memory when loaded on the device. + +A packed libchrome..so is designed so that it can be loaded directly +on Android, but requires the explicit support of a crazy linker that has been +extended to understand packed relocations. Packed relocations are currently +only supported on ARM. + +A packed libchrome..so cannot currently be used with the standard +Android runtime linker. + +See src/*.h for design and implementation notes. + + +Notes: +------ + +Packing does not adjust debug data. An unstripped libchrome..so +can be packed and will run, but may no longer be useful for debugging. + +Unpacking on the device requires the explicit support of an extended crazy +linker. Adds the following new .dynamic tags, used by the crazy linker to +find the packed .android.rel.dyn or .android.rela.dyn section data: + + DT_ANDROID_REL_OFFSET = DT_LOOS (Operating System specific: 0x6000000d) + - The offset of packed relocation data in libchrome..so + DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e) + - The size of packed relocation data in bytes + +32 bit ARM libraries use relocations without addends. 64 bit ARM libraries +use relocations with addends. The packing strategy necessarily differs for +the two relocation types. + +Where libchrome..so contains relocations without addends, the format +of .android.rel.dyn data is: + + "APR1" identifier + N: the number of count-delta pairs in the encoding + A: the initial offset + N * C,D: N count-delta pairs + +Where libchrome..so contains relocations with addends, the format +of .android.rela.dyn data is: + + "APA1" identifier + N: the number of addr-addend delta pairs in the encoding + N * A,V: N addr-addend delta pairs + +All numbers in the encoding stream are stored as LEB128 values. For details +see http://en.wikipedia.org/wiki/LEB128. + +The streaming unpacking algorithm for 32 bit ARM is: + + skip over "APR1" + pairs, addr = next leb128 value, next leb128 value + emit R_ARM_RELATIVE relocation with r_offset = addr + while pairs: + count, delta = next leb128 value, next leb128 value + while count: + addr += delta + emit R_ARM_RELATIVE relocation with r_offset = addr + count-- + pairs-- + +The streaming unpacking algorithm for 64 bit ARM is: + + skip over "APA1" + pairs = next signed leb128 value + addr, addend = 0, 0 + while pairs: + addr += next signed leb128 value + addend += next signed leb128 value + emit R_AARCH64_RELATIVE relocation with r_offset = addr, r_addend = addend + pairs-- + + +Usage instructions: +------------------- + +To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and +then run the tool: + + echo -n 'NULL' >/tmp/small + if file libchrome..so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy + --add-section .android.rel.dyn=/tmp/small + libchrome..so libchrome..so.packed + else + aarch64-linux-android-objcopy + --add-section .android.rela.dyn=/tmp/small + libchrome..so libchrome..so.packed + fi + rm /tmp/small + relocation_packer libchrome..so.packed + +To unpack and restore the shared library to its original state: + + cp libchrome..so.packed unpackable + relocation_packer -u unpackable + if file libchrome..so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy \ + --remove-section=.android.rel.dyn unpackable libchrome..so + else + aarch64-linux-android-objcopy \ + --remove-section=.android.rela.dyn unpackable libchrome..so + endif + rm unpackable + + +Bugs & TODOs: +------------- + +Requires two free slots in the .dynamic section. Uses these to add data that +tells the crazy linker where to find the packed relocation data. Fails +if insufficient free slots exist (use gold --spare-dynamic-slots to increase +the allocation). + +Requires libelf 0.158 or later. Earlier libelf releases may be buggy in +ways that prevent the packer from working correctly. + + +Testing: +-------- + +Unittests run under gtest, on the host system. diff --git a/tools/relocation_packer/config.gni b/tools/relocation_packer/config.gni new file mode 100644 index 000000000..90e397933 --- /dev/null +++ b/tools/relocation_packer/config.gni @@ -0,0 +1,21 @@ +# 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 = "" +} diff --git a/tools/relocation_packer/relocation_packer.gyp b/tools/relocation_packer/relocation_packer.gyp new file mode 100644 index 000000000..1e9c1b95b --- /dev/null +++ b/tools/relocation_packer/relocation_packer.gyp @@ -0,0 +1,161 @@ +# 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)', + ], + }, + ], + }, + ], +} diff --git a/tools/relocation_packer/src/debug.cc b/tools/relocation_packer/src/debug.cc new file mode 100644 index 000000000..29d7ab067 --- /dev/null +++ b/tools/relocation_packer/src/debug.cc @@ -0,0 +1,55 @@ +// 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 "debug.h" + +#include +#include +#include + +namespace relocation_packer { + +// Construct a new message logger. Prints if level is less than or equal to +// the level set with SetVerbose() and predicate is true. +Logger::Logger(Severity severity, int level, bool predicate) { + severity_ = severity; + level_ = level; + predicate_ = predicate; +} + +// On destruction, flush and print the strings accumulated. Abort if FATAL. +Logger::~Logger() { + if (predicate_) { + if (level_ <= max_level_) { + std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_; + std::string tag; + switch (severity_) { + case INFO: tag = "INFO"; break; + case WARNING: tag = "WARNING"; break; + case ERROR: tag = "ERROR"; break; + case FATAL: tag = "FATAL"; break; + } + stream_.flush(); + *log << tag << ": " << stream_.str() << std::endl; + } + if (severity_ == FATAL) + abort(); + } +} + +// Reset to initial state. +void Logger::Reset() { + max_level_ = -1; + info_stream_ = &std::cout; + error_stream_ = &std::cerr; +} + +// Verbosity. Not thread-safe. +int Logger::max_level_ = -1; + +// Logging streams. Not thread-safe. +std::ostream* Logger::info_stream_ = &std::cout; +std::ostream* Logger::error_stream_ = &std::cerr; + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/debug.h b/tools/relocation_packer/src/debug.h new file mode 100644 index 000000000..48be6c19b --- /dev/null +++ b/tools/relocation_packer/src/debug.h @@ -0,0 +1,115 @@ +// 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. + +// Logging and checks. Avoids a dependency on base. +// +// LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL. +// INFO prints to stdout, the others to stderr. FATAL aborts after printing. +// +// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent. +// +// VLOG(level) logs INFO messages where level is less than or equal to the +// verbosity level set with SetVerbose(). +// +// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true, +// else silent. +// +// CHECK(predicate) logs a FATAL error if predicate is false. +// NOTREACHED() always aborts. +// Log streams can be changed with SetStreams(). Logging is not thread-safe. +// + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ + +#include +#include +#include + +namespace relocation_packer { + +class Logger { + public: + enum Severity {INFO = 0, WARNING, ERROR, FATAL}; + + // Construct a new message logger. Prints if level is less than or + // equal to the level set with SetVerbose() and predicate is true. + // |severity| is an enumerated severity. + // |level| is the verbosity level. + // |predicate| controls if the logger prints or is silent. + Logger(Severity severity, int level, bool predicate); + + // On destruction, flush and print the strings accumulated in stream_. + ~Logger(); + + // Return the stream for this logger. + std::ostream& GetStream() { return stream_; } + + // Set verbosity level. Messages with a level less than or equal to + // this level are printed, others are discarded. Static, not thread-safe. + static void SetVerbose(int level) { max_level_ = level; } + + // Set info and error logging streams. Static, not thread-safe. + static void SetStreams(std::ostream* info_stream, + std::ostream* error_stream) { + info_stream_ = info_stream; + error_stream_ = error_stream; + } + + // Reset to initial state. + static void Reset(); + + private: + // Message severity, verbosity level, and predicate. + Severity severity_; + int level_; + bool predicate_; + + // String stream, accumulates message text. + std::ostringstream stream_; + + // Verbosity for INFO messages. Not thread-safe. + static int max_level_; + + // Logging streams. Not thread-safe. + static std::ostream* info_stream_; + static std::ostream* error_stream_; +}; + +} // namespace relocation_packer + +// Make logging severities visible globally. +typedef relocation_packer::Logger::Severity LogSeverity; +using LogSeverity::INFO; +using LogSeverity::WARNING; +using LogSeverity::ERROR; +using LogSeverity::FATAL; + +// LOG(severity) prints a message with the given severity, and aborts if +// severity is FATAL. LOG_IF(severity, predicate) does the same but only if +// predicate is true. INT_MIN is guaranteed to be less than or equal to +// any verbosity level. +#define LOG(severity) \ + (relocation_packer::Logger(severity, INT_MIN, true).GetStream()) +#define LOG_IF(severity, predicate) \ + (relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream()) + +// VLOG(level) prints its message as INFO if level is less than or equal to +// the current verbosity level. +#define VLOG(level) \ + (relocation_packer::Logger(INFO, (level), true).GetStream()) +#define VLOG_IF(level, predicate) \ + (relocation_packer::Logger(INFO, (level), (predicate)).GetStream()) + +// CHECK(predicate) fails with a FATAL log message if predicate is false. +#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \ + << __FILE__ << ":" << __LINE__ << ": " \ + << __FUNCTION__ << ": CHECK '" #predicate "' failed") + +// NOTREACHED() always fails with a FATAL log message. +#define NOTREACHED(_) (LOG(FATAL) \ + << __FILE__ << ":" << __LINE__ << ": " \ + << __FUNCTION__ << ": NOTREACHED() hit") + +#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ diff --git a/tools/relocation_packer/src/debug_unittest.cc b/tools/relocation_packer/src/debug_unittest.cc new file mode 100644 index 000000000..1b65cd16e --- /dev/null +++ b/tools/relocation_packer/src/debug_unittest.cc @@ -0,0 +1,122 @@ +// 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 "debug.h" + +#include +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Debug, Log) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + LOG(INFO) << "INFO log message"; + LOG(WARNING) << "WARNING log message"; + LOG(ERROR) << "ERROR log message"; + + EXPECT_EQ("INFO: INFO log message\n", info.str()); + EXPECT_EQ("WARNING: WARNING log message\n" + "ERROR: ERROR log message\n", error.str()); + Logger::Reset(); +} + +TEST(Debug, LogIf) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + LOG_IF(INFO, true) << "INFO log message"; + LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT"; + LOG_IF(WARNING, true) << "WARNING log message"; + LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT"; + LOG_IF(ERROR, true) << "ERROR log message"; + LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT"; + LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: INFO log message\n", info.str()); + EXPECT_EQ("WARNING: WARNING log message\n" + "ERROR: ERROR log message\n", error.str()); + Logger::Reset(); +} + +TEST(Debug, Vlog) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("", info.str()); + EXPECT_EQ("", error.str()); + + Logger::SetVerbose(1); + + VLOG(0) << "VLOG 0 INFO log message"; + VLOG(1) << "VLOG 1 INFO log message"; + VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: VLOG 0 INFO log message\n" + "INFO: VLOG 1 INFO log message\n", info.str()); + EXPECT_EQ("", error.str()); + Logger::Reset(); +} + +TEST(Debug, VlogIf) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("", info.str()); + EXPECT_EQ("", error.str()); + + Logger::SetVerbose(1); + + VLOG_IF(0, true) << "VLOG 0 INFO log message"; + VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(1, true) << "VLOG 1 INFO log message"; + VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: VLOG 0 INFO log message\n" + "INFO: VLOG 1 INFO log message\n", info.str()); + EXPECT_EQ("", error.str()); + Logger::Reset(); +} + +TEST(DebugDeathTest, Fatal) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message"); + EXPECT_DEATH( + LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message"); +} + +TEST(DebugDeathTest, Check) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + CHECK(0 == 0); + EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed"); +} + +TEST(DebugDeathTest, NotReached) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit"); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/delta_encoder.cc b/tools/relocation_packer/src/delta_encoder.cc new file mode 100644 index 000000000..69cc91a51 --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder.cc @@ -0,0 +1,72 @@ +// 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 "delta_encoder.h" + +#include + +#include "debug.h" +#include "elf_traits.h" + +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& relocations, + std::vector* packed) { + // One relocation is sufficient for delta encoding. + if (relocations.size() < 1) + return; + + // Start with the element count, then append the delta pairs. + packed->push_back(relocations.size()); + + ELF::Addr offset = 0; + ELF::Sxword addend = 0; + + for (size_t i = 0; i < relocations.size(); ++i) { + const ELF::Rela* relocation = &relocations[i]; + CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode); + + packed->push_back(relocation->r_offset - offset); + offset = relocation->r_offset; + packed->push_back(relocation->r_addend - addend); + addend = relocation->r_addend; + } +} + +// Decode relative relocations with addends from a delta encoded (packed) +// representation. +void RelocationDeltaCodec::Decode(const std::vector& packed, + std::vector* relocations) { + // We need at least one packed pair after the packed pair count to be + // able to unpack. + if (packed.size() < 3) + return; + + // Ensure that the packed data offers enough pairs. There may be zero + // padding on it that we ignore. + CHECK(static_cast(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); + } +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/delta_encoder.h b/tools/relocation_packer/src/delta_encoder.h new file mode 100644 index 000000000..498b6d1af --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder.h @@ -0,0 +1,80 @@ +// 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. + +// Delta encode and decode relative relocations with addends. +// +// Relative relocations are the bulk of dynamic relocations (the +// .rel.dyn or .rela.dyn sections) in libchrome..so, and the ELF +// standard representation of them is wasteful. .rel.dyn contains +// relocations without addends, .rela.dyn relocations with addends. +// +// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes +// 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..so' 64 bit: +// +// 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 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. +// +// Delta encoding reduces the size of the data modestly, so that the first +// three relocations above can be represented as: +// +// initial offset initial addend offset delta addend delta +// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028 +// offset delta addend delta ... +// 00000000 00000008 00000000 0000009f +// +// 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. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ + +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationDeltaCodec packs vectors of relative relocations with +// addends into more compact forms, and unpacks them to reproduce the +// pre-packed data. +class RelocationDeltaCodec { + public: + // Encode relative 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& relocations, + std::vector* 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& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ diff --git a/tools/relocation_packer/src/delta_encoder_unittest.cc b/tools/relocation_packer/src/delta_encoder_unittest.cc new file mode 100644 index 000000000..b9bf39adb --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder_unittest.cc @@ -0,0 +1,150 @@ +// 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 "delta_encoder.h" + +#include +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, + ELF::Sxword addend, + std::vector* relocations) { + ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, + ELF::Sxword addend, + const 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_addend == addend; +} + +} // namespace + +namespace relocation_packer { + +TEST(Delta, Encode) { + std::vector relocations; + std::vector packed; + + RelocationDeltaCodec codec; + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(0, packed.size()); + + // Initial relocation. + AddRelocation(0xf00d0000, 10000, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(3, 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]); + + // Add a second relocation, 4 byte offset delta, 12 byte addend delta. + AddRelocation(0xf00d0004, 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]); + + // Add a third relocation, 4 byte offset delta, 12 byte addend delta. + AddRelocation(0xf00d0008, 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); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(13, packed.size()); + // Six pairs present. + EXPECT_EQ(6, packed[0]); + // Initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + // 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]); + // 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]); +} + +TEST(Delta, Decode) { + std::vector packed; + std::vector relocations; + + RelocationDeltaCodec codec; + codec.Decode(packed, &relocations); + + EXPECT_EQ(0, relocations.size()); + + // Six pairs. + packed.push_back(6); + // Initial relocation. + packed.push_back(0xc0de0000); + packed.push_back(10000); + // 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); + // 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); + + relocations.clear(); + codec.Decode(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xc0de0000, 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])); + // 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])); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc new file mode 100644 index 000000000..3ffccecd7 --- /dev/null +++ b/tools/relocation_packer/src/elf_file.cc @@ -0,0 +1,1283 @@ +// 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. + +// 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. + +#include "elf_file.h" + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "elf_traits.h" +#include "libelf.h" +#include "packer.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; + +// 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 { + +// 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) { + 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) { + Elf_Data* data = GetSectionData(section); + CHECK(size == data->d_size); + uint8_t* area = new uint8_t[size]; + memcpy(area, section_data, size); + data->d_buf = area; +} + +// Verbose ELF header logging. +void VerboseLogElfHeader(const ELF::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; + VLOG(1) << "e_phentsize = " << elf_header->e_phentsize; + VLOG(1) << "e_phnum = " << elf_header->e_phnum; + VLOG(1) << "e_shnum = " << elf_header->e_shnum; + VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx; +} + +// Verbose ELF program header logging. +void VerboseLogProgramHeader(size_t program_header_index, + const ELF::Phdr* program_header) { + std::string type; + switch (program_header->p_type) { + case PT_NULL: type = "NULL"; break; + case PT_LOAD: type = "LOAD"; break; + case PT_DYNAMIC: type = "DYNAMIC"; break; + case PT_INTERP: type = "INTERP"; break; + case PT_PHDR: type = "PHDR"; break; + case PT_GNU_RELRO: type = "GNU_RELRO"; break; + case PT_GNU_STACK: type = "GNU_STACK"; break; + case PT_ARM_EXIDX: type = "EXIDX"; break; + default: type = "(OTHER)"; break; + } + VLOG(1) << "phdr[" << program_header_index << "] : " << type; + VLOG(1) << " p_offset = " << program_header->p_offset; + VLOG(1) << " p_vaddr = " << program_header->p_vaddr; + VLOG(1) << " p_paddr = " << program_header->p_paddr; + VLOG(1) << " p_filesz = " << program_header->p_filesz; + VLOG(1) << " p_memsz = " << program_header->p_memsz; + VLOG(1) << " p_flags = " << program_header->p_flags; + VLOG(1) << " p_align = " << program_header->p_align; +} + +// Verbose ELF section header logging. +void VerboseLogSectionHeader(const std::string& section_name, + const ELF::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_addralign = " << section_header->sh_addralign; +} + +// Verbose ELF section data logging. +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_size = " << data->d_size; + 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() { + if (elf_) + return true; + + Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL); + CHECK(elf); + + if (elf_kind(elf) != ELF_K_ELF) { + LOG(ERROR) << "File not in ELF format"; + return false; + } + + ELF::Ehdr* 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; + } + + // Require that our endianness matches that of the target, and that both + // are little-endian. Safe for all current build/target combinations. + const int endian = elf_header->e_ident[EI_DATA]; + 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); + + const 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]; + VerboseLogProgramHeader(i, program_header); + + if (program_header->p_type == PT_DYNAMIC) { + CHECK(dynamic_program_header == NULL); + dynamic_program_header = program_header; + } + } + CHECK(dynamic_program_header != NULL); + + 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; + + // Notes of relocation section types seen. We require one or the other of + // these; both is unsupported. + bool has_rel_relocations = false; + bool has_rela_relocations = false; + + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != NULL) { + const ELF::Shdr* 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) { + has_rel_relocations = true; + } + if (section_header->sh_type == SHT_RELA) { + has_rela_relocations = true; + } + + // Note special sections as we encounter them. + if ((name == ".rel.dyn" || name == ".rela.dyn") && + 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; + } + + // Ensure we preserve alignment, repeated later for the data block(s). + CHECK(section_header->sh_addralign <= kPreserveAlignment); + + Elf_Data* data = NULL; + while ((data = elf_getdata(section, data)) != NULL) { + CHECK(data->d_align <= kPreserveAlignment); + VerboseLogSectionData(data); + } + } + + // Loading failed if we did not find the required special sections. + if (!found_relocations_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; + } + + // Loading failed if we could not identify the relocations type. + if (!has_rel_relocations && !has_rela_relocations) { + LOG(ERROR) << "No relocations sections found"; + return false; + } + if (has_rel_relocations && has_rela_relocations) { + LOG(ERROR) << "Multiple relocations sections with different types found, " + << "not currently supported"; + 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) { + if (elf_header->e_phoff > hole_start) { + elf_header->e_phoff += hole_size; + VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff; + } + if (elf_header->e_shoff > hole_start) { + elf_header->e_shoff += hole_size; + VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff; + } +} + +// Helper for ResizeSection(). Adjust all section headers for the hole. +void AdjustSectionHeadersForHole(Elf* elf, + 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); + 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; + } + } +} + +// 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) { + 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; + + 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; + } + } +} + +// 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; + + for (size_t i = 0; i < count; ++i) { + 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 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); + } + } + + // 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); + } + } +} + +// 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); + 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 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); + + 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); +} + +// 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); +} + +// Helper for ResizeSection(). Locate and return the dynamic section. +Elf_Scn* GetDynamicSection(Elf* elf) { + const ELF::Ehdr* elf_header = ELF::getehdr(elf); + CHECK(elf_header); + + const 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; + for (size_t i = 0; i < elf_header->e_phnum; ++i) { + const ELF::Phdr* program_header = &elf_program_header[i]; + + if (program_header->p_type == PT_DYNAMIC) { + dynamic_program_header = program_header; + } + } + CHECK(dynamic_program_header); + + // Now find the section with the same offset as this 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); + + if (section_header->sh_offset == dynamic_program_header->p_offset) { + dynamic_section = section; + } + } + CHECK(dynamic_section != NULL); + + return dynamic_section; +} + +// Helper for ResizeSection(). Adjust the .dynamic section for the hole. +template +void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, + ELF::Off hole_start, + ssize_t hole_size) { + Elf_Data* data = GetSectionData(dynamic_section); + + const ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + + for (size_t i = 0; i < dynamics.size(); ++i) { + ELF::Dyn* dynamic = &dynamics[i]; + const ELF::Sword tag = dynamic->d_tag; + + // 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) { + 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(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; + } + + // 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)); + } + } + + void* section_data = &dynamics[0]; + size_t bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section, section_data, bytes); +} + +// 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 +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; + + // 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"); + + // Require that the section size and the data size are the same. True + // in practice for all sections we resize when packing or unpacking. + Elf_Data* data = GetSectionData(section); + CHECK(data->d_off == 0 && data->d_size == section_header->sh_size); + + // Require that the section is not zero-length (that is, has allocated + // data that we can validly expand). + CHECK(data->d_size && data->d_buf); + + const ELF::Off 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; + + // Resize the data and the section header. + data->d_size += hole_size; + section_header->sh_size += hole_size; + + // 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); + + // Adjust all section headers. + AdjustSectionHeadersForHole(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); + + Elf_Scn* dynamic_section = GetDynamicSection(elf); + AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size); + } +} + +// 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* 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) + 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* dynamics) { + const size_t slot = FindDynamicEntry(DT_NULL, dynamics); + if (slot == dynamics->size()) { + LOG(FATAL) << "No spare dynamic array slots found " + << "(to fix, increase gold's --spare-dynamic-tags value)"; + } + + // Replace this entry with the one supplied. + dynamics->at(slot) = dyn; + 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* 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 +void PadRelocations(size_t count, std::vector* relocations) { + Rel null_relocation; + NullRelocation(&null_relocation); + std::vector 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() { + // Load the ELF file into libelf. + if (!Load()) { + LOG(ERROR) << "Failed to load as ELF"; + return false; + } + + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + + if (relocations_type_ == REL) { + // Convert data to a vector of relocations. + const ELF::Rel* relocations_base = reinterpret_cast(data->d_buf); + std::vector relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + LOG(INFO) << "Relocations : REL"; + return PackTypedRelocations(relocations); + } + + if (relocations_type_ == RELA) { + // Convert data to a vector of relocations with addends. + const ELF::Rela* relocations_base = + reinterpret_cast(data->d_buf); + std::vector relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + LOG(INFO) << "Relocations : RELA"; + return PackTypedRelocations(relocations); + } + + NOTREACHED(); + return false; +} + +// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template +bool ElfFile::PackTypedRelocations(const std::vector& relocations) { + // Filter relocations into those that are relative and others. + std::vector relative_relocations; + std::vector 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"; + + // If no relative 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?)"; + 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; + } + + // 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]); + + // Padding may have removed any packing benefit. + if (padding >= relative_relocations.size()) { + LOG(INFO) << "Too few relative relocations to pack after padding"; + return false; + } + + // Add null relocations to other_relocations to preserve alignment. + PadRelocations(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(padding, &other_relocations); + } + + // Pack relative relocations. + const size_t initial_bytes = + relative_relocations.size() * sizeof(relative_relocations[0]); + LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes"; + std::vector 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"; + + // If we have insufficient relative relocations to form a run then + // packing fails. + if (packed.empty()) { + LOG(INFO) << "Too few relative relocations to pack"; + return false; + } + + // Run a loopback self-test as a check that packing is lossless. + std::vector unpacked; + packer.UnpackRelativeRelocations(packed, &unpacked); + CHECK(unpacked.size() == relative_relocations.size()); + CHECK(!memcmp(&unpacked[0], + &relative_relocations[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(elf_, relocations_section_, bytes); + RewriteSectionData(relocations_section_, section_data, bytes); + + // Rewrite the current packed android relocations section to hold the packed + // relative relocations. + ResizeSection(elf_, android_relocations_section_, packed_bytes); + RewriteSectionData(android_relocations_section_, packed_data, packed_bytes); + + // Rewrite .dynamic to include two new tags describing the packed android + // relocations. + Elf_Data* data = GetSectionData(dynamic_section_); + const ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector 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_); + { + ELF::Dyn dyn; + dyn.d_tag = DT_ANDROID_REL_OFFSET; + dyn.d_un.d_ptr = section_header->sh_offset; + AddDynamicEntry(dyn, &dynamics); + } + { + ELF::Dyn dyn; + dyn.d_tag = DT_ANDROID_REL_SIZE; + dyn.d_un.d_val = section_header->sh_size; + AddDynamicEntry(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(); + return true; +} + +// 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() { + // Load the ELF file into libelf. + if (!Load()) { + LOG(ERROR) << "Failed to load as ELF"; + return false; + } + + // Retrieve the current packed android relocations section data. + Elf_Data* data = GetSectionData(android_relocations_section_); + + // Convert data to a vector of bytes. + const uint8_t* packed_base = reinterpret_cast(data->d_buf); + std::vector packed( + packed_base, + packed_base + data->d_size / sizeof(packed[0])); + + if (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(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(packed); + } + + LOG(ERROR) << "Packed relative relocations not found (not packed?)"; + return false; +} + +// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template +bool ElfFile::UnpackTypedRelocations(const std::vector& 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 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"; + + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + + // Interpret data as relocations. + const Rel* relocations_base = reinterpret_cast(data->d_buf); + std::vector relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + std::vector 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"; + + // 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. + 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"; + } + + // 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(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( + elf_, android_relocations_section_, sizeof(kStubIdentifier)); + RewriteSectionData( + android_relocations_section_, &kStubIdentifier, sizeof(kStubIdentifier)); + + // Rewrite .dynamic to remove two tags describing packed android relocations. + data = GetSectionData(dynamic_section_); + const ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + RemoveDynamicEntry(DT_ANDROID_REL_OFFSET, &dynamics); + RemoveDynamicEntry(DT_ANDROID_REL_SIZE, &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(); + return true; +} + +// Flush rewritten shared object file data. +void ElfFile::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); + elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT); + + // Write ELF data back to disk. + const off_t file_bytes = elf_update(elf_, ELF_C_WRITE); + CHECK(file_bytes > 0); + VLOG(1) << "elf_update returned: " << file_bytes; + + // Clean up libelf, and truncate the output file to the number of bytes + // written by elf_update(). + elf_end(elf_); + elf_ = NULL; + const int truncate = ftruncate(fd_, file_bytes); + CHECK(truncate == 0); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h new file mode 100644 index 000000000..655027497 --- /dev/null +++ b/tools/relocation_packer/src/elf_file.h @@ -0,0 +1,134 @@ +// 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. + +// ELF shared object file updates handler. +// +// Provides functions to remove relative relocations from the .rel.dyn +// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn, +// and unpack to return the file to its pre-packed state. +// +// Files to be packed or unpacked must include an existing .android.rel.dyn +// or android.rela.dyn section. A standard libchrome..so will not +// contain this section, so the following can be used to add one: +// +// echo -n 'NULL' >/tmp/small +// if file libchrome..so | grep -q 'ELF 32'; then +// arm-linux-androideabi-objcopy +// --add-section .android.rel.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// else +// aarch64-linux-android-objcopy +// --add-section .android.rela.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// fi +// rm /tmp/small +// +// To use, open the file and pass the file descriptor to the constructor, +// then pack or unpack as desired. Packing or unpacking will flush the file +// descriptor on success. Example: +// +// int fd = open(..., O_RDWR); +// ElfFile elf_file(fd); +// bool status; +// if (is_packing) +// status = elf_file.PackRelocations(); +// else +// status = elf_file.UnpackRelocations(); +// close(fd); +// +// SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with +// NONE-type entries rather than cutting a hole out of the shared object +// file. This keeps all load addresses and offsets constant, and enables +// easier debugging and testing. +// +// A packed shared object file has all of its relative relocations +// removed from .rel.dyn or .rela.dyn, and replaced as packed data in +// .android.rel.dyn or .android.rela.dyn respectively. The resulting file +// is shorter than its non-packed original. +// +// Unpacking a packed file restores the file to its non-packed state, by +// expanding the packed data in .android.rel.dyn or .android.rela.dyn, +// combining the relative relocations with the data already in .rel.dyn +// or .rela.dyn, and then writing back the now expanded section. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ +#define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ + +#include +#include + +#include "elf.h" +#include "libelf.h" +#include "packer.h" + +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. +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) {} + ~ElfFile() {} + + // Set padding mode. When padding, PackRelocations() will not shrink + // the .rel.dyn or .rela.dyn section, but instead replace relative with + // NONE-type entries. + // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it. + inline void SetPadding(bool flag) { is_padding_relocations_ = flag; } + + // Transfer relative relocations from .rel.dyn or .rela.dyn to a packed + // representation in .android.rel.dyn or .android.rela.dyn. Returns true + // on success. + bool PackRelocations(); + + // Transfer relative relocations from a packed representation in + // .android.rel.dyn or .android.rela.dyn to .rel.dyn or .rela.dyn. Returns + // true on success. + bool UnpackRelocations(); + + private: + // 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. + bool Load(); + + // Templated packer, helper for PackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + template + bool PackTypedRelocations(const std::vector& relocations); + + // Templated unpacker, helper for UnpackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + template + bool UnpackTypedRelocations(const std::vector& packed); + + // Write ELF file changes. + void Flush(); + + // File descriptor opened on the shared object. + int fd_; + + // If set, pad rather than shrink .rel.dyn or .rela.dyn. Primarily for + // debugging, allows packing to be checked without affecting load addresses. + bool is_padding_relocations_; + + // Libelf handle, assigned by Load(). + Elf* elf_; + + // 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_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc new file mode 100644 index 000000000..37abd0d95 --- /dev/null +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -0,0 +1,164 @@ +// 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 "elf_file.h" + +#include +#include +#include +#include +#include +#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 + +namespace { + +void GetDataFilePath(const char* name, std::string* path) { + std::string data_dir; + + const char* bindir = getenv("bindir"); + 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)); + + data_dir = std::string(path); + size_t pos = data_dir.rfind('/'); + ASSERT_NE(std::string::npos, pos); + + data_dir.erase(pos + 1); + data_dir += std::string(XSTR(INTERMEDIATE_DIR)); + } + + *path = data_dir + "/" + name; +} + +void OpenRelocsTestFile(const char* name, FILE** stream) { + std::string path; + GetDataFilePath(name, &path); + + FILE* testfile = fopen(path.c_str(), "rb"); + ASSERT_FALSE(testfile == NULL); + + FILE* temporary = tmpfile(); + ASSERT_FALSE(temporary == NULL); + + static const size_t buffer_size = 4096; + unsigned char buffer[buffer_size]; + + size_t bytes; + do { + bytes = fread(buffer, 1, sizeof(buffer), testfile); + ASSERT_EQ(bytes, fwrite(buffer, 1, bytes, temporary)); + } while (bytes > 0); + + ASSERT_EQ(0, fclose(testfile)); + ASSERT_EQ(0, fseek(temporary, 0, SEEK_SET)); + ASSERT_EQ(0, lseek(fileno(temporary), 0, SEEK_SET)); + + *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); + + 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"; + + OpenRelocsTestFile(relocs.c_str(), relocs_so); + OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so); +} + +void CloseRelocsTestFile(FILE* temporary) { + fclose(temporary); +} + +void CloseRelocsTestFiles(FILE* relocs_so, FILE* packed_relocs_so) { + CloseRelocsTestFile(relocs_so); + CloseRelocsTestFile(packed_relocs_so); +} + +void CheckFileContentsEqual(FILE* first, FILE* second) { + ASSERT_EQ(0, fseek(first, 0, SEEK_SET)); + ASSERT_EQ(0, fseek(second, 0, SEEK_SET)); + + static const size_t buffer_size = 4096; + unsigned char first_buffer[buffer_size]; + unsigned char second_buffer[buffer_size]; + + do { + size_t first_read = fread(first_buffer, 1, sizeof(first_buffer), first); + size_t second_read = fread(second_buffer, 1, sizeof(second_buffer), second); + + EXPECT_EQ(first_read, second_read); + EXPECT_EQ(0, memcmp(first_buffer, second_buffer, first_read)); + } while (!feof(first) && !feof(second)); + + EXPECT_TRUE(feof(first) && feof(second)); +} + +} // namespace + +namespace relocation_packer { + +TEST(ElfFile, PackRelocations) { + ASSERT_NE(EV_NONE, elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(&relocs_so, &packed_relocs_so); + if (HasFatalFailure()) + return; + + ElfFile elf_file(fileno(relocs_so)); + + // Ensure unpacking fails (not packed). + EXPECT_FALSE(elf_file.UnpackRelocations()); + + // Pack relocations, and check files are now identical. + EXPECT_TRUE(elf_file.PackRelocations()); + CheckFileContentsEqual(relocs_so, packed_relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +TEST(ElfFile, UnpackRelocations) { + ASSERT_NE(EV_NONE, elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(&relocs_so, &packed_relocs_so); + if (HasFatalFailure()) + return; + + ElfFile elf_file(fileno(packed_relocs_so)); + + // Ensure packing fails (already packed). + EXPECT_FALSE(elf_file.PackRelocations()); + + // 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); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_traits.h b/tools/relocation_packer/src/elf_traits.h new file mode 100644 index 000000000..f099bab60 --- /dev/null +++ b/tools/relocation_packer/src/elf_traits.h @@ -0,0 +1,105 @@ +// 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. + +// Target-specific ELF type traits. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ +#define TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ + +#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. + +#if defined(TARGET_ARM) +struct ELF { + typedef Elf32_Addr Addr; + typedef Elf32_Dyn Dyn; + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Off Off; + typedef Elf32_Phdr Phdr; + typedef Elf32_Rel Rel; + typedef Elf32_Rela Rela; + typedef Elf32_Shdr Shdr; + typedef Elf32_Sword Sword; + typedef Elf32_Sxword Sxword; + typedef Elf32_Sym Sym; + typedef Elf32_Word Word; + typedef Elf32_Xword Xword; + + 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) +}; + +#elif defined(TARGET_ARM64) +struct ELF { + typedef Elf64_Addr Addr; + typedef Elf64_Dyn Dyn; + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Off Off; + typedef Elf64_Phdr Phdr; + typedef Elf64_Rel Rel; + typedef Elf64_Rela Rela; + typedef Elf64_Shdr Shdr; + typedef Elf64_Sword Sword; + typedef Elf64_Sxword Sxword; + typedef Elf64_Sym Sym; + typedef Elf64_Word Word; + typedef Elf64_Xword Xword; + + 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) +}; +#endif + +#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ diff --git a/tools/relocation_packer/src/leb128.cc b/tools/relocation_packer/src/leb128.cc new file mode 100644 index 000000000..b48739c32 --- /dev/null +++ b/tools/relocation_packer/src/leb128.cc @@ -0,0 +1,69 @@ +// 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 "leb128.h" + +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +Leb128Encoder::Leb128Encoder() { } +Leb128Encoder::~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) { + do { + const uint8_t byte = value & 127; + value >>= 7; + encoding_.push_back((value ? 128 : 0) | byte); + } while (value); +} + +// Add a vector of values to the encoding. +void Leb128Encoder::EnqueueAll(const std::vector& 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& encoding) { + encoding_ = encoding; + cursor_ = 0; +} + +// Empty destructor to silence chromium-style. +Leb128Decoder::~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; + + 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(byte & 127) << shift; + shift += 7; + } while (byte & 128); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +void Leb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) + values->push_back(Dequeue()); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h new file mode 100644 index 000000000..6cc2d7caf --- /dev/null +++ b/tools/relocation_packer/src/leb128.h @@ -0,0 +1,74 @@ +// 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. + +// LEB128 encoder and decoder for packed relative relocations. +// +// Run-length encoded relative relocations consist of a large number +// of pairs of relatively small positive integer values. Encoding these as +// LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ + +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a LEB128 byte stream. +class Leb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Leb128Encoder(); + ~Leb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the unsigned int to add. + void Enqueue(ELF::Xword value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of unsigned ints to add. + void EnqueueAll(const std::vector& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +class Leb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Leb128Decoder(const std::vector& encoding); + + // Explicit (but empty) destructor, for chromium-style. + ~Leb128Decoder(); + + // Retrieve the next value from the encoded stream. + ELF::Xword Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ diff --git a/tools/relocation_packer/src/leb128_unittest.cc b/tools/relocation_packer/src/leb128_unittest.cc new file mode 100644 index 000000000..bd607b717 --- /dev/null +++ b/tools/relocation_packer/src/leb128_unittest.cc @@ -0,0 +1,111 @@ +// 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 "leb128.h" + +#include +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Leb128, Encoder) { + std::vector values; + values.push_back(624485); + values.push_back(0); + values.push_back(1); + values.push_back(127); + values.push_back(128); + + Leb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(4294967295); + encoder.Enqueue(18446744073709551615ul); + + std::vector encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(23, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 127 + EXPECT_EQ(0x7f, encoding[5]); + // 128 + EXPECT_EQ(0x80, encoding[6]); + EXPECT_EQ(0x01, encoding[7]); + // 4294967295 + EXPECT_EQ(0xff, encoding[8]); + EXPECT_EQ(0xff, encoding[9]); + EXPECT_EQ(0xff, encoding[10]); + EXPECT_EQ(0xff, encoding[11]); + EXPECT_EQ(0x0f, encoding[12]); + // 18446744073709551615 + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0xff, encoding[16]); + EXPECT_EQ(0xff, encoding[17]); + EXPECT_EQ(0xff, encoding[18]); + EXPECT_EQ(0xff, encoding[19]); + EXPECT_EQ(0xff, encoding[20]); + EXPECT_EQ(0xff, encoding[21]); + EXPECT_EQ(0x01, encoding[22]); +} + +TEST(Leb128, Decoder) { + std::vector encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 127 + encoding.push_back(0x7f); + // 128 + encoding.push_back(0x80); + encoding.push_back(0x01); + // 4294967295 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x0f); + // 18446744073709551615 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x01); + + Leb128Decoder decoder(encoding); + + EXPECT_EQ(624485, decoder.Dequeue()); + + std::vector 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]); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc new file mode 100644 index 000000000..28f5b0469 --- /dev/null +++ b/tools/relocation_packer/src/main.cc @@ -0,0 +1,175 @@ +// 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. + +// Tool to pack and unpack relative relocations in a shared library. +// +// Packing removes relative relocations from .rel.dyn and writes them +// in a more compact form to .android.rel.dyn. Unpacking does the reverse. +// +// Invoke with -v to trace actions taken when packing or unpacking. +// Invoke with -p to pad removed relocations with R_*_NONE. Suppresses +// shrinking of .rel.dyn. +// See PrintUsage() below for full usage details. +// +// NOTE: Breaks with libelf 0.152, which is buggy. libelf 0.158 works. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "elf_file.h" +#include "libelf.h" + +namespace { + +void PrintUsage(const char* argv0) { + std::string temporary = argv0; + const size_t last_slash = temporary.find_last_of("/"); + if (last_slash != temporary.npos) { + temporary.erase(0, last_slash + 1); + } + const char* basename = temporary.c_str(); + + printf( + "Usage: %s [-u] [-v] [-p] file\n\n" + "Pack or unpack relative relocations in a shared library.\n\n" + " -u, --unpack unpack previously packed relative relocations\n" + " -v, --verbose trace object file modifications (for debugging)\n" + " -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..so\n" + " rm /tmp/small\n" + " %s libchrome..so\n\n" + "To unpack and restore the shared library to its original state:\n\n" + " %s -u libchrome..so\n" + " arm-linux-androideabi-objcopy \\\n" + " --remove-section=.android.rel.dyn libchrome..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..so\n" + " rm /tmp/small\n" + " %s libchrome..so\n\n" + "To unpack and restore the shared library to its original state:\n\n" + " %s -u libchrome..so\n" + " aarch64-linux-android-objcopy \\\n" + " --remove-section=.android.rela.dyn libchrome..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; + bool is_padding = false; + + static const option options[] = { + {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'}, + {"help", 0, 0, 'h'}, {NULL, 0, 0, 0} + }; + bool has_options = true; + while (has_options) { + int c = getopt_long(argc, argv, "uvph", options, NULL); + switch (c) { + case 'u': + is_unpacking = true; + break; + case 'v': + is_verbose = true; + break; + case 'p': + is_padding = true; + break; + case 'h': + PrintUsage(argv[0]); + return 0; + case '?': + LOG(INFO) << "Try '" << argv[0] << " --help' for more information."; + return 1; + case -1: + has_options = false; + break; + default: + NOTREACHED(); + return 1; + } + } + if (optind != argc - 1) { + LOG(INFO) << "Try '" << argv[0] << " --help' for more information."; + return 1; + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + 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) { + 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); + + bool status; + if (is_unpacking) + status = elf_file.UnpackRelocations(); + else + status = elf_file.PackRelocations(); + + close(fd); + + if (!status) { + LOG(ERROR) << file << ": failed to pack/unpack file"; + return 1; + } + + return 0; +} diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc new file mode 100644 index 000000000..29bec1e3c --- /dev/null +++ b/tools/relocation_packer/src/packer.cc @@ -0,0 +1,124 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "packer.h" + +#include + +#include "debug.h" +#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& relocations, + std::vector* packed) { + // Run-length encode. + std::vector packed_words; + RelocationRunLengthCodec codec; + codec.Encode(relocations, &packed_words); + + // If insufficient data to run-length encode, 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); +} + +// Unpack relative relocations from a run-length encoded packed +// representation. +void RelocationPacker::UnpackRelativeRelocations( + const std::vector& packed, + std::vector* relocations) { + // LEB128 decode, after checking and stripping "APR1" prefix. + std::vector 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; + codec.Decode(packed_words, relocations); +} + +// Pack relative relocations with addends into a delta encoded packed +// representation. +void RelocationPacker::PackRelativeRelocations( + const std::vector& relocations, + std::vector* packed) { + // Delta encode. + std::vector 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 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& packed, + std::vector* 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 packed_words; + std::vector stripped(packed.begin() + 4, packed.end()); + Sleb128Decoder decoder(stripped); + decoder.DequeueAll(&packed_words); + + // Delta decode. + RelocationDeltaCodec codec; + codec.Decode(packed_words, relocations); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h new file mode 100644 index 000000000..db09ce8cc --- /dev/null +++ b/tools/relocation_packer/src/packer.h @@ -0,0 +1,78 @@ +// 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. + +// Pack relative relocations into a more compact form. +// +// +// For relative relocations without addends (32 bit platforms) +// ----------------------------------------------------------- +// +// Applies two packing strategies. The first is run-length encoding, which +// turns a large set of relative relocations into a much smaller set +// of delta-count pairs, prefixed with a two-word header comprising the +// count of pairs and the initial relocation offset. The second is LEB128 +// encoding, which compresses the result of run-length encoding. +// +// Once packed, data is prefixed by an identifier that allows for any later +// versioning of packing strategies. +// +// A complete packed stream of relocations without addends might look +// something like: +// +// "APR1" pairs init_offset count1 delta1 count2 delta2 ... +// 41505231 f2b003 b08ac716 e001 04 01 10 ... +// +// +// For relative relocations with addends (64 bit platforms) +// -------------------------------------------------------- +// +// Applies two packing strategies. The first is delta encoding, which +// turns a large set of relative relocations into a smaller set +// of offset and addend delta pairs, prefixed with a header indicating the +// count of pairs. The second is signed LEB128 encoding, which compacts +// the result of delta encoding. +// +// Once packed, data is prefixed by an identifier that allows for any later +// versioning of packing strategies. +// +// A complete packed stream might look something like: +// +// "APA1" pairs offset_d1 addend_d1 offset_d2 addend_d2 ... +// 41505232 f2b018 04 28 08 9f01 ... + +#ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ + +#include +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationPacker packs vectors of relative relocations into more +// compact forms, and unpacks them to reproduce the pre-packed data. +class RelocationPacker { + public: + // Pack relative relocations into a more compact form. + // |relocations| is a vector of relative relocation structs. + // |packed| is the vector of packed bytes into which relocations are packed. + static void PackRelativeRelocations(const std::vector& relocations, + std::vector* packed); + static void PackRelativeRelocations(const std::vector& relocations, + std::vector* packed); + + // Unpack relative 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& packed, + std::vector* relocations); + static void UnpackRelativeRelocations(const std::vector& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc new file mode 100644 index 000000000..de5be7979 --- /dev/null +++ b/tools/relocation_packer/src/packer_unittest.cc @@ -0,0 +1,250 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "packer.h" + +#include +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, std::vector* 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; +} + +void AddRelocation(ELF::Addr addr, + ELF::Sxword addend, + std::vector* relocations) { + ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, + ELF::Sxword addend, + const 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_addend == addend; +} + +} // namespace + +namespace relocation_packer { + +TEST(Packer, PackRel) { + std::vector relocations; + std::vector packed; + + RelocationPacker packer; + + // Initial relocation. + AddRelocation(0xd1ce0000, &relocations); + // Two more relocations, 4 byte deltas. + AddRelocation(0xd1ce0004, &relocations); + AddRelocation(0xd1ce0008, &relocations); + // Three more relocations, 8 byte deltas. + AddRelocation(0xd1ce0010, &relocations); + AddRelocation(0xd1ce0018, &relocations); + AddRelocation(0xd1ce0020, &relocations); + + packed.clear(); + packer.PackRelativeRelocations(relocations, &packed); + + EXPECT_EQ(16, 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]); +} + +TEST(Packer, UnpackRel) { + std::vector packed; + std::vector relocations; + + RelocationPacker packer; + + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('R'); + packed.push_back('1'); + // Count-delta pairs count. + packed.push_back(2); + // 0xd1ce0000 + packed.push_back(128); + packed.push_back(128); + packed.push_back(184); + packed.push_back(142); + packed.push_back(13); + // Run of two relocations, 4 byte deltas. + packed.push_back(2); + packed.push_back(4); + // Run of three relocations, 8 byte deltas. + packed.push_back(3); + packed.push_back(8); + // Padding. + packed.push_back(0); + packed.push_back(0); + + relocations.clear(); + packer.UnpackRelativeRelocations(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0])); + // Two relocations, 4 byte deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2])); + // Three relocations, 8 byte deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5])); +} + +TEST(Packer, PackRela) { + std::vector relocations; + std::vector packed; + + RelocationPacker packer; + + // Initial relocation. + AddRelocation(0xd1ce0000, 10000, &relocations); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + AddRelocation(0xd1ce0004, 10012, &relocations); + AddRelocation(0xd1ce0008, 10024, &relocations); + // Three more relocations, 8 byte deltas, -24 byte addend deltas. + AddRelocation(0xd1ce0010, 10000, &relocations); + AddRelocation(0xd1ce0018, 9976, &relocations); + AddRelocation(0xd1ce0020, 9952, &relocations); + + packed.clear(); + packer.PackRelativeRelocations(relocations, &packed); + + EXPECT_EQ(24, packed.size()); + // Identifier. + EXPECT_EQ('A', packed[0]); + EXPECT_EQ('P', packed[1]); + EXPECT_EQ('A', packed[2]); + EXPECT_EQ('1', packed[3]); + // Delta pairs count. + EXPECT_EQ(6, packed[4]); + // 0xd1ce0000 + EXPECT_EQ(128, packed[5]); + EXPECT_EQ(128, packed[6]); + EXPECT_EQ(184, packed[7]); + EXPECT_EQ(142, packed[8]); + EXPECT_EQ(13, packed[9]); + // 10000 + EXPECT_EQ(144, packed[10]); + EXPECT_EQ(206, packed[11]); + EXPECT_EQ(0, packed[12]); + // 4, 12 + EXPECT_EQ(4, packed[13]); + EXPECT_EQ(12, packed[14]); + // 4, 12 + EXPECT_EQ(4, packed[15]); + EXPECT_EQ(12, packed[16]); + // 8, -24 + EXPECT_EQ(8, packed[17]); + EXPECT_EQ(104, packed[18]); + // 8, -24 + EXPECT_EQ(8, packed[19]); + EXPECT_EQ(104, packed[20]); + // 8, -24 + EXPECT_EQ(8, packed[21]); + EXPECT_EQ(104, packed[22]); + // Padding. + EXPECT_EQ(0, packed[23]); +} + +TEST(Packer, UnpackRela) { + std::vector packed; + std::vector relocations; + + RelocationPacker packer; + + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('A'); + packed.push_back('1'); + // Delta pairs count. + packed.push_back(6); + // 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); + + relocations.clear(); + packer.UnpackRelativeRelocations(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 10000, relocations[0])); + // 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])); + // 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])); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/run_all_unittests.cc b/tools/relocation_packer/src/run_all_unittests.cc new file mode 100644 index 000000000..4122be18c --- /dev/null +++ b/tools/relocation_packer/src/run_all_unittests.cc @@ -0,0 +1,10 @@ +// 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(); +} diff --git a/tools/relocation_packer/src/run_length_encoder.cc b/tools/relocation_packer/src/run_length_encoder.cc new file mode 100644 index 000000000..2f2e1c315 --- /dev/null +++ b/tools/relocation_packer/src/run_length_encoder.cc @@ -0,0 +1,144 @@ +// 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 + +#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& relocations, + std::vector* 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& deltas, + std::vector* 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& packed, + size_t start_index, + size_t end_index, + std::vector* 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& relocations, + std::vector* 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 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& packed, + std::vector* 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 diff --git a/tools/relocation_packer/src/run_length_encoder.h b/tools/relocation_packer/src/run_length_encoder.h new file mode 100644 index 000000000..f3a80e602 --- /dev/null +++ b/tools/relocation_packer/src/run_length_encoder.h @@ -0,0 +1,81 @@ +// 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. + +// Run-length encode and decode relative relocations. +// +// Relative relocations are the bulk of dynamic relocations (the +// .rel.dyn or .rela.dyn sections) in libchrome..so, and the ELF +// standard representation of them is wasteful. .rel.dyn contains +// relocations without addends, .rela.dyn relocations with addends. +// +// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes +// on 64 bit plaforms, split into offset and info fields. Offsets strictly +// increase, and each is commonly a few bytes different from its predecessor. +// There are long runs where the difference does not change. The info field +// is constant. Example, from 'readelf -x4 libchrome..so' 32 bit: +// +// offset info offset info +// 808fef01 17000000 848fef01 17000000 ................ +// 888fef01 17000000 8c8fef01 17000000 ................ +// 908fef01 17000000 948fef01 17000000 ................ +// +// Run length encoding packs this data more efficiently, by representing it +// as a delta and a count of entries each differing from its predecessor +// by this delta. The above can be represented as a start address followed +// by an encoded count of 6 and offset difference of 4: +// +// start count diff +// 01ef8f80 00000006 00000004 +// +// Because relative relocation offsets strictly increase, the complete +// set of relative relocations in libchrome..so can be +// represented by a single start address followed by one or more difference +// and count encoded word pairs: +// +// start run1 count run1 diff run2 count run2 diff +// 01ef8f80 00000006 00000004 00000010 00000008 ... +// +// Decoding regenerates relative relocations beginning at address +// 'start' and for each encoded run, incrementing the address by 'difference' +// for 'count' iterations and emitting a new relative relocation. +// +// Once encoded, data is prefixed by a single word count of packed delta and +// count pairs. A final run-length encoded relative relocations vector +// might therefore look something like: +// +// pairs start run 1 run 2 ... run 15 +// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ... +// Interpreted as: +// pairs=15 start=.. count=6,delta=4 count=16,delta=8 + +#ifndef TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ + +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationRunLengthCodec packs vectors of relative relocations +// into more compact forms, and unpacks them to reproduce the pre-packed data. +class RelocationRunLengthCodec { + public: + // Encode relative relocations into a more compact form. + // |relocations| is a vector of relative relocation structs. + // |packed| is the vector of packed words into which relocations are packed. + static void Encode(const std::vector& relocations, + std::vector* packed); + + // Decode relative relocations from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relative relocation structs. + static void Decode(const std::vector& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ diff --git a/tools/relocation_packer/src/run_length_encoder_unittest.cc b/tools/relocation_packer/src/run_length_encoder_unittest.cc new file mode 100644 index 000000000..83370f2a9 --- /dev/null +++ b/tools/relocation_packer/src/run_length_encoder_unittest.cc @@ -0,0 +1,124 @@ +// 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 +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, std::vector* 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 relocations; + std::vector 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 packed; + std::vector 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 diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc new file mode 100644 index 000000000..a10bd79a7 --- /dev/null +++ b/tools/relocation_packer/src/sleb128.cc @@ -0,0 +1,95 @@ +// 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 "sleb128.h" + +#include +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +Sleb128Encoder::Sleb128Encoder() { } +Sleb128Encoder::~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, +// and the most significant bit is set on each byte except the last. The +// value is sign extended up to a multiple of 7 bits (ensuring that the +// most significant bit is zero for a positive number and one for a +// negative number). +void Sleb128Encoder::Enqueue(ELF::Sxword value) { + static const size_t size = CHAR_BIT * sizeof(value); + + bool more = true; + const bool negative = value < 0; + + while (more) { + uint8_t byte = value & 127; + value >>= 7; + + // Sign extend if encoding a -ve value. + if (negative) + value |= -(static_cast(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)) + more = false; + else + byte |= 128; + encoding_.push_back(byte); + } +} + +// Add a vector of values to the encoding. +void Sleb128Encoder::EnqueueAll(const std::vector& 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& encoding) { + encoding_ = encoding; + cursor_ = 0; +} + +// Empty destructor to silence chromium-style. +Sleb128Decoder::~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; + static const size_t size = CHAR_BIT * sizeof(value); + + 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(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(1) << shift); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +void Sleb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) + values->push_back(Dequeue()); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h new file mode 100644 index 000000000..3544543c0 --- /dev/null +++ b/tools/relocation_packer/src/sleb128.h @@ -0,0 +1,75 @@ +// 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. + +// SLEB128 encoder and decoder for packed relative relocations. +// +// Delta encoded relative relocations consist of a large number +// of pairs signed integer values, many with small values. Encoding these +// as signed LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ + +#include +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a signed LEB128 byte stream. +class Sleb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Sleb128Encoder(); + ~Sleb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the signed int to add. + void Enqueue(ELF::Sxword value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of signed ints to add. + void EnqueueAll(const std::vector& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +class Sleb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Sleb128Decoder(const std::vector& encoding); + + // Explicit (but empty) destructor, for chromium-style. + ~Sleb128Decoder(); + + // Retrieve the next value from the encoded stream. + ELF::Sxword Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ diff --git a/tools/relocation_packer/src/sleb128_unittest.cc b/tools/relocation_packer/src/sleb128_unittest.cc new file mode 100644 index 000000000..60a5d0de7 --- /dev/null +++ b/tools/relocation_packer/src/sleb128_unittest.cc @@ -0,0 +1,166 @@ +// 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 "sleb128.h" + +#include +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Sleb128, Encoder) { + std::vector 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); + + Sleb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(2147483647); + encoder.Enqueue(-2147483648); + encoder.Enqueue(9223372036854775807ll); + encoder.Enqueue(-9223372036854775807ll - 1); + + std::vector encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(42u, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 63 + EXPECT_EQ(0x3f, encoding[5]); + // 64 + EXPECT_EQ(0xc0, encoding[6]); + EXPECT_EQ(0x00, encoding[7]); + // -1 + EXPECT_EQ(0x7f, encoding[8]); + // -624485 + EXPECT_EQ(0x9b, encoding[9]); + EXPECT_EQ(0xf1, encoding[10]); + EXPECT_EQ(0x59, encoding[11]); + // 2147483647 + EXPECT_EQ(0xff, encoding[12]); + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0x07, encoding[16]); + // -2147483648 + EXPECT_EQ(0x80, encoding[17]); + EXPECT_EQ(0x80, encoding[18]); + EXPECT_EQ(0x80, encoding[19]); + EXPECT_EQ(0x80, encoding[20]); + EXPECT_EQ(0x78, encoding[21]); + // 9223372036854775807 + EXPECT_EQ(0xff, encoding[22]); + EXPECT_EQ(0xff, encoding[23]); + EXPECT_EQ(0xff, encoding[24]); + EXPECT_EQ(0xff, encoding[25]); + EXPECT_EQ(0xff, encoding[26]); + EXPECT_EQ(0xff, encoding[27]); + EXPECT_EQ(0xff, encoding[28]); + EXPECT_EQ(0xff, encoding[29]); + EXPECT_EQ(0xff, encoding[30]); + EXPECT_EQ(0x00, encoding[31]); + // -9223372036854775808 + EXPECT_EQ(0x80, encoding[32]); + EXPECT_EQ(0x80, encoding[33]); + EXPECT_EQ(0x80, encoding[34]); + EXPECT_EQ(0x80, encoding[35]); + EXPECT_EQ(0x80, encoding[36]); + EXPECT_EQ(0x80, encoding[37]); + EXPECT_EQ(0x80, encoding[38]); + EXPECT_EQ(0x80, encoding[39]); + EXPECT_EQ(0x80, encoding[40]); + EXPECT_EQ(0x7f, encoding[41]); +} + +TEST(Sleb128, Decoder) { + std::vector encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 63 + encoding.push_back(0x3f); + // 64 + encoding.push_back(0xc0); + encoding.push_back(0x00); + // -1 + encoding.push_back(0x7f); + // -624485 + encoding.push_back(0x9b); + encoding.push_back(0xf1); + encoding.push_back(0x59); + // 2147483647 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x07); + // -2147483648 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x78); + // 9223372036854775807 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x00); + // -9223372036854775808 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x7f); + + Sleb128Decoder decoder(encoding); + + EXPECT_EQ(624485, decoder.Dequeue()); + + std::vector 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]); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc new file mode 100644 index 000000000..5e1fa747e --- /dev/null +++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc @@ -0,0 +1,1014 @@ +// 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. + +// Test data for packing/unpacking. When compiled, creates a run of +// relative relocations. +// +// See generate_elf_file_unittest_relocs.sh for instructions on how to build +// unit test data from this source file. + +const int i = 0; + +// Generator: +// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i' +const void* pointer_0 = &i; +const void* pointer_1 = &i; +const void* pointer_2 = &i; +const void* pointer_3 = &i; +const void* pointer_4 = &i; +const void* pointer_5 = &i; +const void* pointer_6 = &i; +const void* pointer_7 = &i; +const void* pointer_8 = &i; +const void* pointer_9 = &i; +const void* pointer_10 = &i; +const void* pointer_11 = &i; +const void* pointer_12 = &i; +const void* pointer_13 = &i; +const void* pointer_14 = &i; +const void* pointer_15 = &i; +const void* pointer_16 = &i; +const void* pointer_17 = &i; +const void* pointer_18 = &i; +const void* pointer_19 = &i; +const void* pointer_20 = &i; +const void* pointer_21 = &i; +const void* pointer_22 = &i; +const void* pointer_23 = &i; +const void* pointer_24 = &i; +const void* pointer_25 = &i; +const void* pointer_26 = &i; +const void* pointer_27 = &i; +const void* pointer_28 = &i; +const void* pointer_29 = &i; +const void* pointer_30 = &i; +const void* pointer_31 = &i; +const void* pointer_32 = &i; +const void* pointer_33 = &i; +const void* pointer_34 = &i; +const void* pointer_35 = &i; +const void* pointer_36 = &i; +const void* pointer_37 = &i; +const void* pointer_38 = &i; +const void* pointer_39 = &i; +const void* pointer_40 = &i; +const void* pointer_41 = &i; +const void* pointer_42 = &i; +const void* pointer_43 = &i; +const void* pointer_44 = &i; +const void* pointer_45 = &i; +const void* pointer_46 = &i; +const void* pointer_47 = &i; +const void* pointer_48 = &i; +const void* pointer_49 = &i; +const void* pointer_50 = &i; +const void* pointer_51 = &i; +const void* pointer_52 = &i; +const void* pointer_53 = &i; +const void* pointer_54 = &i; +const void* pointer_55 = &i; +const void* pointer_56 = &i; +const void* pointer_57 = &i; +const void* pointer_58 = &i; +const void* pointer_59 = &i; +const void* pointer_60 = &i; +const void* pointer_61 = &i; +const void* pointer_62 = &i; +const void* pointer_63 = &i; +const void* pointer_64 = &i; +const void* pointer_65 = &i; +const void* pointer_66 = &i; +const void* pointer_67 = &i; +const void* pointer_68 = &i; +const void* pointer_69 = &i; +const void* pointer_70 = &i; +const void* pointer_71 = &i; +const void* pointer_72 = &i; +const void* pointer_73 = &i; +const void* pointer_74 = &i; +const void* pointer_75 = &i; +const void* pointer_76 = &i; +const void* pointer_77 = &i; +const void* pointer_78 = &i; +const void* pointer_79 = &i; +const void* pointer_80 = &i; +const void* pointer_81 = &i; +const void* pointer_82 = &i; +const void* pointer_83 = &i; +const void* pointer_84 = &i; +const void* pointer_85 = &i; +const void* pointer_86 = &i; +const void* pointer_87 = &i; +const void* pointer_88 = &i; +const void* pointer_89 = &i; +const void* pointer_90 = &i; +const void* pointer_91 = &i; +const void* pointer_92 = &i; +const void* pointer_93 = &i; +const void* pointer_94 = &i; +const void* pointer_95 = &i; +const void* pointer_96 = &i; +const void* pointer_97 = &i; +const void* pointer_98 = &i; +const void* pointer_99 = &i; +const void* pointer_100 = &i; +const void* pointer_101 = &i; +const void* pointer_102 = &i; +const void* pointer_103 = &i; +const void* pointer_104 = &i; +const void* pointer_105 = &i; +const void* pointer_106 = &i; +const void* pointer_107 = &i; +const void* pointer_108 = &i; +const void* pointer_109 = &i; +const void* pointer_110 = &i; +const void* pointer_111 = &i; +const void* pointer_112 = &i; +const void* pointer_113 = &i; +const void* pointer_114 = &i; +const void* pointer_115 = &i; +const void* pointer_116 = &i; +const void* pointer_117 = &i; +const void* pointer_118 = &i; +const void* pointer_119 = &i; +const void* pointer_120 = &i; +const void* pointer_121 = &i; +const void* pointer_122 = &i; +const void* pointer_123 = &i; +const void* pointer_124 = &i; +const void* pointer_125 = &i; +const void* pointer_126 = &i; +const void* pointer_127 = &i; +const void* pointer_128 = &i; +const void* pointer_129 = &i; +const void* pointer_130 = &i; +const void* pointer_131 = &i; +const void* pointer_132 = &i; +const void* pointer_133 = &i; +const void* pointer_134 = &i; +const void* pointer_135 = &i; +const void* pointer_136 = &i; +const void* pointer_137 = &i; +const void* pointer_138 = &i; +const void* pointer_139 = &i; +const void* pointer_140 = &i; +const void* pointer_141 = &i; +const void* pointer_142 = &i; +const void* pointer_143 = &i; +const void* pointer_144 = &i; +const void* pointer_145 = &i; +const void* pointer_146 = &i; +const void* pointer_147 = &i; +const void* pointer_148 = &i; +const void* pointer_149 = &i; +const void* pointer_150 = &i; +const void* pointer_151 = &i; +const void* pointer_152 = &i; +const void* pointer_153 = &i; +const void* pointer_154 = &i; +const void* pointer_155 = &i; +const void* pointer_156 = &i; +const void* pointer_157 = &i; +const void* pointer_158 = &i; +const void* pointer_159 = &i; +const void* pointer_160 = &i; +const void* pointer_161 = &i; +const void* pointer_162 = &i; +const void* pointer_163 = &i; +const void* pointer_164 = &i; +const void* pointer_165 = &i; +const void* pointer_166 = &i; +const void* pointer_167 = &i; +const void* pointer_168 = &i; +const void* pointer_169 = &i; +const void* pointer_170 = &i; +const void* pointer_171 = &i; +const void* pointer_172 = &i; +const void* pointer_173 = &i; +const void* pointer_174 = &i; +const void* pointer_175 = &i; +const void* pointer_176 = &i; +const void* pointer_177 = &i; +const void* pointer_178 = &i; +const void* pointer_179 = &i; +const void* pointer_180 = &i; +const void* pointer_181 = &i; +const void* pointer_182 = &i; +const void* pointer_183 = &i; +const void* pointer_184 = &i; +const void* pointer_185 = &i; +const void* pointer_186 = &i; +const void* pointer_187 = &i; +const void* pointer_188 = &i; +const void* pointer_189 = &i; +const void* pointer_190 = &i; +const void* pointer_191 = &i; +const void* pointer_192 = &i; +const void* pointer_193 = &i; +const void* pointer_194 = &i; +const void* pointer_195 = &i; +const void* pointer_196 = &i; +const void* pointer_197 = &i; +const void* pointer_198 = &i; +const void* pointer_199 = &i; +const void* pointer_200 = &i; +const void* pointer_201 = &i; +const void* pointer_202 = &i; +const void* pointer_203 = &i; +const void* pointer_204 = &i; +const void* pointer_205 = &i; +const void* pointer_206 = &i; +const void* pointer_207 = &i; +const void* pointer_208 = &i; +const void* pointer_209 = &i; +const void* pointer_210 = &i; +const void* pointer_211 = &i; +const void* pointer_212 = &i; +const void* pointer_213 = &i; +const void* pointer_214 = &i; +const void* pointer_215 = &i; +const void* pointer_216 = &i; +const void* pointer_217 = &i; +const void* pointer_218 = &i; +const void* pointer_219 = &i; +const void* pointer_220 = &i; +const void* pointer_221 = &i; +const void* pointer_222 = &i; +const void* pointer_223 = &i; +const void* pointer_224 = &i; +const void* pointer_225 = &i; +const void* pointer_226 = &i; +const void* pointer_227 = &i; +const void* pointer_228 = &i; +const void* pointer_229 = &i; +const void* pointer_230 = &i; +const void* pointer_231 = &i; +const void* pointer_232 = &i; +const void* pointer_233 = &i; +const void* pointer_234 = &i; +const void* pointer_235 = &i; +const void* pointer_236 = &i; +const void* pointer_237 = &i; +const void* pointer_238 = &i; +const void* pointer_239 = &i; +const void* pointer_240 = &i; +const void* pointer_241 = &i; +const void* pointer_242 = &i; +const void* pointer_243 = &i; +const void* pointer_244 = &i; +const void* pointer_245 = &i; +const void* pointer_246 = &i; +const void* pointer_247 = &i; +const void* pointer_248 = &i; +const void* pointer_249 = &i; +const void* pointer_250 = &i; +const void* pointer_251 = &i; +const void* pointer_252 = &i; +const void* pointer_253 = &i; +const void* pointer_254 = &i; +const void* pointer_255 = &i; +const void* pointer_256 = &i; +const void* pointer_257 = &i; +const void* pointer_258 = &i; +const void* pointer_259 = &i; +const void* pointer_260 = &i; +const void* pointer_261 = &i; +const void* pointer_262 = &i; +const void* pointer_263 = &i; +const void* pointer_264 = &i; +const void* pointer_265 = &i; +const void* pointer_266 = &i; +const void* pointer_267 = &i; +const void* pointer_268 = &i; +const void* pointer_269 = &i; +const void* pointer_270 = &i; +const void* pointer_271 = &i; +const void* pointer_272 = &i; +const void* pointer_273 = &i; +const void* pointer_274 = &i; +const void* pointer_275 = &i; +const void* pointer_276 = &i; +const void* pointer_277 = &i; +const void* pointer_278 = &i; +const void* pointer_279 = &i; +const void* pointer_280 = &i; +const void* pointer_281 = &i; +const void* pointer_282 = &i; +const void* pointer_283 = &i; +const void* pointer_284 = &i; +const void* pointer_285 = &i; +const void* pointer_286 = &i; +const void* pointer_287 = &i; +const void* pointer_288 = &i; +const void* pointer_289 = &i; +const void* pointer_290 = &i; +const void* pointer_291 = &i; +const void* pointer_292 = &i; +const void* pointer_293 = &i; +const void* pointer_294 = &i; +const void* pointer_295 = &i; +const void* pointer_296 = &i; +const void* pointer_297 = &i; +const void* pointer_298 = &i; +const void* pointer_299 = &i; +const void* pointer_300 = &i; +const void* pointer_301 = &i; +const void* pointer_302 = &i; +const void* pointer_303 = &i; +const void* pointer_304 = &i; +const void* pointer_305 = &i; +const void* pointer_306 = &i; +const void* pointer_307 = &i; +const void* pointer_308 = &i; +const void* pointer_309 = &i; +const void* pointer_310 = &i; +const void* pointer_311 = &i; +const void* pointer_312 = &i; +const void* pointer_313 = &i; +const void* pointer_314 = &i; +const void* pointer_315 = &i; +const void* pointer_316 = &i; +const void* pointer_317 = &i; +const void* pointer_318 = &i; +const void* pointer_319 = &i; +const void* pointer_320 = &i; +const void* pointer_321 = &i; +const void* pointer_322 = &i; +const void* pointer_323 = &i; +const void* pointer_324 = &i; +const void* pointer_325 = &i; +const void* pointer_326 = &i; +const void* pointer_327 = &i; +const void* pointer_328 = &i; +const void* pointer_329 = &i; +const void* pointer_330 = &i; +const void* pointer_331 = &i; +const void* pointer_332 = &i; +const void* pointer_333 = &i; +const void* pointer_334 = &i; +const void* pointer_335 = &i; +const void* pointer_336 = &i; +const void* pointer_337 = &i; +const void* pointer_338 = &i; +const void* pointer_339 = &i; +const void* pointer_340 = &i; +const void* pointer_341 = &i; +const void* pointer_342 = &i; +const void* pointer_343 = &i; +const void* pointer_344 = &i; +const void* pointer_345 = &i; +const void* pointer_346 = &i; +const void* pointer_347 = &i; +const void* pointer_348 = &i; +const void* pointer_349 = &i; +const void* pointer_350 = &i; +const void* pointer_351 = &i; +const void* pointer_352 = &i; +const void* pointer_353 = &i; +const void* pointer_354 = &i; +const void* pointer_355 = &i; +const void* pointer_356 = &i; +const void* pointer_357 = &i; +const void* pointer_358 = &i; +const void* pointer_359 = &i; +const void* pointer_360 = &i; +const void* pointer_361 = &i; +const void* pointer_362 = &i; +const void* pointer_363 = &i; +const void* pointer_364 = &i; +const void* pointer_365 = &i; +const void* pointer_366 = &i; +const void* pointer_367 = &i; +const void* pointer_368 = &i; +const void* pointer_369 = &i; +const void* pointer_370 = &i; +const void* pointer_371 = &i; +const void* pointer_372 = &i; +const void* pointer_373 = &i; +const void* pointer_374 = &i; +const void* pointer_375 = &i; +const void* pointer_376 = &i; +const void* pointer_377 = &i; +const void* pointer_378 = &i; +const void* pointer_379 = &i; +const void* pointer_380 = &i; +const void* pointer_381 = &i; +const void* pointer_382 = &i; +const void* pointer_383 = &i; +const void* pointer_384 = &i; +const void* pointer_385 = &i; +const void* pointer_386 = &i; +const void* pointer_387 = &i; +const void* pointer_388 = &i; +const void* pointer_389 = &i; +const void* pointer_390 = &i; +const void* pointer_391 = &i; +const void* pointer_392 = &i; +const void* pointer_393 = &i; +const void* pointer_394 = &i; +const void* pointer_395 = &i; +const void* pointer_396 = &i; +const void* pointer_397 = &i; +const void* pointer_398 = &i; +const void* pointer_399 = &i; +const void* pointer_400 = &i; +const void* pointer_401 = &i; +const void* pointer_402 = &i; +const void* pointer_403 = &i; +const void* pointer_404 = &i; +const void* pointer_405 = &i; +const void* pointer_406 = &i; +const void* pointer_407 = &i; +const void* pointer_408 = &i; +const void* pointer_409 = &i; +const void* pointer_410 = &i; +const void* pointer_411 = &i; +const void* pointer_412 = &i; +const void* pointer_413 = &i; +const void* pointer_414 = &i; +const void* pointer_415 = &i; +const void* pointer_416 = &i; +const void* pointer_417 = &i; +const void* pointer_418 = &i; +const void* pointer_419 = &i; +const void* pointer_420 = &i; +const void* pointer_421 = &i; +const void* pointer_422 = &i; +const void* pointer_423 = &i; +const void* pointer_424 = &i; +const void* pointer_425 = &i; +const void* pointer_426 = &i; +const void* pointer_427 = &i; +const void* pointer_428 = &i; +const void* pointer_429 = &i; +const void* pointer_430 = &i; +const void* pointer_431 = &i; +const void* pointer_432 = &i; +const void* pointer_433 = &i; +const void* pointer_434 = &i; +const void* pointer_435 = &i; +const void* pointer_436 = &i; +const void* pointer_437 = &i; +const void* pointer_438 = &i; +const void* pointer_439 = &i; +const void* pointer_440 = &i; +const void* pointer_441 = &i; +const void* pointer_442 = &i; +const void* pointer_443 = &i; +const void* pointer_444 = &i; +const void* pointer_445 = &i; +const void* pointer_446 = &i; +const void* pointer_447 = &i; +const void* pointer_448 = &i; +const void* pointer_449 = &i; +const void* pointer_450 = &i; +const void* pointer_451 = &i; +const void* pointer_452 = &i; +const void* pointer_453 = &i; +const void* pointer_454 = &i; +const void* pointer_455 = &i; +const void* pointer_456 = &i; +const void* pointer_457 = &i; +const void* pointer_458 = &i; +const void* pointer_459 = &i; +const void* pointer_460 = &i; +const void* pointer_461 = &i; +const void* pointer_462 = &i; +const void* pointer_463 = &i; +const void* pointer_464 = &i; +const void* pointer_465 = &i; +const void* pointer_466 = &i; +const void* pointer_467 = &i; +const void* pointer_468 = &i; +const void* pointer_469 = &i; +const void* pointer_470 = &i; +const void* pointer_471 = &i; +const void* pointer_472 = &i; +const void* pointer_473 = &i; +const void* pointer_474 = &i; +const void* pointer_475 = &i; +const void* pointer_476 = &i; +const void* pointer_477 = &i; +const void* pointer_478 = &i; +const void* pointer_479 = &i; +const void* pointer_480 = &i; +const void* pointer_481 = &i; +const void* pointer_482 = &i; +const void* pointer_483 = &i; +const void* pointer_484 = &i; +const void* pointer_485 = &i; +const void* pointer_486 = &i; +const void* pointer_487 = &i; +const void* pointer_488 = &i; +const void* pointer_489 = &i; +const void* pointer_490 = &i; +const void* pointer_491 = &i; +const void* pointer_492 = &i; +const void* pointer_493 = &i; +const void* pointer_494 = &i; +const void* pointer_495 = &i; +const void* pointer_496 = &i; +const void* pointer_497 = &i; +const void* pointer_498 = &i; +const void* pointer_499 = &i; +const void* pointer_500 = &i; +const void* pointer_501 = &i; +const void* pointer_502 = &i; +const void* pointer_503 = &i; +const void* pointer_504 = &i; +const void* pointer_505 = &i; +const void* pointer_506 = &i; +const void* pointer_507 = &i; +const void* pointer_508 = &i; +const void* pointer_509 = &i; +const void* pointer_510 = &i; +const void* pointer_511 = &i; +const void* pointer_512 = &i; +const void* pointer_513 = &i; +const void* pointer_514 = &i; +const void* pointer_515 = &i; +const void* pointer_516 = &i; +const void* pointer_517 = &i; +const void* pointer_518 = &i; +const void* pointer_519 = &i; +const void* pointer_520 = &i; +const void* pointer_521 = &i; +const void* pointer_522 = &i; +const void* pointer_523 = &i; +const void* pointer_524 = &i; +const void* pointer_525 = &i; +const void* pointer_526 = &i; +const void* pointer_527 = &i; +const void* pointer_528 = &i; +const void* pointer_529 = &i; +const void* pointer_530 = &i; +const void* pointer_531 = &i; +const void* pointer_532 = &i; +const void* pointer_533 = &i; +const void* pointer_534 = &i; +const void* pointer_535 = &i; +const void* pointer_536 = &i; +const void* pointer_537 = &i; +const void* pointer_538 = &i; +const void* pointer_539 = &i; +const void* pointer_540 = &i; +const void* pointer_541 = &i; +const void* pointer_542 = &i; +const void* pointer_543 = &i; +const void* pointer_544 = &i; +const void* pointer_545 = &i; +const void* pointer_546 = &i; +const void* pointer_547 = &i; +const void* pointer_548 = &i; +const void* pointer_549 = &i; +const void* pointer_550 = &i; +const void* pointer_551 = &i; +const void* pointer_552 = &i; +const void* pointer_553 = &i; +const void* pointer_554 = &i; +const void* pointer_555 = &i; +const void* pointer_556 = &i; +const void* pointer_557 = &i; +const void* pointer_558 = &i; +const void* pointer_559 = &i; +const void* pointer_560 = &i; +const void* pointer_561 = &i; +const void* pointer_562 = &i; +const void* pointer_563 = &i; +const void* pointer_564 = &i; +const void* pointer_565 = &i; +const void* pointer_566 = &i; +const void* pointer_567 = &i; +const void* pointer_568 = &i; +const void* pointer_569 = &i; +const void* pointer_570 = &i; +const void* pointer_571 = &i; +const void* pointer_572 = &i; +const void* pointer_573 = &i; +const void* pointer_574 = &i; +const void* pointer_575 = &i; +const void* pointer_576 = &i; +const void* pointer_577 = &i; +const void* pointer_578 = &i; +const void* pointer_579 = &i; +const void* pointer_580 = &i; +const void* pointer_581 = &i; +const void* pointer_582 = &i; +const void* pointer_583 = &i; +const void* pointer_584 = &i; +const void* pointer_585 = &i; +const void* pointer_586 = &i; +const void* pointer_587 = &i; +const void* pointer_588 = &i; +const void* pointer_589 = &i; +const void* pointer_590 = &i; +const void* pointer_591 = &i; +const void* pointer_592 = &i; +const void* pointer_593 = &i; +const void* pointer_594 = &i; +const void* pointer_595 = &i; +const void* pointer_596 = &i; +const void* pointer_597 = &i; +const void* pointer_598 = &i; +const void* pointer_599 = &i; +const void* pointer_600 = &i; +const void* pointer_601 = &i; +const void* pointer_602 = &i; +const void* pointer_603 = &i; +const void* pointer_604 = &i; +const void* pointer_605 = &i; +const void* pointer_606 = &i; +const void* pointer_607 = &i; +const void* pointer_608 = &i; +const void* pointer_609 = &i; +const void* pointer_610 = &i; +const void* pointer_611 = &i; +const void* pointer_612 = &i; +const void* pointer_613 = &i; +const void* pointer_614 = &i; +const void* pointer_615 = &i; +const void* pointer_616 = &i; +const void* pointer_617 = &i; +const void* pointer_618 = &i; +const void* pointer_619 = &i; +const void* pointer_620 = &i; +const void* pointer_621 = &i; +const void* pointer_622 = &i; +const void* pointer_623 = &i; +const void* pointer_624 = &i; +const void* pointer_625 = &i; +const void* pointer_626 = &i; +const void* pointer_627 = &i; +const void* pointer_628 = &i; +const void* pointer_629 = &i; +const void* pointer_630 = &i; +const void* pointer_631 = &i; +const void* pointer_632 = &i; +const void* pointer_633 = &i; +const void* pointer_634 = &i; +const void* pointer_635 = &i; +const void* pointer_636 = &i; +const void* pointer_637 = &i; +const void* pointer_638 = &i; +const void* pointer_639 = &i; +const void* pointer_640 = &i; +const void* pointer_641 = &i; +const void* pointer_642 = &i; +const void* pointer_643 = &i; +const void* pointer_644 = &i; +const void* pointer_645 = &i; +const void* pointer_646 = &i; +const void* pointer_647 = &i; +const void* pointer_648 = &i; +const void* pointer_649 = &i; +const void* pointer_650 = &i; +const void* pointer_651 = &i; +const void* pointer_652 = &i; +const void* pointer_653 = &i; +const void* pointer_654 = &i; +const void* pointer_655 = &i; +const void* pointer_656 = &i; +const void* pointer_657 = &i; +const void* pointer_658 = &i; +const void* pointer_659 = &i; +const void* pointer_660 = &i; +const void* pointer_661 = &i; +const void* pointer_662 = &i; +const void* pointer_663 = &i; +const void* pointer_664 = &i; +const void* pointer_665 = &i; +const void* pointer_666 = &i; +const void* pointer_667 = &i; +const void* pointer_668 = &i; +const void* pointer_669 = &i; +const void* pointer_670 = &i; +const void* pointer_671 = &i; +const void* pointer_672 = &i; +const void* pointer_673 = &i; +const void* pointer_674 = &i; +const void* pointer_675 = &i; +const void* pointer_676 = &i; +const void* pointer_677 = &i; +const void* pointer_678 = &i; +const void* pointer_679 = &i; +const void* pointer_680 = &i; +const void* pointer_681 = &i; +const void* pointer_682 = &i; +const void* pointer_683 = &i; +const void* pointer_684 = &i; +const void* pointer_685 = &i; +const void* pointer_686 = &i; +const void* pointer_687 = &i; +const void* pointer_688 = &i; +const void* pointer_689 = &i; +const void* pointer_690 = &i; +const void* pointer_691 = &i; +const void* pointer_692 = &i; +const void* pointer_693 = &i; +const void* pointer_694 = &i; +const void* pointer_695 = &i; +const void* pointer_696 = &i; +const void* pointer_697 = &i; +const void* pointer_698 = &i; +const void* pointer_699 = &i; +const void* pointer_700 = &i; +const void* pointer_701 = &i; +const void* pointer_702 = &i; +const void* pointer_703 = &i; +const void* pointer_704 = &i; +const void* pointer_705 = &i; +const void* pointer_706 = &i; +const void* pointer_707 = &i; +const void* pointer_708 = &i; +const void* pointer_709 = &i; +const void* pointer_710 = &i; +const void* pointer_711 = &i; +const void* pointer_712 = &i; +const void* pointer_713 = &i; +const void* pointer_714 = &i; +const void* pointer_715 = &i; +const void* pointer_716 = &i; +const void* pointer_717 = &i; +const void* pointer_718 = &i; +const void* pointer_719 = &i; +const void* pointer_720 = &i; +const void* pointer_721 = &i; +const void* pointer_722 = &i; +const void* pointer_723 = &i; +const void* pointer_724 = &i; +const void* pointer_725 = &i; +const void* pointer_726 = &i; +const void* pointer_727 = &i; +const void* pointer_728 = &i; +const void* pointer_729 = &i; +const void* pointer_730 = &i; +const void* pointer_731 = &i; +const void* pointer_732 = &i; +const void* pointer_733 = &i; +const void* pointer_734 = &i; +const void* pointer_735 = &i; +const void* pointer_736 = &i; +const void* pointer_737 = &i; +const void* pointer_738 = &i; +const void* pointer_739 = &i; +const void* pointer_740 = &i; +const void* pointer_741 = &i; +const void* pointer_742 = &i; +const void* pointer_743 = &i; +const void* pointer_744 = &i; +const void* pointer_745 = &i; +const void* pointer_746 = &i; +const void* pointer_747 = &i; +const void* pointer_748 = &i; +const void* pointer_749 = &i; +const void* pointer_750 = &i; +const void* pointer_751 = &i; +const void* pointer_752 = &i; +const void* pointer_753 = &i; +const void* pointer_754 = &i; +const void* pointer_755 = &i; +const void* pointer_756 = &i; +const void* pointer_757 = &i; +const void* pointer_758 = &i; +const void* pointer_759 = &i; +const void* pointer_760 = &i; +const void* pointer_761 = &i; +const void* pointer_762 = &i; +const void* pointer_763 = &i; +const void* pointer_764 = &i; +const void* pointer_765 = &i; +const void* pointer_766 = &i; +const void* pointer_767 = &i; +const void* pointer_768 = &i; +const void* pointer_769 = &i; +const void* pointer_770 = &i; +const void* pointer_771 = &i; +const void* pointer_772 = &i; +const void* pointer_773 = &i; +const void* pointer_774 = &i; +const void* pointer_775 = &i; +const void* pointer_776 = &i; +const void* pointer_777 = &i; +const void* pointer_778 = &i; +const void* pointer_779 = &i; +const void* pointer_780 = &i; +const void* pointer_781 = &i; +const void* pointer_782 = &i; +const void* pointer_783 = &i; +const void* pointer_784 = &i; +const void* pointer_785 = &i; +const void* pointer_786 = &i; +const void* pointer_787 = &i; +const void* pointer_788 = &i; +const void* pointer_789 = &i; +const void* pointer_790 = &i; +const void* pointer_791 = &i; +const void* pointer_792 = &i; +const void* pointer_793 = &i; +const void* pointer_794 = &i; +const void* pointer_795 = &i; +const void* pointer_796 = &i; +const void* pointer_797 = &i; +const void* pointer_798 = &i; +const void* pointer_799 = &i; +const void* pointer_800 = &i; +const void* pointer_801 = &i; +const void* pointer_802 = &i; +const void* pointer_803 = &i; +const void* pointer_804 = &i; +const void* pointer_805 = &i; +const void* pointer_806 = &i; +const void* pointer_807 = &i; +const void* pointer_808 = &i; +const void* pointer_809 = &i; +const void* pointer_810 = &i; +const void* pointer_811 = &i; +const void* pointer_812 = &i; +const void* pointer_813 = &i; +const void* pointer_814 = &i; +const void* pointer_815 = &i; +const void* pointer_816 = &i; +const void* pointer_817 = &i; +const void* pointer_818 = &i; +const void* pointer_819 = &i; +const void* pointer_820 = &i; +const void* pointer_821 = &i; +const void* pointer_822 = &i; +const void* pointer_823 = &i; +const void* pointer_824 = &i; +const void* pointer_825 = &i; +const void* pointer_826 = &i; +const void* pointer_827 = &i; +const void* pointer_828 = &i; +const void* pointer_829 = &i; +const void* pointer_830 = &i; +const void* pointer_831 = &i; +const void* pointer_832 = &i; +const void* pointer_833 = &i; +const void* pointer_834 = &i; +const void* pointer_835 = &i; +const void* pointer_836 = &i; +const void* pointer_837 = &i; +const void* pointer_838 = &i; +const void* pointer_839 = &i; +const void* pointer_840 = &i; +const void* pointer_841 = &i; +const void* pointer_842 = &i; +const void* pointer_843 = &i; +const void* pointer_844 = &i; +const void* pointer_845 = &i; +const void* pointer_846 = &i; +const void* pointer_847 = &i; +const void* pointer_848 = &i; +const void* pointer_849 = &i; +const void* pointer_850 = &i; +const void* pointer_851 = &i; +const void* pointer_852 = &i; +const void* pointer_853 = &i; +const void* pointer_854 = &i; +const void* pointer_855 = &i; +const void* pointer_856 = &i; +const void* pointer_857 = &i; +const void* pointer_858 = &i; +const void* pointer_859 = &i; +const void* pointer_860 = &i; +const void* pointer_861 = &i; +const void* pointer_862 = &i; +const void* pointer_863 = &i; +const void* pointer_864 = &i; +const void* pointer_865 = &i; +const void* pointer_866 = &i; +const void* pointer_867 = &i; +const void* pointer_868 = &i; +const void* pointer_869 = &i; +const void* pointer_870 = &i; +const void* pointer_871 = &i; +const void* pointer_872 = &i; +const void* pointer_873 = &i; +const void* pointer_874 = &i; +const void* pointer_875 = &i; +const void* pointer_876 = &i; +const void* pointer_877 = &i; +const void* pointer_878 = &i; +const void* pointer_879 = &i; +const void* pointer_880 = &i; +const void* pointer_881 = &i; +const void* pointer_882 = &i; +const void* pointer_883 = &i; +const void* pointer_884 = &i; +const void* pointer_885 = &i; +const void* pointer_886 = &i; +const void* pointer_887 = &i; +const void* pointer_888 = &i; +const void* pointer_889 = &i; +const void* pointer_890 = &i; +const void* pointer_891 = &i; +const void* pointer_892 = &i; +const void* pointer_893 = &i; +const void* pointer_894 = &i; +const void* pointer_895 = &i; +const void* pointer_896 = &i; +const void* pointer_897 = &i; +const void* pointer_898 = &i; +const void* pointer_899 = &i; +const void* pointer_900 = &i; +const void* pointer_901 = &i; +const void* pointer_902 = &i; +const void* pointer_903 = &i; +const void* pointer_904 = &i; +const void* pointer_905 = &i; +const void* pointer_906 = &i; +const void* pointer_907 = &i; +const void* pointer_908 = &i; +const void* pointer_909 = &i; +const void* pointer_910 = &i; +const void* pointer_911 = &i; +const void* pointer_912 = &i; +const void* pointer_913 = &i; +const void* pointer_914 = &i; +const void* pointer_915 = &i; +const void* pointer_916 = &i; +const void* pointer_917 = &i; +const void* pointer_918 = &i; +const void* pointer_919 = &i; +const void* pointer_920 = &i; +const void* pointer_921 = &i; +const void* pointer_922 = &i; +const void* pointer_923 = &i; +const void* pointer_924 = &i; +const void* pointer_925 = &i; +const void* pointer_926 = &i; +const void* pointer_927 = &i; +const void* pointer_928 = &i; +const void* pointer_929 = &i; +const void* pointer_930 = &i; +const void* pointer_931 = &i; +const void* pointer_932 = &i; +const void* pointer_933 = &i; +const void* pointer_934 = &i; +const void* pointer_935 = &i; +const void* pointer_936 = &i; +const void* pointer_937 = &i; +const void* pointer_938 = &i; +const void* pointer_939 = &i; +const void* pointer_940 = &i; +const void* pointer_941 = &i; +const void* pointer_942 = &i; +const void* pointer_943 = &i; +const void* pointer_944 = &i; +const void* pointer_945 = &i; +const void* pointer_946 = &i; +const void* pointer_947 = &i; +const void* pointer_948 = &i; +const void* pointer_949 = &i; +const void* pointer_950 = &i; +const void* pointer_951 = &i; +const void* pointer_952 = &i; +const void* pointer_953 = &i; +const void* pointer_954 = &i; +const void* pointer_955 = &i; +const void* pointer_956 = &i; +const void* pointer_957 = &i; +const void* pointer_958 = &i; +const void* pointer_959 = &i; +const void* pointer_960 = &i; +const void* pointer_961 = &i; +const void* pointer_962 = &i; +const void* pointer_963 = &i; +const void* pointer_964 = &i; +const void* pointer_965 = &i; +const void* pointer_966 = &i; +const void* pointer_967 = &i; +const void* pointer_968 = &i; +const void* pointer_969 = &i; +const void* pointer_970 = &i; +const void* pointer_971 = &i; +const void* pointer_972 = &i; +const void* pointer_973 = &i; +const void* pointer_974 = &i; +const void* pointer_975 = &i; +const void* pointer_976 = &i; +const void* pointer_977 = &i; +const void* pointer_978 = &i; +const void* pointer_979 = &i; +const void* pointer_980 = &i; +const void* pointer_981 = &i; +const void* pointer_982 = &i; +const void* pointer_983 = &i; +const void* pointer_984 = &i; +const void* pointer_985 = &i; +const void* pointer_986 = &i; +const void* pointer_987 = &i; +const void* pointer_988 = &i; +const void* pointer_989 = &i; +const void* pointer_990 = &i; +const void* pointer_991 = &i; +const void* pointer_992 = &i; +const void* pointer_993 = &i; +const void* pointer_994 = &i; +const void* pointer_995 = &i; +const void* pointer_996 = &i; +const void* pointer_997 = &i; +const void* pointer_998 = &i; +const void* pointer_999 = &i; diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so new file mode 100755 index 0000000000000000000000000000000000000000..6ce6d0cd22dba72959ffa1fb7f9abacc917c28bc GIT binary patch literal 93210 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¯hsM;W*lZM%@E8K%sQH37+H$s;}V(?m<*Ug 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 MrM05kwWzcBKMZ=B{{R30 literal 0 HcmV?d00001 diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so new file mode 100755 index 0000000000000000000000000000000000000000..7cfdd609451d207f835362b2262be27e69206d8e GIT binary patch literal 89126 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 literal 0 HcmV?d00001 diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so new file mode 100755 index 0000000000000000000000000000000000000000..945b450e3ae1706dea37589b1b1708f8492994ca GIT binary patch literal 134131 zcmeF)f3#y&eLws&GX%m9F#=*lg@AzJ*O27q#|a`vK!{W^BG8CGCNnTY28SWc3>Z)_ zB4R+rKn0^Bjc7Ger70qf)MBJcQ=~MaVx&s1i1bP=MpSx5)aQKWeBN_EYba@ ze>`i=T`;-#m7RS)+2?ci-bqf*e(!5vf5P%*%RKz7@czM*zecM=Dd`>ky_d?AJl~6W zB`@y1aBJC!mU+&bHLAGX`Ur39zdHtZMK^}`M>mG`Sa=(r6Z;d#pgmd7RZaS44K zZU-7BZlhr}-kW~DQ(F3Z(O}${l#nb&(&MjHrH?QHmqNB=E}|I zd8^i*wR+3y;J!7RH?P{fW%Z^l_V-n*^sluW&)WLC-Uc^P!=|+x&O3AUmi6atT-8{8 z<~!GJTD4{E<}ItFMyXqWVw$%rbVuWOi|T%WKfOA9K#X{tc!GG6c#61ByhOZAyh6N6 zyhglEyg|H4yhXfCyhFT8yhprGd_a6id_;Uqd_sIme4h9M@fq<&;@iuMw{kZxC-1 zZxL@3?-1`2?-B149}piB9}yoDpAerCpC`URd`5hcxVMGmKk*pxIPnDWB=Hn+pLmIQ znRtbGm3WPKop^(IlX#1Gn|OzKmw1nOpZI|IkobuBnD~VFl=wXH1>!T}i^RPPNd6O# z5swp35Kj_M5%-Cgh?j|1h*yc%h}VfXh&PG1h_{J%hwX+h);>n z6JH=cBfd!7yO88R@fh(q@dWWC@f2~Nc!_wKc!hYCc#U|Sc!PM8c#C+Oc!zkGc#n9W z_<;D3_=xzJ_=NbB_&o6i;xpol#JzWs{3jkG9w(k4o+O?k?h`K&FB7j2uM)2juM=+& zZxU}2Zxinj?-K73?-L&o9}*uC9}}MtpAw%ZzCe6Ne37`7J~Mm$bDK|D!3McgM| zB3>q5Azme3BVH%oAl@Y2BHkw6A>Jk4Bi<)IAU-5MB0eTQAwDHOPke#+jQApP?;?`_ z#AC$c#1q7m#8bq5;w9o`;uYdm;x*!R;tk?W;w|EB;vM2$;yvPh;sfGC;v?c?;uGRi z;`788h|h>G68A19`AE5xhBYsBls z8^oK$Tg2PMJH)%hd&K+12gHZON5sd(C&Z`3=ZP;6pAlaq?!AZPKk*pxIPnDWB=Hn+ zpLmIQnRtbGm3WPKop^(IlX#1Gn|OzKmw1nOpZI|IkobuBnD~VFl=wXH1>!T}i^RQ4 zNd6O#5swp35Kj_M5%-Cgh?j|1h*yc%h}VfXh&PG1h_{J%hwX+ zh);>n6JH=cBfd!7yOiWV@fh(q@dWWC@f2~Nc!_wKc!hYCc#U|Sc!PM8c#C+Oc!zkG zc#n9W_<;D3_=xzJ_=NbB_&o6i;xpol#J%^D{3jkG9w(k4o+O?k?h`K&FB7j2uM)2j zuM=+&ZxU}2Zxinj?-K73?-L&o9}*uC9}}MtpAw%ZzCe6Ne37`q5Azme3BVH%oAl@Y2BHkw6A>Jk4Bi<)IAU-5MB0eTQAwDHOPke#+jQApP z?|mfyiN}b?i6@9BiKmGB#7o4>#4E(B#B0Rs#2dt$#9PGM#5=^h#Cyd1#0SKO#7D%( z#3#h3#OH}G5T6lWB<@{C@}GE&c$|2Gc#?RExKF%9yiB}8yh^-AyiUA9yh*%8yiL4A zyi2@Cyia^Ud`Nsmd`x^od`f(t_yX}6@kQd^`$_&2j}ea(PY_QMPZ9Ttmxz~%SBO`M z*NE4NH;6Zhw}`iicZhe1_lWn24~P$mkBE!T} zi^RPTko+eeBOWK7Af6#4E(B#B0Rs#2dt$#9PGM#5=^h#Cyd1#0SKO z#7D%(#3#h3#OH}G5T6lWB<@{B@}GE&c$|2Gc#?RExKF%9yiB}8yh^-AyiUA9yh*%8 zyiL4Ayi2@Cyia^Ud`Nsmd`x^od`f(t_yX}6@kQd^he-Ytj}ea(PY_QMPZ9Ttmxz~% zSBO`M*NE4NH;6Zhw}`iicZhe1_lWn24~P$mkBEWHt`PeF7Y1mKJfwZA@LFMG4ToUDe-yY z3&dx{7m0fxA^A@{Mm$bDK|D!3McgM|B3>q5Azme3BVH%oAl@Y2BHkw6A>Jk4Bi<)I zAU-5MB0eTQAwDHOPke#+jQApP?`o3&#AC$c#1q7m#8bq5;w9o`;uYdm;x*!R;tk?W z;w|EB;vM2$;yvPh;sfGC;v?c?;uGRi;`788h|h>G68H4F^3S^dGw~SlIPnDWB=Hn+ zpLmJ*!g=9wy*#+CyS;}0%sAEnkSn{--u;a)c08~1f+tS=eeAMVUz1qA-vj$4R{Q|} zZ@ury;J%x}`?mi5RPgs*;omLK8wAx{^W>lZ{GvYwb^URLcTLIjZu7iXKk&zQf3rVY zvFd@JuJCTYEG*YKHYn?*cDm;ApnUMS?LXUFV0#N}Z-MPCu)PJgx4`xm*xmx$TVQ(& zY;S>WEfBt%@w}I*?5?+~^ftDwWcbUA^>3`>_7eQ9xBKWV*1viw{?=Q(W~%Cju{PRF z7JdtUu!h@2@5ef7?+fqtWUV#%iHA2H)=Zn=^_O}|Z}-w$ye7koqRQ&<4tP^5!a8o# zw}EwH@>wzt6c7TDeb+go6J3v6$J?Jcmq1-7@q_7>RQ z0^3_)dkbuDf$c4@y#=pPl;du-=jo_Kj@ztl{r19voj~yQTzAiZJqO4~MEMM;}+f`0m*m=d% z`eFWxT=3@(J4DuR+~P&94{Bfb{5Nea;OVMcJ4XL9_mD8vGcZfdlQNb65SOU%j;~|!Trv&GWEPHW? zSWY$acENo@^mL8MNL}zHAx43l1z#Fs40x&F%R-C;uN2%j#02nTg8PM-1pc*PBE%H% zX2JbK^nqU$JRrmpa4|S5X4%U_#6pab74qB<3=yj^Ms^oGC`2s57@+9&*!IMM8dM6R@zyRME zA{IM|>?&9Z5i6ZU_7;3oh*;(%vcKRdA$q#@L*#J5H-{Jn76eZXF$Vn8K>?l?B9^#_ z?0j&5r-z93Eh2jf{$+?*+#<4{U^PUnY!NwB@GT)?S&K+c@Qe_#rbVPA_|_1yphe^_ z1m6~-r&sfdoG$owMZLs-WVPTsLW}}q$pBY{=tO8FIJS)T+@D#zdA=ZI!7hD%&16UV4JH#e%vtT{M7VwHg0$d*=F54ZsM(~^v zan0_?4TA3s5f|)^d_izSh`3sJu`hZVD0C=7>D>!T>ji*gSt(FlF$($YX+A6!j_wktYQ&2oaZ%h%Dbb zzzajf^&=v?3%)BvTs$JOk6<%ITsb0gu;4`@;<6EujNrvuQrA$A93%K{MZ|fM;CuAp zz*Bu_Mcab8wek z86v)h9ob#*su1xV?8rWX9|{p)z>XX&_~8)o?dwQJ@FOAOtJjfZ1g{RUb)g1-lHf-} zYy(dh?1b0>j$~?dO^9{!A>tdnk%ZtULhPgD;ewwGaR6K=Q=?CX7;R{SY!bX7#2D~W!B2-6 z2VNz3V~7dhO|pysI>aRK7QtSKDc~J~p9#?i-Ys}jh$Z0tf}ahs417rNb0JoMN6Xac zZ$g{`%YvT|aUOW8;1@z%0M-QiA};>!j9HpD2fBlx8dW562( zzZ_y5*cZGd#02nGnHv3Fh<)Ilf`bqTz!nB z@LnycSAULdkg3tXX`L77K57cyrzL@x3;w&-3G4_?LaYLB5d3b4HDF)x{t)ZH+XTNC zVgoo5{C(iS;X@^c#EBJwjY_@fX#B-pWn4~7^8 zo-FtuA;y4b2>v+4IB=cdPeM!pHwiuzBGz4tTq^jVA*N9BD#7^>W0 zoQ2o|J}LO?5Zl1z(#TJQSi#Y;yWnp^?4sm8g1-&1ijq&uGx}YKxMX(Z5y{UdL+l_w zXM+E$bpoFjTnw=XjJ_hk--n3HZAbPJd@95NN+tyVpe2Ea3qBnpuF4(p1^*c07$uJv z{F9J<8zXjF9``dfLev9oGfC-G5FcHu6n73glFyQ)^9anapW zYtRElpQl=frbKsFZ9tC}eZFcFS{99|wxFkqzCg7Nt%>fT+JQz73v^G_vc9nw-Ai;Y z)e1Br`a;zz^l;I=RclaR^hK(5=<%X))dsX8`eM~4^lhU1sJ5VW(U+)}_2pc2i|9*L zE6^iX2KqA9$wlF46@8`XzN%B`iK6?d&O_fUnowPUt`gl}bq0N>=mDyW(07TxT-Dc$ zC`I2ddZ20v`cct?RLjs$i5{$4f&S-_fxZIu>YIJh{}xT6Ug-ad9)fzID}wjnmmP|F zq0bjROtlJqiRj^~HRvluk5H{c=R{YkHlRTd5WHQg+Jx>Vno`9jYNGN=b6HxouW$QB zyP_G@0rYdCbE-q=Ks2j5f__~zr#gm?Mf0i?==Vhns#EB^sINK?{iSG8bpg64dX#Eg zClS$yBZ0nBwS%l~N&X(K+J$~t^i`@oXjk+Y)jsrdqOVpRKnJ2F)gkojqOVaMLC2!U zs*a)G7d=k3h9hfU^mx@4p4%Ne2l`soDl+v8lD{XQ2IyBrPgJc#m+um2S+xQEhLnAs zY7_b$(UVkL&>xEag=!o6Gtt+pR`dgz=&wZIpjw6gLG)zR8Z`5~K;Nj^LjDFXHG}t4 zRol>Dz7ULgsvT%>SOo8=s&=7Ai@sU42Q7=9s@jL1Dtemg09q40U3CcEAo`c8BWP2! zsyc=q`}{!PqB=wV?yx-2GgKF$G10fGdb%il^rfP2Q;kBCqHkA?L9?RoP>n-NqN`LB z(ASIBRFlxE=xWsz^eoXesy_6p6@i|q8rL`6qt}a`rJ8`=B)V2L3B5&hooWhthv?a= zKJ;$Ux@rk}zvz0^GV~$Qb5twP$3)+$T7^C-xcnt<-OW1ttQCZT(XUaXpe?koCkRUdk&=zCO4(7fm+s%7Z0qL->xpeKvI zSG5W~L$sw@gYFUy^nI!weLFt7x9DZ6UFZR#?^o?XSBhS)+J_z``T^Ae^aRl>REN-0 zL_erHg1$qvtvZIDBYLIk1bU(9RjO0y-7*GzNHwZ2;G*}7epodIeMs~ps&VLJqF1XX zpihc^R5b~GK=PrZnu6{wqsTR?K6D?^YgJ3ogGK*JwG7ROeoS?Sx$@P!26`Rp)dk_A zpA@|w^+G={`f=0?y;ZcUT7ljv`U%x4^d8Yqs@9+nh<-}74t-ek2Gs`ianVn!Hla_6 z-l&>Fzka%W`taAP9gOJfB!7FVb>#0R(a)$hpqGl?q}qgDCHh&_7W8`2&#AVdH;Mj@ zY6p6Y=;u|t&^tuGpxT4pE!tP@L)XZ=_Fq)>^;K;2GRfbYRp*hvSBw6w>H_qWqF+*- zK|e40Wz|LKt)jQ6dir@)^iI*gQ;kCJ5gn+;pbv=ty=ol#u;{I-3FxIVGJHj~g8V&3 z^7l5?D)c1Lud3Fdr;FaMT8FL?{RhcMM)MEV~o+LiZ5;2I__GD>_2G&_hMPsoI0)MekC@@|MwKMgLiK zfU+lxeoJ)-Jwx={sw3z+(YsZ#tY!2VnNR+UYF*zJik>7oR&78}7yVb&CUlMHJ*q9} zM$zx6wxJh`-mBVyULpE#s$J-{qW7uxpf`&CyJ{bLv*<*106k^*K)I8s?-0FT zH4Z&T^n0oa=!K%+S4~1M6a9f|3VOBZ1FAmslcGOVEkQpoI#n%0Zx#KKY6W_y=!2?N z=sKAz|A*=fx=HlMs*BJ|MSr5|=>l5Ot3)4CjY6*%{ZG{x^d`}H)j0GP(Vwa&pm&Hq zteS-0E&4On6!d=4M^t_2eX&4)u8IZ4q7RBbs*2^rqK}IHmns$$i#{Q`po*o$qJI+o zg(?;ji|+D*Kp#`Za#GQ~MgLnhs;6m04-oyOY7Dwk^nX<2(4#~jSH*Hv(L?tL^jE4_ zj4GNJovC6es_3zzzgERURMC?~pHRgzRM9g;f1`>;sG{pcf2)cmsG^%hf2WECsG^sO zKBY+s!8Z6qR&@NLEj-7Q}vq5H@y%NMD(bn%?%O_INH z)hP1!7SR{0#-MkI?xPxq-YxnP)dci@(U+>br zY8m=<`TS#l)fw~&DSLqGBJ@w9FIV-@4!Z=GYg=}pY81M+=s~J6=mDY!tHz-#MPH$s zfF329R82xp5IsaS1wBRdP*orLfaqbWZRo?IhpTp=kBc6m+J!zPx>B_V-BC`sI#RU{ z-9t2`I)LsgnpPb`4;9U*j-YwbIn^=rSkbKN1iDT%r-}=oM>mP)RXy}Omx>ltqtL5F zebpHBdeNe49D0-JQK|{(EuybfO+xPwJz6ydy<7BEsy_67(PLCg(3=v0zFM^p{pDeS zmQ)AOefAIZHL64C{qoxBSk)19jl3>APIV03D0;l=1bVUPYgMPvD@0FForhj4dZOwA z^hVJ#>P2tA;m|-|r`pr=(4ul>`ei4n_Mx{)*}qU7K<|+=(O$1Qggzkp2GtStVbPOS z$I!<`->5o)J|$XFokDk%Gmze-IuG4L^c2+v=$&%3yjgXO_p9#_JymrA-CdsXX{u9b zM)Y*mdFV!Y&HI1XQ$Ovhp6{>(bcLWXhn35>KOVq(KA&i(7NbZs#E9| z(Y31c(3a>r)iypCxmiZ^vsGuv-#B>;+eO!_df4-KiJqgn^)s(vIuN~2^qs0P zlzmWigK8Z5sOY(>3Fs4|8 gKZ%~Fnu6{k`P)$Sp=)H!I$yPfcfa)l}^P*c+J!I6eq8F$}p(l%8s2YQwA^I-WICPz8Q#AqIBzlo*5_+lV#i}Xj$?vNEM5kM87ZkVbug`m>2zsDi$<}{!;X6RV-%`eM-(L{itd~&$f*2C||AV zs5YT{h+d=Gg6=DNt!f*3sOVp*cA$CDkEwQ{$BJI3+Jl}fdcA5NdVuVqkE;%#XGqzu z>JU1T^E*GGn#6mr_lkZ}H3glDeoEDcJ|cR9Y6&_M{j_Qs`n2ecsugHdPJ{bv)hcu^ z(Vl7znh^brY8`sG=uN5(=#w%p`K)SE&s&S`E;EqNp)C5zgGK*F)koRurH0R|mY}Cg z*)OP;p|?oczG?+}hv*kotI)eeZ&s~A?-%`B)jISc(J!espjR9e=$BE0p6eF9QS=tn z0KHxG?@$ADc0iy5)h6_5(Z5%1L8C7Z^j6h2bT84bsCJ+U(c4tJ(8EQ)s@jA4qPMH| zp}QXv=s%!dJ(n$du;@^=hWGo95&atK-Bwnf@g1lani2g+)dBPv(XXoxp(ly{lj;b1 zy6By%W9S;uZ>Ub78%0N`7x6C0C&k}Hz4$y}QRd2bsZKHXjLG{C|BQN3wj#5YZ=o#o zZKB^+orl&%?^a!aHl*HvQJq0sQg*D`hkjV}UsXMfSzXb4R2Nb93OO#mqdJdIKpvC) zy;rq~{JmDn{+ntGx?J==)i!h>S@Q3y9q89ZC#qfOSoFK9J!nSiyk)le~22OZxfxWj-YkXAE}O^TSOmJoj_Zn|DigC zepvL!s`JpU=ucD^pq~?cNOcC?D96x$qF&_h#qx~jQSUaDvOh(=&}*ga!>AXUS`p~a zR2QK)OW8+MJ><^OQugPnQRrP#_EFUs^ghx5QjJ3&6kSkFK!0;Ypua%9$luRN{yv6! zp##zXM!nEKOAWt7z0k3g{U6mN^!uWZtEQmyqQ6r0p}!QJsg|INqQ6!xLw869`h;o) z`lRS@RA660rUn@Z^fHDdH;6^?Tao` z9YJrF*Rji0$Iy|KU7J&N^ji}B;9}(SAbpg6&r$Bd7UBs)pi$$ZV6X>;~ zJF8BiH;V3}IuE^BbXU~{=nm4`@1{C~-Y#XIr&_{mr@KUVSM_inX;Nx>Tt&+b-srHb+XN$g4wGZ7a zdbH{Q`d-mjsScqZ5fgER0q%lMNd~9LJyYF^e;JxjF(y-akiY8!gB=sML7^pm1zt9GHE7p<%Iptp*ySM5XZ6g@|E z0Nq>Ok$tD?7&&=>=mymZbfxIIs#EAuq8m{!UI9KNpYEKeI)pwZ+E5)qpA*yy}%6noLs8%s6K1%dL)f)5!(RZmfppVNra81=D&VhSM z^di+1bjO_oy;#+U?jiba)e>}H(f6p9p@)iIqFRCGMK4vYLT{FL_TH~$OZr&x(%T#-Kb$h$$`&IkUyF@Qn9YF6B{ebEa`k?3)sw3#5q90TpL!S_B zqXryrYxWQHO4T~{#YWMqRGZL?ML(oELT|Kbw?IFvI>9XSQqhm7E+BWV61`e=9%Zi= z{V2*pZxZdO&Y-u5UZc7Qy+ia`RS)k4oghccU#T{*FAkRc{g`SKni0KDb%uD45xrh@ z5qgs7$5lO?8+y8ES2YU#7a4g!p&Em3l(L^xjYGdDWk02wfL zh2A9kdDR~D9_c;5pgKaP{z>w;uR29m?;`WFFRIQ%_ZGcbbpd*S=-;Z&pesecq`C;b zMI!sMs)sx~LCW5u8in2_@4Ek;Y7F`gDLYV&Lm!pbIDfC2!rr`F^7mHMi~PM`^ed;irC#391bpd*YdiuU`53jQhkh0%WjY97c{kCci`he)&s&(kYqW_}WfIcodR&7F`68%@z7Ieqw2YQcc z8@h++cT_vjeMRq8?LrR~{WsMfG%tFeY9G4m%0T~Jb%Zn6UL-nE9YbF(`d!rt^dy-T z->=$+o-X=5)edxx==W8-(5vJV-5;p-pchNo2UPpe>!s`uRR_>(rR-F72(8PjgCD83 z@d{hsD_!=WY6mL)`?CK~?Lu#rbKrig+Q2z*cZ&W*wF$jP^dZ$2^a0WTRBb~a7M)k^ zKpz+VscIMcl<32%J?QN+2K-EQir46Oi9Vt_54}(H=c)_P-Q_jIqpB4=<9$T`OSK9; zSad(BMZz6b>`BkZFLq*`~`ohPT$1uXO`mkGfUg@GfUg@GfUg@ zGfUeFF0i|8TYhFKe&5EQU8=v3U8=K?U8<*8ScGRaj8tEXv-|c zpD8YFD^pyGKT}-VZl<^tf2Oz;f2O$9R&#Sp+nSqO+Sc6M(zfR2mbNuFx3sOfxutE* z%`I(fZfnWAWx3GVDqLU! zEvr^(4s!__b>;ID1=0Wiv~Ad@gH$H+#fn*Iag~_2x{b&1KCVFNY5=j$D+T~ zRz-iQt%@f8x!h8jx!h8jx!lrooXeT^%$W=~+nURn49}VN%$fGgEw!g_w(Fbi`ewVn z*{*N4>znQRX1l)Gu5Y&Mo9!0O{+csA=UmFPL*D*w+9Yq*!NgxM z@fS@mS2QC?(TpIz$p_!WIcG+wInyW1nGtHPu(ZGC3QPNIuCTPf<_b&uYp$@gzvc={ z`)jVSw7;_HrFO`sO>U%@wv|mUZ7Z8z+EzBbw5@DK zd(Of0 zZ{qh&{J!byd=tNK;`dGbzKOqR;xC%`izfb}iN9##FPP({XyPxJW2b21FPP)VH~TId zT$Ks4@i}w+&Y9zP&K$pUW(GB9W>9lx1~q49P;+JmHD_i}b7lsWH8ZHJnL%aE3@U48 zfLSwx%9HWH1QWr{BvfU&6;^pHf#DZGcU@Tc~REPi?U{3lr{6BteF>O&AcdU=S60_MYG+a z*>2Hnw`g*#XtrB0{YKGjw_y5^qS2Hnw`jIoG}|qj?H0{;=S0+T1tCb=LIBS_kzeU^sn7$%w`iHFP6S8KE&YCegYsToT8KbkN z56PM_I%~%0oEf8YW{l36F*;|)=$sj&b7qc_Gh=kl%rSCij4qn^izfb}iN9##FPiv^ zCjO#{KX1l*-}Kd4b1Y;{{%1|jXU$ldHDhJgjFnk4R%XpunKff&){K=oGgjuzSeY|p zWzHN^IWtz~%vhN-V`a{al{qt37ESy`6MxafUo`O-P5ea@f6>IBH^+X?KpEKiQ&Wy`BGd||bagj6QW8RE|Mf+GYHs#FNlrv*f&WuetGdAVS z*pxG4Q_hS{IWsop%-EDReP-H>1!*%Dq|I26He*5Bj0I^k7NpHskT!iq+KdHRv)!B- z3vy;G$eFP$XU2k@8Ow5JEXbSq^Ctehi9c`R&zt!3CjPvMKX2mCoA`Y*Ui)Ue_RV4~Wl(ZRB(q>Fan=vJA#+0-fQ_^NkNt-bxZN`+W$%mX7Q*vfZ$(b=F zXU3GA8B=n0Ofm82P5gNif8NBOH}U69{CN|9-o&3b@#jr@`euIRoB5S*=2zzFn(Q&j zV{*r&j>#O8I3{mQ+L)|Ko6#U`Mz*vW+0tfYOPi4`ZAP}V8QIciWJ{ZoEp0}&oEh12 zW@O8mku7INwwxK+a%N=9J!52BdIgy`@#jtac@ux$#Gg0u=S}>16Mx>sZw>~NOD2^} zCYeMsd1TVaWRXcClS9EumD}FqNSpCEZN}rY8IMirZI4@eXVAPaxb5Dh_XL;T5nOuz zYU$m;rS}5!CZ@cJDQ{xRo0#&ZAIh74C~x|qyy=JXLErO#_;Kx#|979(;=Li?%yoP- z*YVB#+Z+t$STLz%GRY*8$s?0SCW}lGnH)-+Sxwr^YSLy_lQy%Ow3*eU&8#MEW;JOu zt4W(#P1?+AiYCeOCWgF;A#Y;Hn;7yYhP;U(Z(_)s81g2Dyon)WMtqYMWS} z^QJv>raklK_kxMPVB#-4BmSk&D}3`h&o{60eDgZ5V6v%T+SVku$!(L`CbLaqo4ht@ zZL-=VwaMwU8Qs%nbWhvS-OMu6W|o;Yv&^)aWv0z6Gi_#>X*0{rn0Z3ROcXL^rjRjH zg^ZaiWXxnCV`d8(GhN7-`9j{rpEvR6P5gNif8NBOH}U69`~?$#!NgxM@fS?|1rvY4 z#9uJ+7fk#G6Mw-RVFh!970eN4lH267Nnvwzna<%E$JVw(-5yy>!>Bo~(q>#rn{g>^ z#-+4*UnOnkz-coFPMbM!+RTB|W)7S-bKuM~<}FLJgN&WI*r|)1yV%K#oxPZmC1d6< zc@ux$#Gg0u=S}>16Mx>spEvOrO#FFs5f^1rvY4#Gf&f@`C++ zX`=OvEp6NT=4deoi#b-zp<<2{bD)^xByD=awCM@crYB6Bd3D;%tJ7v)oi_98w3%0@ z&Ad8o=G7T97s{B)P{z!LGG;oIG4r8}nGj{nj3{HKL>V(D%A5G}CjPvMKWCnA-o&3b z@#jta1rvY4#GfotTAeiI890uknFb9D- z1~O*qlreLsjF~)T%uF>|Pb*>1sXH*cPP!E85g+M-~# zn>P(pFxxGd?H25I&C9NWd9h^<40BwVG%;CalFHMUwaA#cMaE1nGG=y>G1H5TnP22=FK1#Xm>3EshJuNqU}DJG-p|BPFfkNN3F$j2SmGX5h@2kuzgvr5Q7JX3VTKV@A(8Gb^1lGru`AE1ffA z{+t=}=ggQtXJ(~yW>z|9W~Djvyz(Z`3MT%%NxGb!m74eqCVt<<@0<926Tffb&znT` zP5gck|A{A`mN@RP!xGt*#YFIGBRIh;Q%EEmo7M)WF0Wm^dF>&IBhFnDobJ5-%;1*| z!D-DK)*i8T<5}xhZw&rky=MKZHZ=0t#1Utwg8#Sv_r~D2H*QWGv3bi`uR43<1&1A$ zPA867xAD9HHmyH%%Mq(LtY5u({l=j1x(nBDUcY92Q0Yals;@umthGUv>ng81PK(#i z+q5Ov)CK1Tm91O9al`tJYmeBl{@nFjUL_k`C#PH=v3c#8axS)%Sa-q3t-o8lVB@>i zZ#?UWEvwgTSSw|MDAqR5T-(57g1UWAx5D44V_m?5xe4uPUcm7+Za z%psgHQ-+K=Br|5xkTJ7{jF~oM%)B9ECJq@hbI6#f!KIs7wbK+BjxEMo?)j2Y51W=zkRK|Nze^-M6VpM2WuU+=Ble9^f!OW8k??lQCU0eM9Isy4*s#T0xn*rL_;=HJTfeNZa((a}g;lFJZCZVi zw=y`q*5mi!@vF~Wf2Ozc?DK-sVc9d!JNMkR8|6{_G$88+_?-+x-)LJZ=;EcW_Ywxio=?#|Pgi+4^%x@UJ+wKhs}u`@XQ) zwtuy{ZGYb#{3fyB@lB~L_`&V;?cv|J-`##XxZ6IyvN|k}+f?}H*7}35u|F&Rd|(oj z+$TRReLQYUTXpH5;0t40e{lQC;4l05kv<-`vYwm$LA%3V6Z|5N$MbtyACFtNy!-d- zg2(P2{IAXCMK?-q!SmGALVIn+;Gghb{N;J41-}Mg@Z0wIm_9zHkH_=GBi#BVTxmOO zKcSCL=;H^vLEMkWquz6Z$4U|0KRGXaz~sCDgP&A*v+Z9z9=B4=KHk&Ifq0GZA?4sI zl=cUYgIy5(B9C{sQ~qdJ;vf{%+c3JNcDyujm@Rs)K0aabIm&+IIZPh%5qeO``0b|h2S37q;UG@;X}>}Z?^sGmiB&wrKyQVabJWgc^M3 z&U-TW&LHv!{a^5v@8{fp=f~mW&pguB`t0pL^WJCvwIYZX{kXknMG!Ii=V$r~5B=^l zeTQxR=`%gzp`U!F@3^fWe5UW@phz9i66gncJl6B{mgrwzxB^&w{y6~XSZAE&)p8swcX&W?#~$y>WMn-DdX$1AnUPzaXg}Y z%qb6#6HgFN5>FBLiI<3%iC2i@GI`iLcwRM9zD~SByh*%8yiL4Ayi2@Cyia^Ud`Nsm zd`x^od`f(t_yX}6@kQdEUZ~pL?@{70;&I{$;<(JWyIox9+jU&(+jU&*+jU&-+jU&< z+jU&>+jU&@+jU&_+jU&{+w~@ChZgZR@ec7W@gDI$@d5E6@e%Pc@d@!M@p#4E(B#B0Rs#2dt$#9PGM#5=^h#Cyd1#0SKO z#7D%(#3#h3#OH}G5T6lWB<|@2xSy5(#AC$c#1q7m#8bq5;w9o`;uYdm;x*!R;tk?W z;w|EB;vM2$;yvPh;sfGC;v?c?;uGRi;`788h|h>G68H3y@XyMB;xXcJ;tAqO;wj=j z@e=Ve@e1)O@fz_u@doiG@fPtm@ec7W@gDI$@d5E6@e%Pc@d@!M@p#OuTx#GAxh#M{I> z#Jj|M#QVet#D~O3#K*)Z#HYmPi7ybJ5nm*ZlIQDE=TN^#|4R9$7OI`$EAv0#|3g-Z;^P~#5=^h#Cyd1#0SKO#7D%( z#3#h3#OH}G5T6lWB<|^gAntyN630?3Zh0)m;(CJAlO&EMS=@TCD2wYQQcsz9g?N>C zjd-1SgLsp8i+G!Ohj^EGk9eQ>fcTL3i1?WJg!q*BJn;qMGvbTHy=M5im77;l;xXcJ z;tAqO;wj=j@e=Ve@e1)O@fz_u@doiG@fPtm@ec7W@gDI$@d5E6@e%Pc@d@!M@p~p|^5#00!EwDxwp$}!C*C05B;F$4Cf*_5CEg?6Cq5uPBt9ZO zCO#oPB|cAlf%uI0B5_ZbsB-fmN<2n9PCP+8Njyc|Ctf06CSD<4C0-+5C*C05B;F$4 zCf*_5CEg?6Cq5uPBt9ZOCO#oPB|cAlf%uI0B5_Zb&U#k<6OR#(6HgFN5>FBLiI<3% ziC2hMiPwnNi8qKhiMNQiiFb&1iT8;2i4TYmiI0eniBE`6iO&;XAU-3$NZflbdH<7m zjCh=Qf_Rd6invd_M7&JALcB`6M!ZhELA*)4MZ8VCL%d78N4!sbKzvAiM0`wqLVQYm zp7;Xs8SzEpUW?>E@fh(q@dWWC@f2~Nc!_wKc!hYCc#U|Sc!PM8c#C+Oc!zkGc#n9W z_<;D3_=xzJ_=NbB_&o6i;xpol#IZE%v*tg#V5RF=p4D|M(CRvtXmuTnw7QPvK3(@o zJSE~~;uYdm;x*!R;tk?W;w|D>Fw|{_4k_Oy-Xq>8J|I3MJ|aFQJ|R9OK2Lms_>A}> zaZeZ2b=x^gJVrcDJV883JVo3mULsy5ULjs3UL#&7-XPv2-Xh*6-XY#4-Xq>8J|I3M zJ|aFQJ|R9OK2Lms_>A}>aqsXGePmp?& z#8bq5;w9o`;uYdm;x*!R;tk?W;w|EB;vM2$;yvPh;sfGC;v?c?;uGRi;`788h|h>G z68ChWcQG68Ekk^B>|d;&I{$;z{Bu;y&>b@iOrW@hb5e@jCGa@h0&W z@iy@e@h8J|I3MJ|aFQJ|R9OK2Lms_>A}>aj#ACpLmRT zoOptGl6Z=^PrO9DOuRz8O1wtAPP{?9NxVh8O}s#!Wh{uU1h$o4si2KA##LL7h#H++>#OuTx#GAxh#M{I>#Jj|M#QVet z#D~O3#K*)Z#HYmPi7ybJ5nm+kT}ASrc#L?Qc!GG6c#61ByhOZAyh6N6yhglEyg|H4 zyhXfCyhFT8yhprGd_a6id_;Uqd_sIme4h9M@fq<&;@*cy{u7T8j}uQ2PZCcN_lcLD z)0eK_=)SL2e$MjG^D4xv#B0Rs#2dt$#9PGM#5=^h#Cyd1#0SKO#7D%(#3#h3#OH}G z5T6lWB<_8f92dl6#N)&h#FNBR#C_r=;$`9$;#J}`;&tK;;!WZ$;%(v`;$7lB;(g)+ z;zQyi;$z|y;#1=D#21Lqh%XZNK0@-Jc#L?Qc!GG6c#61ByhOZAyh6N6yhglEyg|H4 zyhXfCyhFT8yhprGd_a6id_;Uqd_sIme4h9M@fq<&;@;IH|B1(l$B8G1CyA$s`@~Dc z%fu_htHf)>>%<$xo5Wkh+r&G>yTp6M`@{#thr~z3$HXVZr^M%pFA$#*UnK5*l;l70 z81Xpq1o0&C6mg$;$#Hod@Lbo2F9g2Kd!859?}uW&gJackePj5^>Q!ww_!)4$&MJJJ zdQ{K9g`ck;*Xt6%FHuLH!>?5L^>ZBfb?SIO2mYDBmwQXs+sFD1cWC*#UJnQ5?^eh6 zAK~|_$MpJ9@Q2iKy&d>t>bTAW{7H3Orv<(|NHb}FT(=6oyE@JNh z2Y5!ks{K6t7{X6*S>J>eY4!%Ym*VBNXt6tOV&cS7O%kzyn`6C}si(@=O z`D@fMABKNg9q)U?zo?$j>s`aYiu(0BFz|1x<2oMjdr@BBH-P_89p|CJe}?kJ4YRPpPN$dl@LdW3V`hv;)5P2Y&(FY5$k1H}w2olz)XfzDEqtsN;LG@MF|#dYxGK zN$PkV0Y6KI4hI|mCTNj~^a z{_LfW>jt2FLOto^;o<5-rygHD<-~csdeq6!H>!7?_Blfx-z!8s>(qUx{3i9LuFHV( zm#Qb7?Ovr`a{BG-)p344>bXfh=Jb2Fpnj)+xI-Q16{DWJQQkT3?pODm{^22Ye9nt{ z9#gM4{o#}9HD}yh9xS{f`yJQwL_NE!k%3qn@9ui8ZLeCJ@%KgkEY{|SGg zI?khj@2`&YAK*u*H}!fr@S-|CCxX9L9p77oSJZKy1N?1U{nvkX*L7N}<#GNV%Ab$= zop>%$$GRRUf2Dd^&u4>Q2Y1Gi&!B$&{x8aZNxkIwP(9%s*WXgd`(3DK0@u&i;6GN! z`&94+^^%k4zg15;c`l3X$$n2c`L>(7?~K=R^{CU&9H@@#fg_%jddwM@k5cw(etV*x)X!5;&yUrIPChKC zVYmeXZ&Ob={qQ>VxN}@=QtvwXcBy*9X@{%Sv3?xl zxnA9KUI*Qzj_cv0{4MIZ-Z=aY^{O*ZzFQsFIYIgFs(Vg<_!D(}Zyn`-p^oou!GEXj zJMFn_mvFyh9T}8=o;t1<4S%tE#c9uj)IDdMO{=$@{qibxobQMFU#E`iOu|o7?>fi( zYLs{K{9JXcKZSZOQja_Fe?YzJyUW;g74go%!uk>Io+wb_^EtmVUr@_SYWjF(;mvsdt?C4^dA# z<+JMeK0D$psi&Or==JI)XC7Ep_nrN9mO8Erjrtqvxc(3P-RiiW8~lUnC8z)YEA^Pu z5B#-y!N2@2Db}p-@oV+?!z3TMaHT9a)J{#0M z=Xh_bPaVHpz2f9;M;+J0L_6G|UU%Z`t2dnIdz(7GKZ|-s>NRJcbFX^W8Skd*B_|Ib zQOEgjsDGx8@8iS&pzb;Ijh%uefu$e7b?Q)lPq@?1@26gIp6_Am`1}a<6x0(=yB(*F z>+z!e$?8+5{T!;vE7coL|9PEy)5)LDsP`TJvUW``pMs@SDpFlD)p9=C+}2GIpfH?P>=I^{Qc^6 zC!UY0x1HzpDfPIs-7l!uoY&W1QExfN`#02ao+|dschmhbMAJkjUJaeaDLGi8g0jK|YfqL8Vm#Jf2PHguO^|-U$oO;vAhu5fkPW!(>9qWOj z{Tze>xlSF|)j>R;QOEUp;9pj6Is5Bt>QSft zzpY+!j`#1Xd(QFr6ZNK(C%;g~^`8;X@6;zwJiC`l>Uptx*=e7H z)W=Sqq}995^L>>%)?-EeuT!r(`{gvaGw!ce$Mxh<&$;St=Y6(|)P1L&KcGH#wtJ1b z=j7WB>WfZ0^wncd{kN&NoqQOf9_Rgm@2HoYe&&bjeP@6DOnv0^SHDspILF-|)IBGE zc6vdW4+Cd@yQlidiE}^oigSD&rrvPsFR0_ZYwWM%)UiG({Eg~;r#;`Q?m79kRvq8} zLOtiJx14_F67`7_|CQ=9XS>&_H=ODe0^EH?6kwz;LiNv+v+tZ&hM&soqB$v z9(UsTg*wi^MLYaXJ>ksfy*UZV?m#bHuI6LYUXWn>&dc*O)y6^1A+te4G@+0-3 zGk)Ex-gDyop?clve}1O!IrG3@smGlD3beP=(OtiIsHbB21&d0y*Kzmw0K)O*fxcd2^J=~u5(uRHVY>(pCL zJfA`R&hhePly~;aP<`yom%gPwbK;+<*PT51vAXB<&kLx>IgWm-UUTC4vwG4QFLvE4 z%!isYKYx*W+sVV1tK&L9*uO`r_nhP6mFg+yd7Y?UcE@L^@(%5e?=YFBSQIas3)A)JKs^qb?Q+50d?QW zw};j7{RotQTs`IVZ@*U`I{6lPVYuIMeR$LpgFDCFOVqp0xcLh8snc)IskfZt@zv^M zCvX2k-E;bf)8S4$tJN2rdd^kvIC;`kuR7z!Y^}4fP7E!-*yzj7gm=7`M`Nq`iPCPF~ zd1riih5E?pZ|Br~ryX9c9(D5eB=w||Kc}m=o%+|Pr<~(@qk7ee|6=t;r+uzaA3OPW zje6hduRe|Xo%Z~qdemvpuc{B6=lf06@ANMdvf|3~$d zvmbYUQJ4=E=Xt$Qz3Gf=`>U6o?H-|y^VM;@6xCzS`0`qHT;~Mk-=rRQj`z2#H=W~p zJ={57E>Isj@xM>K;pERpP~I6&KcOCX{BP8!PM-X|ddWH7|4BXJ^c(+*@=p8wKpo$| zK>Peu-E;Qu|EO1-{^9rPZRdE51SiJJ`vwDNyDw0$IOF)s)LYK@dx(0~8FzB(eWx8> zqn>h(mp7=-9Dj>?)aeJ-s>hr>IbXf+pJy{GjHsvH=I2DlKR+b zx1su?v)ym0x14#!M1A7)hd)*?JLBepdc*PGswbTN;=MTB?{%l0cT>mrN|0}H^}f@8 z9;m+P?3a{!+-ZlS)l*J8l+`;r;aaClyqtK&MW@DALG z=LYqGGf(TIyc6eb>TRcA8mV`j_P>ICI(afvPdWMU2la$=yzjJ6 zm=9g2KipHj<{V%9si&NJ4pVP9<4Zw(;NR9I&&-ab$Wv9P=t9r+&=WMvsKAY8h zj=xvE;ykYpsr%0H@^SU4v%fy89(Bg+FRLe=`oE^$cV0JrTfOb{C*M^cIr;Vz^_kNT z|3ZDyspog)Dw=M zre1gY=hf;-r++?IedNS*k$T1HA3mTSbDr-t>K&*2r`7w;@%2UZvXc*AQLi}jz;CE8 zI{WcE>Q(2s{-Ju`IZl73UUT~AU#WMTcKd^R+nK-Z^wKaNVov_=sor$vE&HigocYyZ z>Io;Ff_lR_E{;>5IP;T}QIFFPoPqLA{Oi=?PCK8k?mKZ_qCW4$c_r$1^5T4; z>LaI~FR8bjc!uf?Cr`em-gfe8f_j|(_Q&c;XWUs(?>fibZ`ET?J9{q+_xpmA=ew!* zoc=tnK5*LeK=r7TpDFdW<43Dcoc&u?PdWX-sp<)*eO9T@oc26Necs9c3)BbBxbQyp zwsYKlM7`M3Xb|4a3j z?R&VHFwk2>|g8udH<`AOyfeQ1O1Qd5sQ`*DMM!)fQHde!MyFNZt*d`Eri#Ce1I zg43RT^`Vmwx51rwM(TB^pSf3k>>Q_4^{zAje?%SY%%Yut1$Wx>DfNys-tD-*jth?O zp+0une_!>w(>{l)CmheK&z#qH$Ey2IKA)^UbdI|-)N4*2u2Y|P%5Q=@@n5Q5amrtX z@=iS0tJj_W|0eaMGY|Z-debTYHT9CSU%stAbK?1~dfC}8KSBLYKL0{J>9q6j)N$Ps zeB8ymNz*a9OILB2iOW33+T z**n}}R4yV(bsCNY-5DgQAX!OtTvU=eiIzlXqmtBV^hjhj3ds$Pc8SA_gy?s@*Z1C! zK27hRe*5hE^M1d6JYUb(>;1X*`h$BvPvh$ggX>@8*GkvFrpEY-?+@37Z{KJ5l>Ho% z$v!iNFN=P@!SFe;ry4#j_3~!JXGBkq7=A?h)O!uzk$O34_(|D+)$n!Ew{^p3B=5gw z_>}N@bFyCI|GS1?khpCbzA5X!Wca+Cd)e^I!iT>%d{+9))yY2M*S{OSBkOk!UzU0J zyI;ST_j!r`0mIjY&##%R7e3D#z9xK#h98r;Vs3(qJ%;|_@?ahNy9G)e%|n9+2{8rxaieI!;eb7{@n0QiRTr=&&m3KGW?3v#g5_g zlK1~I{G!z3;C9o0+5dYCuQESRPu2^5yWv~H^P%B$V$T~5zaV*;H+&#|J#6@lyK>x zy6k^xvcKrY-x$6v{Mk1An#Aqvh98i5t}{yuK_ zob2;i!?(oWCk;O@`F(1#kLdr?hF=o>e`d0e#O+zbcSKjeY521A!EYHpBX-{ULwkAO z7X0OgUy*pe+VE?FA2fVU?j!dYz9Vt|QNzzk9X)9H6{+jD8-7vd@b^vjk$nA>;d9cD zes+S(ee;(L-;n#!uNyuTdw$FCd8xb48NMU^bj$E5$?uo05G* z4^434!#fS%lzDH#@GXhM2MynrynNX3OOnTr8NMUB_X)#K3eP`n_z}TBKUpt2|Ha9A z;o+AjxbW~RhHpx}eAV!)!iR4deo^B5yy54?p6?hwkodoBc8?F|#m-k6eogFro#DHZ zm%9uIF z{7u8p3;#c3_@?y1Ck;O-aeK<}HR)eZ8$J{sK4bWX)bF!~&xya!8GcOk`~|}=i~YC! zi0Qx7`)!6#OPpV2_>}16LBlV~eCP~6D)zr|f=i#8H~fOc=i$lz!nb3F&xsB!89pm~ zUYp>OmtQt~L*lSu__p}_+lCKB_nt6(O6>fD$^OEtKQ(+-_Wvuxwoi7+ZFM4~+oqKtom%QI*__W05`wbt+d!g4Gen9-X z$M9v*r5`hVUh3#AhHpxreTU(5QZFOJPs%<&ZTMxGD;_m`O6H%B8a@!7eBAJ(68}#b zJ}Y(ogyG9_?jIVyBYO2`hR=&W|Fz*m$?rcJJ|jH;SHrJJ-TkNG>tg@64IfAxUiyYT zJ{*vF=@o_#1b?mJYoarE8h%OgRSe&i`nuonb>Z7#!#72*-edT(=*c67PYcgi48JCM z`H0~&lK009pO<((KEdVO&l-M3;`{}}=fs{rG5nJF^_PYp6F&c);k!~_|7`fS=*+(v zep&eW-;;f$zu$V-UfwSX{&K_T1%I{S=cKL=8h%9XWzO)S=;WIW-w}TwGJGI@z0>e2 z>lX|^Cj9xJ;Tw{#4^Q?H-hRyRqoO~bFnn6-<ci-vCq|G#Yb72(5I z44;)aeAV!C5{GXXJ|lKMZ}^t%^PLGUad_F?dwjSmI`c}y=ftnq89psKc9-GHvc4I< zA$C4s_)zNTZH8}29sT5Ff6=8=hHuOI4;g+y`rW$WmxZ5?8NMq#e|)lD?rWbh{FvnT zNyFzQo=+LRCU!n;_&KS&XAIwvIrCY=FA5L8Y4}me%eM@l5&OsgJbnE2lEHa%V;0VEm7h|0_~7=n!R;poi%TCEec;s6;^;%CPpz&luB?vEE-sxp zzH;~R_N{-;eNE|b9lHG`F2w!MF{S>w4F{vq z!s_DMsnyZRh0`aN#z#JSc42vW@$CL0pZdeT^-W9YpF`~j`!d{rHo%Ayffv|ZyI)~! z(7x-ge@<9aLtkG@-Cys!U#@kN6HH6BAM0G-rePbGx+^7j6MR3Z^%W6t>YMxAjR~nA zgpaMSu4Uhe*wGOB;TbV?uCH;fo04L;F#5h@>f3nVKSvsXSk%XQFkr&B?)0`!yXbhL{m0*qD)$ z3}!ZTkmkC--I|P|_3+B~yf)C%!GOr}fubeIb%Km}6nsX&3zF<&=ZC|&sdWzd+=CpT z%$4N;ZZ)(a4?Xs1J**h0NGtuQ1hsdFvE#7;xq|9cz@m7hwwKrCS%Wnm_$&ZfhG}SNke=xQjt+l9_uq&R=3ij zd3hu{G(;BK9#}PqxCik(yuv(?!10QCqxo)*=a>V>3(PE#aL?pI8zc0#qHrrn81QU? z-W9lrh@cTTC}qGr(?Bn3LG7%LoCi^e5p2oWK7*xb>=7hF&4kjkmQXVhZdXWCfrdls z0y;=Q)&l87)xkCn(wi~NI%WsKN#hB8G&-?z#>VOWo|0jIhV*$D>Y@1_S`fg%fb{{i z%g~XG9^s*79=cEvhzQ4O>V2W1j|5ai!8M~$3phpefudlW3bq%-Fu?r80!1#Mf)K0> zk@8@50W~A)FhbBP)_AZz!;y?eUw}7=8Y=>w2?je9ghMqaOhXRk8K5){Lv00TE7DNW z$s#Un6)`S1$ZEGUAl{WNBK9>6HaE=S3FVqFwMAS|9U|2Wx}dl#vy34qB034gtcXrv zgTS5*UPUyQ2FoI}G_Z2Q!}aSQAk+0sN|H)((TGE4?TkM*ECD zQqf-;=B!4sf&D1B3cYA(7YT$!1Owh66v$LX)GA~uViYKdcmYo%JW1Ho4SP2&hcd>y35L1aBH1|E1u zqyk>pGikYjz`T$ML5VnBYDk$9uQ3(*dj6^krKEnQl;5SS^ z9_mz4>mKCuV0^)F?O=n$wA2vt09rWM>ELf=pMcOt^mLEuEmJ@c^Nec9*fXPxdgLpC z_!UjY!S_G~!GH&GNKjgj{?R~4&#?<5o9DfP;lu#pQDhGF;ZPG9Vapi(GE}Bul*?p1 z=#VfHG=!%>q7w8zLufn%)?vJgh(n_981li41SUDmHVvjFFtVa=M6je_rmAqgVd`~g zwhrz#=z63g^V$8&YC>Hm=tKjL5(wZB@Q7Ky(Q`@|KV#1TY4^DGBO?b?q9xWr`-U5Zg7zKkJ z!sO6L3gVGrFxpqdNFQ-c2$;nK>b*j?kxLJ2GKSy68wSD=5V8iRpqO|OgYygQDtJE@ z5cdQvkMOmkEfthh!TN$B&BHM0P=+}HF$j>+fXD|(e8Ify(26|p3=ON8*&1fk4A(R0 z8gMJiU`0laXS#^>9;y_fT)5m}E-84A;6YZ80t@gYqfSwe97_D@JeK++prMBZ4jXlD&xHQ*TmbO>O?G?8!}NJzg&niG_(fsP(6 zFT#exbNgUlLGX&6kw~*Az67Gz`eg+vEN!78cD`j>2PF%j5}-{pxptlkP0aT zk%=h01kyXqF9BI-;6X((B;-4xfmMv20rzB&Ij*9R0*bTZR#G{H01pUdMWzaDcCanu z+yu=oNL9vJnZp&H#qfU}i<_Rn{Bg+>p}(VQR1w z$9kCQ87hw5DmcSvu*Jf4vvk7p6thUp_=a7Y-z8@5llL}t7-t#HwU!3mj$9QALk zK_xtxSukQ2WW+%+GW^J}ms19YE{FRpbRmt!eDsz>hwr=h(CF}k55D7pher?Jd*7i4 zM*HkQp;pZK0bvPnxk0=|oz=B5$2896sDso%l38+wTl1Bvu=uJUbGiZ@g(nU4N>IQIie$7o&jcYl z4p~W{R71r#L?>W8^RU@N)jZTIf+Pi#s6*9c^vi?{IpB?N4^cK5v5F9egg7`flmsR< zjwMjv$X<(~8X*aoF5yW-CMrCS7;r1vVM6x~h-N{i3mJe6MbuD2*(dfaL?{_G8zh>h!&`ugJ5|6$ zn{LQw&v_VMfJX0MSs5*!J~0}Nr_AGP3**1}etKc))Xy!BfBWJet*kDbUA_K?y3npp UST=sdc3M2Ku(~jK@pps&17yl0#sB~S literal 0 HcmV?d00001 diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so new file mode 100755 index 0000000000000000000000000000000000000000..532beac0bf22c653e9ff7489dd1ac339aa1ffed1 GIT binary patch literal 123855 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 literal 0 HcmV?d00001 diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py new file mode 100755 index 000000000..e71b5cbbd --- /dev/null +++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# +# 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. + +"""Build relocation packer unit test data. + +Uses a built relocation packer to generate 'golden' reference test data +files for elf_file_unittests.cc. +""" + +import optparse +import os +import shutil +import subprocess +import sys +import tempfile + +def PackArmLibraryRelocations(android_pack_relocations, + android_objcopy, + added_section, + input_path, + output_path): + # Copy and add a 'NULL' .android.rel.dyn section for the packing tool. + with tempfile.NamedTemporaryFile() as stream: + stream.write('NULL') + stream.flush() + objcopy_command = [android_objcopy, + '--add-section', '%s=%s' % (added_section, stream.name), + input_path, output_path] + subprocess.check_call(objcopy_command) + + # Pack relocations. + pack_command = [android_pack_relocations, output_path] + subprocess.check_call(pack_command) + + +def UnpackArmLibraryRelocations(android_pack_relocations, + input_path, + output_path): + shutil.copy(input_path, output_path) + + # Unpack relocations. We leave the .android.rel.dyn or .android.rela.dyn + # in place. + unpack_command = [android_pack_relocations, '-u', output_path] + subprocess.check_call(unpack_command) + + +def main(): + parser = optparse.OptionParser() + + parser.add_option('--android-pack-relocations', + help='Path to the ARM relocations packer binary') + parser.add_option('--android-objcopy', + help='Path to the toolchain\'s objcopy binary') + parser.add_option('--added-section', + choices=['.android.rel.dyn', '.android.rela.dyn'], + help='Section to add, one of ".android.rel.dyn" or ".android.rela.dyn"') + parser.add_option('--test-file', + help='Path to the input test file, an unpacked ARM .so') + parser.add_option('--unpacked-output', + help='Path to the output file for reference unpacked data') + parser.add_option('--packed-output', + help='Path to the output file for reference packed data') + + options, _ = parser.parse_args() + + for output in [options.unpacked_output, options.packed_output]: + directory = os.path.dirname(output) + if not os.path.exists(directory): + os.makedirs(directory) + + PackArmLibraryRelocations(options.android_pack_relocations, + options.android_objcopy, + options.added_section, + options.test_file, + options.packed_output) + + UnpackArmLibraryRelocations(options.android_pack_relocations, + options.packed_output, + options.unpacked_output) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh new file mode 100755 index 000000000..f90a2f658 --- /dev/null +++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# 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. + +# Generates elf_file_unittest_relocs_arm{32,64}{,_packed}.so test data files +# from elf_file_unittest_relocs.cc. Run once to create these test data +# files; the files are checked into the source tree. +# +# To use: +# ./generate_elf_file_unittest_relocs.sh +# git add elf_file_unittest_relocs_arm{32,64}{,_packed}.so + +function main() { + local '-r' test_data_directory="$(pwd)" + cd '../../..' + + source tools/cr/cr-bash-helpers.sh + local arch + for arch in 'arm32' 'arm64'; do + cr 'init' '--platform=android' '--type=Debug' '--architecture='"${arch}" + cr 'build' 'relocation_packer_unittests_test_data' + done + + local '-r' packer='out_android/Debug/obj/tools/relocation_packer' + local '-r' gen="${packer}/relocation_packer_unittests_test_data.gen" + + cp "${gen}/elf_file_unittest_relocs_arm"{32,64}{,_packed}'.so' \ + "${test_data_directory}" + + return 0 +} + +main From f8ff6b103bde3433d6f7dbf762fc7bf657d7de5f Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 27 Jan 2015 19:32:56 -0800 Subject: [PATCH 2/4] Generalize compression tool 1. One binary for all architectures 2. Generalize (and slightly improve) compression 2.1 works on all relocation types (rela?.dyn section only so far) 2.2 Uses same format to encode ElfW(Rel) as well as ElfW(Rela) tables Bug: 18051137 Change-Id: I66c95d9076954ca115816fc577d0f5ef274e5e72 --- tools/Android.mk | 19 + tools/relocation_packer/Android.mk | 96 ++ tools/relocation_packer/BUILD.gn | 148 --- tools/relocation_packer/config.gni | 21 - tools/relocation_packer/relocation_packer.gyp | 161 --- tools/relocation_packer/src/debug_unittest.cc | 2 +- tools/relocation_packer/src/delta_encoder.cc | 321 ++++- tools/relocation_packer/src/delta_encoder.h | 136 +- .../src/delta_encoder_unittest.cc | 231 ++-- tools/relocation_packer/src/elf_file.cc | 1145 ++++++----------- tools/relocation_packer/src/elf_file.h | 33 +- .../src/elf_file_unittest.cc | 134 +- tools/relocation_packer/src/elf_traits.h | 63 +- tools/relocation_packer/src/leb128.cc | 52 +- tools/relocation_packer/src/leb128.h | 12 +- .../relocation_packer/src/leb128_unittest.cc | 32 +- tools/relocation_packer/src/main.cc | 102 +- tools/relocation_packer/src/packer.cc | 142 +- tools/relocation_packer/src/packer.h | 24 +- .../relocation_packer/src/packer_unittest.cc | 452 ++++--- .../src/run_all_unittests.cc | 10 - .../src/run_length_encoder.cc | 144 --- .../src/run_length_encoder_unittest.cc | 124 -- tools/relocation_packer/src/sleb128.cc | 72 +- tools/relocation_packer/src/sleb128.h | 12 +- .../relocation_packer/src/sleb128_unittest.cc | 58 +- .../elf_file_unittest_relocs_arm32_packed.so | Bin 89126 -> 89114 bytes .../elf_file_unittest_relocs_arm64_packed.so | Bin 123855 -> 113651 bytes 28 files changed, 1627 insertions(+), 2119 deletions(-) create mode 100644 tools/Android.mk create mode 100644 tools/relocation_packer/Android.mk delete mode 100644 tools/relocation_packer/BUILD.gn delete mode 100644 tools/relocation_packer/config.gni delete mode 100644 tools/relocation_packer/relocation_packer.gyp delete mode 100644 tools/relocation_packer/src/run_all_unittests.cc delete mode 100644 tools/relocation_packer/src/run_length_encoder.cc delete mode 100644 tools/relocation_packer/src/run_length_encoder_unittest.cc diff --git a/tools/Android.mk b/tools/Android.mk new file mode 100644 index 000000000..4dd66fe07 --- /dev/null +++ b/tools/Android.mk @@ -0,0 +1,19 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(call all-subdir-makefiles) diff --git a/tools/relocation_packer/Android.mk b/tools/relocation_packer/Android.mk new file mode 100644 index 000000000..99a39c02b --- /dev/null +++ b/tools/relocation_packer/Android.mk @@ -0,0 +1,96 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := \ + src/debug.cc \ + src/delta_encoder.cc \ + src/elf_file.cc \ + src/leb128.cc \ + src/packer.cc \ + src/sleb128.cc \ + +LOCAL_STATIC_LIBRARIES := libelf +LOCAL_C_INCLUDES := external/elfutils/src/libelf +LOCAL_MODULE := lib_relocation_packer + +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := src/main.cc +LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf +LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include + +LOCAL_MODULE := relocation_packer + +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := \ + src/debug_unittest.cc \ + src/delta_encoder_unittest.cc \ + src/elf_file_unittest.cc \ + src/leb128_unittest.cc \ + src/sleb128_unittest.cc \ + src/packer_unittest.cc \ + +LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf +LOCAL_C_INCLUDES := external/elfutils/src/libelf + +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_MODULE := relocation_packer_unit_tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_NATIVE_TEST) + +# $(1) library name +define copy-test-library +include $(CLEAR_VARS) +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE := $(1) +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES) +LOCAL_STRIP_MODULE := false +LOCAL_SRC_FILES := test_data/$(1) +include $(BUILD_PREBUILT) +endef + +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so)) +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so)) +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so)) +$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so)) diff --git a/tools/relocation_packer/BUILD.gn b/tools/relocation_packer/BUILD.gn deleted file mode 100644 index 0b29c9162..000000000 --- a/tools/relocation_packer/BUILD.gn +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("config.gni") -import("//testing/test.gni") - -assert(relocation_packing_supported) - -if (target_arch == "arm") { - target_define = "TARGET_ARM" -} else if (target_arch == "arm64") { - target_define = "TARGET_ARM64" -} - -if (current_toolchain == host_toolchain) { - # GYP: //tools/relocation_packer/relocation_packer.gyp:lib_relocation_packer - source_set("lib_relocation_packer") { - defines = [ target_define ] - deps = [ - "//third_party/elfutils:libelf", - ] - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] - sources = [ - "src/debug.cc", - "src/delta_encoder.cc", - "src/elf_file.cc", - "src/leb128.cc", - "src/packer.cc", - "src/sleb128.cc", - "src/run_length_encoder.cc", - ] - } - - # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer - executable("relocation_packer") { - defines = [ target_define ] - deps = [ - ":lib_relocation_packer", - "//third_party/elfutils:libelf", - ] - sources = [ - "src/main.cc", - ] - } - - # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests - test("relocation_packer_unittests") { - sources = [ - "src/debug_unittest.cc", - "src/delta_encoder_unittest.cc", - "src/elf_file_unittest.cc", - "src/leb128_unittest.cc", - "src/packer_unittest.cc", - "src/sleb128_unittest.cc", - "src/run_length_encoder_unittest.cc", - "src/run_all_unittests.cc", - ] - rebased_test_data = rebase_path("test_data", root_build_dir) - data = [ - "test_data/elf_file_unittest_relocs_arm32.so", - "test_data/elf_file_unittest_relocs_arm32_packed.so", - "test_data/elf_file_unittest_relocs_arm64.so", - "test_data/elf_file_unittest_relocs_arm64_packed.so", - ] - defines = [ - target_define, - "INTERMEDIATE_DIR=\"$rebased_test_data\"", - ] - include_dirs = [ "//" ] - deps = [ - ":lib_relocation_packer", - ":relocation_packer_test_data", - "//testing:gtest", - ] - } -} - -if (current_toolchain == default_toolchain && - (target_arch == "arm" || target_arch == "arm64")) { - # Targets to build test data. These participate only in building test - # data for use with elf_file_unittest.cc, and are not part of the main - # relocation packer build. Unit test data files are checked in to the - # source tree as 'golden' data, and are not generated 'on the fly' by - # the build. - # - # See test_data/generate_elf_file_unittest_relocs.sh for instructions. - - # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_test_data - shared_library("relocation_packer_test_data") { - cflags = [ - "-O0", - "-g0", - ] - sources = [ - "test_data/elf_file_unittest_relocs.cc", - ] - } - - # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests_test_data - action("relocation_packer_unittests_test_data") { - script = "test_data/generate_elf_file_unittest_relocs.py" - test_file = "$root_build_dir/librelocation_packer_test_data.so" - if (target_arch == "arm") { - added_section = ".android.rel.dyn" - packed_output = "elf_file_unittest_relocs_arm32_packed.so" - unpacked_output = "elf_file_unittest_relocs_arm32.so" - } else if (target_arch == "arm64") { - added_section = ".android.rela.dyn" - packed_output = "elf_file_unittest_relocs_arm64_packed.so" - unpacked_output = "elf_file_unittest_relocs_arm64.so" - } else { - assert(false, "Unsupported target arch for relocation packer") - } - - packed_output = "$root_build_dir/$packed_output" - unpacked_output = "$root_build_dir/$unpacked_output" - - inputs = [ - test_file, - ] - - deps = [ - ":relocation_packer_test_data", - ":relocation_packer($host_toolchain)", - ] - - outputs = [ - packed_output, - unpacked_output, - ] - - args = [ - "--android-pack-relocations", - rebase_path(relocation_packer_exe, root_build_dir), - "--android-objcopy", - rebase_path(android_objcopy, root_build_dir), - "--added-section=$added_section", - "--test-file", - rebase_path(test_file, root_build_dir), - "--packed-output", - rebase_path(packed_output, root_build_dir), - "--unpacked-output", - rebase_path(unpacked_output, root_build_dir), - ] - } -} diff --git a/tools/relocation_packer/config.gni b/tools/relocation_packer/config.gni deleted file mode 100644 index 90e397933..000000000 --- a/tools/relocation_packer/config.gni +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -relocation_packing_supported = target_arch == "arm" || target_arch == "arm64" - -if (relocation_packing_supported) { - relocation_packer_target = "//tools/relocation_packer($host_toolchain)" - relocation_packer_dir = - get_label_info("$relocation_packer_target", "root_out_dir") - relocation_packer_exe = "${relocation_packer_dir}/relocation_packer" - - if (target_arch == "arm") { - relocations_have_addends = 0 - } else if (target_arch == "arm64") { - relocations_have_addends = 1 - } -} else { - relocations_have_addends = 0 - relocation_packer_exe = "" -} diff --git a/tools/relocation_packer/relocation_packer.gyp b/tools/relocation_packer/relocation_packer.gyp deleted file mode 100644 index 1e9c1b95b..000000000 --- a/tools/relocation_packer/relocation_packer.gyp +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'target_define%': 'TARGET_UNSUPPORTED', - 'conditions': [ - [ 'target_arch == "arm"', { - 'target_define': 'TARGET_ARM', - }], - [ 'target_arch == "arm64"', { - 'target_define': 'TARGET_ARM64', - }], - ], - }, - 'targets': [ - { - # GN: //tools/relocation_packer:lib_relocation_packer - 'target_name': 'lib_relocation_packer', - 'toolsets': ['host'], - 'type': 'static_library', - 'defines': [ - '<(target_define)', - ], - 'dependencies': [ - '../../third_party/elfutils/elfutils.gyp:libelf', - ], - 'sources': [ - 'src/debug.cc', - 'src/delta_encoder.cc', - 'src/elf_file.cc', - 'src/leb128.cc', - 'src/packer.cc', - 'src/sleb128.cc', - 'src/run_length_encoder.cc', - ], - }, - { - # GN: //tools/relocation_packer:relocation_packer - 'target_name': 'relocation_packer', - 'toolsets': ['host'], - 'type': 'executable', - 'defines': [ - '<(target_define)', - ], - 'dependencies': [ - '../../third_party/elfutils/elfutils.gyp:libelf', - 'lib_relocation_packer', - ], - 'sources': [ - 'src/main.cc', - ], - }, - { - # GN: //tools/relocation_packer:relocation_packer_unittests - 'target_name': 'relocation_packer_unittests', - 'toolsets': ['host'], - 'type': 'executable', - 'defines': [ - '<(target_define)', - ], - 'cflags': [ - '-DINTERMEDIATE_DIR="<(INTERMEDIATE_DIR)"', - ], - 'dependencies': [ - '../../testing/gtest.gyp:gtest', - 'lib_relocation_packer', - ], - 'include_dirs': [ - '../..', - ], - 'sources': [ - 'src/debug_unittest.cc', - 'src/delta_encoder_unittest.cc', - 'src/elf_file_unittest.cc', - 'src/leb128_unittest.cc', - 'src/packer_unittest.cc', - 'src/sleb128_unittest.cc', - 'src/run_length_encoder_unittest.cc', - 'src/run_all_unittests.cc', - ], - 'copies': [ - { - 'destination': '<(INTERMEDIATE_DIR)', - 'files': [ - 'test_data/elf_file_unittest_relocs_arm32.so', - 'test_data/elf_file_unittest_relocs_arm32_packed.so', - 'test_data/elf_file_unittest_relocs_arm64.so', - 'test_data/elf_file_unittest_relocs_arm64_packed.so', - ], - }, - ], - }, - - # Targets to build test data. These participate only in building test - # data for use with elf_file_unittest.cc, and are not part of the main - # relocation packer build. Unit test data files are checked in to the - # source tree as 'golden' data, and are not generated 'on the fly' by - # the build. - # - # See test_data/generate_elf_file_unittest_relocs.sh for instructions. - { - # GN: //tools/relocation_packer:relocation_packer_test_data - 'target_name': 'relocation_packer_test_data', - 'toolsets': ['target'], - 'type': 'shared_library', - 'cflags': [ - '-O0', - '-g0', - ], - 'sources': [ - 'test_data/elf_file_unittest_relocs.cc', - ], - }, - { - # GN: //tools/relocation_packer:relocation_packer_unittests_test_data - 'target_name': 'relocation_packer_unittests_test_data', - 'toolsets': ['target'], - 'type': 'none', - 'actions': [ - { - 'variables': { - 'test_file': '<(SHARED_LIB_DIR)/librelocation_packer_test_data.so', - 'conditions': [ - [ 'target_arch == "arm"', { - 'added_section': '.android.rel.dyn', - 'unpacked_output': 'elf_file_unittest_relocs_arm32.so', - 'packed_output': 'elf_file_unittest_relocs_arm32_packed.so', - }], - [ 'target_arch == "arm64"', { - 'added_section': '.android.rela.dyn', - 'unpacked_output': 'elf_file_unittest_relocs_arm64.so', - 'packed_output': 'elf_file_unittest_relocs_arm64_packed.so', - }], - ], - }, - 'action_name': 'generate_relocation_packer_test_data', - 'inputs': [ - 'test_data/generate_elf_file_unittest_relocs.py', - '<(PRODUCT_DIR)/relocation_packer', - '<(test_file)', - ], - 'outputs': [ - '<(INTERMEDIATE_DIR)/<(unpacked_output)', - '<(INTERMEDIATE_DIR)/<(packed_output)', - ], - 'action': [ - 'python', 'test_data/generate_elf_file_unittest_relocs.py', - '--android-pack-relocations=<(PRODUCT_DIR)/relocation_packer', - '--android-objcopy=<(android_objcopy)', - '--added-section=<(added_section)', - '--test-file=<(test_file)', - '--unpacked-output=<(INTERMEDIATE_DIR)/<(unpacked_output)', - '--packed-output=<(INTERMEDIATE_DIR)/<(packed_output)', - ], - }, - ], - }, - ], -} diff --git a/tools/relocation_packer/src/debug_unittest.cc b/tools/relocation_packer/src/debug_unittest.cc index 1b65cd16e..b31e2ae7e 100644 --- a/tools/relocation_packer/src/debug_unittest.cc +++ b/tools/relocation_packer/src/debug_unittest.cc @@ -5,7 +5,7 @@ #include "debug.h" #include -#include "testing/gtest/include/gtest/gtest.h" +#include "gtest/gtest.h" namespace relocation_packer { diff --git a/tools/relocation_packer/src/delta_encoder.cc b/tools/relocation_packer/src/delta_encoder.cc index 69cc91a51..8349d7c64 100644 --- a/tools/relocation_packer/src/delta_encoder.cc +++ b/tools/relocation_packer/src/delta_encoder.cc @@ -7,66 +7,301 @@ #include #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& relocations, - std::vector* packed) { - // One relocation is sufficient for delta encoding. - if (relocations.size() < 1) +// Encode relocations into a delta encoded (packed) representation. +template +void RelocationDeltaCodec::Encode(const std::vector& relocations, + std::vector* 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(relocations.size())); - ELF::Addr offset = 0; - ELF::Sxword addend = 0; + // lets write starting offset (offset of the first reloc - first delta) + ElfAddr start_offset = relocations.size() > 1 ? + relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) : + relocations[0].r_offset; - for (size_t i = 0; i < relocations.size(); ++i) { - const ELF::Rela* relocation = &relocations[i]; - CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode); + packed->push_back(start_offset); - packed->push_back(relocation->r_offset - offset); - offset = relocation->r_offset; - packed->push_back(relocation->r_addend - addend); - addend = relocation->r_addend; + // this one is used to calculate delta + ElfAddr previous_addend = 0; + ElfAddr previous_offset = start_offset; + + for (size_t group_start = 0; group_start < relocations.size(); ) { + ElfAddr group_flags = 0; + ElfAddr group_offset_delta = 0; + ElfAddr group_addend = 0; + ElfAddr group_info = 0; + + ElfAddr group_size = 0; + + DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags, + &group_offset_delta, &group_info, &group_addend); + + // write the group header + packed->push_back(group_size); + packed->push_back(group_flags); + + if (is_relocation_grouped_by_offset_delta(group_flags)) { + packed->push_back(group_offset_delta); + } + + if (is_relocation_grouped_by_info(group_flags)) { + packed->push_back(group_info); + } + + if (is_relocation_group_has_addend(group_flags) && + is_relocation_grouped_by_addend(group_flags)) { + packed->push_back(group_addend - previous_addend); + previous_addend = group_addend; + } + + for (size_t i = 0; i < static_cast(group_size); ++i) { + CHECK((group_start + i) < relocations.size()); + const ElfRela* relocation = &relocations[group_start + i]; + + if (!is_relocation_grouped_by_offset_delta(group_flags)) { + packed->push_back(relocation->r_offset - previous_offset); + } + previous_offset = relocation->r_offset; + + if (!is_relocation_grouped_by_info(group_flags)) { + packed->push_back(relocation->r_info); + } + + if (is_relocation_group_has_addend(group_flags) && + !is_relocation_grouped_by_addend(group_flags)) { + packed->push_back(relocation->r_addend - previous_addend); + previous_addend = relocation->r_addend; + } + } + + // If the relocation group does not have an addend - reset it to 0 + // to simplify addend computation for the group following this one. + if (!is_relocation_group_has_addend(group_flags)) { + previous_addend = 0; + } + + group_start += group_size; } } -// Decode relative relocations with addends from a delta encoded (packed) -// representation. -void RelocationDeltaCodec::Decode(const std::vector& packed, - std::vector* relocations) { - // We need at least one packed pair after the packed pair count to be - // able to unpack. - if (packed.size() < 3) +// Decode relocations from a delta encoded (packed) representation. +template +void RelocationDeltaCodec::Decode(const std::vector& packed, + std::vector* relocations) { + if (packed.size() < 5) { return; + } - // Ensure that the packed data offers enough pairs. There may be zero - // padding on it that we ignore. - CHECK(static_cast(packed[0]) <= (packed.size() - 1) >> 1); + size_t ndx = 0; + ElfAddr current_count = 0; + ElfAddr total_count = packed[ndx++]; - ELF::Addr offset = 0; - ELF::Sxword addend = 0; + ElfAddr offset = packed[ndx++]; + ElfAddr info = 0; + ElfAddr addend = 0; - // The first packed vector element is the pairs count. Start uncondensing - // pairs at the second, and finish at the end of the pairs data. - const size_t pairs_count = packed[0]; - for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) { - offset += packed[i]; - addend += packed[i + 1]; + while(current_count < total_count) { + // read group + ElfAddr group_size = packed[ndx++]; + ElfAddr group_flags = packed[ndx++]; + ElfAddr group_offset_delta = 0; - // Generate a relocation for this offset and addend pair. - ELF::Rela relocation; - relocation.r_offset = offset; - relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); - relocation.r_addend = addend; - relocations->push_back(relocation); + if (is_relocation_grouped_by_offset_delta(group_flags)) { + group_offset_delta = packed[ndx++]; + } + + if (is_relocation_grouped_by_info(group_flags)) { + info = packed[ndx++]; + } + + if (is_relocation_group_has_addend(group_flags) && + is_relocation_grouped_by_addend(group_flags)) { + addend += packed[ndx++]; + } + + // now read not grouped info + for (ElfAddr i = 0; ipush_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 +void RelocationDeltaCodec::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(reloc_two.r_offset) - + static_cast(reloc_one.r_offset); + + if (offset_delta == current_offset_delta) { + *group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG; + if (group_offset_delta != nullptr) { + *group_offset_delta = current_offset_delta; + } + } + + if (reloc_one.r_info == reloc_two.r_info) { + *group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG; + if (group_info != nullptr) { + *group_info = reloc_one.r_info; + } + } + + if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) { + *group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG; + if (reloc_one.r_addend == reloc_two.r_addend) { + *group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG; + if (group_addend != nullptr) { + *group_addend = reloc_one.r_addend; + } + } + } +} + +// This function is used to detect if there is better group available +// during RelocationDeltaCodec::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 +void RelocationDeltaCodec::DetectGroup(const std::vector& relocations, + size_t group_starts_with, ElfAddr previous_offset, + ElfAddr* group_size, ElfAddr* group_flags, + ElfAddr* group_offset_delta, ElfAddr* group_info, + ElfAddr* group_addend) { + CHECK(group_starts_with < relocations.size()); + CHECK(group_flags != nullptr); + + const ElfRela& reloc_one = relocations[group_starts_with++]; + if (group_starts_with == relocations.size()) { + *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG; + *group_size = 1; + return; + } + + const ElfAddr offset_delta = reloc_one.r_offset - previous_offset; + + // detect group_flags + DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags, + group_offset_delta, group_info, group_addend); + + if (group_starts_with + 1 == relocations.size()) { + *group_size = 2; + return; + } + + ElfAddr cnt = 1; + for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) { + ElfAddr candidate_flags; + // check if next group (reloc_current; reloc_next) has better grouped_by flags + DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags, + nullptr, nullptr, nullptr); + + if (group_weight(*group_flags) < group_weight(candidate_flags)) { + break; + } + cnt++; + + if (candidate_flags != *group_flags) { + break; + } + + if (i + 1 == relocations.size() - 1) { // last one + cnt++; + } + } + + // if as a result of checking candidates we ended up with cnt == 1 + // reset flags to the default state + if (cnt == 1) { + *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG; + } + + *group_size = cnt; +} + +template class RelocationDeltaCodec; +template class RelocationDeltaCodec; + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/delta_encoder.h b/tools/relocation_packer/src/delta_encoder.h index 498b6d1af..46c324c49 100644 --- a/tools/relocation_packer/src/delta_encoder.h +++ b/tools/relocation_packer/src/delta_encoder.h @@ -2,50 +2,86 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Delta encode and decode relative relocations with addends. +// Delta encode and decode REL/RELA section of elf file. // -// Relative relocations are the bulk of dynamic relocations (the -// .rel.dyn or .rela.dyn sections) in libchrome..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..so' 64 bit: +// [00] relocation_count - the total count of relocations +// [01] initial r_offset - this is initial r_offset for the +// relocation table. +// followed by group structures: +// [02] group +// ... +// [nn] group + +// the generalized format of the group is (! - always present ? - depends on group_flags): +// -------------- +// ! group_size +// ! group_flags +// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set +// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set +// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND +// flag is set // -// offset info -// 80949303 00000000 03040000 00000000 ................ -// addend offset -// fc015b00 00000000 88949303 00000000 ..[............. -// info addend -// 03040000 00000000 24025b00 00000000 ........$.[..... -// offset info -// 90949303 00000000 03040000 00000000 ................ -// addend offset -// 3c025b00 00000000 98949303 00000000 <.[............. -// info addend -// 03040000 00000000 50025b00 00000000 ........P.[..... +// The group description is followed by individual relocations. +// please note that there is a case when individual relocation +// section could be empty - that is if every field ends up grouped. // -// The offset strictly increases, but the addend is unpredictable, so run -// length encoding will not work well with this data. We can however pack -// with delta encoding. The upper four bytes of the eight byte offset and -// addend are invariably zeroes. The difference between adjacent offsets -// is almost always small, and between adjacent addends is often small. And -// info is constant and can be eliminated. +// The format for individual relocations section is: +// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set +// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set +// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set // -// Delta encoding reduces the size of the data modestly, so that the first -// three relocations above can be represented as: +// For example lets pack the following relocations: // -// initial offset initial addend offset delta addend delta -// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028 -// offset delta addend delta ... -// 00000000 00000008 00000000 0000009f +// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries: +// Offset Info Type Symbol's Value Symbol's Name + Addend +// 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8 +// 00000000000a2180 0000000000000403 R_AARCH64_RELATIVE 177cc +// 00000000000a2188 0000000000000403 R_AARCH64_RELATIVE 177e0 +// 00000000000a2190 0000000000000403 R_AARCH64_RELATIVE 177f4 +// 00000000000a2198 0000000000000403 R_AARCH64_RELATIVE 17804 +// 00000000000a21a0 0000000000000403 R_AARCH64_RELATIVE 17818 +// 00000000000a21a8 0000000000000403 R_AARCH64_RELATIVE 1782c +// 00000000000a21b0 0000000000000403 R_AARCH64_RELATIVE 17840 +// 00000000000a21b8 0000000000000403 R_AARCH64_RELATIVE 17854 +// 00000000000a21c0 0000000000000403 R_AARCH64_RELATIVE 17868 +// 00000000000a21c8 0000000000000403 R_AARCH64_RELATIVE 1787c +// 00000000000a21d0 0000000000000403 R_AARCH64_RELATIVE 17890 +// 00000000000a21d8 0000000000000403 R_AARCH64_RELATIVE 178a4 +// 00000000000a21e8 0000000000000403 R_AARCH64_RELATIVE 178b8 // -// The addend delta can be negative as well as positive, but overall the -// deltas have a much smaller range than the input data. When encoded as -// signed LEB128 the total data reduction becomes useful. +// The header is going to be +// [00] 14 <- count +// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta, +// the delta is 8 in this case) +// -- starting the first and only group +// [03] 14 <- group size +// [03] 0xb <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA +// | RELOCATION_GROUPED_BY_INFO +// [04] 8 <- offset delta +// [05] 0x403 <- r_info +// -- end of group definition, starting list of r_addend deltas +// [06] 0x177a8 +// [07] 0x24 = 177cc - 177a8 +// [08] 0x14 = 177e0 - 177cc +// [09] 0x14 = 177f4 - 177e0 +// [10] 0x10 = 17804 - 177f4 +// [11] 0x14 = 17818 - 17804 +// [12] 0x14 = 1782c - 17818 +// [13] 0x14 = 17840 - 1782c +// [14] 0x14 = 17854 - 17840 +// [15] 0x14 = 17868 - 17854 +// [16] 0x14 = 1787c - 17868 +// [17] 0x14 = 17890 - 1787c +// [18] 0x14 = 178a4 - 17890 +// [19] 0x14 = 178b8 - 178a4 +// -- the end. + +// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can +// save us more bytes... + +// The input ends when sum(group_size) == relocation_count #ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ #define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ @@ -60,19 +96,35 @@ namespace relocation_packer { // A RelocationDeltaCodec packs vectors of relative relocations with // addends into more compact forms, and unpacks them to reproduce the // pre-packed data. +template 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& relocations, - std::vector* packed); + static void Encode(const std::vector& relocations, + std::vector* 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& packed, - std::vector* relocations); + static void Decode(const std::vector& packed, + std::vector* relocations); + + private: + static void DetectGroup(const std::vector& 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 diff --git a/tools/relocation_packer/src/delta_encoder_unittest.cc b/tools/relocation_packer/src/delta_encoder_unittest.cc index b9bf39adb..06d9c9673 100644 --- a/tools/relocation_packer/src/delta_encoder_unittest.cc +++ b/tools/relocation_packer/src/delta_encoder_unittest.cc @@ -6,27 +6,29 @@ #include #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* relocations) { - ELF::Rela relocation; +template +void AddRelocation(uint32_t addr, + uint32_t info, + int32_t addend, + std::vector* 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 +bool CheckRelocation(uint32_t addr, + uint32_t info, + int32_t addend, + const T& relocation) { return relocation.r_offset == addr && - ELF_R_SYM(relocation.r_info) == 0 && - ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode && + relocation.r_info == info && relocation.r_addend == addend; } @@ -34,117 +36,188 @@ bool CheckRelocation(ELF::Addr addr, namespace relocation_packer { -TEST(Delta, Encode) { - std::vector relocations; - std::vector packed; +template +static void encode() { + std::vector relocations; + std::vector packed; - RelocationDeltaCodec codec; + RelocationDeltaCodec 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(-24), packed[ndx++]); + EXPECT_EQ(static_cast(-24), packed[ndx++]); + EXPECT_EQ(static_cast(-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 packed; - std::vector relocations; +TEST(Delta, Encode32) { + encode(); +} - RelocationDeltaCodec codec; +TEST(Delta, Encode64) { + encode(); +} + +template +static void decode() { + std::vector packed; + std::vector relocations; + + RelocationDeltaCodec 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(-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(-24)); // addend + packed.push_back(static_cast(-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(); +} + +TEST(Delta, Decode64) { + decode(); +} + +// 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 diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index 3ffccecd7..20b25ef82 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -5,26 +5,10 @@ // 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" @@ -42,28 +26,29 @@ 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; @@ -71,9 +56,9 @@ Elf_Data* GetSectionData(Elf_Scn* section) { // 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]; @@ -82,7 +67,8 @@ void RewriteSectionData(Elf_Scn* section, } // Verbose ELF header logging. -void VerboseLogElfHeader(const ELF::Ehdr* elf_header) { +template +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; @@ -93,8 +79,9 @@ void VerboseLogElfHeader(const ELF::Ehdr* elf_header) { } // Verbose ELF program header logging. -void VerboseLogProgramHeader(size_t program_header_index, - const ELF::Phdr* program_header) { +template +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; @@ -118,17 +105,19 @@ void VerboseLogProgramHeader(size_t program_header_index, } // Verbose ELF section header logging. -void VerboseLogSectionHeader(const std::string& section_name, - const ELF::Shdr* section_header) { +template +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; @@ -136,12 +125,11 @@ void VerboseLogSectionData(const Elf_Data* data) { 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 +bool ElfFile::Load() { if (elf_) return true; @@ -153,15 +141,12 @@ bool ElfFile::Load() { 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; @@ -173,19 +158,16 @@ bool ElfFile::Load() { 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) { @@ -193,7 +175,7 @@ bool ElfFile::Load() { dynamic_program_header = program_header; } } - CHECK(dynamic_program_header != NULL); + CHECK(dynamic_program_header != nullptr); size_t string_index; elf_getshdrstrndx(elf, &string_index); @@ -201,9 +183,8 @@ bool ElfFile::Load() { // 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. @@ -211,16 +192,16 @@ bool ElfFile::Load() { 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; } @@ -229,10 +210,7 @@ bool ElfFile::Load() { 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; } @@ -252,12 +230,6 @@ bool ElfFile::Load() { 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; @@ -277,17 +249,15 @@ bool ElfFile::Load() { 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 +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; @@ -299,437 +269,119 @@ void AdjustElfHeaderForHole(ELF::Ehdr* elf_header, } // Helper for ResizeSection(). Adjust all section headers for the hole. -void AdjustSectionHeadersForHole(Elf* elf, - ELF::Off hole_start, - ssize_t hole_size) { +template +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 +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; - +template +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]; 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; + program_header->p_offset <= hole_start && + (program_header->p_offset + program_header->p_filesz) >= hole_start ) { + return program_header; } } - LOG_IF(FATAL, !first_loadable_segment) - << "Cannot locate a LOAD segment with address and offset zero"; + LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start; + NOTREACHED(); - return first_loadable_segment; + return nullptr; } -// 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; - - for (size_t i = 0; i < count; ++i) { - 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 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); - } - } - - // 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); - } - } -} - -// 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 +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); + typename ELF::Phdr* target_load_header = + FindLoadSegmentForHole(elf_program_header, program_header_count, hole_start); - VLOG(1) << "phdr[" << split_header - elf_program_header << "] split"; - VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] new LOAD"; + 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; - // 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); - - 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); -} - -// 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_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 +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; @@ -741,7 +393,7 @@ Elf_Scn* GetDynamicSection(Elf* elf) { 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; @@ -753,47 +405,61 @@ Elf_Scn* GetDynamicSection(Elf* elf) { } // Helper for ResizeSection(). Adjust the .dynamic section for the hole. -template -void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, - ELF::Off hole_start, - ssize_t hole_size) { +template +void ElfFile::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(data->d_buf); - std::vector dynamics( + auto dynamic_base = reinterpret_cast(data->d_buf); + std::vector 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(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]; @@ -804,19 +470,19 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, // 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 -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 +void ElfFile::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. @@ -827,61 +493,72 @@ void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) { // 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_header, hole_start, hole_size); // Adjust all section headers. - AdjustSectionHeadersForHole(elf, hole_start, hole_size); + AdjustSectionHeadersForHole(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, hole_start, hole_size); - Elf_Scn* dynamic_section = GetDynamicSection(elf); - AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size); - } + Elf_Scn* dynamic_section = GetDynamicSection(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* dynamics) { +template +static size_t FindDynamicEntry(typename ELF::Sword tag, + std::vector* 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* dynamics) { - const size_t slot = FindDynamicEntry(DT_NULL, dynamics); +// Replace dynamic entry. +template +static void ReplaceDynamicEntry(typename ELF::Sword tag, + const typename ELF::Dyn& dyn, + std::vector* dynamics) { + const size_t slot = FindDynamicEntry(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. @@ -889,53 +566,10 @@ void AddDynamicEntry(const ELF::Dyn& dyn, 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* 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 -void PadRelocations(size_t count, std::vector* relocations) { - Rel null_relocation; - NullRelocation(&null_relocation); - std::vector 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 +bool ElfFile::PackRelocations() { // Load the ELF file into libelf. if (!Load()) { LOG(ERROR) << "Failed to load as ELF"; @@ -944,175 +578,129 @@ bool ElfFile::PackRelocations() { // 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 relocations; if (relocations_type_ == REL) { // Convert data to a vector of relocations. - const ELF::Rel* relocations_base = reinterpret_cast(data->d_buf); - std::vector relocations( - relocations_base, - relocations_base + data->d_size / sizeof(relocations[0])); - + const typename ELF::Rel* relocations_base = reinterpret_cast(data->d_buf); + ConvertRelArrayToRelaVector(relocations_base, + data->d_size / sizeof(typename ELF::Rel), &relocations); LOG(INFO) << "Relocations : REL"; - return PackTypedRelocations(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(data->d_buf); - std::vector relocations( + const typename ELF::Rela* relocations_base = reinterpret_cast(data->d_buf); + relocations = std::vector( relocations_base, relocations_base + data->d_size / sizeof(relocations[0])); LOG(INFO) << "Relocations : RELA"; - return PackTypedRelocations(relocations); + } else { + NOTREACHED(); } - NOTREACHED(); - return false; + return PackTypedRelocations(&relocations); } // Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. -template -bool ElfFile::PackTypedRelocations(const std::vector& relocations) { - // Filter relocations into those that are relative and others. - std::vector relative_relocations; - std::vector other_relocations; +template +bool ElfFile::PackTypedRelocations(std::vector* relocations) { + typedef typename ELF::Rela Rela; - 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"; - - // 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; + 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; - // 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; - } - - // 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]); - - // Padding may have removed any packing benefit. - if (padding >= relative_relocations.size()) { - LOG(INFO) << "Too few relative relocations to pack after padding"; - return false; - } - - // Add null relocations to other_relocations to preserve alignment. - PadRelocations(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(padding, &other_relocations); - } - - // Pack relative relocations. - const size_t initial_bytes = - relative_relocations.size() * sizeof(relative_relocations[0]); - LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes"; + LOG(INFO) << "Unpacked : " << initial_bytes << " bytes"; std::vector 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"; + RelocationPacker packer; + + // 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"; - // If we have insufficient relative relocations to form a run then - // packing fails. if (packed.empty()) { - LOG(INFO) << "Too few relative relocations to pack"; + LOG(INFO) << "Too few relocations to pack"; return false; } + // 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; + + // 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 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 unpacked; - packer.UnpackRelativeRelocations(packed, &unpacked); - CHECK(unpacked.size() == relative_relocations.size()); + std::vector 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 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 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(elf_, relocations_section_, bytes); - RewriteSectionData(relocations_section_, section_data, bytes); + // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt - // Rewrite the current packed android relocations section to hold the packed - // relative relocations. - ResizeSection(elf_, android_relocations_section_, packed_bytes); - RewriteSectionData(android_relocations_section_, packed_data, packed_bytes); - - // 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(data->d_buf); - std::vector dynamics( + const typename ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector 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(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(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); @@ -1124,15 +712,17 @@ bool ElfFile::PackTypedRelocations(const std::vector& relocations) { // 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 +bool ElfFile::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(data->d_buf); @@ -1140,118 +730,94 @@ bool ElfFile::UnpackRelocations() { 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(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; } - 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(packed); - } - - 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 -bool ElfFile::UnpackTypedRelocations(const std::vector& packed) { +template +bool ElfFile::UnpackTypedRelocations(const std::vector& 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 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 unpacked_relocations; + RelocationPacker 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(data->d_buf); - std::vector relocations( - relocations_base, - relocations_base + data->d_size / sizeof(relocations[0])); - - std::vector 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(elf_, relocations_section_, bytes); - RewriteSectionData(relocations_section_, section_data, bytes); + // Rewrite the current dynamic relocations section with unpacked version of + // relocations. + const void* section_data = nullptr; + std::vector 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(); + } - // 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( - elf_, android_relocations_section_, sizeof(kStubIdentifier)); - RewriteSectionData( - android_relocations_section_, &kStubIdentifier, sizeof(kStubIdentifier)); + 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(data->d_buf); - std::vector dynamics( + const typename ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector 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(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(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); @@ -1261,7 +827,8 @@ bool ElfFile::UnpackTypedRelocations(const std::vector& packed) { } // Flush rewritten shared object file data. -void ElfFile::Flush() { +template +void ElfFile::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); @@ -1269,6 +836,10 @@ void ElfFile::Flush() { // 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; @@ -1280,4 +851,32 @@ void ElfFile::Flush() { CHECK(truncate == 0); } +template +void ElfFile::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, + size_t rel_array_size, + std::vector* rela_vector) { + for (size_t i = 0; ipush_back(rela); + } +} + +template +void ElfFile::ConvertRelaVectorToRelVector(const std::vector& rela_vector, + std::vector* 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; +template class ElfFile; + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h index 655027497..73c31923f 100644 --- a/tools/relocation_packer/src/elf_file.h +++ b/tools/relocation_packer/src/elf_file.h @@ -67,12 +67,13 @@ namespace relocation_packer { // An ElfFile reads shared objects, and shuttles relative relocations // between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn // sections. +template class ElfFile { public: explicit ElfFile(int fd) : fd_(fd), is_padding_relocations_(false), elf_(NULL), relocations_section_(NULL), dynamic_section_(NULL), - android_relocations_section_(NULL), relocations_type_(NONE) {} + relocations_type_(NONE) {} ~ElfFile() {} // Set padding mode. When padding, PackRelocations() will not shrink @@ -92,6 +93,10 @@ class ElfFile { bool UnpackRelocations(); private: + enum relocations_type_t { + NONE = 0, REL, RELA + }; + // Load a new ElfFile from a filedescriptor. If flushing, the file must // be open for read/write. Returns true on successful ELF file load. // |fd| is an open file descriptor for the shared object. @@ -99,17 +104,34 @@ class ElfFile { // Templated packer, helper for PackRelocations(). Rel type is one of // ELF::Rel or ELF::Rela. - template - bool PackTypedRelocations(const std::vector& relocations); + bool PackTypedRelocations(std::vector* relocations); // Templated unpacker, helper for UnpackRelocations(). Rel type is one of // ELF::Rel or ELF::Rela. - template bool UnpackTypedRelocations(const std::vector& packed); // Write ELF file changes. void Flush(); + void AdjustRelativeRelocationTargets(typename ELF::Off hole_start, + ssize_t hole_size, + std::vector* 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* rela_vector); + + static void ConvertRelaVectorToRelVector(const std::vector& rela_vector, + std::vector* rel_vector); + + // File descriptor opened on the shared object. int fd_; @@ -123,10 +145,9 @@ class ElfFile { // Sections that we manipulate, assigned by Load(). Elf_Scn* relocations_section_; Elf_Scn* dynamic_section_; - Elf_Scn* android_relocations_section_; // Relocation type found, assigned by Load(). - enum { NONE = 0, REL, RELA } relocations_type_; + relocations_type_t relocations_type_; }; } // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc index 37abd0d95..434f10102 100644 --- a/tools/relocation_packer/src/elf_file_unittest.cc +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -11,12 +11,7 @@ #include #include "debug.h" #include "elf_traits.h" -#include "testing/gtest/include/gtest/gtest.h" - -// Macro stringification. -// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html -#define XSTR(S) STR(S) -#define STR(S) #S +#include "gtest/gtest.h" namespace { @@ -27,8 +22,6 @@ void GetDataFilePath(const char* name, std::string* path) { if (bindir) { data_dir = std::string(bindir); } else { - // Test data is in the gyp INTERMEDIATE_DIR subdirectory of the directory - // that contains the current binary. char path[PATH_MAX]; memset(path, 0, sizeof(path)); ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1)); @@ -37,8 +30,7 @@ void GetDataFilePath(const char* name, std::string* path) { size_t pos = data_dir.rfind('/'); ASSERT_NE(std::string::npos, pos); - data_dir.erase(pos + 1); - data_dir += std::string(XSTR(INTERMEDIATE_DIR)); + data_dir.erase(pos); } *path = data_dir + "/" + name; @@ -49,7 +41,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) { GetDataFilePath(name, &path); FILE* testfile = fopen(path.c_str(), "rb"); - ASSERT_FALSE(testfile == NULL); + ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'"; FILE* temporary = tmpfile(); ASSERT_FALSE(temporary == NULL); @@ -70,15 +62,7 @@ void OpenRelocsTestFile(const char* name, FILE** stream) { *stream = temporary; } -void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) { - const char* arch = NULL; - if (ELF::kMachine == EM_ARM) { - arch = "arm32"; - } else if (ELF::kMachine == EM_AARCH64) { - arch = "arm64"; - } - ASSERT_FALSE(arch == NULL); - +void OpenRelocsTestFiles(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) { const std::string base = std::string("elf_file_unittest_relocs_") + arch; const std::string relocs = base + ".so"; const std::string packed_relocs = base + "_packed.so"; @@ -115,41 +99,9 @@ void CheckFileContentsEqual(FILE* first, FILE* second) { EXPECT_TRUE(feof(first) && feof(second)); } -} // namespace - -namespace relocation_packer { - -TEST(ElfFile, PackRelocations) { - ASSERT_NE(EV_NONE, elf_version(EV_CURRENT)); - - FILE* relocs_so = NULL; - FILE* packed_relocs_so = NULL; - OpenRelocsTestFiles(&relocs_so, &packed_relocs_so); - if (HasFatalFailure()) - return; - - ElfFile elf_file(fileno(relocs_so)); - - // Ensure unpacking fails (not packed). - EXPECT_FALSE(elf_file.UnpackRelocations()); - - // Pack relocations, and check files are now identical. - EXPECT_TRUE(elf_file.PackRelocations()); - CheckFileContentsEqual(relocs_so, packed_relocs_so); - - CloseRelocsTestFiles(relocs_so, packed_relocs_so); -} - -TEST(ElfFile, UnpackRelocations) { - ASSERT_NE(EV_NONE, elf_version(EV_CURRENT)); - - FILE* relocs_so = NULL; - FILE* packed_relocs_so = NULL; - OpenRelocsTestFiles(&relocs_so, &packed_relocs_so); - if (HasFatalFailure()) - return; - - ElfFile elf_file(fileno(packed_relocs_so)); +template +static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) { + relocation_packer::ElfFile elf_file(fileno(packed_relocs_so)); // Ensure packing fails (already packed). EXPECT_FALSE(elf_file.PackRelocations()); @@ -161,4 +113,76 @@ TEST(ElfFile, UnpackRelocations) { CloseRelocsTestFiles(relocs_so, packed_relocs_so); } +static void RunUnpackRelocationsTestFor(const std::string& arch) { + ASSERT_NE(static_cast(EV_NONE), elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so); + + if (relocs_so != NULL && packed_relocs_so != NULL) { + // lets detect elf class + ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET)) + << "Invalid file length: " << strerror(errno); + uint8_t elf_class = 0; + ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so)); + ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET)); + if (elf_class == ELFCLASS32) { + ProcessUnpack(relocs_so, packed_relocs_so); + } else { + ProcessUnpack(relocs_so, packed_relocs_so); + } + } +} + +template +static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) { + relocation_packer::ElfFile elf_file(fileno(relocs_so)); + + // Ensure unpacking fails (not packed). + EXPECT_FALSE(elf_file.UnpackRelocations()); + + // Pack relocations, and check files are now identical. + EXPECT_TRUE(elf_file.PackRelocations()); + CheckFileContentsEqual(relocs_so, packed_relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +static void RunPackRelocationsTestFor(const std::string& arch) { + ASSERT_NE(static_cast(EV_NONE), elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so); + + if (relocs_so != NULL && packed_relocs_so != NULL) { + // lets detect elf class + ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET)) + << "Invalid file length: " << strerror(errno); + uint8_t elf_class = 0; + ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so)); + fseek(packed_relocs_so, 0, SEEK_SET); + if (elf_class == ELFCLASS32) { + ProcessPack(relocs_so, packed_relocs_so); + } else { + ProcessPack(relocs_so, packed_relocs_so); + } + } +} + +} // namespace + +namespace relocation_packer { + +TEST(ElfFile, PackRelocations) { + RunPackRelocationsTestFor("arm32"); + RunPackRelocationsTestFor("arm64"); +} + +TEST(ElfFile, UnpackRelocations) { + RunUnpackRelocationsTestFor("arm32"); + RunUnpackRelocationsTestFor("arm64"); +} + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_traits.h b/tools/relocation_packer/src/elf_traits.h index f099bab60..41b06c857 100644 --- a/tools/relocation_packer/src/elf_traits.h +++ b/tools/relocation_packer/src/elf_traits.h @@ -10,31 +10,10 @@ #include "elf.h" #include "libelf.h" -// The TARGET_ macro controls which Elf types we expect and handle. -// Either TARGET_ARM or TARGET_ARM64 must be defined, but not both. - -#if !defined(TARGET_ARM) && !defined(TARGET_ARM64) -# error "Unsupported target, define one of TARGET_ARM or TARGET_ARM64" -#elif defined(TARGET_ARM) && defined(TARGET_ARM64) -# error "Define one of TARGET_ARM or TARGET_ARM64, but not both" -#endif - -// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h. -#ifndef EM_AARCH64 -#define EM_AARCH64 183 -#endif -#ifndef R_AARCH64_RELATIVE -#define R_AARCH64_RELATIVE 1027 -#endif -#ifndef R_AARCH64_NONE -#define R_AARCH64_NONE 0 -#endif - // ELF is a traits structure used to provide convenient aliases for -// 32/64 bit Elf types and functions, depending on the target specified. +// 32/64 bit Elf types and functions, depending on the target file. -#if defined(TARGET_ARM) -struct ELF { +struct ELF32_traits { typedef Elf32_Addr Addr; typedef Elf32_Dyn Dyn; typedef Elf32_Ehdr Ehdr; @@ -48,27 +27,17 @@ struct ELF { typedef Elf32_Sym Sym; typedef Elf32_Word Word; typedef Elf32_Xword Xword; + typedef Elf32_Half Half; static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); } static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); } static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); } - - enum { kMachine = EM_ARM }; - enum { kFileClass = ELFCLASS32 }; - enum { kRelativeRelocationCode = R_ARM_RELATIVE }; - enum { kNoRelocationCode = R_ARM_NONE }; - enum { kGnuStackSegmentAlignment = 0 }; - - static inline const char* Machine() { return "ARM"; } - -# define ELF_R_SYM(val) ELF32_R_SYM(val) -# define ELF_R_TYPE(val) ELF32_R_TYPE(val) -# define ELF_R_INFO(sym, type) ELF32_R_INFO(sym, type) -# define ELF_ST_TYPE(val) ELF32_ST_TYPE(val) + static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); } + static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); } + static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); } }; -#elif defined(TARGET_ARM64) -struct ELF { +struct ELF64_traits { typedef Elf64_Addr Addr; typedef Elf64_Dyn Dyn; typedef Elf64_Ehdr Ehdr; @@ -82,24 +51,14 @@ struct ELF { typedef Elf64_Sym Sym; typedef Elf64_Word Word; typedef Elf64_Xword Xword; + typedef Elf64_Half Half; static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); } static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); } static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); } - - enum { kMachine = EM_AARCH64 }; - enum { kFileClass = ELFCLASS64 }; - enum { kRelativeRelocationCode = R_AARCH64_RELATIVE }; - enum { kNoRelocationCode = R_AARCH64_NONE }; - enum { kGnuStackSegmentAlignment = 16 }; - - static inline const char* Machine() { return "ARM64"; } - -# define ELF_R_SYM(val) ELF64_R_SYM(val) -# define ELF_R_TYPE(val) ELF64_R_TYPE(val) -# define ELF_R_INFO(sym, type) ELF64_R_INFO(sym, type) -# define ELF_ST_TYPE(val) ELF64_ST_TYPE(val) + static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); } + static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); } + static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); } }; -#endif #endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ diff --git a/tools/relocation_packer/src/leb128.cc b/tools/relocation_packer/src/leb128.cc index b48739c32..101c55778 100644 --- a/tools/relocation_packer/src/leb128.cc +++ b/tools/relocation_packer/src/leb128.cc @@ -12,40 +12,50 @@ namespace relocation_packer { // Empty constructor and destructor to silence chromium-style. -Leb128Encoder::Leb128Encoder() { } -Leb128Encoder::~Leb128Encoder() { } +template +Leb128Encoder::Leb128Encoder() { } + +template +Leb128Encoder::~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 +void Leb128Encoder::Enqueue(uint_t value) { + uint_t uvalue = static_cast(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& values) { - for (size_t i = 0; i < values.size(); ++i) +template +void Leb128Encoder::EnqueueAll(const std::vector& 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& encoding) { +template +Leb128Decoder::Leb128Decoder(const std::vector& encoding, size_t start_with) { encoding_ = encoding; - cursor_ = 0; + cursor_ = start_with; } // Empty destructor to silence chromium-style. -Leb128Decoder::~Leb128Decoder() { } +template +Leb128Decoder::~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 +uint_t Leb128Decoder::Dequeue() { + uint_t value = 0; size_t shift = 0; uint8_t byte; @@ -53,7 +63,7 @@ ELF::Xword Leb128Decoder::Dequeue() { // Loop until we reach a byte with its high order bit clear. do { byte = encoding_[cursor_++]; - value |= static_cast(byte & 127) << shift; + value |= static_cast(byte & 127) << shift; shift += 7; } while (byte & 128); @@ -61,9 +71,17 @@ ELF::Xword Leb128Decoder::Dequeue() { } // Decode and retrieve all remaining values from the encoding. -void Leb128Decoder::DequeueAll(std::vector* values) { - while (cursor_ < encoding_.size()) +template +void Leb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) { values->push_back(Dequeue()); + } } +template class Leb128Encoder; +template class Leb128Encoder; + +template class Leb128Decoder; +template class Leb128Decoder; + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h index 6cc2d7caf..2c5b5d079 100644 --- a/tools/relocation_packer/src/leb128.h +++ b/tools/relocation_packer/src/leb128.h @@ -21,6 +21,7 @@ namespace relocation_packer { // Encode packed words as a LEB128 byte stream. +template class Leb128Encoder { public: // Explicit (but empty) constructor and destructor, for chromium-style. @@ -29,11 +30,11 @@ class Leb128Encoder { // Add a value to the encoding stream. // |value| is the unsigned int to add. - void Enqueue(ELF::Xword value); + void Enqueue(uint_t value); // Add a vector of values to the encoding stream. // |values| is the vector of unsigned ints to add. - void EnqueueAll(const std::vector& values); + void EnqueueAll(const std::vector& values); // Retrieve the encoded representation of the values. // |encoding| is the returned vector of encoded data. @@ -45,21 +46,22 @@ class Leb128Encoder { }; // Decode a LEB128 byte stream to produce packed words. +template class Leb128Decoder { public: // Create a new decoder for the given encoded stream. // |encoding| is the vector of encoded data. - explicit Leb128Decoder(const std::vector& encoding); + explicit Leb128Decoder(const std::vector& 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* values); + void DequeueAll(std::vector* values); private: // Encoded LEB128 stream. diff --git a/tools/relocation_packer/src/leb128_unittest.cc b/tools/relocation_packer/src/leb128_unittest.cc index bd607b717..8a7028cbc 100644 --- a/tools/relocation_packer/src/leb128_unittest.cc +++ b/tools/relocation_packer/src/leb128_unittest.cc @@ -5,19 +5,19 @@ #include "leb128.h" #include -#include "testing/gtest/include/gtest/gtest.h" +#include "gtest/gtest.h" namespace relocation_packer { -TEST(Leb128, Encoder) { - std::vector values; +TEST(Leb128, Encoder64) { + std::vector values; values.push_back(624485); values.push_back(0); values.push_back(1); values.push_back(127); values.push_back(128); - Leb128Encoder encoder; + Leb128Encoder encoder; encoder.EnqueueAll(values); encoder.Enqueue(4294967295); @@ -26,7 +26,7 @@ TEST(Leb128, Encoder) { std::vector encoding; encoder.GetEncoding(&encoding); - EXPECT_EQ(23, encoding.size()); + EXPECT_EQ(23U, encoding.size()); // 624485 EXPECT_EQ(0xe5, encoding[0]); EXPECT_EQ(0x8e, encoding[1]); @@ -59,7 +59,7 @@ TEST(Leb128, Encoder) { EXPECT_EQ(0x01, encoding[22]); } -TEST(Leb128, Decoder) { +TEST(Leb128, Decoder64) { std::vector encoding; // 624485 encoding.push_back(0xe5); @@ -92,20 +92,20 @@ TEST(Leb128, Decoder) { encoding.push_back(0xff); encoding.push_back(0x01); - Leb128Decoder decoder(encoding); + Leb128Decoder decoder(encoding, 0); - EXPECT_EQ(624485, decoder.Dequeue()); + EXPECT_EQ(624485U, decoder.Dequeue()); - std::vector dequeued; + std::vector 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 diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc index 28f5b0469..3f784e4f3 100644 --- a/tools/relocation_packer/src/main.cc +++ b/tools/relocation_packer/src/main.cc @@ -25,11 +25,12 @@ #include "debug.h" #include "elf_file.h" +#include "elf_traits.h" #include "libelf.h" -namespace { +#include "nativehelper/ScopedFd.h" -void PrintUsage(const char* argv0) { +static void PrintUsage(const char* argv0) { std::string temporary = argv0; const size_t last_slash = temporary.find_last_of("/"); if (last_slash != temporary.npos) { @@ -45,59 +46,11 @@ void PrintUsage(const char* argv0) { " -p, --pad do not shrink relocations, but pad (for debugging)\n\n", basename); - if (ELF::kMachine == EM_ARM) { - printf( - "Extracts relative relocations from the .rel.dyn section, packs them\n" - "into a more compact format, and stores the packed relocations in\n" - ".android.rel.dyn. Expands .android.rel.dyn to hold the packed\n" - "data, and shrinks .rel.dyn by the amount of unpacked data removed\n" - "from it.\n\n" - "Before being packed, a shared library needs to be prepared by adding\n" - "a null .android.rel.dyn section.\n\n" - "To pack relocations in a shared library:\n\n" - " echo -n 'NULL' >/tmp/small\n" - " arm-linux-androideabi-objcopy \\\n" - " --add-section .android.rel.dyn=/tmp/small \\\n" - " libchrome..so\n" - " rm /tmp/small\n" - " %s libchrome..so\n\n" - "To unpack and restore the shared library to its original state:\n\n" - " %s -u libchrome..so\n" - " arm-linux-androideabi-objcopy \\\n" - " --remove-section=.android.rel.dyn libchrome..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..so\n" - " rm /tmp/small\n" - " %s libchrome..so\n\n" - "To unpack and restore the shared library to its original state:\n\n" - " %s -u libchrome..so\n" - " aarch64-linux-android-objcopy \\\n" - " --remove-section=.android.rela.dyn libchrome..so\n\n", - basename, basename); - } else { - NOTREACHED(); - } - printf( "Debug sections are not handled, so packing should not be used on\n" "shared libraries compiled for debugging or otherwise unstripped.\n"); } -} // namespace - int main(int argc, char* argv[]) { bool is_unpacking = false; bool is_verbose = false; @@ -143,11 +96,9 @@ int main(int argc, char* argv[]) { LOG(WARNING) << "Elf Library is out of date!"; } - LOG(INFO) << "Configured for " << ELF::Machine(); - const char* file = argv[argc - 1]; - const int fd = open(file, O_RDWR); - if (fd == -1) { + ScopedFd fd(open(file, O_RDWR)); + if (fd.get() == -1) { LOG(ERROR) << file << ": " << strerror(errno); return 1; } @@ -155,16 +106,43 @@ int main(int argc, char* argv[]) { if (is_verbose) relocation_packer::Logger::SetVerbose(1); - relocation_packer::ElfFile elf_file(fd); - elf_file.SetPadding(is_padding); + // We need to detect elf class in order to create + // correct implementation + uint8_t e_ident[EI_NIDENT]; + if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) { + LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno); + return 1; + } - bool status; - if (is_unpacking) - status = elf_file.UnpackRelocations(); - else - status = elf_file.PackRelocations(); + if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) { + LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno); + return 1; + } - close(fd); + bool status = false; + + if (e_ident[EI_CLASS] == ELFCLASS32) { + relocation_packer::ElfFile 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 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"; diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc index 29bec1e3c..8e30612e5 100644 --- a/tools/relocation_packer/src/packer.cc +++ b/tools/relocation_packer/src/packer.cc @@ -10,115 +10,79 @@ #include "delta_encoder.h" #include "elf_traits.h" #include "leb128.h" -#include "run_length_encoder.h" #include "sleb128.h" namespace relocation_packer { -// Pack relative relocations into a run-length encoded packed -// representation. -void RelocationPacker::PackRelativeRelocations( - const std::vector& relocations, - std::vector* packed) { +// Pack relocations into a group encoded packed representation. +template +void RelocationPacker::PackRelocations(const std::vector& relocations, + std::vector* packed) { // Run-length encode. - std::vector packed_words; - RelocationRunLengthCodec codec; + std::vector packed_words; + RelocationDeltaCodec codec; codec.Encode(relocations, &packed_words); - // If insufficient data to run-length encode, do nothing. + // If insufficient data do nothing. if (packed_words.empty()) return; - // LEB128 encode, with "APR1" prefix. - Leb128Encoder encoder; - encoder.Enqueue('A'); - encoder.Enqueue('P'); - encoder.Enqueue('R'); - encoder.Enqueue('1'); - encoder.EnqueueAll(packed_words); + Sleb128Encoder sleb128_encoder; + Leb128Encoder leb128_encoder; - encoder.GetEncoding(packed); + std::vector leb128_packed; + std::vector sleb128_packed; - // Pad packed to a whole number of words. This padding will decode as - // LEB128 zeroes. Run-length decoding ignores it because encoding - // embeds the pairs count in the stream itself. - while (packed->size() % sizeof(ELF::Word)) - packed->push_back(0); + leb128_encoder.EnqueueAll(packed_words); + leb128_encoder.GetEncoding(&leb128_packed); + + sleb128_encoder.EnqueueAll(packed_words); + sleb128_encoder.GetEncoding(&sleb128_packed); + + // TODO (simonb): Estimate savings on current android system image and consider using + // one encoder for all packed relocations to reduce complexity. + if (leb128_packed.size() <= sleb128_packed.size()) { + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('U'); + packed->push_back('2'); + packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end()); + } else { + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('S'); + packed->push_back('2'); + packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end()); + } } // Unpack relative relocations from a run-length encoded packed // representation. -void RelocationPacker::UnpackRelativeRelocations( +template +void RelocationPacker::UnpackRelocations( const std::vector& packed, - std::vector* relocations) { - // LEB128 decode, after checking and stripping "APR1" prefix. - std::vector packed_words; - Leb128Decoder decoder(packed); - CHECK(decoder.Dequeue() == 'A' && - decoder.Dequeue() == 'P' && - decoder.Dequeue() == 'R' && - decoder.Dequeue() == '1'); - decoder.DequeueAll(&packed_words); + std::vector* relocations) { - // Run-length decode. - RelocationRunLengthCodec codec; + std::vector 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 decoder(packed, 4); + decoder.DequeueAll(&packed_words); + } else { + Sleb128Decoder decoder(packed, 4); + decoder.DequeueAll(&packed_words); + } + + RelocationDeltaCodec codec; codec.Decode(packed_words, relocations); } -// Pack relative relocations with addends into a delta encoded packed -// representation. -void RelocationPacker::PackRelativeRelocations( - const std::vector& relocations, - std::vector* packed) { - // Delta encode. - std::vector 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 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& packed, - std::vector* 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 packed_words; - std::vector 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; +template class RelocationPacker; } // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h index db09ce8cc..8a57e623b 100644 --- a/tools/relocation_packer/src/packer.h +++ b/tools/relocation_packer/src/packer.h @@ -48,29 +48,25 @@ #include #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 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& relocations, - std::vector* packed); - static void PackRelativeRelocations(const std::vector& relocations, - std::vector* packed); + static void PackRelocations(const std::vector& relocations, + std::vector* 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& packed, - std::vector* relocations); - static void UnpackRelativeRelocations(const std::vector& packed, - std::vector* relocations); + // |relocations| is a vector of unpacked relocation structs. + static void UnpackRelocations(const std::vector& packed, + std::vector* relocations); }; } // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc index de5be7979..8dddd8b41 100644 --- a/tools/relocation_packer/src/packer_unittest.cc +++ b/tools/relocation_packer/src/packer_unittest.cc @@ -7,244 +7,286 @@ #include #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* relocations) { - ELF::Rel relocation; +template +static void AddRelocation(typename ELF::Addr addr, + typename ELF::Xword info, + typename ELF::Sxword addend, + std::vector* 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* 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 +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 relocations; +template +static void DoPackNoAddend() { + std::vector relocations; std::vector packed; - - RelocationPacker packer; - // Initial relocation. - AddRelocation(0xd1ce0000, &relocations); + AddRelocation(0xd1ce0000, 0x11, 0, &relocations); // Two more relocations, 4 byte deltas. - AddRelocation(0xd1ce0004, &relocations); - AddRelocation(0xd1ce0008, &relocations); + AddRelocation(0xd1ce0004, 0x11, 0, &relocations); + AddRelocation(0xd1ce0008, 0x11, 0, &relocations); // Three more relocations, 8 byte deltas. - AddRelocation(0xd1ce0010, &relocations); - AddRelocation(0xd1ce0018, &relocations); - AddRelocation(0xd1ce0020, &relocations); + AddRelocation(0xd1ce0010, 0x11, 0, &relocations); + AddRelocation(0xd1ce0018, 0x11, 0, &relocations); + AddRelocation(0xd1ce0020, 0x11, 0, &relocations); + + RelocationPacker packer; packed.clear(); - packer.PackRelativeRelocations(relocations, &packed); + packer.PackRelocations(relocations, &packed); - EXPECT_EQ(16, packed.size()); + ASSERT_EQ(18U, packed.size()); // Identifier. - EXPECT_EQ('A', packed[0]); - EXPECT_EQ('P', packed[1]); - EXPECT_EQ('R', packed[2]); - EXPECT_EQ('1', packed[3]); - // Count-delta pairs count. - EXPECT_EQ(2, packed[4]); - // 0xd1ce0000 - EXPECT_EQ(128, packed[5]); - EXPECT_EQ(128, packed[6]); - EXPECT_EQ(184, packed[7]); - EXPECT_EQ(142, packed[8]); - EXPECT_EQ(13, packed[9]); - // Run of two relocations, 4 byte deltas. - EXPECT_EQ(2, packed[10]); - EXPECT_EQ(4, packed[11]); - // Run of three relocations, 8 byte deltas. - EXPECT_EQ(3, packed[12]); - EXPECT_EQ(8, packed[13]); - // Padding. - EXPECT_EQ(0, packed[14]); - EXPECT_EQ(0, packed[15]); + size_t ndx = 0; + EXPECT_EQ('A', packed[ndx++]); + EXPECT_EQ('P', packed[ndx++]); + EXPECT_EQ('U', packed[ndx++]); + EXPECT_EQ('2', packed[ndx++]); + // relocation count + EXPECT_EQ(6, packed[ndx++]); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + EXPECT_EQ(0xfc, packed[ndx++]); + EXPECT_EQ(0xff, packed[ndx++]); + EXPECT_EQ(0xb7, packed[ndx++]); + EXPECT_EQ(0x8e, packed[ndx++]); + EXPECT_EQ(0x0d, packed[ndx++]); + // first group + EXPECT_EQ(3, packed[ndx++]); // size + EXPECT_EQ(3, packed[ndx++]); // flags + EXPECT_EQ(4, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x11, packed[ndx++]); // r_info + // second group + EXPECT_EQ(3, packed[ndx++]); // size + EXPECT_EQ(3, packed[ndx++]); // flags + EXPECT_EQ(8, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x11, packed[ndx++]); // r_info + + EXPECT_EQ(ndx, packed.size()); } -TEST(Packer, UnpackRel) { +TEST(Packer, PackNoAddend) { + DoPackNoAddend(); + DoPackNoAddend(); +} + +template +static void DoUnpackNoAddend() { + std::vector relocations; std::vector packed; - std::vector relocations; - - RelocationPacker packer; - - // Identifier. packed.push_back('A'); packed.push_back('P'); - packed.push_back('R'); - packed.push_back('1'); - // Count-delta pairs count. - packed.push_back(2); - // 0xd1ce0000 - packed.push_back(128); - packed.push_back(128); - packed.push_back(184); - packed.push_back(142); - packed.push_back(13); - // Run of two relocations, 4 byte deltas. - packed.push_back(2); - packed.push_back(4); - // Run of three relocations, 8 byte deltas. - packed.push_back(3); - packed.push_back(8); - // Padding. - packed.push_back(0); - packed.push_back(0); - - relocations.clear(); - packer.UnpackRelativeRelocations(packed, &relocations); - - EXPECT_EQ(6, relocations.size()); - // Initial relocation. - EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0])); - // Two relocations, 4 byte deltas. - EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1])); - EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2])); - // Three relocations, 8 byte deltas. - EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3])); - EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4])); - EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5])); -} - -TEST(Packer, PackRela) { - std::vector relocations; - std::vector packed; - - RelocationPacker packer; - - // Initial relocation. - AddRelocation(0xd1ce0000, 10000, &relocations); - // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. - AddRelocation(0xd1ce0004, 10012, &relocations); - AddRelocation(0xd1ce0008, 10024, &relocations); - // Three more relocations, 8 byte deltas, -24 byte addend deltas. - AddRelocation(0xd1ce0010, 10000, &relocations); - AddRelocation(0xd1ce0018, 9976, &relocations); - AddRelocation(0xd1ce0020, 9952, &relocations); - - packed.clear(); - packer.PackRelativeRelocations(relocations, &packed); - - EXPECT_EQ(24, packed.size()); - // Identifier. - EXPECT_EQ('A', packed[0]); - EXPECT_EQ('P', packed[1]); - EXPECT_EQ('A', packed[2]); - EXPECT_EQ('1', packed[3]); - // Delta pairs count. - EXPECT_EQ(6, packed[4]); - // 0xd1ce0000 - EXPECT_EQ(128, packed[5]); - EXPECT_EQ(128, packed[6]); - EXPECT_EQ(184, packed[7]); - EXPECT_EQ(142, packed[8]); - EXPECT_EQ(13, packed[9]); - // 10000 - EXPECT_EQ(144, packed[10]); - EXPECT_EQ(206, packed[11]); - EXPECT_EQ(0, packed[12]); - // 4, 12 - EXPECT_EQ(4, packed[13]); - EXPECT_EQ(12, packed[14]); - // 4, 12 - EXPECT_EQ(4, packed[15]); - EXPECT_EQ(12, packed[16]); - // 8, -24 - EXPECT_EQ(8, packed[17]); - EXPECT_EQ(104, packed[18]); - // 8, -24 - EXPECT_EQ(8, packed[19]); - EXPECT_EQ(104, packed[20]); - // 8, -24 - EXPECT_EQ(8, packed[21]); - EXPECT_EQ(104, packed[22]); - // Padding. - EXPECT_EQ(0, packed[23]); -} - -TEST(Packer, UnpackRela) { - std::vector packed; - std::vector relocations; - - RelocationPacker packer; - - // Identifier. - packed.push_back('A'); - packed.push_back('P'); - packed.push_back('A'); - packed.push_back('1'); - // Delta pairs count. + packed.push_back('U'); + packed.push_back('2'); + // relocation count packed.push_back(6); - // 0xd1ce0000 - packed.push_back(128); - packed.push_back(128); - packed.push_back(184); - packed.push_back(142); - packed.push_back(13); - // 10000 - packed.push_back(144); - packed.push_back(206); - packed.push_back(0); - // 4, 12 - packed.push_back(4); - packed.push_back(12); - // 4, 12 - packed.push_back(4); - packed.push_back(12); - // 8, -24 - packed.push_back(8); - packed.push_back(104); - // 8, -24 - packed.push_back(8); - packed.push_back(104); - // 8, -24 - packed.push_back(8); - packed.push_back(104); - // Padding. - packed.push_back(0); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + packed.push_back(0xfc); + packed.push_back(0xff); + packed.push_back(0xb7); + packed.push_back(0x8e); + packed.push_back(0x0d); + // first group + packed.push_back(3); // size + packed.push_back(3); // flags + packed.push_back(4); // r_offset_delta + packed.push_back(0x11); // r_info + // second group + packed.push_back(3); // size + packed.push_back(3); // flags + packed.push_back(8); // r_offset_delta + packed.push_back(0x11); // r_info + + RelocationPacker packer; + packer.UnpackRelocations(packed, &relocations); + + size_t ndx = 0; + EXPECT_EQ(6U, relocations.size()); + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 0x11, 0, relocations[ndx++])); + + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 0x11, 0, relocations[ndx++])); + + EXPECT_EQ(ndx, relocations.size()); +} + +TEST(Packer, UnpackNoAddend) { + DoUnpackNoAddend(); + DoUnpackNoAddend(); +} + +template +static void DoPackWithAddend() { + std::vector relocations; + + // Initial relocation. + AddRelocation(0xd1ce0000, 0x01, 10024, &relocations); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + AddRelocation(0xd1ce0004, 0x01, 10012, &relocations); + AddRelocation(0xd1ce0008, 0x01, 10024, &relocations); + // Three more relocations, 8 byte deltas, -24 byte addend deltas. + AddRelocation(0xd1ce0010, 0x01, 10000, &relocations); + AddRelocation(0xd1ce0018, 0x01, 9976, &relocations); + AddRelocation(0xd1ce0020, 0x01, 9952, &relocations); + + std::vector packed; + + RelocationPacker packer; + + packed.clear(); + packer.PackRelocations(relocations, &packed); + + EXPECT_EQ(26U, packed.size()); + size_t ndx = 0; + // Identifier. + EXPECT_EQ('A', packed[ndx++]); + EXPECT_EQ('P', packed[ndx++]); + EXPECT_EQ('S', packed[ndx++]); + EXPECT_EQ('2', packed[ndx++]); + // Relocation count + EXPECT_EQ(6U, packed[ndx++]); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr) + EXPECT_EQ(0xfc, packed[ndx++]); + EXPECT_EQ(0xff, packed[ndx++]); + EXPECT_EQ(0xb7, packed[ndx++]); + EXPECT_EQ(0x8e, packed[ndx++]); + if (sizeof(typename ELF::Addr) == 8) { + // positive for uint64_t + EXPECT_EQ(0x0d, packed[ndx++]); + } else { + // negative for uint32_t + EXPECT_EQ(0x7d, packed[ndx++]); + } + // group 1 + EXPECT_EQ(0x03, packed[ndx++]); // size + EXPECT_EQ(0x0b, packed[ndx++]); // flags + EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x01, packed[ndx++]); // r_info + // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80 + EXPECT_EQ(0xa8, packed[ndx++]); + EXPECT_EQ(0xce, packed[ndx++]); + EXPECT_EQ(0x00, packed[ndx++]); + // group 1 - addend 2: -12 = 0x74 + EXPECT_EQ(0x74, packed[ndx++]); + // group 1 - addend 3: +12 = 0x0c + EXPECT_EQ(0x0c, packed[ndx++]); + + // group 2 + EXPECT_EQ(0x03, packed[ndx++]); // size + EXPECT_EQ(0x0b, packed[ndx++]); // flags + EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta + EXPECT_EQ(0x01, packed[ndx++]); // r_info + + // group 2 - addend 1: -24 = 0x68 + EXPECT_EQ(0x68, packed[ndx++]); + // group 2 - addend 2: -24 = 0x68 + EXPECT_EQ(0x68, packed[ndx++]); + // group 2 - addend 3: -24 = 0x68 + EXPECT_EQ(0x68, packed[ndx++]); + + EXPECT_EQ(ndx, packed.size()); +} + +TEST(Packer, PackWithAddend) { + DoPackWithAddend(); + DoPackWithAddend(); +} + +template +static void DoUnpackWithAddend() { + std::vector packed; + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('S'); + packed.push_back('2'); + // Relocation count + packed.push_back(6U); + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + packed.push_back(0xfc); + packed.push_back(0xff); + packed.push_back(0xb7); + packed.push_back(0x8e); + if (sizeof(typename ELF::Addr) == 8) { + // positive for uint64_t + packed.push_back(0x0d); + } else { + // negative for uint32_t + packed.push_back(0x7d); + } + // group 1 + packed.push_back(0x03); // size + packed.push_back(0x0b); // flags + packed.push_back(0x04); // r_offset_delta + packed.push_back(0x01); // r_info + // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80 + packed.push_back(0xa8); + packed.push_back(0xce); + packed.push_back(0x00); + // group 1 - addend 2: -12 = 0x74 + packed.push_back(0x74); + // group 1 - addend 3: +12 = 0x0c + packed.push_back(0x0c); + + // group 2 + packed.push_back(0x03); // size + packed.push_back(0x0b); // flags + packed.push_back(0x08); // r_offset_delta + packed.push_back(0x01); // r_info + + // group 2 - addend 1: -24 = 0x68 + packed.push_back(0x68); + // group 2 - addend 2: -24 = 0x68 + packed.push_back(0x68); + // group 2 - addend 3: -24 = 0x68 + packed.push_back(0x68); + + std::vector relocations; + + RelocationPacker 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(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(0xd1ce0004, 0x01, 10012, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(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(0xd1ce0010, 0x01, 10000, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 0x01, 9976, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 0x01, 9952, relocations[ndx++])); + + EXPECT_EQ(ndx, relocations.size()); +} + +TEST(Packer, UnpackWithAddend) { + DoUnpackWithAddend(); + DoUnpackWithAddend(); } } // namespace relocation_packer diff --git a/tools/relocation_packer/src/run_all_unittests.cc b/tools/relocation_packer/src/run_all_unittests.cc deleted file mode 100644 index 4122be18c..000000000 --- a/tools/relocation_packer/src/run_all_unittests.cc +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tools/relocation_packer/src/run_length_encoder.cc b/tools/relocation_packer/src/run_length_encoder.cc deleted file mode 100644 index 2f2e1c315..000000000 --- a/tools/relocation_packer/src/run_length_encoder.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "run_length_encoder.h" - -#include - -#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& relocations, - std::vector* 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& deltas, - std::vector* 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& packed, - size_t start_index, - size_t end_index, - std::vector* 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& relocations, - std::vector* 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 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& packed, - std::vector* 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 diff --git a/tools/relocation_packer/src/run_length_encoder_unittest.cc b/tools/relocation_packer/src/run_length_encoder_unittest.cc deleted file mode 100644 index 83370f2a9..000000000 --- a/tools/relocation_packer/src/run_length_encoder_unittest.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "run_length_encoder.h" - -#include -#include "elf.h" -#include "elf_traits.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -void AddRelocation(ELF::Addr addr, std::vector* 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 relocations; - std::vector 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 packed; - std::vector 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 diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc index a10bd79a7..12c21e364 100644 --- a/tools/relocation_packer/src/sleb128.cc +++ b/tools/relocation_packer/src/sleb128.cc @@ -10,11 +10,33 @@ #include "elf_traits.h" +namespace { + +template +class uint_traits {}; + +template <> +class uint_traits { + public: + typedef int64_t int_t; +}; + +template <> +class uint_traits { + public: + typedef int32_t int_t; +}; + +} + namespace relocation_packer { // Empty constructor and destructor to silence chromium-style. -Sleb128Encoder::Sleb128Encoder() { } -Sleb128Encoder::~Sleb128Encoder() { } +template +Sleb128Encoder::Sleb128Encoder() { } + +template +Sleb128Encoder::~Sleb128Encoder() { } // Add a single value to the encoding. Values are encoded with variable // length. The least significant 7 bits of each byte hold 7 bits of data, @@ -22,11 +44,13 @@ Sleb128Encoder::~Sleb128Encoder() { } // value is sign extended up to a multiple of 7 bits (ensuring that the // most significant bit is zero for a positive number and one for a // negative number). -void Sleb128Encoder::Enqueue(ELF::Sxword value) { +template +void Sleb128Encoder::Enqueue(uint_t value) { + typedef typename uint_traits::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(value) < 0; while (more) { uint8_t byte = value & 127; @@ -34,11 +58,11 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) { // Sign extend if encoding a -ve value. if (negative) - value |= -(static_cast(1) << (size - 7)); + value |= -(static_cast(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(-1) && sign_bit)) more = false; else byte |= 128; @@ -47,25 +71,30 @@ void Sleb128Encoder::Enqueue(ELF::Sxword value) { } // Add a vector of values to the encoding. -void Sleb128Encoder::EnqueueAll(const std::vector& values) { - for (size_t i = 0; i < values.size(); ++i) +template +void Sleb128Encoder::EnqueueAll(const std::vector& 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& encoding) { +template +Sleb128Decoder::Sleb128Decoder(const std::vector& encoding, size_t start_with) { encoding_ = encoding; - cursor_ = 0; + cursor_ = start_with; } // Empty destructor to silence chromium-style. -Sleb128Decoder::~Sleb128Decoder() { } +template +Sleb128Decoder::~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 +uint_t Sleb128Decoder::Dequeue() { + uint_t value = 0; static const size_t size = CHAR_BIT * sizeof(value); size_t shift = 0; @@ -74,22 +103,29 @@ ELF::Sxword Sleb128Decoder::Dequeue() { // Loop until we reach a byte with its high order bit clear. do { byte = encoding_[cursor_++]; - value |= (static_cast(byte & 127) << shift); + value |= (static_cast(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(1) << shift); + value |= -(static_cast(1) << shift); - return value; + return static_cast(value); } // Decode and retrieve all remaining values from the encoding. -void Sleb128Decoder::DequeueAll(std::vector* values) { - while (cursor_ < encoding_.size()) +template +void Sleb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) { values->push_back(Dequeue()); + } } +template class Sleb128Encoder; +template class Sleb128Encoder; +template class Sleb128Decoder; +template class Sleb128Decoder; + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h index 3544543c0..fa0a24610 100644 --- a/tools/relocation_packer/src/sleb128.h +++ b/tools/relocation_packer/src/sleb128.h @@ -22,6 +22,7 @@ namespace relocation_packer { // Encode packed words as a signed LEB128 byte stream. +template class Sleb128Encoder { public: // Explicit (but empty) constructor and destructor, for chromium-style. @@ -30,11 +31,11 @@ class Sleb128Encoder { // Add a value to the encoding stream. // |value| is the signed int to add. - void Enqueue(ELF::Sxword value); + void Enqueue(int_t value); // Add a vector of values to the encoding stream. // |values| is the vector of signed ints to add. - void EnqueueAll(const std::vector& values); + void EnqueueAll(const std::vector& values); // Retrieve the encoded representation of the values. // |encoding| is the returned vector of encoded data. @@ -46,21 +47,22 @@ class Sleb128Encoder { }; // Decode a LEB128 byte stream to produce packed words. +template class Sleb128Decoder { public: // Create a new decoder for the given encoded stream. // |encoding| is the vector of encoded data. - explicit Sleb128Decoder(const std::vector& encoding); + explicit Sleb128Decoder(const std::vector& 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* values); + void DequeueAll(std::vector* values); private: // Encoded LEB128 stream. diff --git a/tools/relocation_packer/src/sleb128_unittest.cc b/tools/relocation_packer/src/sleb128_unittest.cc index 60a5d0de7..49a553c2f 100644 --- a/tools/relocation_packer/src/sleb128_unittest.cc +++ b/tools/relocation_packer/src/sleb128_unittest.cc @@ -6,27 +6,27 @@ #include #include "elf_traits.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "gtest/gtest.h" namespace relocation_packer { -TEST(Sleb128, Encoder) { - std::vector 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 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(-1)); + values.push_back(static_cast(-624485)); - Sleb128Encoder encoder; + Sleb128Encoder encoder; encoder.EnqueueAll(values); - encoder.Enqueue(2147483647); - encoder.Enqueue(-2147483648); - encoder.Enqueue(9223372036854775807ll); - encoder.Enqueue(-9223372036854775807ll - 1); + encoder.Enqueue(2147483647U); + encoder.Enqueue(static_cast(-2147483648)); + encoder.Enqueue(9223372036854775807ULL); + encoder.Enqueue(static_cast(-9223372036854775807LL - 1)); std::vector encoding; encoder.GetEncoding(&encoding); @@ -143,24 +143,24 @@ TEST(Sleb128, Decoder) { encoding.push_back(0x80); encoding.push_back(0x7f); - Sleb128Decoder decoder(encoding); + Sleb128Decoder decoder(encoding, 0); - EXPECT_EQ(624485, decoder.Dequeue()); + EXPECT_EQ(624485U, decoder.Dequeue()); - std::vector dequeued; + std::vector 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(-1), dequeued[4]); + EXPECT_EQ(static_cast(-624485), dequeued[5]); + EXPECT_EQ(2147483647U, dequeued[6]); + EXPECT_EQ(static_cast(-2147483648), dequeued[7]); + EXPECT_EQ(9223372036854775807ULL, dequeued[8]); + EXPECT_EQ(static_cast(-9223372036854775807LL - 1), dequeued[9]); } } // namespace relocation_packer diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so index 7cfdd609451d207f835362b2262be27e69206d8e..d97ef825260d3a5dfec58d42b477693dc6220532 100755 GIT binary patch delta 351 zcmZ3sgLT#p)(HxXOC~DHgbFY)m;mtu1_)*W(jd$X#3GD9EC|8EKt2eAZd zJAiyHARQ0HAjiHCoLnUDHCcqoX7dmEW$i3}p*}vF*DW_^WE7nIY=t_Ib zN;yTK_Y#souaBQ|=QsAHLz@)G_ zM(_f!8_+xk5ui#TAnm~qaR3L9{pqwUUc5l-6Xq>vVBiDN0+S7y^f&*IU)C<*7!YL0)KJIR!t|USqrixjFWGz zPzRDsD?y~zN_7XIp)Y_~2dD((au5J12jQPU%n8IGagbv{801)FbsU=~u6)kKC@}fq c3gO8;YXlhAO`f|(9mqclBtL9^yGD0C0ED7LOaK4? diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so index 532beac0bf22c653e9ff7489dd1ac339aa1ffed1..e44e4597b30e56d749a748ae7df6c8756e4cd1db 100755 GIT binary patch delta 464 zcmX|+JxIeq6vuOyejG~Bb_m$vOedv7p#>L_1V^Eiq*BMwK^zMOhwfb+TAqSaH=(P< zsVE9kT?CyZQ#MgSO>GJ~`Ys~}RCY%#BzZ~})xGjY><4%dx>B_d*tiESY-85bN zm3!>wmTbLdmspQE9r;>u%*u{Zw4$@=c*L|370GQ+7L4_SLQaxe!szZsHoiU7t^%8e zL1gI2ZRgGSI7`)=wVHGBStP`Q=Pv`P`UYvho?qR-PqoCyL?n~Ca->})%8Ab+DG|8x zHIf4lr{sW71?`YSBnK}f{}#24T!kT00}p8cp9$I_hsa~_kSD+cK^r-N_EZH;XK)#4 V)SZAA=ydb|e6G{}kz>z0{vUDbbT|M2 delta 2590 zcmezTo$dT__6ZtHE$=33sZZX^$R8D8$N&Z~nt_24%m0<&4vjMS!-NapbTp&|GJPx~wO?q5GPfJo*z`V^`jDAd;ITh^XHw!4t2_T6pPa)EL zQ2qn<$rG9VHouU6v$WnZz|rs}`-D5J2M#m-V7B34pf9YauXPx<)zjI(=nfe5|G-AT zX#At6Nc!?e)BkAtAIK0GQTZR#3eYRA%q>Yw0@5W#Kzj3wqk@c#jFW#Ivj>t^$L$%J zCRZIdG6l6zpiK{WBLmjhxS*505J_8ct?9VGksoz+@AcJaodI!w#Z} zW3r%}_+*8Xii~!Xole>V$tECaKlu=lbePO^%AO-0qEHpAFyWLUOh5rFu;7#;WBlYp zKy_On>JlLao>t`83K2+wXgsaRxOH;VX?sp+;|JL2Id$5MbIV(ZstXWpXB3!PzD#yH OW5<-UWpmS+!uJ5^4xoSl From fa26eee77685e8dee7986e62a7d263003f5bd25a Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 3 Feb 2015 16:06:47 -0800 Subject: [PATCH 3/4] Refactoring: introduce reloc_iterators Replace rel/rela array with reloc_iterators. Change-Id: I6165d062e0390b6bc60da2e8279aabbedf828ec9 --- linker/linker.cpp | 18 +++++++------ linker/linker.h | 4 +-- linker/linker_mips.cpp | 11 +++++--- linker/linker_reloc_iterators.h | 47 +++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 linker/linker_reloc_iterators.h diff --git a/linker/linker.cpp b/linker/linker.cpp index 5d2425f52..ae9df52aa 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -50,11 +50,12 @@ #include "private/UniquePtr.h" #include "linker.h" +#include "linker_allocator.h" #include "linker_debug.h" #include "linker_environ.h" #include "linker_phdr.h" #include "linker_relocs.h" -#include "linker_allocator.h" +#include "linker_reloc_iterators.h" /* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< * @@ -1297,9 +1298,10 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { } #endif -template -bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { - for (size_t idx = 0; idx < count; ++idx, ++rel) { +template +bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + for (size_t idx = 0; rel_iterator.has_next(); ++idx) { + const auto rel = rel_iterator.next(); ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); @@ -2273,26 +2275,26 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& #if defined(USE_RELA) if (rela_ != nullptr) { DEBUG("[ relocating %s ]", name); - if (!relocate(rela_, rela_count_, global_group, local_group)) { + if (!relocate(plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) { return false; } } if (plt_rela_ != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) { + if (!relocate(plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) { return false; } } #else if (rel_ != nullptr) { DEBUG("[ relocating %s ]", name); - if (!relocate(rel_, rel_count_, global_group, local_group)) { + if (!relocate(plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) { return false; } } if (plt_rel_ != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) { + if (!relocate(plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) { return false; } } diff --git a/linker/linker.h b/linker/linker.h index 2afbaf62f..222ddbf73 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -286,8 +286,8 @@ struct soinfo { void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void call_function(const char* function_name, linker_function_t function); - template - bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group); + template + bool relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group); private: // This part of the structure is only available diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index 7fbde3d35..d71659e9a 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -29,10 +29,15 @@ #include "linker.h" #include "linker_debug.h" #include "linker_relocs.h" +#include "linker_reloc_iterators.h" + +template bool soinfo::relocate(plain_reloc_iterator&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group); + +template +bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + for (size_t idx = 0; rel_iterator.has_next(); ++idx) { + const auto rel = rel_iterator.next(); -template<> -bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { - for (size_t idx = 0; idx < count; ++idx, ++rel) { ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); diff --git a/linker/linker_reloc_iterators.h b/linker/linker_reloc_iterators.h new file mode 100644 index 000000000..6388bb0d7 --- /dev/null +++ b/linker/linker_reloc_iterators.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __LINKER_RELOC_ITERATORS_H +#define __LINKER_RELOC_ITERATORS_H + +#include "linker.h" + +class plain_reloc_iterator { +#if defined(USE_RELA) + typedef ElfW(Rela) rel_t; +#else + typedef ElfW(Rel) rel_t; +#endif + public: + plain_reloc_iterator(rel_t* rel_array, size_t count) + : begin_(rel_array), end_(begin_ + count), current_(begin_) {} + + bool has_next() { + return current_ < end_; + } + + rel_t* next() { + return current_++; + } + private: + rel_t* const begin_; + rel_t* const end_; + rel_t* current_; + + DISALLOW_COPY_AND_ASSIGN(plain_reloc_iterator); +}; + +#endif // __LINKER_RELOC_ITERATORS_H From 18a6956b76a071097fc658c5fe13ef010e31864a Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 4 Feb 2015 16:05:30 -0800 Subject: [PATCH 4/4] Add support for packed relocations. Change-Id: I796a4ce86d3fccb8361c19889419c96147ee3c9f --- libc/include/elf.h | 7 +++ linker/linker.cpp | 89 +++++++++++++++++++++++++-- linker/linker.h | 3 + linker/linker_leb128.h | 87 ++++++++++++++++++++++++++ linker/linker_mips.cpp | 19 +++++- linker/linker_phdr.cpp | 2 +- linker/linker_reloc_iterators.h | 105 ++++++++++++++++++++++++++++++++ 7 files changed, 305 insertions(+), 7 deletions(-) create mode 100644 linker/linker_leb128.h diff --git a/libc/include/elf.h b/libc/include/elf.h index a41a2441a..41680a521 100644 --- a/libc/include/elf.h +++ b/libc/include/elf.h @@ -94,6 +94,13 @@ typedef struct { #define DT_PREINIT_ARRAY 32 #define DT_PREINIT_ARRAYSZ 33 +/* Android compressed rel/rela sections */ +#define DT_ANDROID_REL (DT_LOOS + 2) +#define DT_ANDROID_RELSZ (DT_LOOS + 3) + +#define DT_ANDROID_RELA (DT_LOOS + 4) +#define DT_ANDROID_RELASZ (DT_LOOS + 5) + /* gnu hash entry */ #define DT_GNU_HASH 0x6ffffef5 diff --git a/linker/linker.cpp b/linker/linker.cpp index ae9df52aa..87fce95c2 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -53,6 +53,7 @@ #include "linker_allocator.h" #include "linker_debug.h" #include "linker_environ.h" +#include "linker_leb128.h" #include "linker_phdr.h" #include "linker_relocs.h" #include "linker_reloc_iterators.h" @@ -1302,6 +1303,10 @@ template bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { for (size_t idx = 0; rel_iterator.has_next(); ++idx) { const auto rel = rel_iterator.next(); + if (rel == nullptr) { + return false; + } + ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); @@ -1407,16 +1412,16 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n", reinterpret_cast(reloc), - reinterpret_cast(base + addend)); - *reinterpret_cast(reloc) = (base + addend); + reinterpret_cast(load_bias + addend)); + *reinterpret_cast(reloc) = (load_bias + addend); break; case R_GENERIC_IRELATIVE: count_relocation(kRelocRelative); MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n", reinterpret_cast(reloc), - reinterpret_cast(base + addend)); - *reinterpret_cast(reloc) = call_ifunc_resolver(base + addend); + reinterpret_cast(load_bias + addend)); + *reinterpret_cast(reloc) = call_ifunc_resolver(load_bias + addend); break; #if defined(__aarch64__) @@ -2053,6 +2058,22 @@ bool soinfo::prelink_image() { rela_count_ = d->d_un.d_val / sizeof(ElfW(Rela)); break; + case DT_ANDROID_RELA: + android_relocs_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + break; + + case DT_ANDROID_RELASZ: + android_relocs_size_ = d->d_un.d_val; + break; + + case DT_ANDROID_REL: + DL_ERR("unsupported DT_ANDROID_REL in \"%s\"", name); + return false; + + case DT_ANDROID_RELSZ: + DL_ERR("unsupported DT_ANDROID_RELSZ in \"%s\"", name); + return false; + case DT_RELAENT: if (d->d_un.d_val != sizeof(ElfW(Rela))) { DL_ERR("invalid DT_RELAENT: %zd", static_cast(d->d_un.d_val)); @@ -2071,6 +2092,7 @@ bool soinfo::prelink_image() { case DT_RELSZ: DL_ERR("unsupported DT_RELSZ in \"%s\"", name); return false; + #else case DT_REL: rel_ = reinterpret_cast(load_bias + d->d_un.d_ptr); @@ -2087,6 +2109,22 @@ bool soinfo::prelink_image() { } break; + case DT_ANDROID_REL: + android_relocs_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + break; + + case DT_ANDROID_RELSZ: + android_relocs_size_ = d->d_un.d_val; + break; + + case DT_ANDROID_RELA: + DL_ERR("unsupported DT_ANDROID_RELA in \"%s\"", name); + return false; + + case DT_ANDROID_RELASZ: + DL_ERR("unsupported DT_ANDROID_RELASZ in \"%s\"", name); + return false; + // "Indicates that all RELATIVE relocations have been concatenated together, // and specifies the RELATIVE relocation count." // @@ -2094,9 +2132,15 @@ bool soinfo::prelink_image() { // Not currently used by bionic linker - ignored. case DT_RELCOUNT: break; + case DT_RELA: DL_ERR("unsupported DT_RELA in \"%s\"", name); return false; + + case DT_RELASZ: + DL_ERR("unsupported DT_RELASZ in \"%s\"", name); + return false; + #endif case DT_INIT: init_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); @@ -2251,7 +2295,8 @@ bool soinfo::prelink_image() { return true; } -bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) { +bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, + const android_dlextinfo* extinfo) { local_group_root_ = local_group.front(); if (local_group_root_ == nullptr) { @@ -2272,6 +2317,40 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& } #endif + if (android_relocs_ != nullptr) { + // check signature + if (android_relocs_size_ > 3 && + android_relocs_[0] == 'A' && + android_relocs_[1] == 'P' && + (android_relocs_[2] == 'U' || android_relocs_[2] == 'S') && + android_relocs_[3] == '2') { + DEBUG("[ android relocating %s ]", name); + + bool relocated = false; + const uint8_t* packed_relocs = android_relocs_ + 4; + const size_t packed_relocs_size = android_relocs_size_ - 4; + + if (android_relocs_[2] == 'U') { + relocated = relocate( + packed_reloc_iterator( + leb128_decoder(packed_relocs, packed_relocs_size)), + global_group, local_group); + } else { // android_relocs_[2] == 'S' + relocated = relocate( + packed_reloc_iterator( + sleb128_decoder(packed_relocs, packed_relocs_size)), + global_group, local_group); + } + + if (!relocated) { + return false; + } + } else { + DL_ERR("bad android relocation header."); + return false; + } + } + #if defined(USE_RELA) if (rela_ != nullptr) { DEBUG("[ relocating %s ]", name); diff --git a/linker/linker.h b/linker/linker.h index 222ddbf73..f8640a0e5 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -315,6 +315,9 @@ struct soinfo { soinfo* local_group_root_; + uint8_t* android_relocs_; + size_t android_relocs_size_; + friend soinfo* get_libdl_info(); }; diff --git a/linker/linker_leb128.h b/linker/linker_leb128.h new file mode 100644 index 000000000..d5c6488c0 --- /dev/null +++ b/linker/linker_leb128.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef _LINKER_LEB128_H +#define _LINKER_LEB128_H + +#include + +// Helper classes for decoding LEB128, used in packed relocation data. +// http://en.wikipedia.org/wiki/LEB128 + +class leb128_decoder { + public: + leb128_decoder(const uint8_t* buffer, size_t count) + : current_(buffer), end_(buffer + count) { } + + size_t pop_front() { + size_t value = 0; + + size_t shift = 0; + uint8_t byte; + + do { + if (current_ >= end_) { + __libc_fatal("leb128_decoder ran out of bounds"); + } + byte = *current_++; + value |= static_cast(byte & 127) << shift; + shift += 7; + } while (byte & 128); + + return value; + } + + private: + const uint8_t* current_; + const uint8_t* const end_; +}; + +class sleb128_decoder { + public: + sleb128_decoder(const uint8_t* buffer, size_t count) + : current_(buffer), end_(buffer+count) { } + + size_t pop_front() { + size_t value = 0; + static const size_t size = CHAR_BIT * sizeof(value); + + size_t shift = 0; + uint8_t byte; + + do { + if (current_ >= end_) { + __libc_fatal("leb128_decoder ran out of bounds"); + } + byte = *current_++; + value |= (static_cast(byte & 127) << shift); + shift += 7; + } while (byte & 128); + + if (shift < size && (byte & 64)) { + value |= -(static_cast(1) << shift); + } + + return value; + } + + private: + const uint8_t* current_; + const uint8_t* const end_; +}; + +#endif // __LINKER_LEB128_H + diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index d71659e9a..f0bde55cd 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -30,14 +30,31 @@ #include "linker_debug.h" #include "linker_relocs.h" #include "linker_reloc_iterators.h" +#include "linker_leb128.h" -template bool soinfo::relocate(plain_reloc_iterator&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group); +template bool soinfo::relocate(plain_reloc_iterator&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template bool soinfo::relocate>( + packed_reloc_iterator&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template bool soinfo::relocate>( + packed_reloc_iterator&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); template bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { for (size_t idx = 0; rel_iterator.has_next(); ++idx) { const auto rel = rel_iterator.next(); + if (rel == nullptr) { + return false; + } + ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 91a2fb80d..38e62627a 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -332,7 +332,7 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { return false; } int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; - start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0); + start = mmap(nullptr, load_size_, PROT_NONE, mmap_flags, -1, 0); if (start == MAP_FAILED) { DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_); return false; diff --git a/linker/linker_reloc_iterators.h b/linker/linker_reloc_iterators.h index 6388bb0d7..5db31f9f6 100644 --- a/linker/linker_reloc_iterators.h +++ b/linker/linker_reloc_iterators.h @@ -19,6 +19,18 @@ #include "linker.h" +#include + +#define RELOCATION_GROUPED_BY_INFO_FLAG 1 +#define RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG 2 +#define RELOCATION_GROUPED_BY_ADDEND_FLAG 4 +#define RELOCATION_GROUP_HAS_ADDEND_FLAG 8 + +#define RELOCATION_GROUPED_BY_INFO(flags) (((flags) & RELOCATION_GROUPED_BY_INFO_FLAG) != 0) +#define RELOCATION_GROUPED_BY_OFFSET_DELTA(flags) (((flags) & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0) +#define RELOCATION_GROUPED_BY_ADDEND(flags) (((flags) & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0) +#define RELOCATION_GROUP_HAS_ADDEND(flags) (((flags) & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0) + class plain_reloc_iterator { #if defined(USE_RELA) typedef ElfW(Rela) rel_t; @@ -44,4 +56,97 @@ class plain_reloc_iterator { DISALLOW_COPY_AND_ASSIGN(plain_reloc_iterator); }; +template +class packed_reloc_iterator { +#if defined(USE_RELA) + typedef ElfW(Rela) rel_t; +#else + typedef ElfW(Rel) rel_t; +#endif + public: + explicit packed_reloc_iterator(decoder_t&& decoder) + : decoder_(decoder) { + // initialize fields + memset(&reloc_, 0, sizeof(reloc_)); + relocation_count_ = decoder_.pop_front(); + reloc_.r_offset = decoder_.pop_front(); + relocation_index_ = 0; + relocation_group_index_ = 0; + group_size_ = 0; + } + + bool has_next() const { + return relocation_index_ < relocation_count_; + } + + rel_t* next() { + if (relocation_group_index_ == group_size_) { + if (!read_group_fields()) { + // Iterator is inconsistent state; it should not be called again + // but in case it is let's make sure has_next() returns false. + relocation_index_ = relocation_count_ = 0; + return nullptr; + } + } + + if (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) { + reloc_.r_offset += group_r_offset_delta_; + } else { + reloc_.r_offset += decoder_.pop_front(); + } + + if (!RELOCATION_GROUPED_BY_INFO(group_flags_)) { + reloc_.r_info = decoder_.pop_front(); + } + +#if defined(USE_RELA) + if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && !RELOCATION_GROUPED_BY_ADDEND(group_flags_)) { + reloc_.r_addend += decoder_.pop_front(); + } +#endif + + relocation_index_++; + relocation_group_index_++; + + return &reloc_; + } + private: + bool read_group_fields() { + group_size_ = decoder_.pop_front(); + group_flags_ = decoder_.pop_front(); + + if (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) { + group_r_offset_delta_ = decoder_.pop_front(); + } + + if (RELOCATION_GROUPED_BY_INFO(group_flags_)) { + reloc_.r_info = decoder_.pop_front(); + } + + if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && RELOCATION_GROUPED_BY_ADDEND(group_flags_)) { +#if !defined(USE_RELA) + // This platform does not support rela, and yet we have it encoded in android_rel section. + DL_ERR("unexpected r_addend in android.rel section"); + return false; +#else + reloc_.r_addend += decoder_.pop_front(); + } else if (!RELOCATION_GROUP_HAS_ADDEND(group_flags_)) { + reloc_.r_addend = 0; +#endif + } + + relocation_group_index_ = 0; + return true; + } + + decoder_t decoder_; + size_t relocation_count_; + size_t group_size_; + size_t group_flags_; + size_t group_r_offset_delta_; + size_t relocation_index_; + size_t relocation_group_index_; + rel_t reloc_; +}; + #endif // __LINKER_RELOC_ITERATORS_H