Import relocation packer from chromium repo

Bug: 18051137
Change-Id: Ia67fa11da8247e3f86f70a8ce99e6695f2c05423
diff --git a/tools/relocation_packer/BUILD.gn b/tools/relocation_packer/BUILD.gn
new file mode 100644
index 0000000..0b29c91
--- /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 0000000..972bb2e
--- /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 0000000..071ab5d
--- /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.<version>.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.<version>.so in the filesystem, in APK downloads,
+and in memory when loaded on the device.
+
+A packed libchrome.<version>.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.<version>.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.<version>.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.<version>.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.<version>.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.<version>.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.<version>.so | grep -q 'ELF 32'; then
+      arm-linux-androideabi-objcopy
+          --add-section .android.rel.dyn=/tmp/small
+          libchrome.<version>.so libchrome.<version>.so.packed
+    else
+      aarch64-linux-android-objcopy
+          --add-section .android.rela.dyn=/tmp/small
+          libchrome.<version>.so libchrome.<version>.so.packed
+    fi
+    rm /tmp/small
+    relocation_packer libchrome.<version>.so.packed
+
+To unpack and restore the shared library to its original state:
+
+    cp libchrome.<version>.so.packed unpackable
+    relocation_packer -u unpackable
+    if file libchrome.<version>.so | grep -q 'ELF 32'; then
+      arm-linux-androideabi-objcopy \
+          --remove-section=.android.rel.dyn unpackable libchrome.<version>.so
+    else
+      aarch64-linux-android-objcopy \
+          --remove-section=.android.rela.dyn unpackable libchrome.<version>.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 0000000..90e3979
--- /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 0000000..1e9c1b9
--- /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 0000000..29d7ab0
--- /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 <stdlib.h>
+#include <iostream>
+#include <string>
+
+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 0000000..48be6c1
--- /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 <limits.h>
+#include <ostream>
+#include <sstream>
+
+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 0000000..1b65cd1
--- /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 <sstream>
+#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 0000000..69cc91a
--- /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 <vector>
+
+#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<ELF::Rela>& relocations,
+                                  std::vector<ELF::Sxword>* 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<ELF::Sxword>& packed,
+                                  std::vector<ELF::Rela>* relocations) {
+  // We need at least one packed pair after the packed pair count to be
+  // able to unpack.
+  if (packed.size() < 3)
+    return;
+
+  // Ensure that the packed data offers enough pairs.  There may be zero
+  // padding on it that we ignore.
+  CHECK(static_cast<size_t>(packed[0]) <= (packed.size() - 1) >> 1);
+
+  ELF::Addr offset = 0;
+  ELF::Sxword addend = 0;
+
+  // The first packed vector element is the pairs count.  Start uncondensing
+  // pairs at the second, and finish at the end of the pairs data.
+  const size_t pairs_count = packed[0];
+  for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) {
+    offset += packed[i];
+    addend += packed[i + 1];
+
+    // Generate a relocation for this offset and addend pair.
+    ELF::Rela relocation;
+    relocation.r_offset = offset;
+    relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+    relocation.r_addend = addend;
+    relocations->push_back(relocation);
+  }
+}
+
+}  // 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 0000000..498b6d1
--- /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.<version>.so, and the ELF
+// standard representation of them is wasteful.  .rel.dyn contains
+// relocations without addends, .rela.dyn relocations with addends.
+//
+// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes
+// on 64 bit plaforms.  It is split into offset, info, and addend fields.
+// Offsets strictly increase, and each is commonly a few bytes different
+// from its predecessor.  Addends are less well behaved.  The info field is
+// constant.  Example, from 'readelf -x4 libchrome.<version>.so' 64 bit:
+//
+//   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 <vector>
+
+#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<ELF::Rela>& relocations,
+                     std::vector<ELF::Sxword>* packed);
+
+  // Decode relative relocations with addends from their more compact form.
+  // |packed| is the vector of packed relocations.
+  // |relocations| is a vector of unpacked relative relocations.
+  static void Decode(const std::vector<ELF::Sxword>& packed,
+                     std::vector<ELF::Rela>* relocations);
+};
+
+}  // 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 0000000..b9bf39a
--- /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 <vector>
+#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<ELF::Rela>* 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<ELF::Rela> relocations;
+  std::vector<ELF::Sxword> 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<ELF::Sxword> packed;
+  std::vector<ELF::Rela> 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 0000000..3ffccec
--- /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 <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#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<ELF::Phdr*> orderable;
+
+  // Collect together orderable program headers.  These are all the LOAD
+  // segments, and any GNU_STACK that may be present (removed on packing,
+  // but replaced on unpacking).
+  for (size_t i = 0; i < count; ++i) {
+    ELF::Phdr* program_header = &program_headers[i];
+
+    if (program_header->p_type == PT_LOAD ||
+        program_header->p_type == PT_GNU_STACK) {
+      orderable.push_back(program_header);
+    }
+  }
+
+  // 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 <typename Rel>
+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<ELF::Dyn*>(data->d_buf);
+  std::vector<ELF::Dyn> 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<ssize_t>(sizeof(Rel));
+      dynamic->d_un.d_val += hole_size / sizeof_rel;
+      VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+              << " d_val adjusted to " << dynamic->d_un.d_val;
+    }
+
+    // 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 <typename Rel>
+void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) {
+  ELF::Shdr* section_header = ELF::getshdr(section);
+  if (section_header->sh_size == new_size)
+    return;
+
+  // 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<Rel>(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<ELF::Dyn>* dynamics) {
+  // Loop until the penultimate entry.  We exclude the end sentinel.
+  for (size_t i = 0; i < dynamics->size() - 1; ++i) {
+    if (dynamics->at(i).d_tag == tag)
+      return i;
+  }
+
+  // The tag was not found.
+  return dynamics->size();
+}
+
+// Replace the first free (unused) slot in a dynamics vector with the given
+// value.  The vector always ends with a free (unused) element, so the slot
+// found cannot be the last one in the vector.
+void AddDynamicEntry(const ELF::Dyn& dyn,
+                     std::vector<ELF::Dyn>* dynamics) {
+  const size_t slot = FindDynamicEntry(DT_NULL, dynamics);
+  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<ELF::Dyn>* dynamics) {
+  const size_t slot = FindDynamicEntry(tag, dynamics);
+  CHECK(slot != dynamics->size());
+
+  // Remove this entry by shuffling up everything that follows.
+  for (size_t i = slot; i < dynamics->size() - 1; ++i) {
+    dynamics->at(i) = dynamics->at(i + 1);
+    VLOG(1) << "dynamic[" << i
+            << "] overwritten with dynamic[" << i + 1 << "]";
+  }
+
+  // Ensure that the end sentinel is still present.
+  CHECK(dynamics->at(dynamics->size() - 1).d_tag == DT_NULL);
+}
+
+// Construct a null relocation without addend.
+void NullRelocation(ELF::Rel* relocation) {
+  relocation->r_offset = 0;
+  relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
+}
+
+// Construct a null relocation with addend.
+void NullRelocation(ELF::Rela* relocation) {
+  relocation->r_offset = 0;
+  relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
+  relocation->r_addend = 0;
+}
+
+// Pad relocations with the given number of null entries.  Generates its
+// null entry with the appropriate NullRelocation() invocation.
+template <typename Rel>
+void PadRelocations(size_t count, std::vector<Rel>* relocations) {
+  Rel null_relocation;
+  NullRelocation(&null_relocation);
+  std::vector<Rel> padding(count, null_relocation);
+  relocations->insert(relocations->end(), padding.begin(), padding.end());
+}
+
+}  // namespace
+
+// Remove relative entries from dynamic relocations and write as packed
+// data into android packed relocations.
+bool ElfFile::PackRelocations() {
+  // 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<ELF::Rel*>(data->d_buf);
+    std::vector<ELF::Rel> relocations(
+        relocations_base,
+        relocations_base + data->d_size / sizeof(relocations[0]));
+
+    LOG(INFO) << "Relocations   : REL";
+    return PackTypedRelocations<ELF::Rel>(relocations);
+  }
+
+  if (relocations_type_ == RELA) {
+    // Convert data to a vector of relocations with addends.
+    const ELF::Rela* relocations_base =
+        reinterpret_cast<ELF::Rela*>(data->d_buf);
+    std::vector<ELF::Rela> relocations(
+        relocations_base,
+        relocations_base + data->d_size / sizeof(relocations[0]));
+
+    LOG(INFO) << "Relocations   : RELA";
+    return PackTypedRelocations<ELF::Rela>(relocations);
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// Helper for PackRelocations().  Rel type is one of ELF::Rel or ELF::Rela.
+template <typename Rel>
+bool ElfFile::PackTypedRelocations(const std::vector<Rel>& relocations) {
+  // Filter relocations into those that are relative and others.
+  std::vector<Rel> relative_relocations;
+  std::vector<Rel> other_relocations;
+
+  for (size_t i = 0; i < relocations.size(); ++i) {
+    const Rel& relocation = relocations[i];
+    if (ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode) {
+      CHECK(ELF_R_SYM(relocation.r_info) == 0);
+      relative_relocations.push_back(relocation);
+    } else {
+      other_relocations.push_back(relocation);
+    }
+  }
+  LOG(INFO) << "Relative      : " << relative_relocations.size() << " entries";
+  LOG(INFO) << "Other         : " << other_relocations.size() << " entries";
+  LOG(INFO) << "Total         : " << relocations.size() << " entries";
+
+  // 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<Rel>(padding, &other_relocations);
+    LOG(INFO) << "Alignment pad : " << padding << " relocations";
+  } else {
+    // If padding, add NONE-type relocations to other_relocations to make it
+    // the same size as the the original relocations we read in.  This makes
+    // the ResizeSection() below a no-op.
+    const size_t padding = relocations.size() - other_relocations.size();
+    PadRelocations<Rel>(padding, &other_relocations);
+  }
+
+  // Pack relative relocations.
+  const size_t initial_bytes =
+      relative_relocations.size() * sizeof(relative_relocations[0]);
+  LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes";
+  std::vector<uint8_t> packed;
+  RelocationPacker packer;
+  packer.PackRelativeRelocations(relative_relocations, &packed);
+  const void* packed_data = &packed[0];
+  const size_t packed_bytes = packed.size() * sizeof(packed[0]);
+  LOG(INFO) << "Packed   relative: " << packed_bytes << " bytes";
+
+  // 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<Rel> 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<Rel>(elf_, relocations_section_, bytes);
+  RewriteSectionData(relocations_section_, section_data, bytes);
+
+  // Rewrite the current packed android relocations section to hold the packed
+  // relative relocations.
+  ResizeSection<Rel>(elf_, android_relocations_section_, packed_bytes);
+  RewriteSectionData(android_relocations_section_, packed_data, packed_bytes);
+
+  // 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<ELF::Dyn*>(data->d_buf);
+  std::vector<ELF::Dyn> dynamics(
+      dynamic_base,
+      dynamic_base + data->d_size / sizeof(dynamics[0]));
+  // Use two of the spare slots to describe the packed section.
+  ELF::Shdr* section_header = ELF::getshdr(android_relocations_section_);
+  {
+    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<uint8_t*>(data->d_buf);
+  std::vector<uint8_t> 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<ELF::Rel>(packed);
+  }
+
+  if (packed.size() > 3 &&
+      packed[0] == 'A' &&
+      packed[1] == 'P' &&
+      packed[2] == 'A' &&
+      packed[3] == '1') {
+    // Signature is APA1, unpack relocations with addends.
+    CHECK(relocations_type_ == RELA);
+    LOG(INFO) << "Relocations   : RELA";
+    return UnpackTypedRelocations<ELF::Rela>(packed);
+  }
+
+  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 <typename Rel>
+bool ElfFile::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
+  // Unpack the data to re-materialize the relative relocations.
+  const size_t packed_bytes = packed.size() * sizeof(packed[0]);
+  LOG(INFO) << "Packed   relative: " << packed_bytes << " bytes";
+  std::vector<Rel> relative_relocations;
+  RelocationPacker packer;
+  packer.UnpackRelativeRelocations(packed, &relative_relocations);
+  const size_t unpacked_bytes =
+      relative_relocations.size() * sizeof(relative_relocations[0]);
+  LOG(INFO) << "Unpacked relative: " << unpacked_bytes << " bytes";
+
+  // Retrieve the current dynamic relocations section data.
+  Elf_Data* data = GetSectionData(relocations_section_);
+
+  // Interpret data as relocations.
+  const Rel* relocations_base = reinterpret_cast<Rel*>(data->d_buf);
+  std::vector<Rel> relocations(
+      relocations_base,
+      relocations_base + data->d_size / sizeof(relocations[0]));
+
+  std::vector<Rel> other_relocations;
+  size_t padding = 0;
+
+  // Filter relocations to locate any that are NONE-type.  These will occur
+  // if padding was turned on for packing.
+  for (size_t i = 0; i < relocations.size(); ++i) {
+    const Rel& relocation = relocations[i];
+    if (ELF_R_TYPE(relocation.r_info) != ELF::kNoRelocationCode) {
+      other_relocations.push_back(relocation);
+    } else {
+      ++padding;
+    }
+  }
+  LOG(INFO) << "Relative      : " << relative_relocations.size() << " entries";
+  LOG(INFO) << "Other         : " << other_relocations.size() << " entries";
+
+  // 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<Rel>(elf_, relocations_section_, bytes);
+  RewriteSectionData(relocations_section_, section_data, bytes);
+
+  // Nearly empty the current packed android relocations section.  Leaves a
+  // four-byte stub so that some data remains allocated to the section.
+  // This is a convenience which allows us to re-pack this file again without
+  // having to remove the section and then add a new small one with objcopy.
+  // The way we resize sections relies on there being some data in a section.
+  ResizeSection<Rel>(
+      elf_, android_relocations_section_, sizeof(kStubIdentifier));
+  RewriteSectionData(
+      android_relocations_section_, &kStubIdentifier, sizeof(kStubIdentifier));
+
+  // Rewrite .dynamic to remove two tags describing packed android relocations.
+  data = GetSectionData(dynamic_section_);
+  const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
+  std::vector<ELF::Dyn> dynamics(
+      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 0000000..6550274
--- /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.<version>.so will not
+// contain this section, so the following can be used to add one:
+//
+//   echo -n 'NULL' >/tmp/small
+//   if file libchrome.<version>.so | grep -q 'ELF 32'; then
+//     arm-linux-androideabi-objcopy
+//         --add-section .android.rel.dyn=/tmp/small
+//         libchrome.<version>.so libchrome.<version>.so.packed
+//   else
+//     aarch64-linux-android-objcopy
+//         --add-section .android.rela.dyn=/tmp/small
+//         libchrome.<version>.so libchrome.<version>.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 <string.h>
+#include <vector>
+
+#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 <typename Rel>
+  bool PackTypedRelocations(const std::vector<Rel>& relocations);
+
+  // Templated unpacker, helper for UnpackRelocations().  Rel type is one of
+  // ELF::Rel or ELF::Rela.
+  template <typename Rel>
+  bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
+
+  // Write ELF file changes.
+  void Flush();
+
+  // 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 0000000..37abd0d
--- /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 <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include "debug.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Macro stringification.
+// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html
+#define XSTR(S) STR(S)
+#define STR(S) #S
+
+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 0000000..f099bab
--- /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 0000000..b48739c
--- /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 <stdint.h>
+#include <vector>
+
+#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<ELF::Xword>& values) {
+  for (size_t i = 0; i < values.size(); ++i)
+    Enqueue(values[i]);
+}
+
+// Create a new decoder for the given encoded stream.
+Leb128Decoder::Leb128Decoder(const std::vector<uint8_t>& encoding) {
+  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<ELF::Xword>(byte & 127) << shift;
+    shift += 7;
+  } while (byte & 128);
+
+  return value;
+}
+
+// Decode and retrieve all remaining values from the encoding.
+void Leb128Decoder::DequeueAll(std::vector<ELF::Xword>* 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 0000000..6cc2d7c
--- /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 <stdint.h>
+#include <vector>
+
+#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<ELF::Xword>& values);
+
+  // Retrieve the encoded representation of the values.
+  // |encoding| is the returned vector of encoded data.
+  void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+  // Growable vector holding the encoded LEB128 stream.
+  std::vector<uint8_t> 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<uint8_t>& 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<ELF::Xword>* values);
+
+ private:
+  // Encoded LEB128 stream.
+  std::vector<uint8_t> 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 0000000..bd607b7
--- /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 <vector>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Leb128, Encoder) {
+  std::vector<ELF::Xword> 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<uint8_t> 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<uint8_t> 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<ELF::Xword> 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 0000000..28f5b04
--- /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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+
+#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.<version>.so\n"
+        "    rm /tmp/small\n"
+        "    %s libchrome.<version>.so\n\n"
+        "To unpack and restore the shared library to its original state:\n\n"
+        "    %s -u libchrome.<version>.so\n"
+        "    arm-linux-androideabi-objcopy \\\n"
+        "        --remove-section=.android.rel.dyn libchrome.<version>.so\n\n",
+        basename, basename);
+  } else if (ELF::kMachine == EM_AARCH64) {
+    printf(
+        "Extracts relative relocations from the .rela.dyn section, packs them\n"
+        "into a more compact format, and stores the packed relocations in\n"
+        ".android.rela.dyn.  Expands .android.rela.dyn to hold the packed\n"
+        "data, and shrinks .rela.dyn by the amount of unpacked data removed\n"
+        "from it.\n\n"
+        "Before being packed, a shared library needs to be prepared by adding\n"
+        "a null .android.rela.dyn section.\n\n"
+        "To pack relocations in a shared library:\n\n"
+        "    echo -n 'NULL' >/tmp/small\n"
+        "    aarch64-linux-android-objcopy \\\n"
+        "        --add-section .android.rela.dyn=/tmp/small \\\n"
+        "        libchrome.<version>.so\n"
+        "    rm /tmp/small\n"
+        "    %s libchrome.<version>.so\n\n"
+        "To unpack and restore the shared library to its original state:\n\n"
+        "    %s -u libchrome.<version>.so\n"
+        "    aarch64-linux-android-objcopy \\\n"
+        "        --remove-section=.android.rela.dyn libchrome.<version>.so\n\n",
+        basename, basename);
+  } else {
+    NOTREACHED();
+  }
+
+  printf(
+      "Debug sections are not handled, so packing should not be used on\n"
+      "shared libraries compiled for debugging or otherwise unstripped.\n");
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  bool is_unpacking = false;
+  bool is_verbose = false;
+  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 0000000..29bec1e
--- /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 <vector>
+
+#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<ELF::Rel>& relocations,
+    std::vector<uint8_t>* packed) {
+  // Run-length encode.
+  std::vector<ELF::Xword> 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<uint8_t>& packed,
+    std::vector<ELF::Rel>* relocations) {
+  // LEB128 decode, after checking and stripping "APR1" prefix.
+  std::vector<ELF::Xword> packed_words;
+  Leb128Decoder decoder(packed);
+  CHECK(decoder.Dequeue() == 'A' &&
+        decoder.Dequeue() == 'P' &&
+        decoder.Dequeue() == 'R' &&
+        decoder.Dequeue() == '1');
+  decoder.DequeueAll(&packed_words);
+
+  // Run-length decode.
+  RelocationRunLengthCodec codec;
+  codec.Decode(packed_words, relocations);
+}
+
+// Pack relative relocations with addends into a delta encoded packed
+// representation.
+void RelocationPacker::PackRelativeRelocations(
+    const std::vector<ELF::Rela>& relocations,
+    std::vector<uint8_t>* packed) {
+  // Delta encode.
+  std::vector<ELF::Sxword> packed_words;
+  RelocationDeltaCodec codec;
+  codec.Encode(relocations, &packed_words);
+
+  // If insufficient data to delta encode, do nothing.
+  if (packed_words.empty())
+    return;
+
+  // Signed LEB128 encode, with "APA1" prefix.  ASCII does not encode as
+  // itself under signed LEB128, so we have to treat it specially.
+  Sleb128Encoder encoder;
+  encoder.EnqueueAll(packed_words);
+  std::vector<uint8_t> encoded;
+  encoder.GetEncoding(&encoded);
+
+  packed->push_back('A');
+  packed->push_back('P');
+  packed->push_back('A');
+  packed->push_back('1');
+  packed->insert(packed->end(), encoded.begin(), encoded.end());
+
+  // Pad packed to a whole number of words.  This padding will decode as
+  // signed LEB128 zeroes.  Delta decoding ignores it because encoding
+  // embeds the pairs count in the stream itself.
+  while (packed->size() % sizeof(ELF::Word))
+    packed->push_back(0);
+}
+
+// Unpack relative relocations with addends from a delta encoded
+// packed representation.
+void RelocationPacker::UnpackRelativeRelocations(
+    const std::vector<uint8_t>& packed,
+    std::vector<ELF::Rela>* relocations) {
+  // Check "APA1" prefix.
+  CHECK(packed.at(0) == 'A' &&
+        packed.at(1) == 'P' &&
+        packed.at(2) == 'A' &&
+        packed.at(3) == '1');
+
+  // Signed LEB128 decode, after stripping "APA1" prefix.
+  std::vector<ELF::Sxword> packed_words;
+  std::vector<uint8_t> stripped(packed.begin() + 4, packed.end());
+  Sleb128Decoder decoder(stripped);
+  decoder.DequeueAll(&packed_words);
+
+  // Delta decode.
+  RelocationDeltaCodec codec;
+  codec.Decode(packed_words, relocations);
+}
+
+}  // namespace relocation_packer
diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h
new file mode 100644
index 0000000..db09ce8
--- /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 <stdint.h>
+#include <vector>
+
+#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<ELF::Rel>& relocations,
+                                      std::vector<uint8_t>* packed);
+  static void PackRelativeRelocations(const std::vector<ELF::Rela>& relocations,
+                                      std::vector<uint8_t>* packed);
+
+  // Unpack relative relocations from their more compact form.
+  // |packed| is the vector of packed relocations.
+  // |relocations| is a vector of unpacked relative relocation structs.
+  static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
+                                        std::vector<ELF::Rel>* relocations);
+  static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
+                                        std::vector<ELF::Rela>* relocations);
+};
+
+}  // 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 0000000..de5be79
--- /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 <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
+  ELF::Rel relocation;
+  relocation.r_offset = addr;
+  relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+  relocations->push_back(relocation);
+}
+
+bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
+  return relocation.r_offset == addr &&
+      ELF_R_SYM(relocation.r_info) == 0 &&
+      ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
+}
+
+void AddRelocation(ELF::Addr addr,
+                   ELF::Sxword addend,
+                   std::vector<ELF::Rela>* relocations) {
+  ELF::Rela relocation;
+  relocation.r_offset = addr;
+  relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+  relocation.r_addend = addend;
+  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<ELF::Rel> relocations;
+  std::vector<uint8_t> 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<uint8_t> packed;
+  std::vector<ELF::Rel> relocations;
+
+  RelocationPacker packer;
+
+  // Identifier.
+  packed.push_back('A');
+  packed.push_back('P');
+  packed.push_back('R');
+  packed.push_back('1');
+  // Count-delta pairs count.
+  packed.push_back(2);
+  // 0xd1ce0000
+  packed.push_back(128);
+  packed.push_back(128);
+  packed.push_back(184);
+  packed.push_back(142);
+  packed.push_back(13);
+  // Run of two relocations, 4 byte deltas.
+  packed.push_back(2);
+  packed.push_back(4);
+  // Run of three relocations, 8 byte deltas.
+  packed.push_back(3);
+  packed.push_back(8);
+  // Padding.
+  packed.push_back(0);
+  packed.push_back(0);
+
+  relocations.clear();
+  packer.UnpackRelativeRelocations(packed, &relocations);
+
+  EXPECT_EQ(6, relocations.size());
+  // Initial relocation.
+  EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0]));
+  // Two relocations, 4 byte deltas.
+  EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1]));
+  EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2]));
+  // Three relocations, 8 byte deltas.
+  EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3]));
+  EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4]));
+  EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5]));
+}
+
+TEST(Packer, PackRela) {
+  std::vector<ELF::Rela> relocations;
+  std::vector<uint8_t> packed;
+
+  RelocationPacker packer;
+
+  // Initial relocation.
+  AddRelocation(0xd1ce0000, 10000, &relocations);
+  // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+  AddRelocation(0xd1ce0004, 10012, &relocations);
+  AddRelocation(0xd1ce0008, 10024, &relocations);
+  // Three more relocations, 8 byte deltas, -24 byte addend deltas.
+  AddRelocation(0xd1ce0010, 10000, &relocations);
+  AddRelocation(0xd1ce0018, 9976, &relocations);
+  AddRelocation(0xd1ce0020, 9952, &relocations);
+
+  packed.clear();
+  packer.PackRelativeRelocations(relocations, &packed);
+
+  EXPECT_EQ(24, packed.size());
+  // Identifier.
+  EXPECT_EQ('A', packed[0]);
+  EXPECT_EQ('P', packed[1]);
+  EXPECT_EQ('A', packed[2]);
+  EXPECT_EQ('1', packed[3]);
+  // Delta pairs count.
+  EXPECT_EQ(6, packed[4]);
+  // 0xd1ce0000
+  EXPECT_EQ(128, packed[5]);
+  EXPECT_EQ(128, packed[6]);
+  EXPECT_EQ(184, packed[7]);
+  EXPECT_EQ(142, packed[8]);
+  EXPECT_EQ(13, packed[9]);
+  // 10000
+  EXPECT_EQ(144, packed[10]);
+  EXPECT_EQ(206, packed[11]);
+  EXPECT_EQ(0, packed[12]);
+  // 4, 12
+  EXPECT_EQ(4, packed[13]);
+  EXPECT_EQ(12, packed[14]);
+  // 4, 12
+  EXPECT_EQ(4, packed[15]);
+  EXPECT_EQ(12, packed[16]);
+  // 8, -24
+  EXPECT_EQ(8, packed[17]);
+  EXPECT_EQ(104, packed[18]);
+  // 8, -24
+  EXPECT_EQ(8, packed[19]);
+  EXPECT_EQ(104, packed[20]);
+  // 8, -24
+  EXPECT_EQ(8, packed[21]);
+  EXPECT_EQ(104, packed[22]);
+  // Padding.
+  EXPECT_EQ(0, packed[23]);
+}
+
+TEST(Packer, UnpackRela) {
+  std::vector<uint8_t> packed;
+  std::vector<ELF::Rela> relocations;
+
+  RelocationPacker packer;
+
+  // Identifier.
+  packed.push_back('A');
+  packed.push_back('P');
+  packed.push_back('A');
+  packed.push_back('1');
+  // Delta pairs count.
+  packed.push_back(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 0000000..4122be1
--- /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 0000000..2f2e1c3
--- /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 <vector>
+
+#include "debug.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+namespace {
+
+// Generate a vector of deltas between the r_offset fields of adjacent
+// relative relocations.
+void GetDeltas(const std::vector<ELF::Rel>& relocations,
+               std::vector<ELF::Addr>* deltas) {
+  CHECK(relocations.size() >= 2);
+
+  for (size_t i = 0; i < relocations.size() - 1; ++i) {
+    const ELF::Rel* first = &relocations[i];
+    CHECK(ELF_R_TYPE(first->r_info) == ELF::kRelativeRelocationCode);
+
+    const ELF::Rel* second = &relocations[i + 1];
+    CHECK(ELF_R_TYPE(second->r_info) == ELF::kRelativeRelocationCode);
+
+    // Requires that offsets are 'strictly increasing'.  The packing
+    // algorithm fails if this does not hold.
+    CHECK(second->r_offset > first->r_offset);
+    deltas->push_back(second->r_offset - first->r_offset);
+  }
+}
+
+// Condense a set of r_offset deltas into a run-length encoded packing.
+// Represented as count-delta pairs, where count is the run length and
+// delta the common difference between adjacent r_offsets.
+void Condense(const std::vector<ELF::Addr>& deltas,
+              std::vector<ELF::Xword>* packed) {
+  CHECK(!deltas.empty());
+  size_t count = 0;
+  ELF::Addr current = deltas[0];
+
+  // Identify spans of identically valued deltas.
+  for (size_t i = 0; i < deltas.size(); ++i) {
+    const ELF::Addr delta = deltas[i];
+    if (delta == current) {
+      count++;
+    } else {
+      // We reached the end of a span of identically valued deltas.
+      packed->push_back(count);
+      packed->push_back(current);
+      current = delta;
+      count = 1;
+    }
+  }
+
+  // Write the final span.
+  packed->push_back(count);
+  packed->push_back(current);
+}
+
+// Uncondense a set of r_offset deltas from a run-length encoded packing.
+// The initial address for uncondensing, the start index for the first
+// condensed slot in packed, and the count of pairs are provided.
+void Uncondense(ELF::Addr addr,
+                const std::vector<ELF::Xword>& packed,
+                size_t start_index,
+                size_t end_index,
+                std::vector<ELF::Rel>* relocations) {
+  // The first relocation is just one created from the initial address.
+  ELF::Rel initial;
+  initial.r_offset = addr;
+  initial.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+  relocations->push_back(initial);
+
+  // Read each count and delta pair, beginning at the start index and
+  // finishing at the end index.
+  for (size_t i = start_index; i < end_index; i += 2) {
+    size_t count = packed[i];
+    const ELF::Addr delta = packed[i + 1];
+    CHECK(count > 0 && delta > 0);
+
+    // Generate relocations for this count and delta pair.
+    while (count) {
+      addr += delta;
+      ELF::Rel relocation;
+      relocation.r_offset = addr;
+      relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+      relocations->push_back(relocation);
+      count--;
+    }
+  }
+}
+
+}  // namespace
+
+// Encode relative relocations into a run-length encoded (packed)
+// representation.
+void RelocationRunLengthCodec::Encode(const std::vector<ELF::Rel>& relocations,
+                                      std::vector<ELF::Xword>* packed) {
+  // If we have zero or one relocation only then there is no packing
+  // possible; a run-length encoding needs a run.
+  if (relocations.size() < 2)
+    return;
+
+  std::vector<ELF::Addr> deltas;
+  GetDeltas(relocations, &deltas);
+
+  // Reserve space for the element count.
+  packed->push_back(0);
+
+  // Initialize the packed data with the first offset, then follow up with
+  // the condensed deltas vector.
+  packed->push_back(relocations[0].r_offset);
+  Condense(deltas, packed);
+
+  // Fill in the packed pair count.
+  packed->at(0) = (packed->size() - 2) >> 1;
+}
+
+// Decode relative relocations from a run-length encoded (packed)
+// representation.
+void RelocationRunLengthCodec::Decode(const std::vector<ELF::Xword>& packed,
+                                      std::vector<ELF::Rel>* relocations) {
+  // We need at least one packed pair after the packed pair count and start
+  // address to be able to unpack.
+  if (packed.size() < 4)
+    return;
+
+  // Ensure that the packed data offers enough pairs.  There may be zero
+  // padding on it that we ignore.
+  CHECK(packed[0] <= (packed.size() - 2) >> 1);
+
+  // The first packed vector element is the pairs count and the second the
+  // initial address.  Start uncondensing pairs at the third, and finish
+  // at the end of the pairs data.
+  const size_t pairs_count = packed[0];
+  const ELF::Addr addr = packed[1];
+  Uncondense(addr, packed, 2, 2 + (pairs_count << 1), relocations);
+}
+
+}  // namespace relocation_packer
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 0000000..f3a80e6
--- /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.<version>.so, and the ELF
+// standard representation of them is wasteful.  .rel.dyn contains
+// relocations without addends, .rela.dyn relocations with addends.
+//
+// A relocation with 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.<version>.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.<version>.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 <vector>
+
+#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<ELF::Rel>& relocations,
+                     std::vector<ELF::Xword>* 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<ELF::Xword>& packed,
+                     std::vector<ELF::Rel>* 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 0000000..83370f2
--- /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 <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
+  ELF::Rel relocation;
+  relocation.r_offset = addr;
+  relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+  relocations->push_back(relocation);
+}
+
+bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
+  return relocation.r_offset == addr &&
+      ELF_R_SYM(relocation.r_info) == 0 &&
+      ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
+}
+
+}  // namespace
+
+namespace relocation_packer {
+
+TEST(RunLength, Encode) {
+  std::vector<ELF::Rel> relocations;
+  std::vector<ELF::Xword> packed;
+
+  RelocationRunLengthCodec codec;
+
+  packed.clear();
+  codec.Encode(relocations, &packed);
+
+  EXPECT_EQ(0, packed.size());
+
+  // Add one relocation (insufficient data to encode).
+  AddRelocation(0xf00d0000, &relocations);
+
+  packed.clear();
+  codec.Encode(relocations, &packed);
+
+  EXPECT_EQ(0, packed.size());
+
+  // Add a second relocation, 4 byte delta (minimum data to encode).
+  AddRelocation(0xf00d0004, &relocations);
+
+  packed.clear();
+  codec.Encode(relocations, &packed);
+
+  EXPECT_EQ(4, packed.size());
+  // One count-delta pair present.
+  EXPECT_EQ(1, packed[0]);
+  // Initial relocation.
+  EXPECT_EQ(0xf00d0000, packed[1]);
+  // Run of a single relocation, 4 byte delta.
+  EXPECT_EQ(1, packed[2]);
+  EXPECT_EQ(4, packed[3]);
+
+  // Add a third relocation, 4 byte delta.
+  AddRelocation(0xf00d0008, &relocations);
+
+  // Add three more relocations, 8 byte deltas.
+  AddRelocation(0xf00d0010, &relocations);
+  AddRelocation(0xf00d0018, &relocations);
+  AddRelocation(0xf00d0020, &relocations);
+
+  packed.clear();
+  codec.Encode(relocations, &packed);
+
+  EXPECT_EQ(6, packed.size());
+  // Two count-delta pairs present.
+  EXPECT_EQ(2, packed[0]);
+  // Initial relocation.
+  EXPECT_EQ(0xf00d0000, packed[1]);
+  // Run of two relocations, 4 byte deltas.
+  EXPECT_EQ(2, packed[2]);
+  EXPECT_EQ(4, packed[3]);
+  // Run of three relocations, 8 byte deltas.
+  EXPECT_EQ(3, packed[4]);
+  EXPECT_EQ(8, packed[5]);
+}
+
+TEST(RunLength, Decode) {
+  std::vector<ELF::Xword> packed;
+  std::vector<ELF::Rel> relocations;
+
+  RelocationRunLengthCodec codec;
+  codec.Decode(packed, &relocations);
+
+  EXPECT_EQ(0, relocations.size());
+
+  // Two count-delta pairs.
+  packed.push_back(2);
+  // Initial relocation.
+  packed.push_back(0xc0de0000);
+  // Run of two relocations, 4 byte deltas.
+  packed.push_back(2);
+  packed.push_back(4);
+  // Run of three relocations, 8 byte deltas.
+  packed.push_back(3);
+  packed.push_back(8);
+
+  relocations.clear();
+  codec.Decode(packed, &relocations);
+
+  EXPECT_EQ(6, relocations.size());
+  // Initial relocation.
+  EXPECT_TRUE(CheckRelocation(0xc0de0000, relocations[0]));
+  // Two relocations, 4 byte deltas.
+  EXPECT_TRUE(CheckRelocation(0xc0de0004, relocations[1]));
+  EXPECT_TRUE(CheckRelocation(0xc0de0008, relocations[2]));
+  // Three relocations, 8 byte deltas.
+  EXPECT_TRUE(CheckRelocation(0xc0de0010, relocations[3]));
+  EXPECT_TRUE(CheckRelocation(0xc0de0018, relocations[4]));
+  EXPECT_TRUE(CheckRelocation(0xc0de0020, relocations[5]));
+}
+
+}  // namespace relocation_packer
diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc
new file mode 100644
index 0000000..a10bd79
--- /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 <limits.h>
+#include <stdint.h>
+#include <vector>
+
+#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<ELF::Sxword>(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<ELF::Sxword>& values) {
+  for (size_t i = 0; i < values.size(); ++i)
+    Enqueue(values[i]);
+}
+
+// Create a new decoder for the given encoded stream.
+Sleb128Decoder::Sleb128Decoder(const std::vector<uint8_t>& encoding) {
+  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<ELF::Sxword>(byte & 127) << shift);
+    shift += 7;
+  } while (byte & 128);
+
+  // The sign bit is second high order bit of the final byte decoded.
+  // Sign extend if value is -ve and we did not shift all of it.
+  if (shift < size && (byte & 64))
+    value |= -(static_cast<ELF::Sxword>(1) << shift);
+
+  return value;
+}
+
+// Decode and retrieve all remaining values from the encoding.
+void Sleb128Decoder::DequeueAll(std::vector<ELF::Sxword>* 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 0000000..3544543
--- /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 <stdint.h>
+#include <unistd.h>
+#include <vector>
+
+#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<ELF::Sxword>& values);
+
+  // Retrieve the encoded representation of the values.
+  // |encoding| is the returned vector of encoded data.
+  void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+  // Growable vector holding the encoded LEB128 stream.
+  std::vector<uint8_t> 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<uint8_t>& 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<ELF::Sxword>* values);
+
+ private:
+  // Encoded LEB128 stream.
+  std::vector<uint8_t> 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 0000000..60a5d0d
--- /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 <vector>
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Sleb128, Encoder) {
+  std::vector<ELF::Sxword> values;
+  values.push_back(624485);
+  values.push_back(0);
+  values.push_back(1);
+  values.push_back(63);
+  values.push_back(64);
+  values.push_back(-1);
+  values.push_back(-624485);
+
+  Sleb128Encoder encoder;
+  encoder.EnqueueAll(values);
+
+  encoder.Enqueue(2147483647);
+  encoder.Enqueue(-2147483648);
+  encoder.Enqueue(9223372036854775807ll);
+  encoder.Enqueue(-9223372036854775807ll - 1);
+
+  std::vector<uint8_t> 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<uint8_t> 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<ELF::Sxword> 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 0000000..5e1fa74
--- /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 0000000..6ce6d0c
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Binary files differ
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 0000000..7cfdd60
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Binary files differ
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 0000000..945b450
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Binary files differ
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 0000000..532beac
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Binary files differ
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 0000000..e71b5cb
--- /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 0000000..f90a2f6
--- /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