Import relocation packer from chromium repo

Bug: 18051137
Change-Id: Ia67fa11da8247e3f86f70a8ce99e6695f2c05423
This commit is contained in:
Dmitriy Ivanov 2015-02-06 10:56:28 -08:00
parent 45ee73a7fb
commit 87a0617ebe
36 changed files with 5485 additions and 0 deletions

View 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),
]
}
}

View 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.

View 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.

View 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 = ""
}

View 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)',
],
},
],
},
],
}

View 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

View 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_

View 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

View 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

View 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_

View 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

File diff suppressed because it is too large Load Diff

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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;
}

View 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

View 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_

View 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

View 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();
}

View 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

View 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_

View 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

View 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

View 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_

View 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

File diff suppressed because it is too large Load Diff

View 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())

View 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