Commit 418115b3 authored by Simon Baldwin's avatar Simon Baldwin

Port Android relocation packer to chromium build

- Copy Android relocation packer source from AOSP:
    bionic/tools/relocation_packer/
- Remove golden test data generation scripts (not needed here)
- Add a local ScopedFd.h, to avoid any dependency on base
- Add gyp and gn build files for chromium
- Update third_party/android_platform/README.chromium

BUG=385553
R=rmcilroy@chromium.org

Review URL: https://codereview.chromium.org/1027823002

Cr-Commit-Position: refs/heads/master@{#322401}
parent 81366316
# Copyright 2015 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")
gypi_values = exec_script("//build/gypi_to_gn.py",
[ rebase_path("relocation_packer.gyp") ],
"scope",
[ "relocation_packer.gyp" ])
if (current_toolchain == host_toolchain) {
# GYP: //third_party/android_platform/relocation_packer.gyp:android_lib_relocation_packer
source_set("android_lib_relocation_packer") {
deps = [
"//third_party/elfutils:libelf",
]
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ "//build/config/compiler:no_chromium_code" ]
sources = gypi_values.relocation_packer_sources
}
# GYP: //third_party/android_platform/relocation_packer.gyp:android_relocation_packer
executable("android_relocation_packer") {
deps = [
":android_lib_relocation_packer",
"//third_party/elfutils:libelf",
]
sources = gypi_values.relocation_packer_main_source
}
}
...@@ -15,6 +15,9 @@ glue layer against. The AOSP framework is built from the Android release tag ...@@ -15,6 +15,9 @@ glue layer against. The AOSP framework is built from the Android release tag
after which it is named, and the jar can be built by invoking make on the after which it is named, and the jar can be built by invoking make on the
android_system_stubs target. android_system_stubs target.
Also includes a ported copy of the Android relocation packing tool source,
along with the files required to build it in the chromium tree.
Local Modifications: Local Modifications:
Only picked the few scripts needed by chrome. Only picked the few scripts needed by chrome.
Updated output directories to use environment variable. Updated output directories to use environment variable.
...@@ -24,3 +27,10 @@ Added support for parsing LOG(FATAL) and DCHECK errors and their ...@@ -24,3 +27,10 @@ Added support for parsing LOG(FATAL) and DCHECK errors and their
Added support for finding symbols when library is loaded directly from the APK. Added support for finding symbols when library is loaded directly from the APK.
Changed the toolchain to remove references to 4.6 toolchains. Changed the toolchain to remove references to 4.6 toolchains.
Added support for arch=x64 as an alias to arch=x86_64 Added support for arch=x64 as an alias to arch=x86_64
Android relocation packing tool details:
Copy sources from AOSP bionic/tools/relocation_packer
Remove scripts that regenerate golden test data (not relevant here)
Create a nativehelper/ScopedFd.h to satisfy inclusion from main.cc
Create gyp build
Create gn build (currently packer only; no unit tests)
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
src/debug.cc \
src/delta_encoder.cc \
src/elf_file.cc \
src/leb128.cc \
src/packer.cc \
src/sleb128.cc \
LOCAL_STATIC_LIBRARIES := libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf
LOCAL_MODULE := lib_relocation_packer
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := src/main.cc
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
LOCAL_MODULE := relocation_packer
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
src/debug_unittest.cc \
src/delta_encoder_unittest.cc \
src/elf_file_unittest.cc \
src/leb128_unittest.cc \
src/sleb128_unittest.cc \
src/packer_unittest.cc \
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_MODULE := relocation_packer_unit_tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_NATIVE_TEST)
# $(1) library name
define copy-test-library
include $(CLEAR_VARS)
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE := $(1)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES)
LOCAL_STRIP_MODULE := false
LOCAL_SRC_FILES := test_data/$(1)
include $(BUILD_PREBUILT)
endef
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))
// 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.
// 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
// 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_
// 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 "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
// 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 REL/RELA section of elf file.
//
// The encoded data format is sequence of elements of ElfAddr type (unsigned long):
//
// [00] relocation_count - the total count of relocations
// [01] initial r_offset - this is initial r_offset for the
// relocation table.
// followed by group structures:
// [02] group
// ...
// [nn] group
// the generalized format of the group is (! - always present ? - depends on group_flags):
// --------------
// ! group_size
// ! group_flags
// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set
// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set
// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND
// flag is set
//
// The group description is followed by individual relocations.
// please note that there is a case when individual relocation
// section could be empty - that is if every field ends up grouped.
//
// The format for individual relocations section is:
// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set
// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set
// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set
//
// For example lets pack the following relocations:
//
// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries:
// Offset Info Type Symbol's Value Symbol's Name + Addend
// 00000000000a2178 0000000000000403 R_AARCH64_RELATIVE 177a8
// 00000000000a2180 0000000000000403 R_AARCH64_RELATIVE 177cc
// 00000000000a2188 0000000000000403 R_AARCH64_RELATIVE 177e0
// 00000000000a2190 0000000000000403 R_AARCH64_RELATIVE 177f4
// 00000000000a2198 0000000000000403 R_AARCH64_RELATIVE 17804
// 00000000000a21a0 0000000000000403 R_AARCH64_RELATIVE 17818
// 00000000000a21a8 0000000000000403 R_AARCH64_RELATIVE 1782c
// 00000000000a21b0 0000000000000403 R_AARCH64_RELATIVE 17840
// 00000000000a21b8 0000000000000403 R_AARCH64_RELATIVE 17854
// 00000000000a21c0 0000000000000403 R_AARCH64_RELATIVE 17868
// 00000000000a21c8 0000000000000403 R_AARCH64_RELATIVE 1787c
// 00000000000a21d0 0000000000000403 R_AARCH64_RELATIVE 17890
// 00000000000a21d8 0000000000000403 R_AARCH64_RELATIVE 178a4
// 00000000000a21e8 0000000000000403 R_AARCH64_RELATIVE 178b8
//
// The header is going to be
// [00] 14 <- count
// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta,
// the delta is 8 in this case)
// -- starting the first and only group
// [03] 14 <- group size
// [03] 0xb <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA
// | RELOCATION_GROUPED_BY_INFO
// [04] 8 <- offset delta
// [05] 0x403 <- r_info
// -- end of group definition, starting list of r_addend deltas
// [06] 0x177a8
// [07] 0x24 = 177cc - 177a8
// [08] 0x14 = 177e0 - 177cc
// [09] 0x14 = 177f4 - 177e0
// [10] 0x10 = 17804 - 177f4
// [11] 0x14 = 17818 - 17804
// [12] 0x14 = 1782c - 17818
// [13] 0x14 = 17840 - 1782c
// [14] 0x14 = 17854 - 17840
// [15] 0x14 = 17868 - 17854
// [16] 0x14 = 1787c - 17868
// [17] 0x14 = 17890 - 1787c
// [18] 0x14 = 178a4 - 17890
// [19] 0x14 = 178b8 - 178a4
// -- the end.
// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can
// save us more bytes...
// The input ends when sum(group_size) == relocation_count
#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
#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.
template <typename ELF>
class RelocationDeltaCodec {
public:
typedef typename ELF::Addr ElfAddr;
typedef typename ELF::Rela ElfRela;
// Encode relocations with addends into a more compact form.
// |relocations| is a vector of relative relocation with addend structs.
// |packed| is the vector of packed words into which relocations are packed.
static void Encode(const std::vector<ElfRela>& relocations,
std::vector<ElfAddr>* packed);
// Decode relative relocations with addends from their more compact form.
// |packed| is the vector of packed relocations.
// |relocations| is a vector of unpacked relative relocations.
static void Decode(const std::vector<ElfAddr>& packed,
std::vector<ElfRela>* relocations);
private:
static void DetectGroup(const std::vector<ElfRela>& relocations,
size_t group_starts_with, ElfAddr previous_offset,
ElfAddr* group_size, ElfAddr* group_flags,
ElfAddr* group_offset_delta, ElfAddr* group_info,
ElfAddr* group_addend);
static void DetectGroupFields(const ElfRela& reloc_one, const ElfRela& reloc_two,
ElfAddr current_offset_delta, ElfAddr* group_flags,
ElfAddr* group_offset_delta, ElfAddr* group_info,
ElfAddr* group_addend);
};
} // namespace relocation_packer
#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
// 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 "gtest/gtest.h"
namespace {
template <typename T>
void AddRelocation(uint32_t addr,
uint32_t info,
int32_t addend,
std::vector<T>* relocations) {
T relocation;
relocation.r_offset = addr;
relocation.r_info = info;
relocation.r_addend = addend;
relocations->push_back(relocation);
}
template <typename T>
bool CheckRelocation(uint32_t addr,
uint32_t info,
int32_t addend,
const T& relocation) {
return relocation.r_offset == addr &&
relocation.r_info == info &&
relocation.r_addend == addend;
}
} // namespace
namespace relocation_packer {
template <typename ELF>
static void encode() {
std::vector<typename ELF::Rela> relocations;
std::vector<typename ELF::Addr> packed;
RelocationDeltaCodec<ELF> codec;
codec.Encode(relocations, &packed);
ASSERT_EQ(0U, packed.size());
// Initial relocation.
AddRelocation(0xf00d0000, 11U, 10000, &relocations);
codec.Encode(relocations, &packed);
// size of reloc table, size of group, flags, 3 fields, zero
EXPECT_EQ(7U, packed.size());
// One pair present.
size_t ndx = 0;
EXPECT_EQ(1U, packed[ndx++]);
EXPECT_EQ(0xf00d0000, packed[ndx++]);
EXPECT_EQ(1U, packed[ndx++]); // group_size
EXPECT_EQ(8U, packed[ndx++]); // flags
// Delta from the neutral element is zero
EXPECT_EQ(0U, packed[ndx++]); // offset_delta
EXPECT_EQ(11U, packed[ndx++]); // info
EXPECT_EQ(10000U, packed[ndx++]); // addend_delta
// Add a second relocation, 4 byte offset delta, 12 byte addend delta.
// same info
AddRelocation(0xf00d0004, 11U, 10012, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
ndx = 0;
EXPECT_EQ(8U, packed.size());
EXPECT_EQ(2U, packed[ndx++]); // relocs count
EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset
EXPECT_EQ(2U, packed[ndx++]); // group count
EXPECT_EQ(11U, packed[ndx++]); // flags
EXPECT_EQ(4U, packed[ndx++]); // group offset delta
EXPECT_EQ(11U, packed[ndx++]); // info
EXPECT_EQ(10000U, packed[ndx++]); // addend delta
EXPECT_EQ(12U, packed[ndx++]); // addend delta
// Add a third relocation, 4 byte offset delta, 12 byte addend delta.
// different info
AddRelocation(0xf00d0008, 41U, 10024, &relocations);
// Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
AddRelocation(0xf00d0010, 42U, 10000, &relocations);
AddRelocation(0xf00d0018, 42U, 9976, &relocations);
AddRelocation(0xf00d0020, 42U, 9952, &relocations);
AddRelocation(0xf00d2028, 1042U, 0, &relocations);
AddRelocation(0xf00d2030, 3442U, 0, &relocations);
packed.clear();
codec.Encode(relocations, &packed);
ndx = 0;
EXPECT_EQ(26U, packed.size());
// Total number of relocs
EXPECT_EQ(8U, packed[ndx++]);
EXPECT_EQ(0xf00cfffc, packed[ndx++]);
// 2 in first group
EXPECT_EQ(2U, packed[ndx++]);
EXPECT_EQ(11U, packed[ndx++]); //flags
EXPECT_EQ(4U, packed[ndx++]); // group offset delta
EXPECT_EQ(11U, packed[ndx++]); // info
// Initial relocation.
EXPECT_EQ(10000U, packed[ndx++]); // addend delta
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_EQ(12U, packed[ndx++]); // addend delta
// second group has only one reloc
EXPECT_EQ(1U, packed[ndx++]); // count
EXPECT_EQ(8U, packed[ndx++]); // flags
EXPECT_EQ(4U, packed[ndx++]); // offset delta
EXPECT_EQ(41U, packed[ndx++]); // info
EXPECT_EQ(12U, packed[ndx++]); // addend delta
// next - 3 relocs grouped by info
EXPECT_EQ(3U, packed[ndx++]); // count
EXPECT_EQ(11U, packed[ndx++]); // flags
EXPECT_EQ(8U, packed[ndx++]); // group offset delta
EXPECT_EQ(42U, packed[ndx++]); // info
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
// and last - 2 relocations without addend
EXPECT_EQ(2U, packed[ndx++]);
EXPECT_EQ(0U, packed[ndx++]); // flags
// offset_deltas and r_infos for next 2 relocations
EXPECT_EQ(0x2008U, packed[ndx++]); // offset delta
EXPECT_EQ(1042U, packed[ndx++]); // r_info
EXPECT_EQ(0x8U, packed[ndx++]); // offset delta
EXPECT_EQ(3442U, packed[ndx++]); // r_info
EXPECT_EQ(packed.size(), ndx);
}
TEST(Delta, Encode32) {
encode<ELF32_traits>();
}
TEST(Delta, Encode64) {
encode<ELF64_traits>();
}
template <typename ELF>
static void decode() {
std::vector<typename ELF::Addr> packed;
std::vector<typename ELF::Rela> relocations;
RelocationDeltaCodec<ELF> codec;
codec.Decode(packed, &relocations);
EXPECT_EQ(0U, relocations.size());
// Six pairs.
packed.push_back(6U); // count
packed.push_back(0xc0ddfffc); // base offset
packed.push_back(3U); // group count
packed.push_back(11U); // flags
packed.push_back(4U); // offset delta
packed.push_back(11U); // info
// Initial relocation.
packed.push_back(10000U);
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
packed.push_back(12U); // addend
packed.push_back(12U); // addend
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
packed.push_back(1U); // group count
packed.push_back(9U); // flags
packed.push_back(11U); // info
packed.push_back(8U);
packed.push_back(static_cast<typename ELF::Addr>(-24));
// next group with 2 relocs
packed.push_back(2U); // group count
packed.push_back(11U); // flags
packed.push_back(8U); // offset
packed.push_back(42U); // info
packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
relocations.clear();
codec.Decode(packed, &relocations);
EXPECT_EQ(6U, relocations.size());
// Initial relocation.
EXPECT_TRUE(CheckRelocation(0xc0de0000, 11U, 10000, relocations[0]));
// Two relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1]));
EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2]));
// Three relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3]));
EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4]));
EXPECT_TRUE(CheckRelocation(0xc0de0020, 42U, 9952, relocations[5]));
}
TEST(Delta, Decode32) {
decode<ELF32_traits>();
}
TEST(Delta, Decode64) {
decode<ELF64_traits>();
}
// TODO (dimitry): add more tests (fix by 19 January 2038 03:14:07 UTC)
// TODO (dimtiry): 1. Incorrect packed array for decode
// TODO (dimtiry): 2. Try to catch situation where it is likely to get series of groups with size 1
} // namespace relocation_packer
// 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.
template <typename ELF>
class ElfFile {
public:
explicit ElfFile(int fd)
: fd_(fd), is_padding_relocations_(false), elf_(NULL),
relocations_section_(NULL), dynamic_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:
enum relocations_type_t {
NONE = 0, REL, RELA
};
// Load a new ElfFile from a filedescriptor. If flushing, the file must
// be open for read/write. Returns true on successful ELF file load.
// |fd| is an open file descriptor for the shared object.
bool Load();
// Templated packer, helper for PackRelocations(). Rel type is one of
// ELF::Rel or ELF::Rela.
bool PackTypedRelocations(std::vector<typename ELF::Rela>* relocations);
// Templated unpacker, helper for UnpackRelocations(). Rel type is one of
// ELF::Rel or ELF::Rela.
bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
// Write ELF file changes.
void Flush();
void AdjustRelativeRelocationTargets(typename ELF::Off hole_start,
ssize_t hole_size,
std::vector<typename ELF::Rela>* relocations);
static void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
typename ELF::Word new_sh_type, relocations_type_t relocations_type);
static void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
typename ELF::Off hole_start,
ssize_t hole_size,
relocations_type_t relocations_type);
static void ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, size_t rel_array_size,
std::vector<typename ELF::Rela>* rela_vector);
static void ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
std::vector<typename ELF::Rel>* rel_vector);
// File descriptor opened on the shared object.
int fd_;
// 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_;
// Relocation type found, assigned by Load().
relocations_type_t relocations_type_;
};
} // namespace relocation_packer
#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
// 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 "gtest/gtest.h"
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 {
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);
}
*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) << "Error opening '" << path << "'";
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(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) {
const std::string base = std::string("elf_file_unittest_relocs_") + arch;
const std::string relocs = base + ".so";
const std::string packed_relocs = base + "_packed.so";
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));
}
template <typename ELF>
static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
relocation_packer::ElfFile<ELF> 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);
}
static void RunUnpackRelocationsTestFor(const std::string& arch) {
ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
if (relocs_so != NULL && packed_relocs_so != NULL) {
// lets detect elf class
ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET))
<< "Invalid file length: " << strerror(errno);
uint8_t elf_class = 0;
ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so));
ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET));
if (elf_class == ELFCLASS32) {
ProcessUnpack<ELF32_traits>(relocs_so, packed_relocs_so);
} else {
ProcessUnpack<ELF64_traits>(relocs_so, packed_relocs_so);
}
}
}
template <typename ELF>
static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) {
relocation_packer::ElfFile<ELF> elf_file(fileno(relocs_so));
// Ensure unpacking fails (not packed).
EXPECT_FALSE(elf_file.UnpackRelocations());
// Pack relocations, and check files are now identical.
EXPECT_TRUE(elf_file.PackRelocations());
CheckFileContentsEqual(relocs_so, packed_relocs_so);
CloseRelocsTestFiles(relocs_so, packed_relocs_so);
}
static void RunPackRelocationsTestFor(const std::string& arch) {
ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
FILE* relocs_so = NULL;
FILE* packed_relocs_so = NULL;
OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
if (relocs_so != NULL && packed_relocs_so != NULL) {
// lets detect elf class
ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET))
<< "Invalid file length: " << strerror(errno);
uint8_t elf_class = 0;
ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so));
fseek(packed_relocs_so, 0, SEEK_SET);
if (elf_class == ELFCLASS32) {
ProcessPack<ELF32_traits>(relocs_so, packed_relocs_so);
} else {
ProcessPack<ELF64_traits>(relocs_so, packed_relocs_so);
}
}
}
} // namespace
namespace relocation_packer {
TEST(ElfFile, PackRelocations) {
RunPackRelocationsTestFor("arm32");
RunPackRelocationsTestFor("arm64");
}
TEST(ElfFile, UnpackRelocations) {
RunUnpackRelocationsTestFor("arm32");
RunUnpackRelocationsTestFor("arm64");
}
} // namespace relocation_packer
// 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"
// ELF is a traits structure used to provide convenient aliases for
// 32/64 bit Elf types and functions, depending on the target file.
struct ELF32_traits {
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;
typedef Elf32_Half Half;
static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); }
static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); }
static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); }
};
struct ELF64_traits {
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;
typedef Elf64_Half Half;
static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); }
static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); }
static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); }
};
#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
// 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.
template <typename uint_t>
Leb128Encoder<uint_t>::Leb128Encoder() { }
template <typename uint_t>
Leb128Encoder<uint_t>::~Leb128Encoder() { }
// Add a single value to the encoding. Values are encoded with variable
// length. The least significant 7 bits of each byte hold 7 bits of data,
// and the most significant bit is set on each byte except the last.
template <typename uint_t>
void Leb128Encoder<uint_t>::Enqueue(uint_t value) {
uint_t uvalue = static_cast<uint_t>(value);
do {
const uint8_t byte = uvalue & 127;
uvalue >>= 7;
encoding_.push_back((uvalue ? 128 : 0) | byte);
} while (uvalue);
}
// Add a vector of values to the encoding.
template <typename uint_t>
void Leb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
for (size_t i = 0; i < values.size(); ++i) {
Enqueue(values[i]);
}
}
// Create a new decoder for the given encoded stream.
template <typename uint_t>
Leb128Decoder<uint_t>::Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
encoding_ = encoding;
cursor_ = start_with;
}
// Empty destructor to silence chromium-style.
template <typename uint_t>
Leb128Decoder<uint_t>::~Leb128Decoder() { }
// Decode and retrieve a single value from the encoding. Read forwards until
// a byte without its most significant bit is found, then read the 7 bit
// fields of the bytes spanned to re-form the value.
template <typename uint_t>
uint_t Leb128Decoder<uint_t>::Dequeue() {
uint_t value = 0;
size_t shift = 0;
uint8_t byte;
// Loop until we reach a byte with its high order bit clear.
do {
byte = encoding_[cursor_++];
value |= static_cast<uint_t>(byte & 127) << shift;
shift += 7;
} while (byte & 128);
return value;
}
// Decode and retrieve all remaining values from the encoding.
template <typename uint_t>
void Leb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
while (cursor_ < encoding_.size()) {
values->push_back(Dequeue());
}
}
template class Leb128Encoder<uint32_t>;
template class Leb128Encoder<uint64_t>;
template class Leb128Decoder<uint32_t>;
template class Leb128Decoder<uint64_t>;
} // namespace relocation_packer
// 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.
template <typename uint_t>
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(uint_t value);
// Add a vector of values to the encoding stream.
// |values| is the vector of unsigned ints to add.
void EnqueueAll(const std::vector<uint_t>& 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.
template <typename uint_t>
class Leb128Decoder {
public:
// Create a new decoder for the given encoded stream.
// |encoding| is the vector of encoded data.
explicit Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
// Explicit (but empty) destructor, for chromium-style.
~Leb128Decoder();
// Retrieve the next value from the encoded stream.
uint_t Dequeue();
// Retrieve all remaining values from the encoded stream.
// |values| is the vector of decoded data.
void DequeueAll(std::vector<uint_t>* 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_
// 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 "gtest/gtest.h"
namespace relocation_packer {
TEST(Leb128, Encoder64) {
std::vector<uint64_t> values;
values.push_back(624485);
values.push_back(0);
values.push_back(1);
values.push_back(127);
values.push_back(128);
Leb128Encoder<uint64_t> encoder;
encoder.EnqueueAll(values);
encoder.Enqueue(4294967295);
encoder.Enqueue(18446744073709551615ul);
std::vector<uint8_t> encoding;
encoder.GetEncoding(&encoding);
EXPECT_EQ(23U, 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, Decoder64) {
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<uint64_t> decoder(encoding, 0);
EXPECT_EQ(624485U, decoder.Dequeue());
std::vector<uint64_t> dequeued;
decoder.DequeueAll(&dequeued);
EXPECT_EQ(6U, dequeued.size());
EXPECT_EQ(0U, dequeued[0]);
EXPECT_EQ(1U, dequeued[1]);
EXPECT_EQ(127U, dequeued[2]);
EXPECT_EQ(128U, dequeued[3]);
EXPECT_EQ(4294967295U, dequeued[4]);
EXPECT_EQ(18446744073709551615UL, dequeued[5]);
}
} // namespace relocation_packer
// 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 "elf_traits.h"
#include "libelf.h"
#include "nativehelper/ScopedFd.h"
static 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);
printf(
"Debug sections are not handled, so packing should not be used on\n"
"shared libraries compiled for debugging or otherwise unstripped.\n");
}
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!";
}
const char* file = argv[argc - 1];
ScopedFd fd(open(file, O_RDWR));
if (fd.get() == -1) {
LOG(ERROR) << file << ": " << strerror(errno);
return 1;
}
if (is_verbose)
relocation_packer::Logger::SetVerbose(1);
// We need to detect elf class in order to create
// correct implementation
uint8_t e_ident[EI_NIDENT];
if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) {
LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno);
return 1;
}
if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
return 1;
}
bool status = false;
if (e_ident[EI_CLASS] == ELFCLASS32) {
relocation_packer::ElfFile<ELF32_traits> elf_file(fd.get());
elf_file.SetPadding(is_padding);
if (is_unpacking) {
status = elf_file.UnpackRelocations();
} else {
status = elf_file.PackRelocations();
}
} else if (e_ident[EI_CLASS] == ELFCLASS64) {
relocation_packer::ElfFile<ELF64_traits> elf_file(fd.get());
elf_file.SetPadding(is_padding);
if (is_unpacking) {
status = elf_file.UnpackRelocations();
} else {
status = elf_file.PackRelocations();
}
} else {
LOG(ERROR) << file << ": unknown ELFCLASS: " << e_ident[EI_CLASS];
return 1;
}
if (!status) {
LOG(ERROR) << file << ": failed to pack/unpack file";
return 1;
}
return 0;
}
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SCOPED_FD_H_included
#define SCOPED_FD_H_included
#include <unistd.h>
// Local definition of DISALLOW_COPY_AND_ASSIGN, avoids depending on base.
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// A smart pointer that closes the given fd on going out of scope.
// Use this when the fd is incidental to the purpose of your function,
// but needs to be cleaned up on exit.
class ScopedFd {
public:
explicit ScopedFd(int fd) : fd_(fd) {
}
~ScopedFd() {
reset();
}
int get() const {
return fd_;
}
int release() __attribute__((warn_unused_result)) {
int localFd = fd_;
fd_ = -1;
return localFd;
}
void reset(int new_fd = -1) {
if (fd_ != -1) {
TEMP_FAILURE_RETRY(close(fd_));
}
fd_ = new_fd;
}
private:
int fd_;
DISALLOW_COPY_AND_ASSIGN(ScopedFd);
};
#endif // SCOPED_FD_H_included
// 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 "sleb128.h"
namespace relocation_packer {
// Pack relocations into a group encoded packed representation.
template <typename ELF>
void RelocationPacker<ELF>::PackRelocations(const std::vector<typename ELF::Rela>& relocations,
std::vector<uint8_t>* packed) {
// Run-length encode.
std::vector<typename ELF::Addr> packed_words;
RelocationDeltaCodec<ELF> codec;
codec.Encode(relocations, &packed_words);
// If insufficient data do nothing.
if (packed_words.empty())
return;
Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
Leb128Encoder<typename ELF::Addr> leb128_encoder;
std::vector<uint8_t> leb128_packed;
std::vector<uint8_t> sleb128_packed;
leb128_encoder.EnqueueAll(packed_words);
leb128_encoder.GetEncoding(&leb128_packed);
sleb128_encoder.EnqueueAll(packed_words);
sleb128_encoder.GetEncoding(&sleb128_packed);
// TODO (simonb): Estimate savings on current android system image and consider using
// one encoder for all packed relocations to reduce complexity.
if (leb128_packed.size() <= sleb128_packed.size()) {
packed->push_back('A');
packed->push_back('P');
packed->push_back('U');
packed->push_back('2');
packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end());
} else {
packed->push_back('A');
packed->push_back('P');
packed->push_back('S');
packed->push_back('2');
packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end());
}
}
// Unpack relative relocations from a run-length encoded packed
// representation.
template <typename ELF>
void RelocationPacker<ELF>::UnpackRelocations(
const std::vector<uint8_t>& packed,
std::vector<typename ELF::Rela>* relocations) {
std::vector<typename ELF::Addr> packed_words;
CHECK(packed.size() > 4 &&
packed[0] == 'A' &&
packed[1] == 'P' &&
(packed[2] == 'U' || packed[2] == 'S') &&
packed[3] == '2');
if (packed[2] == 'U') {
Leb128Decoder<typename ELF::Addr> decoder(packed, 4);
decoder.DequeueAll(&packed_words);
} else {
Sleb128Decoder<typename ELF::Addr> decoder(packed, 4);
decoder.DequeueAll(&packed_words);
}
RelocationDeltaCodec<ELF> codec;
codec.Decode(packed_words, relocations);
}
template class RelocationPacker<ELF32_traits>;
template class RelocationPacker<ELF64_traits>;
} // namespace relocation_packer
// 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"
namespace relocation_packer {
// A RelocationPacker packs vectors of relocations into more
// compact forms, and unpacks them to reproduce the pre-packed data.
template <typename ELF>
class RelocationPacker {
public:
// Pack relocations into a more compact form.
// |relocations| is a vector of relocation structs.
// |packed| is the vector of packed bytes into which relocations are packed.
static void PackRelocations(const std::vector<typename ELF::Rela>& relocations,
std::vector<uint8_t>* packed);
// Unpack relocations from their more compact form.
// |packed| is the vector of packed relocations.
// |relocations| is a vector of unpacked relocation structs.
static void UnpackRelocations(const std::vector<uint8_t>& packed,
std::vector<typename ELF::Rela>* relocations);
};
} // namespace relocation_packer
#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
// 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 "gtest/gtest.h"
template <typename ELF>
static void AddRelocation(typename ELF::Addr addr,
typename ELF::Xword info,
typename ELF::Sxword addend,
std::vector<typename ELF::Rela>* relocations) {
typename ELF::Rela relocation;
relocation.r_offset = addr;
relocation.r_info = info;
relocation.r_addend = addend;
relocations->push_back(relocation);
}
template <typename ELF>
static bool CheckRelocation(typename ELF::Addr addr,
typename ELF::Xword info,
typename ELF::Sxword addend,
const typename ELF::Rela& relocation) {
return relocation.r_offset == addr &&
relocation.r_info == info &&
relocation.r_addend == addend;
}
namespace relocation_packer {
template <typename ELF>
static void DoPackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed;
// Initial relocation.
AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
// Two more relocations, 4 byte deltas.
AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
// Three more relocations, 8 byte deltas.
AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
RelocationPacker<ELF> packer;
packed.clear();
packer.PackRelocations(relocations, &packed);
ASSERT_EQ(18U, packed.size());
// Identifier.
size_t ndx = 0;
EXPECT_EQ('A', packed[ndx++]);
EXPECT_EQ('P', packed[ndx++]);
EXPECT_EQ('U', packed[ndx++]);
EXPECT_EQ('2', packed[ndx++]);
// relocation count
EXPECT_EQ(6, packed[ndx++]);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
EXPECT_EQ(0xfc, packed[ndx++]);
EXPECT_EQ(0xff, packed[ndx++]);
EXPECT_EQ(0xb7, packed[ndx++]);
EXPECT_EQ(0x8e, packed[ndx++]);
EXPECT_EQ(0x0d, packed[ndx++]);
// first group
EXPECT_EQ(3, packed[ndx++]); // size
EXPECT_EQ(3, packed[ndx++]); // flags
EXPECT_EQ(4, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x11, packed[ndx++]); // r_info
// second group
EXPECT_EQ(3, packed[ndx++]); // size
EXPECT_EQ(3, packed[ndx++]); // flags
EXPECT_EQ(8, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x11, packed[ndx++]); // r_info
EXPECT_EQ(ndx, packed.size());
}
TEST(Packer, PackNoAddend) {
DoPackNoAddend<ELF32_traits>();
DoPackNoAddend<ELF64_traits>();
}
template <typename ELF>
static void DoUnpackNoAddend() {
std::vector<typename ELF::Rela> relocations;
std::vector<uint8_t> packed;
packed.push_back('A');
packed.push_back('P');
packed.push_back('U');
packed.push_back('2');
// relocation count
packed.push_back(6);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
packed.push_back(0xfc);
packed.push_back(0xff);
packed.push_back(0xb7);
packed.push_back(0x8e);
packed.push_back(0x0d);
// first group
packed.push_back(3); // size
packed.push_back(3); // flags
packed.push_back(4); // r_offset_delta
packed.push_back(0x11); // r_info
// second group
packed.push_back(3); // size
packed.push_back(3); // flags
packed.push_back(8); // r_offset_delta
packed.push_back(0x11); // r_info
RelocationPacker<ELF> packer;
packer.UnpackRelocations(packed, &relocations);
size_t ndx = 0;
EXPECT_EQ(6U, relocations.size());
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x11, 0, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x11, 0, relocations[ndx++]));
EXPECT_EQ(ndx, relocations.size());
}
TEST(Packer, UnpackNoAddend) {
DoUnpackNoAddend<ELF32_traits>();
DoUnpackNoAddend<ELF64_traits>();
}
template <typename ELF>
static void DoPackWithAddend() {
std::vector<typename ELF::Rela> relocations;
// Initial relocation.
AddRelocation<ELF>(0xd1ce0000, 0x01, 10024, &relocations);
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
AddRelocation<ELF>(0xd1ce0004, 0x01, 10012, &relocations);
AddRelocation<ELF>(0xd1ce0008, 0x01, 10024, &relocations);
// Three more relocations, 8 byte deltas, -24 byte addend deltas.
AddRelocation<ELF>(0xd1ce0010, 0x01, 10000, &relocations);
AddRelocation<ELF>(0xd1ce0018, 0x01, 9976, &relocations);
AddRelocation<ELF>(0xd1ce0020, 0x01, 9952, &relocations);
std::vector<uint8_t> packed;
RelocationPacker<ELF> packer;
packed.clear();
packer.PackRelocations(relocations, &packed);
EXPECT_EQ(26U, packed.size());
size_t ndx = 0;
// Identifier.
EXPECT_EQ('A', packed[ndx++]);
EXPECT_EQ('P', packed[ndx++]);
EXPECT_EQ('S', packed[ndx++]);
EXPECT_EQ('2', packed[ndx++]);
// Relocation count
EXPECT_EQ(6U, packed[ndx++]);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr)
EXPECT_EQ(0xfc, packed[ndx++]);
EXPECT_EQ(0xff, packed[ndx++]);
EXPECT_EQ(0xb7, packed[ndx++]);
EXPECT_EQ(0x8e, packed[ndx++]);
if (sizeof(typename ELF::Addr) == 8) {
// positive for uint64_t
EXPECT_EQ(0x0d, packed[ndx++]);
} else {
// negative for uint32_t
EXPECT_EQ(0x7d, packed[ndx++]);
}
// group 1
EXPECT_EQ(0x03, packed[ndx++]); // size
EXPECT_EQ(0x0b, packed[ndx++]); // flags
EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x01, packed[ndx++]); // r_info
// group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
EXPECT_EQ(0xa8, packed[ndx++]);
EXPECT_EQ(0xce, packed[ndx++]);
EXPECT_EQ(0x00, packed[ndx++]);
// group 1 - addend 2: -12 = 0x74
EXPECT_EQ(0x74, packed[ndx++]);
// group 1 - addend 3: +12 = 0x0c
EXPECT_EQ(0x0c, packed[ndx++]);
// group 2
EXPECT_EQ(0x03, packed[ndx++]); // size
EXPECT_EQ(0x0b, packed[ndx++]); // flags
EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta
EXPECT_EQ(0x01, packed[ndx++]); // r_info
// group 2 - addend 1: -24 = 0x68
EXPECT_EQ(0x68, packed[ndx++]);
// group 2 - addend 2: -24 = 0x68
EXPECT_EQ(0x68, packed[ndx++]);
// group 2 - addend 3: -24 = 0x68
EXPECT_EQ(0x68, packed[ndx++]);
EXPECT_EQ(ndx, packed.size());
}
TEST(Packer, PackWithAddend) {
DoPackWithAddend<ELF32_traits>();
DoPackWithAddend<ELF64_traits>();
}
template <typename ELF>
static void DoUnpackWithAddend() {
std::vector<uint8_t> packed;
// Identifier.
packed.push_back('A');
packed.push_back('P');
packed.push_back('S');
packed.push_back('2');
// Relocation count
packed.push_back(6U);
// base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
packed.push_back(0xfc);
packed.push_back(0xff);
packed.push_back(0xb7);
packed.push_back(0x8e);
if (sizeof(typename ELF::Addr) == 8) {
// positive for uint64_t
packed.push_back(0x0d);
} else {
// negative for uint32_t
packed.push_back(0x7d);
}
// group 1
packed.push_back(0x03); // size
packed.push_back(0x0b); // flags
packed.push_back(0x04); // r_offset_delta
packed.push_back(0x01); // r_info
// group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
packed.push_back(0xa8);
packed.push_back(0xce);
packed.push_back(0x00);
// group 1 - addend 2: -12 = 0x74
packed.push_back(0x74);
// group 1 - addend 3: +12 = 0x0c
packed.push_back(0x0c);
// group 2
packed.push_back(0x03); // size
packed.push_back(0x0b); // flags
packed.push_back(0x08); // r_offset_delta
packed.push_back(0x01); // r_info
// group 2 - addend 1: -24 = 0x68
packed.push_back(0x68);
// group 2 - addend 2: -24 = 0x68
packed.push_back(0x68);
// group 2 - addend 3: -24 = 0x68
packed.push_back(0x68);
std::vector<typename ELF::Rela> relocations;
RelocationPacker<ELF> packer;
relocations.clear();
packer.UnpackRelocations(packed, &relocations);
EXPECT_EQ(6U, relocations.size());
size_t ndx = 0;
// Initial relocation.
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x01, 10024, relocations[ndx++]));
// Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x01, 10012, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x01, 10024, relocations[ndx++]));
// Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x01, 10000, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x01, 9976, relocations[ndx++]));
EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x01, 9952, relocations[ndx++]));
EXPECT_EQ(ndx, relocations.size());
}
TEST(Packer, UnpackWithAddend) {
DoUnpackWithAddend<ELF32_traits>();
DoUnpackWithAddend<ELF64_traits>();
}
} // namespace relocation_packer
// 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();
}
// 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 {
template <typename T>
class uint_traits {};
template <>
class uint_traits<uint64_t> {
public:
typedef int64_t int_t;
};
template <>
class uint_traits<uint32_t> {
public:
typedef int32_t int_t;
};
}
namespace relocation_packer {
// Empty constructor and destructor to silence chromium-style.
template <typename uint_t>
Sleb128Encoder<uint_t>::Sleb128Encoder() { }
template <typename uint_t>
Sleb128Encoder<uint_t>::~Sleb128Encoder() { }
// Add a single value to the encoding. Values are encoded with variable
// length. The least significant 7 bits of each byte hold 7 bits of data,
// 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).
template <typename uint_t>
void Sleb128Encoder<uint_t>::Enqueue(uint_t value) {
typedef typename uint_traits<uint_t>::int_t int_t;
static const size_t size = CHAR_BIT * sizeof(value);
bool more = true;
const bool negative = static_cast<int_t>(value) < 0;
while (more) {
uint8_t byte = value & 127;
value >>= 7;
// Sign extend if encoding a -ve value.
if (negative)
value |= -(static_cast<uint_t>(1) << (size - 7));
// The sign bit of byte is second high order bit.
const bool sign_bit = byte & 64;
if ((value == 0 && !sign_bit) || (value == static_cast<uint_t>(-1) && sign_bit))
more = false;
else
byte |= 128;
encoding_.push_back(byte);
}
}
// Add a vector of values to the encoding.
template <typename uint_t>
void Sleb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
for (size_t i = 0; i < values.size(); ++i) {
Enqueue(values[i]);
}
}
// Create a new decoder for the given encoded stream.
template <typename uint_t>
Sleb128Decoder<uint_t>::Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
encoding_ = encoding;
cursor_ = start_with;
}
// Empty destructor to silence chromium-style.
template <typename uint_t>
Sleb128Decoder<uint_t>::~Sleb128Decoder() { }
// Decode and retrieve a single value from the encoding. Consume bytes
// until one without its most significant bit is found, and re-form the
// value from the 7 bit fields of the bytes consumed.
template <typename uint_t>
uint_t Sleb128Decoder<uint_t>::Dequeue() {
uint_t value = 0;
static const size_t size = CHAR_BIT * sizeof(value);
size_t shift = 0;
uint8_t byte;
// Loop until we reach a byte with its high order bit clear.
do {
byte = encoding_[cursor_++];
value |= (static_cast<uint_t>(byte & 127) << shift);
shift += 7;
} while (byte & 128);
// The sign bit is second high order bit of the final byte decoded.
// Sign extend if value is -ve and we did not shift all of it.
if (shift < size && (byte & 64))
value |= -(static_cast<uint_t>(1) << shift);
return static_cast<uint_t>(value);
}
// Decode and retrieve all remaining values from the encoding.
template <typename uint_t>
void Sleb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
while (cursor_ < encoding_.size()) {
values->push_back(Dequeue());
}
}
template class Sleb128Encoder<uint32_t>;
template class Sleb128Encoder<uint64_t>;
template class Sleb128Decoder<uint32_t>;
template class Sleb128Decoder<uint64_t>;
} // namespace relocation_packer
// 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.
template<typename int_t>
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(int_t value);
// Add a vector of values to the encoding stream.
// |values| is the vector of signed ints to add.
void EnqueueAll(const std::vector<int_t>& 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.
template <typename int_t>
class Sleb128Decoder {
public:
// Create a new decoder for the given encoded stream.
// |encoding| is the vector of encoded data.
explicit Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
// Explicit (but empty) destructor, for chromium-style.
~Sleb128Decoder();
// Retrieve the next value from the encoded stream.
int_t Dequeue();
// Retrieve all remaining values from the encoded stream.
// |values| is the vector of decoded data.
void DequeueAll(std::vector<int_t>* 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_
// 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 "gtest/gtest.h"
namespace relocation_packer {
TEST(Sleb128, Encoder64) {
std::vector<uint64_t> values;
values.push_back(624485U);
values.push_back(0U);
values.push_back(1U);
values.push_back(63U);
values.push_back(64U);
values.push_back(static_cast<uint64_t>(-1));
values.push_back(static_cast<uint64_t>(-624485));
Sleb128Encoder<uint64_t> encoder;
encoder.EnqueueAll(values);
encoder.Enqueue(2147483647U);
encoder.Enqueue(static_cast<uint64_t>(-2147483648));
encoder.Enqueue(9223372036854775807ULL);
encoder.Enqueue(static_cast<uint64_t>(-9223372036854775807LL - 1));
std::vector<uint8_t> encoding;
encoder.GetEncoding(&encoding);
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<uint64_t> decoder(encoding, 0);
EXPECT_EQ(624485U, decoder.Dequeue());
std::vector<uint64_t> dequeued;
decoder.DequeueAll(&dequeued);
EXPECT_EQ(10U, dequeued.size());
EXPECT_EQ(0U, dequeued[0]);
EXPECT_EQ(1U, dequeued[1]);
EXPECT_EQ(63U, dequeued[2]);
EXPECT_EQ(64U, dequeued[3]);
EXPECT_EQ(static_cast<uint64_t>(-1), dequeued[4]);
EXPECT_EQ(static_cast<uint64_t>(-624485), dequeued[5]);
EXPECT_EQ(2147483647U, dequeued[6]);
EXPECT_EQ(static_cast<uint64_t>(-2147483648), dequeued[7]);
EXPECT_EQ(9223372036854775807ULL, dequeued[8]);
EXPECT_EQ(static_cast<uint64_t>(-9223372036854775807LL - 1), dequeued[9]);
}
} // namespace relocation_packer
# Copyright 2015 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_packer_target =
"//third_party/android_platform/relocation_packer($host_toolchain)"
relocation_packer_dir =
get_label_info("$relocation_packer_target", "root_out_dir")
relocation_packer_exe = "${relocation_packer_dir}/android_relocation_packer"
# Copyright 2015 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': {
# These files lists are shared with the GN build.
'relocation_packer_sources': [
'bionic/tools/relocation_packer/src/debug.cc',
'bionic/tools/relocation_packer/src/delta_encoder.cc',
'bionic/tools/relocation_packer/src/elf_file.cc',
'bionic/tools/relocation_packer/src/leb128.cc',
'bionic/tools/relocation_packer/src/packer.cc',
'bionic/tools/relocation_packer/src/sleb128.cc',
],
'relocation_packer_main_source': [
'bionic/tools/relocation_packer/src/main.cc',
],
'relocation_packer_test_sources': [
'bionic/tools/relocation_packer/src/debug_unittest.cc',
'bionic/tools/relocation_packer/src/delta_encoder_unittest.cc',
'bionic/tools/relocation_packer/src/elf_file_unittest.cc',
'bionic/tools/relocation_packer/src/leb128_unittest.cc',
'bionic/tools/relocation_packer/src/packer_unittest.cc',
'bionic/tools/relocation_packer/src/sleb128_unittest.cc',
'bionic/tools/relocation_packer/src/run_all_unittests.cc',
],
},
'targets': [
{
# GN: //third_party/android_platform:android_lib_relocation_packer
'target_name': 'android_lib_relocation_packer',
'toolsets': ['host'],
'type': 'static_library',
'dependencies': [
'../../third_party/elfutils/elfutils.gyp:libelf',
],
'sources': [
'<@(relocation_packer_sources)'
],
},
{
# GN: //third_party/android_platform:android_relocation_packer
'target_name': 'android_relocation_packer',
'toolsets': ['host'],
'type': 'executable',
'dependencies': [
'../../third_party/elfutils/elfutils.gyp:libelf',
'android_lib_relocation_packer',
],
'sources': [
'<@(relocation_packer_main_source)'
],
},
{
# TODO(GN)
'target_name': 'android_relocation_packer_unittests',
'toolsets': ['host'],
'type': 'executable',
'dependencies': [
'../../testing/gtest.gyp:gtest',
'android_lib_relocation_packer',
],
'include_dirs': [
'../..',
],
'sources': [
'<@(relocation_packer_test_sources)'
],
'copies': [
{
'destination': '<(PRODUCT_DIR)',
'files': [
'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so',
'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so',
'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so',
'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so',
],
},
],
},
],
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment