Import relocation packer from chromium repo
Bug: 18051137 Change-Id: Ia67fa11da8247e3f86f70a8ce99e6695f2c05423
This commit is contained in:
parent
45ee73a7fb
commit
87a0617ebe
148
tools/relocation_packer/BUILD.gn
Normal file
148
tools/relocation_packer/BUILD.gn
Normal file
@ -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),
|
||||
]
|
||||
}
|
||||
}
|
27
tools/relocation_packer/LICENSE
Normal file
27
tools/relocation_packer/LICENSE
Normal file
@ -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.
|
135
tools/relocation_packer/README.TXT
Normal file
135
tools/relocation_packer/README.TXT
Normal file
@ -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.
|
21
tools/relocation_packer/config.gni
Normal file
21
tools/relocation_packer/config.gni
Normal file
@ -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 = ""
|
||||
}
|
161
tools/relocation_packer/relocation_packer.gyp
Normal file
161
tools/relocation_packer/relocation_packer.gyp
Normal file
@ -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)',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
55
tools/relocation_packer/src/debug.cc
Normal file
55
tools/relocation_packer/src/debug.cc
Normal file
@ -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
|
115
tools/relocation_packer/src/debug.h
Normal file
115
tools/relocation_packer/src/debug.h
Normal file
@ -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_
|
122
tools/relocation_packer/src/debug_unittest.cc
Normal file
122
tools/relocation_packer/src/debug_unittest.cc
Normal file
@ -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
|
72
tools/relocation_packer/src/delta_encoder.cc
Normal file
72
tools/relocation_packer/src/delta_encoder.cc
Normal file
@ -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
|
80
tools/relocation_packer/src/delta_encoder.h
Normal file
80
tools/relocation_packer/src/delta_encoder.h
Normal file
@ -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_
|
150
tools/relocation_packer/src/delta_encoder_unittest.cc
Normal file
150
tools/relocation_packer/src/delta_encoder_unittest.cc
Normal file
@ -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
|
1283
tools/relocation_packer/src/elf_file.cc
Normal file
1283
tools/relocation_packer/src/elf_file.cc
Normal file
File diff suppressed because it is too large
Load Diff
134
tools/relocation_packer/src/elf_file.h
Normal file
134
tools/relocation_packer/src/elf_file.h
Normal file
@ -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_
|
164
tools/relocation_packer/src/elf_file_unittest.cc
Normal file
164
tools/relocation_packer/src/elf_file_unittest.cc
Normal file
@ -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
|
105
tools/relocation_packer/src/elf_traits.h
Normal file
105
tools/relocation_packer/src/elf_traits.h
Normal file
@ -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_
|
69
tools/relocation_packer/src/leb128.cc
Normal file
69
tools/relocation_packer/src/leb128.cc
Normal file
@ -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
|
74
tools/relocation_packer/src/leb128.h
Normal file
74
tools/relocation_packer/src/leb128.h
Normal file
@ -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_
|
111
tools/relocation_packer/src/leb128_unittest.cc
Normal file
111
tools/relocation_packer/src/leb128_unittest.cc
Normal file
@ -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
|
175
tools/relocation_packer/src/main.cc
Normal file
175
tools/relocation_packer/src/main.cc
Normal file
@ -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;
|
||||
}
|
124
tools/relocation_packer/src/packer.cc
Normal file
124
tools/relocation_packer/src/packer.cc
Normal file
@ -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
|
78
tools/relocation_packer/src/packer.h
Normal file
78
tools/relocation_packer/src/packer.h
Normal file
@ -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_
|
250
tools/relocation_packer/src/packer_unittest.cc
Normal file
250
tools/relocation_packer/src/packer_unittest.cc
Normal file
@ -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
|
10
tools/relocation_packer/src/run_all_unittests.cc
Normal file
10
tools/relocation_packer/src/run_all_unittests.cc
Normal file
@ -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();
|
||||
}
|
144
tools/relocation_packer/src/run_length_encoder.cc
Normal file
144
tools/relocation_packer/src/run_length_encoder.cc
Normal file
@ -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
|
81
tools/relocation_packer/src/run_length_encoder.h
Normal file
81
tools/relocation_packer/src/run_length_encoder.h
Normal file
@ -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_
|
124
tools/relocation_packer/src/run_length_encoder_unittest.cc
Normal file
124
tools/relocation_packer/src/run_length_encoder_unittest.cc
Normal file
@ -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
|
95
tools/relocation_packer/src/sleb128.cc
Normal file
95
tools/relocation_packer/src/sleb128.cc
Normal file
@ -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
|
75
tools/relocation_packer/src/sleb128.h
Normal file
75
tools/relocation_packer/src/sleb128.h
Normal file
@ -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_
|
166
tools/relocation_packer/src/sleb128_unittest.cc
Normal file
166
tools/relocation_packer/src/sleb128_unittest.cc
Normal file
@ -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
|
1014
tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
Normal file
1014
tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Executable file
Binary file not shown.
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Executable file
Binary file not shown.
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Executable file
Binary file not shown.
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Executable file
Binary file not shown.
88
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
Executable file
88
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
Executable file
@ -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())
|
35
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
Executable file
35
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
Executable file
@ -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
|
Loading…
x
Reference in New Issue
Block a user