Merge changes I796a4ce8,I6165d062,I66c95d90,Ia67fa11d

* changes:
  Add support for packed relocations.
  Refactoring: introduce reloc_iterators
  Generalize compression tool
  Import relocation packer from chromium repo
This commit is contained in:
Dmitriy Ivanov 2015-03-07 07:08:50 +00:00 committed by Gerrit Code Review
commit 536ec7359e
39 changed files with 5364 additions and 19 deletions

View File

@ -94,6 +94,13 @@ typedef struct {
#define DT_PREINIT_ARRAY 32
#define DT_PREINIT_ARRAYSZ 33
/* Android compressed rel/rela sections */
#define DT_ANDROID_REL (DT_LOOS + 2)
#define DT_ANDROID_RELSZ (DT_LOOS + 3)
#define DT_ANDROID_RELA (DT_LOOS + 4)
#define DT_ANDROID_RELASZ (DT_LOOS + 5)
/* gnu hash entry */
#define DT_GNU_HASH 0x6ffffef5

View File

@ -50,11 +50,13 @@
#include "private/UniquePtr.h"
#include "linker.h"
#include "linker_allocator.h"
#include "linker_debug.h"
#include "linker_environ.h"
#include "linker_leb128.h"
#include "linker_phdr.h"
#include "linker_relocs.h"
#include "linker_allocator.h"
#include "linker_reloc_iterators.h"
/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<<
*
@ -1297,9 +1299,14 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
}
#endif
template<typename ElfRelT>
bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
for (size_t idx = 0; idx < count; ++idx, ++rel) {
template<typename ElfRelIteratorT>
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
const auto rel = rel_iterator.next();
if (rel == nullptr) {
return false;
}
ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
@ -1405,16 +1412,16 @@ bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_
MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n",
reinterpret_cast<void*>(reloc),
reinterpret_cast<void*>(base + addend));
*reinterpret_cast<ElfW(Addr)*>(reloc) = (base + addend);
reinterpret_cast<void*>(load_bias + addend));
*reinterpret_cast<ElfW(Addr)*>(reloc) = (load_bias + addend);
break;
case R_GENERIC_IRELATIVE:
count_relocation(kRelocRelative);
MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n",
reinterpret_cast<void*>(reloc),
reinterpret_cast<void*>(base + addend));
*reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + addend);
reinterpret_cast<void*>(load_bias + addend));
*reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(load_bias + addend);
break;
#if defined(__aarch64__)
@ -2051,6 +2058,22 @@ bool soinfo::prelink_image() {
rela_count_ = d->d_un.d_val / sizeof(ElfW(Rela));
break;
case DT_ANDROID_RELA:
android_relocs_ = reinterpret_cast<uint8_t*>(load_bias + d->d_un.d_ptr);
break;
case DT_ANDROID_RELASZ:
android_relocs_size_ = d->d_un.d_val;
break;
case DT_ANDROID_REL:
DL_ERR("unsupported DT_ANDROID_REL in \"%s\"", name);
return false;
case DT_ANDROID_RELSZ:
DL_ERR("unsupported DT_ANDROID_RELSZ in \"%s\"", name);
return false;
case DT_RELAENT:
if (d->d_un.d_val != sizeof(ElfW(Rela))) {
DL_ERR("invalid DT_RELAENT: %zd", static_cast<size_t>(d->d_un.d_val));
@ -2069,6 +2092,7 @@ bool soinfo::prelink_image() {
case DT_RELSZ:
DL_ERR("unsupported DT_RELSZ in \"%s\"", name);
return false;
#else
case DT_REL:
rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
@ -2085,6 +2109,22 @@ bool soinfo::prelink_image() {
}
break;
case DT_ANDROID_REL:
android_relocs_ = reinterpret_cast<uint8_t*>(load_bias + d->d_un.d_ptr);
break;
case DT_ANDROID_RELSZ:
android_relocs_size_ = d->d_un.d_val;
break;
case DT_ANDROID_RELA:
DL_ERR("unsupported DT_ANDROID_RELA in \"%s\"", name);
return false;
case DT_ANDROID_RELASZ:
DL_ERR("unsupported DT_ANDROID_RELASZ in \"%s\"", name);
return false;
// "Indicates that all RELATIVE relocations have been concatenated together,
// and specifies the RELATIVE relocation count."
//
@ -2092,9 +2132,15 @@ bool soinfo::prelink_image() {
// Not currently used by bionic linker - ignored.
case DT_RELCOUNT:
break;
case DT_RELA:
DL_ERR("unsupported DT_RELA in \"%s\"", name);
return false;
case DT_RELASZ:
DL_ERR("unsupported DT_RELASZ in \"%s\"", name);
return false;
#endif
case DT_INIT:
init_func_ = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
@ -2249,7 +2295,8 @@ bool soinfo::prelink_image() {
return true;
}
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) {
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
const android_dlextinfo* extinfo) {
local_group_root_ = local_group.front();
if (local_group_root_ == nullptr) {
@ -2270,29 +2317,63 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
}
#endif
if (android_relocs_ != nullptr) {
// check signature
if (android_relocs_size_ > 3 &&
android_relocs_[0] == 'A' &&
android_relocs_[1] == 'P' &&
(android_relocs_[2] == 'U' || android_relocs_[2] == 'S') &&
android_relocs_[3] == '2') {
DEBUG("[ android relocating %s ]", name);
bool relocated = false;
const uint8_t* packed_relocs = android_relocs_ + 4;
const size_t packed_relocs_size = android_relocs_size_ - 4;
if (android_relocs_[2] == 'U') {
relocated = relocate(
packed_reloc_iterator<leb128_decoder>(
leb128_decoder(packed_relocs, packed_relocs_size)),
global_group, local_group);
} else { // android_relocs_[2] == 'S'
relocated = relocate(
packed_reloc_iterator<sleb128_decoder>(
sleb128_decoder(packed_relocs, packed_relocs_size)),
global_group, local_group);
}
if (!relocated) {
return false;
}
} else {
DL_ERR("bad android relocation header.");
return false;
}
}
#if defined(USE_RELA)
if (rela_ != nullptr) {
DEBUG("[ relocating %s ]", name);
if (!relocate(rela_, rela_count_, global_group, local_group)) {
if (!relocate(plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
return false;
}
}
if (plt_rela_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
if (!relocate(plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
return false;
}
}
#else
if (rel_ != nullptr) {
DEBUG("[ relocating %s ]", name);
if (!relocate(rel_, rel_count_, global_group, local_group)) {
if (!relocate(plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) {
return false;
}
}
if (plt_rel_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
if (!relocate(plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) {
return false;
}
}

View File

@ -286,8 +286,8 @@ struct soinfo {
void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
void call_function(const char* function_name, linker_function_t function);
template<typename ElfRelT>
bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
template<typename ElfRelIteratorT>
bool relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
private:
// This part of the structure is only available
@ -315,6 +315,9 @@ struct soinfo {
soinfo* local_group_root_;
uint8_t* android_relocs_;
size_t android_relocs_size_;
friend soinfo* get_libdl_info();
};

87
linker/linker_leb128.h Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LINKER_LEB128_H
#define _LINKER_LEB128_H
#include <stdint.h>
// Helper classes for decoding LEB128, used in packed relocation data.
// http://en.wikipedia.org/wiki/LEB128
class leb128_decoder {
public:
leb128_decoder(const uint8_t* buffer, size_t count)
: current_(buffer), end_(buffer + count) { }
size_t pop_front() {
size_t value = 0;
size_t shift = 0;
uint8_t byte;
do {
if (current_ >= end_) {
__libc_fatal("leb128_decoder ran out of bounds");
}
byte = *current_++;
value |= static_cast<size_t>(byte & 127) << shift;
shift += 7;
} while (byte & 128);
return value;
}
private:
const uint8_t* current_;
const uint8_t* const end_;
};
class sleb128_decoder {
public:
sleb128_decoder(const uint8_t* buffer, size_t count)
: current_(buffer), end_(buffer+count) { }
size_t pop_front() {
size_t value = 0;
static const size_t size = CHAR_BIT * sizeof(value);
size_t shift = 0;
uint8_t byte;
do {
if (current_ >= end_) {
__libc_fatal("leb128_decoder ran out of bounds");
}
byte = *current_++;
value |= (static_cast<size_t>(byte & 127) << shift);
shift += 7;
} while (byte & 128);
if (shift < size && (byte & 64)) {
value |= -(static_cast<size_t>(1) << shift);
}
return value;
}
private:
const uint8_t* current_;
const uint8_t* const end_;
};
#endif // __LINKER_LEB128_H

View File

@ -29,10 +29,32 @@
#include "linker.h"
#include "linker_debug.h"
#include "linker_relocs.h"
#include "linker_reloc_iterators.h"
#include "linker_leb128.h"
template bool soinfo::relocate<plain_reloc_iterator>(plain_reloc_iterator&& rel_iterator,
const soinfo_list_t& global_group,
const soinfo_list_t& local_group);
template bool soinfo::relocate<packed_reloc_iterator<sleb128_decoder>>(
packed_reloc_iterator<sleb128_decoder>&& rel_iterator,
const soinfo_list_t& global_group,
const soinfo_list_t& local_group);
template bool soinfo::relocate<packed_reloc_iterator<leb128_decoder>>(
packed_reloc_iterator<leb128_decoder>&& rel_iterator,
const soinfo_list_t& global_group,
const soinfo_list_t& local_group);
template <typename ElfRelIteratorT>
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
const auto rel = rel_iterator.next();
if (rel == nullptr) {
return false;
}
template<>
bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
for (size_t idx = 0; idx < count; ++idx, ++rel) {
ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);

View File

@ -332,7 +332,7 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) {
return false;
}
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
start = mmap(nullptr, load_size_, PROT_NONE, mmap_flags, -1, 0);
if (start == MAP_FAILED) {
DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
return false;

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __LINKER_RELOC_ITERATORS_H
#define __LINKER_RELOC_ITERATORS_H
#include "linker.h"
#include <string.h>
#define RELOCATION_GROUPED_BY_INFO_FLAG 1
#define RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG 2
#define RELOCATION_GROUPED_BY_ADDEND_FLAG 4
#define RELOCATION_GROUP_HAS_ADDEND_FLAG 8
#define RELOCATION_GROUPED_BY_INFO(flags) (((flags) & RELOCATION_GROUPED_BY_INFO_FLAG) != 0)
#define RELOCATION_GROUPED_BY_OFFSET_DELTA(flags) (((flags) & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0)
#define RELOCATION_GROUPED_BY_ADDEND(flags) (((flags) & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0)
#define RELOCATION_GROUP_HAS_ADDEND(flags) (((flags) & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0)
class plain_reloc_iterator {
#if defined(USE_RELA)
typedef ElfW(Rela) rel_t;
#else
typedef ElfW(Rel) rel_t;
#endif
public:
plain_reloc_iterator(rel_t* rel_array, size_t count)
: begin_(rel_array), end_(begin_ + count), current_(begin_) {}
bool has_next() {
return current_ < end_;
}
rel_t* next() {
return current_++;
}
private:
rel_t* const begin_;
rel_t* const end_;
rel_t* current_;
DISALLOW_COPY_AND_ASSIGN(plain_reloc_iterator);
};
template <typename decoder_t>
class packed_reloc_iterator {
#if defined(USE_RELA)
typedef ElfW(Rela) rel_t;
#else
typedef ElfW(Rel) rel_t;
#endif
public:
explicit packed_reloc_iterator(decoder_t&& decoder)
: decoder_(decoder) {
// initialize fields
memset(&reloc_, 0, sizeof(reloc_));
relocation_count_ = decoder_.pop_front();
reloc_.r_offset = decoder_.pop_front();
relocation_index_ = 0;
relocation_group_index_ = 0;
group_size_ = 0;
}
bool has_next() const {
return relocation_index_ < relocation_count_;
}
rel_t* next() {
if (relocation_group_index_ == group_size_) {
if (!read_group_fields()) {
// Iterator is inconsistent state; it should not be called again
// but in case it is let's make sure has_next() returns false.
relocation_index_ = relocation_count_ = 0;
return nullptr;
}
}
if (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) {
reloc_.r_offset += group_r_offset_delta_;
} else {
reloc_.r_offset += decoder_.pop_front();
}
if (!RELOCATION_GROUPED_BY_INFO(group_flags_)) {
reloc_.r_info = decoder_.pop_front();
}
#if defined(USE_RELA)
if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && !RELOCATION_GROUPED_BY_ADDEND(group_flags_)) {
reloc_.r_addend += decoder_.pop_front();
}
#endif
relocation_index_++;
relocation_group_index_++;
return &reloc_;
}
private:
bool read_group_fields() {
group_size_ = decoder_.pop_front();
group_flags_ = decoder_.pop_front();
if (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) {
group_r_offset_delta_ = decoder_.pop_front();
}
if (RELOCATION_GROUPED_BY_INFO(group_flags_)) {
reloc_.r_info = decoder_.pop_front();
}
if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && RELOCATION_GROUPED_BY_ADDEND(group_flags_)) {
#if !defined(USE_RELA)
// This platform does not support rela, and yet we have it encoded in android_rel section.
DL_ERR("unexpected r_addend in android.rel section");
return false;
#else
reloc_.r_addend += decoder_.pop_front();
} else if (!RELOCATION_GROUP_HAS_ADDEND(group_flags_)) {
reloc_.r_addend = 0;
#endif
}
relocation_group_index_ = 0;
return true;
}
decoder_t decoder_;
size_t relocation_count_;
size_t group_size_;
size_t group_flags_;
size_t group_r_offset_delta_;
size_t relocation_index_;
size_t relocation_group_index_;
rel_t reloc_;
};
#endif // __LINKER_RELOC_ITERATORS_H

19
tools/Android.mk Normal file
View File

@ -0,0 +1,19 @@
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(call all-subdir-makefiles)

View File

@ -0,0 +1,96 @@
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
src/debug.cc \
src/delta_encoder.cc \
src/elf_file.cc \
src/leb128.cc \
src/packer.cc \
src/sleb128.cc \
LOCAL_STATIC_LIBRARIES := libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf
LOCAL_MODULE := lib_relocation_packer
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := src/main.cc
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
LOCAL_MODULE := relocation_packer
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
src/debug_unittest.cc \
src/delta_encoder_unittest.cc \
src/elf_file_unittest.cc \
src/leb128_unittest.cc \
src/sleb128_unittest.cc \
src/packer_unittest.cc \
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
LOCAL_C_INCLUDES := external/elfutils/src/libelf
LOCAL_CPPFLAGS := $(common_cppflags)
LOCAL_MODULE := relocation_packer_unit_tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_NATIVE_TEST)
# $(1) library name
define copy-test-library
include $(CLEAR_VARS)
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE := $(1)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES)
LOCAL_STRIP_MODULE := false
LOCAL_SRC_FILES := test_data/$(1)
include $(BUILD_PREBUILT)
endef
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))

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,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 "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,307 @@
// 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"
static constexpr uint32_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
static constexpr uint32_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
static constexpr uint32_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
static constexpr uint32_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
static bool is_relocation_grouped_by_info(uint64_t flags) {
return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
}
static bool is_relocation_grouped_by_offset_delta(uint64_t flags) {
return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
}
static bool is_relocation_grouped_by_addend(uint64_t flags) {
return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
}
static bool is_relocation_group_has_addend(uint64_t flags) {
return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
}
namespace relocation_packer {
// Encode relocations into a delta encoded (packed) representation.
template <typename ELF>
void RelocationDeltaCodec<ELF>::Encode(const std::vector<ElfRela>& relocations,
std::vector<ElfAddr>* packed) {
if (relocations.size() == 0)
return;
// Start with the relocation count, then append groups
// TODO(dimitry): we might want to move it to DT_ANDROID_RELCOUNT section
packed->push_back(static_cast<ElfAddr>(relocations.size()));
// lets write starting offset (offset of the first reloc - first delta)
ElfAddr start_offset = relocations.size() > 1 ?
relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) :
relocations[0].r_offset;
packed->push_back(start_offset);
// this one is used to calculate delta
ElfAddr previous_addend = 0;
ElfAddr previous_offset = start_offset;
for (size_t group_start = 0; group_start < relocations.size(); ) {
ElfAddr group_flags = 0;
ElfAddr group_offset_delta = 0;
ElfAddr group_addend = 0;
ElfAddr group_info = 0;
ElfAddr group_size = 0;
DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags,
&group_offset_delta, &group_info, &group_addend);
// write the group header
packed->push_back(group_size);
packed->push_back(group_flags);
if (is_relocation_grouped_by_offset_delta(group_flags)) {
packed->push_back(group_offset_delta);
}
if (is_relocation_grouped_by_info(group_flags)) {
packed->push_back(group_info);
}
if (is_relocation_group_has_addend(group_flags) &&
is_relocation_grouped_by_addend(group_flags)) {
packed->push_back(group_addend - previous_addend);
previous_addend = group_addend;
}
for (size_t i = 0; i < static_cast<size_t>(group_size); ++i) {
CHECK((group_start + i) < relocations.size());
const ElfRela* relocation = &relocations[group_start + i];
if (!is_relocation_grouped_by_offset_delta(group_flags)) {
packed->push_back(relocation->r_offset - previous_offset);
}
previous_offset = relocation->r_offset;
if (!is_relocation_grouped_by_info(group_flags)) {
packed->push_back(relocation->r_info);
}
if (is_relocation_group_has_addend(group_flags) &&
!is_relocation_grouped_by_addend(group_flags)) {
packed->push_back(relocation->r_addend - previous_addend);
previous_addend = relocation->r_addend;
}
}
// If the relocation group does not have an addend - reset it to 0
// to simplify addend computation for the group following this one.
if (!is_relocation_group_has_addend(group_flags)) {
previous_addend = 0;
}
group_start += group_size;
}
}
// Decode relocations from a delta encoded (packed) representation.
template <typename ELF>
void RelocationDeltaCodec<ELF>::Decode(const std::vector<ElfAddr>& packed,
std::vector<ElfRela>* relocations) {
if (packed.size() < 5) {
return;
}
size_t ndx = 0;
ElfAddr current_count = 0;
ElfAddr total_count = packed[ndx++];
ElfAddr offset = packed[ndx++];
ElfAddr info = 0;
ElfAddr addend = 0;
while(current_count < total_count) {
// read group
ElfAddr group_size = packed[ndx++];
ElfAddr group_flags = packed[ndx++];
ElfAddr group_offset_delta = 0;
if (is_relocation_grouped_by_offset_delta(group_flags)) {
group_offset_delta = packed[ndx++];
}
if (is_relocation_grouped_by_info(group_flags)) {
info = packed[ndx++];
}
if (is_relocation_group_has_addend(group_flags) &&
is_relocation_grouped_by_addend(group_flags)) {
addend += packed[ndx++];
}
// now read not grouped info
for (ElfAddr i = 0; i<group_size; ++i) {
if (is_relocation_grouped_by_offset_delta(group_flags)) {
offset += group_offset_delta;
} else {
offset += packed[ndx++];
}
if (!is_relocation_grouped_by_info(group_flags)) {
info = packed[ndx++];
}
if (is_relocation_group_has_addend(group_flags) &&
!is_relocation_grouped_by_addend(group_flags)) {
addend += packed[ndx++];
}
ElfRela reloc;
reloc.r_offset = offset;
reloc.r_info = info;
reloc.r_addend = is_relocation_group_has_addend(group_flags) ? addend : 0;
relocations->push_back(reloc);
}
if (!is_relocation_group_has_addend(group_flags)) {
addend = 0;
}
current_count += group_size;
}
}
// This function detects a way to group reloc_one and reloc_two, sets up group_flags
// and initializes values for corresponding group_ fields. For example if relocations
// can be grouped by r_info the function will set group_info variable.
template <typename ELF>
void RelocationDeltaCodec<ELF>::DetectGroupFields(const ElfRela& reloc_one,
const ElfRela& reloc_two,
ElfAddr current_offset_delta,
ElfAddr* group_flags,
ElfAddr* group_offset_delta,
ElfAddr* group_info,
ElfAddr* group_addend) {
*group_flags = 0;
const ElfAddr offset_delta = static_cast<ElfAddr>(reloc_two.r_offset) -
static_cast<ElfAddr>(reloc_one.r_offset);
if (offset_delta == current_offset_delta) {
*group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG;
if (group_offset_delta != nullptr) {
*group_offset_delta = current_offset_delta;
}
}
if (reloc_one.r_info == reloc_two.r_info) {
*group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG;
if (group_info != nullptr) {
*group_info = reloc_one.r_info;
}
}
if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) {
*group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG;
if (reloc_one.r_addend == reloc_two.r_addend) {
*group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG;
if (group_addend != nullptr) {
*group_addend = reloc_one.r_addend;
}
}
}
}
// This function is used to detect if there is better group available
// during RelocationDeltaCodec<ELF>::DetectGroup processing.
// Current implementation prefers having groups without addend (== zero addend)
// to any other groups field with the ratio 3:1. This is because addend tends
// to be more unevenly distributed than other fields.
static uint32_t group_weight(uint64_t flags) {
uint32_t weight = 0;
if (!is_relocation_group_has_addend(flags)) {
weight += 3;
} else if (is_relocation_grouped_by_addend(flags)) {
weight += 1;
}
if (is_relocation_grouped_by_offset_delta(flags)) {
weight += 1;
}
if (is_relocation_grouped_by_info(flags)) {
weight += 1;
}
return weight;
}
template <typename ELF>
void RelocationDeltaCodec<ELF>::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) {
CHECK(group_starts_with < relocations.size());
CHECK(group_flags != nullptr);
const ElfRela& reloc_one = relocations[group_starts_with++];
if (group_starts_with == relocations.size()) {
*group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
*group_size = 1;
return;
}
const ElfAddr offset_delta = reloc_one.r_offset - previous_offset;
// detect group_flags
DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags,
group_offset_delta, group_info, group_addend);
if (group_starts_with + 1 == relocations.size()) {
*group_size = 2;
return;
}
ElfAddr cnt = 1;
for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) {
ElfAddr candidate_flags;
// check if next group (reloc_current; reloc_next) has better grouped_by flags
DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags,
nullptr, nullptr, nullptr);
if (group_weight(*group_flags) < group_weight(candidate_flags)) {
break;
}
cnt++;
if (candidate_flags != *group_flags) {
break;
}
if (i + 1 == relocations.size() - 1) { // last one
cnt++;
}
}
// if as a result of checking candidates we ended up with cnt == 1
// reset flags to the default state
if (cnt == 1) {
*group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
}
*group_size = cnt;
}
template class RelocationDeltaCodec<ELF32_traits>;
template class RelocationDeltaCodec<ELF64_traits>;
} // namespace relocation_packer

View File

@ -0,0 +1,132 @@
// 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_

View File

@ -0,0 +1,223 @@
// 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

View File

@ -0,0 +1,882 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Implementation notes:
//
// We need to remove a piece from the ELF shared library. However, we also
// want to avoid fixing DWARF cfi data and relative relocation addresses.
// So after packing we shift offets and starting address of the RX segment
// while preserving code/data vaddrs location.
// This requires some fixups for symtab/hash/gnu_hash dynamic section addresses.
#include "elf_file.h"
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <string>
#include <vector>
#include "debug.h"
#include "elf_traits.h"
#include "libelf.h"
#include "packer.h"
namespace relocation_packer {
// Out-of-band dynamic tags used to indicate the offset and size of the
// android packed relocations section.
static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2;
static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3;
static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4;
static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5;
static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1;
static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2;
// Alignment to preserve, in bytes. This must be at least as large as the
// largest d_align and sh_addralign values found in the loaded file.
// Out of caution for RELRO page alignment, we preserve to a complete target
// page. See http://www.airs.com/blog/archives/189.
static constexpr size_t kPreserveAlignment = 4096;
// Get section data. Checks that the section has exactly one data entry,
// so that the section size and the data size are the same. True in
// practice for all sections we resize when packing or unpacking. Done
// by ensuring that a call to elf_getdata(section, data) returns NULL as
// the next data entry.
static Elf_Data* GetSectionData(Elf_Scn* section) {
Elf_Data* data = elf_getdata(section, NULL);
CHECK(data && elf_getdata(section, data) == NULL);
return data;
}
// Rewrite section data. Allocates new data and makes it the data element's
// buffer. Relies on program exit to free allocated data.
static void RewriteSectionData(Elf_Scn* section,
const void* section_data,
size_t size) {
Elf_Data* data = GetSectionData(section);
CHECK(size == data->d_size);
uint8_t* area = new uint8_t[size];
memcpy(area, section_data, size);
data->d_buf = area;
}
// Verbose ELF header logging.
template <typename Ehdr>
static void VerboseLogElfHeader(const Ehdr* elf_header) {
VLOG(1) << "e_phoff = " << elf_header->e_phoff;
VLOG(1) << "e_shoff = " << elf_header->e_shoff;
VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
VLOG(1) << "e_phentsize = " << elf_header->e_phentsize;
VLOG(1) << "e_phnum = " << elf_header->e_phnum;
VLOG(1) << "e_shnum = " << elf_header->e_shnum;
VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx;
}
// Verbose ELF program header logging.
template <typename Phdr>
static void VerboseLogProgramHeader(size_t program_header_index,
const Phdr* program_header) {
std::string type;
switch (program_header->p_type) {
case PT_NULL: type = "NULL"; break;
case PT_LOAD: type = "LOAD"; break;
case PT_DYNAMIC: type = "DYNAMIC"; break;
case PT_INTERP: type = "INTERP"; break;
case PT_PHDR: type = "PHDR"; break;
case PT_GNU_RELRO: type = "GNU_RELRO"; break;
case PT_GNU_STACK: type = "GNU_STACK"; break;
case PT_ARM_EXIDX: type = "EXIDX"; break;
default: type = "(OTHER)"; break;
}
VLOG(1) << "phdr[" << program_header_index << "] : " << type;
VLOG(1) << " p_offset = " << program_header->p_offset;
VLOG(1) << " p_vaddr = " << program_header->p_vaddr;
VLOG(1) << " p_paddr = " << program_header->p_paddr;
VLOG(1) << " p_filesz = " << program_header->p_filesz;
VLOG(1) << " p_memsz = " << program_header->p_memsz;
VLOG(1) << " p_flags = " << program_header->p_flags;
VLOG(1) << " p_align = " << program_header->p_align;
}
// Verbose ELF section header logging.
template <typename Shdr>
static void VerboseLogSectionHeader(const std::string& section_name,
const Shdr* section_header) {
VLOG(1) << "section " << section_name;
VLOG(1) << " sh_addr = " << section_header->sh_addr;
VLOG(1) << " sh_offset = " << section_header->sh_offset;
VLOG(1) << " sh_size = " << section_header->sh_size;
VLOG(1) << " sh_entsize = " << section_header->sh_entsize;
VLOG(1) << " sh_addralign = " << section_header->sh_addralign;
}
// Verbose ELF section data logging.
static void VerboseLogSectionData(const Elf_Data* data) {
VLOG(1) << " data";
VLOG(1) << " d_buf = " << data->d_buf;
VLOG(1) << " d_off = " << data->d_off;
VLOG(1) << " d_size = " << data->d_size;
VLOG(1) << " d_align = " << data->d_align;
}
// Load the complete ELF file into a memory image in libelf, and identify
// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or
// .android.rela.dyn sections. No-op if the ELF file has already been loaded.
template <typename ELF>
bool ElfFile<ELF>::Load() {
if (elf_)
return true;
Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL);
CHECK(elf);
if (elf_kind(elf) != ELF_K_ELF) {
LOG(ERROR) << "File not in ELF format";
return false;
}
auto elf_header = ELF::getehdr(elf);
if (!elf_header) {
LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno());
return false;
}
if (elf_header->e_type != ET_DYN) {
LOG(ERROR) << "ELF file is not a shared object";
return false;
}
// Require that our endianness matches that of the target, and that both
// are little-endian. Safe for all current build/target combinations.
const int endian = elf_header->e_ident[EI_DATA];
CHECK(endian == ELFDATA2LSB);
CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
const int file_class = elf_header->e_ident[EI_CLASS];
VLOG(1) << "endian = " << endian << ", file class = " << file_class;
VerboseLogElfHeader(elf_header);
auto elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header != nullptr);
const typename ELF::Phdr* dynamic_program_header = NULL;
for (size_t i = 0; i < elf_header->e_phnum; ++i) {
auto program_header = &elf_program_header[i];
VerboseLogProgramHeader(i, program_header);
if (program_header->p_type == PT_DYNAMIC) {
CHECK(dynamic_program_header == NULL);
dynamic_program_header = program_header;
}
}
CHECK(dynamic_program_header != nullptr);
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
// Notes of the dynamic relocations, packed relocations, and .dynamic
// sections. Found while iterating sections, and later stored in class
// attributes.
Elf_Scn* found_relocations_section = nullptr;
Elf_Scn* found_dynamic_section = nullptr;
// Notes of relocation section types seen. We require one or the other of
// these; both is unsupported.
bool has_rel_relocations = false;
bool has_rela_relocations = false;
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != nullptr) {
auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
VerboseLogSectionHeader(name, section_header);
// Note relocation section types.
if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) {
has_rel_relocations = true;
}
if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) {
has_rela_relocations = true;
}
// Note special sections as we encounter them.
if ((name == ".rel.dyn" || name == ".rela.dyn") &&
section_header->sh_size > 0) {
found_relocations_section = section;
}
if (section_header->sh_offset == dynamic_program_header->p_offset) {
found_dynamic_section = section;
}
// Ensure we preserve alignment, repeated later for the data block(s).
CHECK(section_header->sh_addralign <= kPreserveAlignment);
Elf_Data* data = NULL;
while ((data = elf_getdata(section, data)) != NULL) {
CHECK(data->d_align <= kPreserveAlignment);
VerboseLogSectionData(data);
}
}
// Loading failed if we did not find the required special sections.
if (!found_relocations_section) {
LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section";
return false;
}
if (!found_dynamic_section) {
LOG(ERROR) << "Missing .dynamic section";
return false;
}
// Loading failed if we could not identify the relocations type.
if (!has_rel_relocations && !has_rela_relocations) {
LOG(ERROR) << "No relocations sections found";
return false;
}
if (has_rel_relocations && has_rela_relocations) {
LOG(ERROR) << "Multiple relocations sections with different types found, "
<< "not currently supported";
return false;
}
elf_ = elf;
relocations_section_ = found_relocations_section;
dynamic_section_ = found_dynamic_section;
relocations_type_ = has_rel_relocations ? REL : RELA;
return true;
}
// Helper for ResizeSection(). Adjust the main ELF header for the hole.
template <typename ELF>
static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header,
typename ELF::Off hole_start,
ssize_t hole_size) {
if (elf_header->e_phoff > hole_start) {
elf_header->e_phoff += hole_size;
VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
}
if (elf_header->e_shoff > hole_start) {
elf_header->e_shoff += hole_size;
VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff;
}
}
// Helper for ResizeSection(). Adjust all section headers for the hole.
template <typename ELF>
static void AdjustSectionHeadersForHole(Elf* elf,
typename ELF::Off hole_start,
ssize_t hole_size) {
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != NULL) {
auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
if (section_header->sh_offset > hole_start) {
section_header->sh_offset += hole_size;
VLOG(1) << "section " << name
<< " sh_offset adjusted to " << section_header->sh_offset;
} else {
section_header->sh_addr -= hole_size;
VLOG(1) << "section " << name
<< " sh_addr adjusted to " << section_header->sh_addr;
}
}
}
// Helper for ResizeSection(). Adjust the offsets of any program headers
// that have offsets currently beyond the hole start.
template <typename ELF>
static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers,
size_t count,
typename ELF::Off hole_start,
ssize_t hole_size) {
for (size_t i = 0; i < count; ++i) {
typename ELF::Phdr* program_header = &program_headers[i];
if (program_header->p_offset > hole_start) {
// The hole start is past this segment, so adjust offset.
program_header->p_offset += hole_size;
VLOG(1) << "phdr[" << i
<< "] p_offset adjusted to "<< program_header->p_offset;
} else {
program_header->p_vaddr -= hole_size;
program_header->p_paddr -= hole_size;
VLOG(1) << "phdr[" << i
<< "] p_vaddr adjusted to "<< program_header->p_vaddr
<< "; p_paddr adjusted to "<< program_header->p_paddr;
}
}
}
// Helper for ResizeSection(). Find the first loadable segment in the
// file. We expect it to map from file offset zero.
template <typename ELF>
static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers,
size_t count,
typename ELF::Off hole_start) {
for (size_t i = 0; i < count; ++i) {
typename ELF::Phdr* program_header = &program_headers[i];
if (program_header->p_type == PT_LOAD &&
program_header->p_offset <= hole_start &&
(program_header->p_offset + program_header->p_filesz) >= hole_start ) {
return program_header;
}
}
LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start;
NOTREACHED();
return nullptr;
}
// Helper for ResizeSection(). Rewrite program headers.
template <typename ELF>
static void RewriteProgramHeadersForHole(Elf* elf,
typename ELF::Off hole_start,
ssize_t hole_size) {
const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
CHECK(elf_header);
typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header);
const size_t program_header_count = elf_header->e_phnum;
// Locate the segment that we can overwrite to form the new LOAD entry,
// and the segment that we are going to split into two parts.
typename ELF::Phdr* target_load_header =
FindLoadSegmentForHole<ELF>(elf_program_header, program_header_count, hole_start);
VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust";
// Adjust PT_LOAD program header memsz and filesz
target_load_header->p_filesz += hole_size;
target_load_header->p_memsz += hole_size;
// Adjust the offsets and p_vaddrs
AdjustProgramHeaderOffsets<ELF>(elf_program_header,
program_header_count,
hole_start,
hole_size);
}
// Helper for ResizeSection(). Locate and return the dynamic section.
template <typename ELF>
static Elf_Scn* GetDynamicSection(Elf* elf) {
const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
CHECK(elf_header);
const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header);
// Find the program header that describes the dynamic section.
const typename ELF::Phdr* dynamic_program_header = NULL;
for (size_t i = 0; i < elf_header->e_phnum; ++i) {
const typename ELF::Phdr* program_header = &elf_program_header[i];
if (program_header->p_type == PT_DYNAMIC) {
dynamic_program_header = program_header;
}
}
CHECK(dynamic_program_header);
// Now find the section with the same offset as this program header.
Elf_Scn* dynamic_section = NULL;
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != NULL) {
typename ELF::Shdr* section_header = ELF::getshdr(section);
if (section_header->sh_offset == dynamic_program_header->p_offset) {
dynamic_section = section;
}
}
CHECK(dynamic_section != NULL);
return dynamic_section;
}
// Helper for ResizeSection(). Adjust the .dynamic section for the hole.
template <typename ELF>
void ElfFile<ELF>::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
typename ELF::Off hole_start,
ssize_t hole_size,
relocations_type_t relocations_type) {
CHECK(relocations_type != NONE);
Elf_Data* data = GetSectionData(dynamic_section);
auto dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
if (hole_size > 0) { // expanding
hole_start += hole_size;
}
for (size_t i = 0; i < dynamics.size(); ++i) {
typename ELF::Dyn* dynamic = &dynamics[i];
const typename ELF::Sword tag = dynamic->d_tag;
// Any tags that hold offsets are adjustment candidates.
const bool is_adjustable = (tag == DT_PLTGOT ||
tag == DT_HASH ||
tag == DT_GNU_HASH ||
tag == DT_STRTAB ||
tag == DT_SYMTAB ||
tag == DT_RELA ||
tag == DT_INIT ||
tag == DT_FINI ||
tag == DT_REL ||
tag == DT_JMPREL ||
tag == DT_INIT_ARRAY ||
tag == DT_FINI_ARRAY ||
tag == DT_ANDROID_REL||
tag == DT_ANDROID_RELA);
if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) {
dynamic->d_un.d_ptr -= hole_size;
VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
<< " d_ptr adjusted to " << dynamic->d_un.d_ptr;
}
// DT_RELSZ or DT_RELASZ indicate the overall size of relocations.
// Only one will be present. Adjust by hole size.
if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) {
dynamic->d_un.d_val += hole_size;
VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
<< " d_val adjusted to " << dynamic->d_un.d_val;
}
// Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and
// technically (2) the relative relocation count is not changed.
// DT_RELENT and DT_RELAENT don't change, ignore them as well.
}
void* section_data = &dynamics[0];
size_t bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section, section_data, bytes);
}
// Resize a section. If the new size is larger than the current size, open
// up a hole by increasing file offsets that come after the hole. If smaller
// than the current size, remove the hole by decreasing those offsets.
template <typename ELF>
void ElfFile<ELF>::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
typename ELF::Word new_sh_type,
relocations_type_t relocations_type) {
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
if (section_header->sh_size == new_size) {
return;
}
// Require that the section size and the data size are the same. True
// in practice for all sections we resize when packing or unpacking.
Elf_Data* data = GetSectionData(section);
CHECK(data->d_off == 0 && data->d_size == section_header->sh_size);
// Require that the section is not zero-length (that is, has allocated
// data that we can validly expand).
CHECK(data->d_size && data->d_buf);
const auto hole_start = section_header->sh_offset;
const ssize_t hole_size = new_size - data->d_size;
VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " <<
data->d_size << " -> " << (data->d_size + hole_size);
VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " <<
data->d_size << " -> " << (data->d_size + hole_size);
// libelf overrides sh_entsize for known sh_types, so it does not matter what we set
// for SHT_REL/SHT_RELA.
typename ELF::Xword new_entsize =
(new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0;
VLOG(1) << "Update section (" << name << ") entry size: " <<
section_header->sh_entsize << " -> " << new_entsize;
// Resize the data and the section header.
data->d_size += hole_size;
section_header->sh_size += hole_size;
section_header->sh_entsize = new_entsize;
section_header->sh_type = new_sh_type;
// Add the hole size to all offsets in the ELF file that are after the
// start of the hole. If the hole size is positive we are expanding the
// section to create a new hole; if negative, we are closing up a hole.
// Start with the main ELF header.
typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
AdjustElfHeaderForHole<ELF>(elf_header, hole_start, hole_size);
// Adjust all section headers.
AdjustSectionHeadersForHole<ELF>(elf, hole_start, hole_size);
// Rewrite the program headers to either split or coalesce segments,
// and adjust dynamic entries to match.
RewriteProgramHeadersForHole<ELF>(elf, hole_start, hole_size);
Elf_Scn* dynamic_section = GetDynamicSection<ELF>(elf);
AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type);
}
// Find the first slot in a dynamics array with the given tag. The array
// always ends with a free (unused) element, and which we exclude from the
// search. Returns dynamics->size() if not found.
template <typename ELF>
static size_t FindDynamicEntry(typename ELF::Sword tag,
std::vector<typename ELF::Dyn>* dynamics) {
// Loop until the penultimate entry. We exclude the end sentinel.
for (size_t i = 0; i < dynamics->size() - 1; ++i) {
if (dynamics->at(i).d_tag == tag) {
return i;
}
}
// The tag was not found.
return dynamics->size();
}
// Replace dynamic entry.
template <typename ELF>
static void ReplaceDynamicEntry(typename ELF::Sword tag,
const typename ELF::Dyn& dyn,
std::vector<typename ELF::Dyn>* dynamics) {
const size_t slot = FindDynamicEntry<ELF>(tag, dynamics);
if (slot == dynamics->size()) {
LOG(FATAL) << "Dynamic slot is not found for tag=" << tag;
}
// Replace this entry with the one supplied.
dynamics->at(slot) = dyn;
VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag;
}
// Remove relative entries from dynamic relocations and write as packed
// data into android packed relocations.
template <typename ELF>
bool ElfFile<ELF>::PackRelocations() {
// Load the ELF file into libelf.
if (!Load()) {
LOG(ERROR) << "Failed to load as ELF";
return false;
}
// Retrieve the current dynamic relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
// we always pack rela, because packed format is pretty much the same
std::vector<typename ELF::Rela> relocations;
if (relocations_type_ == REL) {
// Convert data to a vector of relocations.
const typename ELF::Rel* relocations_base = reinterpret_cast<typename ELF::Rel*>(data->d_buf);
ConvertRelArrayToRelaVector(relocations_base,
data->d_size / sizeof(typename ELF::Rel), &relocations);
LOG(INFO) << "Relocations : REL";
} else if (relocations_type_ == RELA) {
// Convert data to a vector of relocations with addends.
const typename ELF::Rela* relocations_base = reinterpret_cast<typename ELF::Rela*>(data->d_buf);
relocations = std::vector<typename ELF::Rela>(
relocations_base,
relocations_base + data->d_size / sizeof(relocations[0]));
LOG(INFO) << "Relocations : RELA";
} else {
NOTREACHED();
}
return PackTypedRelocations(&relocations);
}
// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
template <typename ELF>
bool ElfFile<ELF>::PackTypedRelocations(std::vector<typename ELF::Rela>* relocations) {
typedef typename ELF::Rela Rela;
// If no relocations then we have nothing packable. Perhaps
// the shared object has already been packed?
if (relocations->empty()) {
LOG(ERROR) << "No relocations found (already packed?)";
return false;
}
const size_t rel_size =
relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel);
const size_t initial_bytes = relocations->size() * rel_size;
LOG(INFO) << "Unpacked : " << initial_bytes << " bytes";
std::vector<uint8_t> packed;
RelocationPacker<ELF> packer;
// Pack relocations: dry run to estimate memory savings.
packer.PackRelocations(*relocations, &packed);
const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]);
LOG(INFO) << "Packed (no padding): " << packed_bytes_estimate << " bytes";
if (packed.empty()) {
LOG(INFO) << "Too few relocations to pack";
return false;
}
// Pre-calculate the size of the hole we will close up when we rewrite
// dynamic relocations. We have to adjust relocation addresses to
// account for this.
typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
ssize_t hole_size = initial_bytes - packed_bytes_estimate;
// hole_size needs to be page_aligned.
hole_size -= hole_size % kPreserveAlignment;
LOG(INFO) << "Compaction : " << hole_size << " bytes";
// Adjusting for alignment may have removed any packing benefit.
if (hole_size == 0) {
LOG(INFO) << "Too few relocations to pack after alignment";
return false;
}
if (hole_size <= 0) {
LOG(INFO) << "Packing relocations saves no space";
return false;
}
size_t data_padding_bytes = is_padding_relocations_ ?
initial_bytes - packed_bytes_estimate :
initial_bytes - hole_size - packed_bytes_estimate;
// pad data
std::vector<uint8_t> padding(data_padding_bytes, 0);
packed.insert(packed.end(), padding.begin(), padding.end());
const void* packed_data = &packed[0];
// Run a loopback self-test as a check that packing is lossless.
std::vector<Rela> unpacked;
packer.UnpackRelocations(packed, &unpacked);
CHECK(unpacked.size() == relocations->size());
CHECK(!memcmp(&unpacked[0],
&relocations->at(0),
unpacked.size() * sizeof(unpacked[0])));
// Rewrite the current dynamic relocations section with packed one then shrink it to size.
const size_t bytes = packed.size() * sizeof(packed[0]);
ResizeSection(elf_, relocations_section_, bytes,
relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_);
RewriteSectionData(relocations_section_, packed_data, bytes);
// TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt
// Rewrite .dynamic and rename relocation tags describing the packed android
// relocations.
Elf_Data* data = GetSectionData(dynamic_section_);
const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
section_header = ELF::getshdr(relocations_section_);
{
typename ELF::Dyn dyn;
dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA;
dyn.d_un.d_ptr = section_header->sh_addr;
ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics);
}
{
typename ELF::Dyn dyn;
dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ;
dyn.d_un.d_val = section_header->sh_size;
ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics);
}
const void* dynamics_data = &dynamics[0];
const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
Flush();
return true;
}
// Find packed relative relocations in the packed android relocations
// section, unpack them, and rewrite the dynamic relocations section to
// contain unpacked data.
template <typename ELF>
bool ElfFile<ELF>::UnpackRelocations() {
// Load the ELF file into libelf.
if (!Load()) {
LOG(ERROR) << "Failed to load as ELF";
return false;
}
typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
// Retrieve the current packed android relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
// Convert data to a vector of bytes.
const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf);
std::vector<uint8_t> packed(
packed_base,
packed_base + data->d_size / sizeof(packed[0]));
if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) &&
packed.size() > 3 &&
packed[0] == 'A' &&
packed[1] == 'P' &&
(packed[2] == 'U' || packed[2] == 'S') &&
packed[3] == '2') {
LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA");
} else {
LOG(ERROR) << "Packed relocations not found (not packed?)";
return false;
}
return UnpackTypedRelocations(packed);
}
// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
template <typename ELF>
bool ElfFile<ELF>::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
// Unpack the data to re-materialize the relative relocations.
const size_t packed_bytes = packed.size() * sizeof(packed[0]);
LOG(INFO) << "Packed : " << packed_bytes << " bytes";
std::vector<typename ELF::Rela> unpacked_relocations;
RelocationPacker<ELF> packer;
packer.UnpackRelocations(packed, &unpacked_relocations);
const size_t relocation_entry_size =
relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela);
const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size;
LOG(INFO) << "Unpacked : " << unpacked_bytes << " bytes";
// Retrieve the current dynamic relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
LOG(INFO) << "Relocations : " << unpacked_relocations.size() << " entries";
// If we found the same number of null relocation entries in the dynamic
// relocations section as we hold as unpacked relative relocations, then
// this is a padded file.
const bool is_padded = packed_bytes == unpacked_bytes;
// Unless padded, pre-apply relative relocations to account for the
// hole, and pre-adjust all relocation offsets accordingly.
typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
if (!is_padded) {
LOG(INFO) << "Expansion : " << unpacked_bytes - packed_bytes << " bytes";
}
// Rewrite the current dynamic relocations section with unpacked version of
// relocations.
const void* section_data = nullptr;
std::vector<typename ELF::Rel> unpacked_rel_relocations;
if (relocations_type_ == RELA) {
section_data = &unpacked_relocations[0];
} else if (relocations_type_ == REL) {
ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations);
section_data = &unpacked_rel_relocations[0];
} else {
NOTREACHED();
}
ResizeSection(elf_, relocations_section_, unpacked_bytes,
relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_);
RewriteSectionData(relocations_section_, section_data, unpacked_bytes);
// Rewrite .dynamic to remove two tags describing packed android relocations.
data = GetSectionData(dynamic_section_);
const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
{
typename ELF::Dyn dyn;
dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA;
dyn.d_un.d_ptr = section_header->sh_addr;
ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA,
dyn, &dynamics);
}
{
typename ELF::Dyn dyn;
dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ;
dyn.d_un.d_val = section_header->sh_size;
ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ,
dyn, &dynamics);
}
const void* dynamics_data = &dynamics[0];
const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
Flush();
return true;
}
// Flush rewritten shared object file data.
template <typename ELF>
void ElfFile<ELF>::Flush() {
// Flag all ELF data held in memory as needing to be written back to the
// file, and tell libelf that we have controlled the file layout.
elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY);
elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT);
// Write ELF data back to disk.
const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
if (file_bytes == -1) {
LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno());
}
CHECK(file_bytes > 0);
VLOG(1) << "elf_update returned: " << file_bytes;
// Clean up libelf, and truncate the output file to the number of bytes
// written by elf_update().
elf_end(elf_);
elf_ = NULL;
const int truncate = ftruncate(fd_, file_bytes);
CHECK(truncate == 0);
}
template <typename ELF>
void ElfFile<ELF>::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array,
size_t rel_array_size,
std::vector<typename ELF::Rela>* rela_vector) {
for (size_t i = 0; i<rel_array_size; ++i) {
typename ELF::Rela rela;
rela.r_offset = rel_array[i].r_offset;
rela.r_info = rel_array[i].r_info;
rela.r_addend = 0;
rela_vector->push_back(rela);
}
}
template <typename ELF>
void ElfFile<ELF>::ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
std::vector<typename ELF::Rel>* rel_vector) {
for (auto rela : rela_vector) {
typename ELF::Rel rel;
rel.r_offset = rela.r_offset;
rel.r_info = rela.r_info;
CHECK(rela.r_addend == 0);
rel_vector->push_back(rel);
}
}
template class ElfFile<ELF32_traits>;
template class ElfFile<ELF64_traits>;
} // namespace relocation_packer

View File

@ -0,0 +1,155 @@
// 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_

View File

@ -0,0 +1,188 @@
// 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

View File

@ -0,0 +1,64 @@
// 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_

View File

@ -0,0 +1,87 @@
// 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

View File

@ -0,0 +1,76 @@
// 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_

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 "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

View File

@ -0,0 +1,153 @@
// 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;
}

View File

@ -0,0 +1,88 @@
// 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

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.
// 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_

View File

@ -0,0 +1,292 @@
// 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

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,131 @@
// 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

View File

@ -0,0 +1,77 @@
// 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_

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 "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

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