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:
commit
536ec7359e
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
87
linker/linker_leb128.h
Normal 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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
152
linker/linker_reloc_iterators.h
Normal file
152
linker/linker_reloc_iterators.h
Normal 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
19
tools/Android.mk
Normal 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)
|
96
tools/relocation_packer/Android.mk
Normal file
96
tools/relocation_packer/Android.mk
Normal 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))
|
27
tools/relocation_packer/LICENSE
Normal file
27
tools/relocation_packer/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
135
tools/relocation_packer/README.TXT
Normal file
135
tools/relocation_packer/README.TXT
Normal file
@ -0,0 +1,135 @@
|
||||
Introduction:
|
||||
-------------
|
||||
|
||||
Relative relocations are the bulk of dynamic relocations (the .rel.dyn
|
||||
or .rela.dyn sections) in libchrome.<version>.so. The ELF standard
|
||||
representation of them is wasteful.
|
||||
|
||||
Packing uses a combination of run length encoding, delta encoding, and LEB128
|
||||
encoding to store them more efficiently. Packed relocations are placed in
|
||||
a new .android.rel.dyn or .android.rela.dyn section. Packing reduces
|
||||
the footprint of libchrome.<version>.so in the filesystem, in APK downloads,
|
||||
and in memory when loaded on the device.
|
||||
|
||||
A packed libchrome.<version>.so is designed so that it can be loaded directly
|
||||
on Android, but requires the explicit support of a crazy linker that has been
|
||||
extended to understand packed relocations. Packed relocations are currently
|
||||
only supported on ARM.
|
||||
|
||||
A packed libchrome.<version>.so cannot currently be used with the standard
|
||||
Android runtime linker.
|
||||
|
||||
See src/*.h for design and implementation notes.
|
||||
|
||||
|
||||
Notes:
|
||||
------
|
||||
|
||||
Packing does not adjust debug data. An unstripped libchrome.<version>.so
|
||||
can be packed and will run, but may no longer be useful for debugging.
|
||||
|
||||
Unpacking on the device requires the explicit support of an extended crazy
|
||||
linker. Adds the following new .dynamic tags, used by the crazy linker to
|
||||
find the packed .android.rel.dyn or .android.rela.dyn section data:
|
||||
|
||||
DT_ANDROID_REL_OFFSET = DT_LOOS (Operating System specific: 0x6000000d)
|
||||
- The offset of packed relocation data in libchrome.<version>.so
|
||||
DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e)
|
||||
- The size of packed relocation data in bytes
|
||||
|
||||
32 bit ARM libraries use relocations without addends. 64 bit ARM libraries
|
||||
use relocations with addends. The packing strategy necessarily differs for
|
||||
the two relocation types.
|
||||
|
||||
Where libchrome.<version>.so contains relocations without addends, the format
|
||||
of .android.rel.dyn data is:
|
||||
|
||||
"APR1" identifier
|
||||
N: the number of count-delta pairs in the encoding
|
||||
A: the initial offset
|
||||
N * C,D: N count-delta pairs
|
||||
|
||||
Where libchrome.<version>.so contains relocations with addends, the format
|
||||
of .android.rela.dyn data is:
|
||||
|
||||
"APA1" identifier
|
||||
N: the number of addr-addend delta pairs in the encoding
|
||||
N * A,V: N addr-addend delta pairs
|
||||
|
||||
All numbers in the encoding stream are stored as LEB128 values. For details
|
||||
see http://en.wikipedia.org/wiki/LEB128.
|
||||
|
||||
The streaming unpacking algorithm for 32 bit ARM is:
|
||||
|
||||
skip over "APR1"
|
||||
pairs, addr = next leb128 value, next leb128 value
|
||||
emit R_ARM_RELATIVE relocation with r_offset = addr
|
||||
while pairs:
|
||||
count, delta = next leb128 value, next leb128 value
|
||||
while count:
|
||||
addr += delta
|
||||
emit R_ARM_RELATIVE relocation with r_offset = addr
|
||||
count--
|
||||
pairs--
|
||||
|
||||
The streaming unpacking algorithm for 64 bit ARM is:
|
||||
|
||||
skip over "APA1"
|
||||
pairs = next signed leb128 value
|
||||
addr, addend = 0, 0
|
||||
while pairs:
|
||||
addr += next signed leb128 value
|
||||
addend += next signed leb128 value
|
||||
emit R_AARCH64_RELATIVE relocation with r_offset = addr, r_addend = addend
|
||||
pairs--
|
||||
|
||||
|
||||
Usage instructions:
|
||||
-------------------
|
||||
|
||||
To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and
|
||||
then run the tool:
|
||||
|
||||
echo -n 'NULL' >/tmp/small
|
||||
if file libchrome.<version>.so | grep -q 'ELF 32'; then
|
||||
arm-linux-androideabi-objcopy
|
||||
--add-section .android.rel.dyn=/tmp/small
|
||||
libchrome.<version>.so libchrome.<version>.so.packed
|
||||
else
|
||||
aarch64-linux-android-objcopy
|
||||
--add-section .android.rela.dyn=/tmp/small
|
||||
libchrome.<version>.so libchrome.<version>.so.packed
|
||||
fi
|
||||
rm /tmp/small
|
||||
relocation_packer libchrome.<version>.so.packed
|
||||
|
||||
To unpack and restore the shared library to its original state:
|
||||
|
||||
cp libchrome.<version>.so.packed unpackable
|
||||
relocation_packer -u unpackable
|
||||
if file libchrome.<version>.so | grep -q 'ELF 32'; then
|
||||
arm-linux-androideabi-objcopy \
|
||||
--remove-section=.android.rel.dyn unpackable libchrome.<version>.so
|
||||
else
|
||||
aarch64-linux-android-objcopy \
|
||||
--remove-section=.android.rela.dyn unpackable libchrome.<version>.so
|
||||
endif
|
||||
rm unpackable
|
||||
|
||||
|
||||
Bugs & TODOs:
|
||||
-------------
|
||||
|
||||
Requires two free slots in the .dynamic section. Uses these to add data that
|
||||
tells the crazy linker where to find the packed relocation data. Fails
|
||||
if insufficient free slots exist (use gold --spare-dynamic-slots to increase
|
||||
the allocation).
|
||||
|
||||
Requires libelf 0.158 or later. Earlier libelf releases may be buggy in
|
||||
ways that prevent the packer from working correctly.
|
||||
|
||||
|
||||
Testing:
|
||||
--------
|
||||
|
||||
Unittests run under gtest, on the host system.
|
55
tools/relocation_packer/src/debug.cc
Normal file
55
tools/relocation_packer/src/debug.cc
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
// Construct a new message logger. Prints if level is less than or equal to
|
||||
// the level set with SetVerbose() and predicate is true.
|
||||
Logger::Logger(Severity severity, int level, bool predicate) {
|
||||
severity_ = severity;
|
||||
level_ = level;
|
||||
predicate_ = predicate;
|
||||
}
|
||||
|
||||
// On destruction, flush and print the strings accumulated. Abort if FATAL.
|
||||
Logger::~Logger() {
|
||||
if (predicate_) {
|
||||
if (level_ <= max_level_) {
|
||||
std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_;
|
||||
std::string tag;
|
||||
switch (severity_) {
|
||||
case INFO: tag = "INFO"; break;
|
||||
case WARNING: tag = "WARNING"; break;
|
||||
case ERROR: tag = "ERROR"; break;
|
||||
case FATAL: tag = "FATAL"; break;
|
||||
}
|
||||
stream_.flush();
|
||||
*log << tag << ": " << stream_.str() << std::endl;
|
||||
}
|
||||
if (severity_ == FATAL)
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset to initial state.
|
||||
void Logger::Reset() {
|
||||
max_level_ = -1;
|
||||
info_stream_ = &std::cout;
|
||||
error_stream_ = &std::cerr;
|
||||
}
|
||||
|
||||
// Verbosity. Not thread-safe.
|
||||
int Logger::max_level_ = -1;
|
||||
|
||||
// Logging streams. Not thread-safe.
|
||||
std::ostream* Logger::info_stream_ = &std::cout;
|
||||
std::ostream* Logger::error_stream_ = &std::cerr;
|
||||
|
||||
} // namespace relocation_packer
|
115
tools/relocation_packer/src/debug.h
Normal file
115
tools/relocation_packer/src/debug.h
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Logging and checks. Avoids a dependency on base.
|
||||
//
|
||||
// LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL.
|
||||
// INFO prints to stdout, the others to stderr. FATAL aborts after printing.
|
||||
//
|
||||
// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent.
|
||||
//
|
||||
// VLOG(level) logs INFO messages where level is less than or equal to the
|
||||
// verbosity level set with SetVerbose().
|
||||
//
|
||||
// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true,
|
||||
// else silent.
|
||||
//
|
||||
// CHECK(predicate) logs a FATAL error if predicate is false.
|
||||
// NOTREACHED() always aborts.
|
||||
// Log streams can be changed with SetStreams(). Logging is not thread-safe.
|
||||
//
|
||||
|
||||
#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
|
||||
#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
enum Severity {INFO = 0, WARNING, ERROR, FATAL};
|
||||
|
||||
// Construct a new message logger. Prints if level is less than or
|
||||
// equal to the level set with SetVerbose() and predicate is true.
|
||||
// |severity| is an enumerated severity.
|
||||
// |level| is the verbosity level.
|
||||
// |predicate| controls if the logger prints or is silent.
|
||||
Logger(Severity severity, int level, bool predicate);
|
||||
|
||||
// On destruction, flush and print the strings accumulated in stream_.
|
||||
~Logger();
|
||||
|
||||
// Return the stream for this logger.
|
||||
std::ostream& GetStream() { return stream_; }
|
||||
|
||||
// Set verbosity level. Messages with a level less than or equal to
|
||||
// this level are printed, others are discarded. Static, not thread-safe.
|
||||
static void SetVerbose(int level) { max_level_ = level; }
|
||||
|
||||
// Set info and error logging streams. Static, not thread-safe.
|
||||
static void SetStreams(std::ostream* info_stream,
|
||||
std::ostream* error_stream) {
|
||||
info_stream_ = info_stream;
|
||||
error_stream_ = error_stream;
|
||||
}
|
||||
|
||||
// Reset to initial state.
|
||||
static void Reset();
|
||||
|
||||
private:
|
||||
// Message severity, verbosity level, and predicate.
|
||||
Severity severity_;
|
||||
int level_;
|
||||
bool predicate_;
|
||||
|
||||
// String stream, accumulates message text.
|
||||
std::ostringstream stream_;
|
||||
|
||||
// Verbosity for INFO messages. Not thread-safe.
|
||||
static int max_level_;
|
||||
|
||||
// Logging streams. Not thread-safe.
|
||||
static std::ostream* info_stream_;
|
||||
static std::ostream* error_stream_;
|
||||
};
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
||||
// Make logging severities visible globally.
|
||||
typedef relocation_packer::Logger::Severity LogSeverity;
|
||||
using LogSeverity::INFO;
|
||||
using LogSeverity::WARNING;
|
||||
using LogSeverity::ERROR;
|
||||
using LogSeverity::FATAL;
|
||||
|
||||
// LOG(severity) prints a message with the given severity, and aborts if
|
||||
// severity is FATAL. LOG_IF(severity, predicate) does the same but only if
|
||||
// predicate is true. INT_MIN is guaranteed to be less than or equal to
|
||||
// any verbosity level.
|
||||
#define LOG(severity) \
|
||||
(relocation_packer::Logger(severity, INT_MIN, true).GetStream())
|
||||
#define LOG_IF(severity, predicate) \
|
||||
(relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream())
|
||||
|
||||
// VLOG(level) prints its message as INFO if level is less than or equal to
|
||||
// the current verbosity level.
|
||||
#define VLOG(level) \
|
||||
(relocation_packer::Logger(INFO, (level), true).GetStream())
|
||||
#define VLOG_IF(level, predicate) \
|
||||
(relocation_packer::Logger(INFO, (level), (predicate)).GetStream())
|
||||
|
||||
// CHECK(predicate) fails with a FATAL log message if predicate is false.
|
||||
#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \
|
||||
<< __FILE__ << ":" << __LINE__ << ": " \
|
||||
<< __FUNCTION__ << ": CHECK '" #predicate "' failed")
|
||||
|
||||
// NOTREACHED() always fails with a FATAL log message.
|
||||
#define NOTREACHED(_) (LOG(FATAL) \
|
||||
<< __FILE__ << ":" << __LINE__ << ": " \
|
||||
<< __FUNCTION__ << ": NOTREACHED() hit")
|
||||
|
||||
#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
|
122
tools/relocation_packer/src/debug_unittest.cc
Normal file
122
tools/relocation_packer/src/debug_unittest.cc
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "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
|
307
tools/relocation_packer/src/delta_encoder.cc
Normal file
307
tools/relocation_packer/src/delta_encoder.cc
Normal 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
|
132
tools/relocation_packer/src/delta_encoder.h
Normal file
132
tools/relocation_packer/src/delta_encoder.h
Normal 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_
|
223
tools/relocation_packer/src/delta_encoder_unittest.cc
Normal file
223
tools/relocation_packer/src/delta_encoder_unittest.cc
Normal 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
|
882
tools/relocation_packer/src/elf_file.cc
Normal file
882
tools/relocation_packer/src/elf_file.cc
Normal 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
|
155
tools/relocation_packer/src/elf_file.h
Normal file
155
tools/relocation_packer/src/elf_file.h
Normal 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_
|
188
tools/relocation_packer/src/elf_file_unittest.cc
Normal file
188
tools/relocation_packer/src/elf_file_unittest.cc
Normal 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
|
64
tools/relocation_packer/src/elf_traits.h
Normal file
64
tools/relocation_packer/src/elf_traits.h
Normal 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_
|
87
tools/relocation_packer/src/leb128.cc
Normal file
87
tools/relocation_packer/src/leb128.cc
Normal 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
|
76
tools/relocation_packer/src/leb128.h
Normal file
76
tools/relocation_packer/src/leb128.h
Normal 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_
|
111
tools/relocation_packer/src/leb128_unittest.cc
Normal file
111
tools/relocation_packer/src/leb128_unittest.cc
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "leb128.h"
|
||||
|
||||
#include <vector>
|
||||
#include "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
|
153
tools/relocation_packer/src/main.cc
Normal file
153
tools/relocation_packer/src/main.cc
Normal 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;
|
||||
}
|
88
tools/relocation_packer/src/packer.cc
Normal file
88
tools/relocation_packer/src/packer.cc
Normal 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
|
74
tools/relocation_packer/src/packer.h
Normal file
74
tools/relocation_packer/src/packer.h
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// 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_
|
292
tools/relocation_packer/src/packer_unittest.cc
Normal file
292
tools/relocation_packer/src/packer_unittest.cc
Normal 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
|
81
tools/relocation_packer/src/run_length_encoder.h
Normal file
81
tools/relocation_packer/src/run_length_encoder.h
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Run-length encode and decode relative relocations.
|
||||
//
|
||||
// Relative relocations are the bulk of dynamic relocations (the
|
||||
// .rel.dyn or .rela.dyn sections) in libchrome.<version>.so, and the ELF
|
||||
// standard representation of them is wasteful. .rel.dyn contains
|
||||
// relocations without addends, .rela.dyn relocations with addends.
|
||||
//
|
||||
// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes
|
||||
// on 64 bit plaforms, split into offset and info fields. Offsets strictly
|
||||
// increase, and each is commonly a few bytes different from its predecessor.
|
||||
// There are long runs where the difference does not change. The info field
|
||||
// is constant. Example, from 'readelf -x4 libchrome.<version>.so' 32 bit:
|
||||
//
|
||||
// offset info offset info
|
||||
// 808fef01 17000000 848fef01 17000000 ................
|
||||
// 888fef01 17000000 8c8fef01 17000000 ................
|
||||
// 908fef01 17000000 948fef01 17000000 ................
|
||||
//
|
||||
// Run length encoding packs this data more efficiently, by representing it
|
||||
// as a delta and a count of entries each differing from its predecessor
|
||||
// by this delta. The above can be represented as a start address followed
|
||||
// by an encoded count of 6 and offset difference of 4:
|
||||
//
|
||||
// start count diff
|
||||
// 01ef8f80 00000006 00000004
|
||||
//
|
||||
// Because relative relocation offsets strictly increase, the complete
|
||||
// set of relative relocations in libchrome.<version>.so can be
|
||||
// represented by a single start address followed by one or more difference
|
||||
// and count encoded word pairs:
|
||||
//
|
||||
// start run1 count run1 diff run2 count run2 diff
|
||||
// 01ef8f80 00000006 00000004 00000010 00000008 ...
|
||||
//
|
||||
// Decoding regenerates relative relocations beginning at address
|
||||
// 'start' and for each encoded run, incrementing the address by 'difference'
|
||||
// for 'count' iterations and emitting a new relative relocation.
|
||||
//
|
||||
// Once encoded, data is prefixed by a single word count of packed delta and
|
||||
// count pairs. A final run-length encoded relative relocations vector
|
||||
// might therefore look something like:
|
||||
//
|
||||
// pairs start run 1 run 2 ... run 15
|
||||
// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ...
|
||||
// Interpreted as:
|
||||
// pairs=15 start=.. count=6,delta=4 count=16,delta=8
|
||||
|
||||
#ifndef TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
|
||||
#define TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "elf.h"
|
||||
#include "elf_traits.h"
|
||||
|
||||
namespace relocation_packer {
|
||||
|
||||
// A RelocationRunLengthCodec packs vectors of relative relocations
|
||||
// into more compact forms, and unpacks them to reproduce the pre-packed data.
|
||||
class RelocationRunLengthCodec {
|
||||
public:
|
||||
// Encode relative relocations into a more compact form.
|
||||
// |relocations| is a vector of relative relocation structs.
|
||||
// |packed| is the vector of packed words into which relocations are packed.
|
||||
static void Encode(const std::vector<ELF::Rel>& relocations,
|
||||
std::vector<ELF::Xword>* packed);
|
||||
|
||||
// Decode relative relocations from their more compact form.
|
||||
// |packed| is the vector of packed relocations.
|
||||
// |relocations| is a vector of unpacked relative relocation structs.
|
||||
static void Decode(const std::vector<ELF::Xword>& packed,
|
||||
std::vector<ELF::Rel>* relocations);
|
||||
};
|
||||
|
||||
} // namespace relocation_packer
|
||||
|
||||
#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
|
131
tools/relocation_packer/src/sleb128.cc
Normal file
131
tools/relocation_packer/src/sleb128.cc
Normal 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
|
77
tools/relocation_packer/src/sleb128.h
Normal file
77
tools/relocation_packer/src/sleb128.h
Normal 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_
|
166
tools/relocation_packer/src/sleb128_unittest.cc
Normal file
166
tools/relocation_packer/src/sleb128_unittest.cc
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "sleb128.h"
|
||||
|
||||
#include <vector>
|
||||
#include "elf_traits.h"
|
||||
#include "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
|
1014
tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
Normal file
1014
tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Executable file
Binary file not shown.
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Executable file
Binary file not shown.
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Executable file
Binary file not shown.
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Executable file
BIN
tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Executable file
Binary file not shown.
88
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
Executable file
88
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
Executable file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Build relocation packer unit test data.
|
||||
|
||||
Uses a built relocation packer to generate 'golden' reference test data
|
||||
files for elf_file_unittests.cc.
|
||||
"""
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
def PackArmLibraryRelocations(android_pack_relocations,
|
||||
android_objcopy,
|
||||
added_section,
|
||||
input_path,
|
||||
output_path):
|
||||
# Copy and add a 'NULL' .android.rel.dyn section for the packing tool.
|
||||
with tempfile.NamedTemporaryFile() as stream:
|
||||
stream.write('NULL')
|
||||
stream.flush()
|
||||
objcopy_command = [android_objcopy,
|
||||
'--add-section', '%s=%s' % (added_section, stream.name),
|
||||
input_path, output_path]
|
||||
subprocess.check_call(objcopy_command)
|
||||
|
||||
# Pack relocations.
|
||||
pack_command = [android_pack_relocations, output_path]
|
||||
subprocess.check_call(pack_command)
|
||||
|
||||
|
||||
def UnpackArmLibraryRelocations(android_pack_relocations,
|
||||
input_path,
|
||||
output_path):
|
||||
shutil.copy(input_path, output_path)
|
||||
|
||||
# Unpack relocations. We leave the .android.rel.dyn or .android.rela.dyn
|
||||
# in place.
|
||||
unpack_command = [android_pack_relocations, '-u', output_path]
|
||||
subprocess.check_call(unpack_command)
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser()
|
||||
|
||||
parser.add_option('--android-pack-relocations',
|
||||
help='Path to the ARM relocations packer binary')
|
||||
parser.add_option('--android-objcopy',
|
||||
help='Path to the toolchain\'s objcopy binary')
|
||||
parser.add_option('--added-section',
|
||||
choices=['.android.rel.dyn', '.android.rela.dyn'],
|
||||
help='Section to add, one of ".android.rel.dyn" or ".android.rela.dyn"')
|
||||
parser.add_option('--test-file',
|
||||
help='Path to the input test file, an unpacked ARM .so')
|
||||
parser.add_option('--unpacked-output',
|
||||
help='Path to the output file for reference unpacked data')
|
||||
parser.add_option('--packed-output',
|
||||
help='Path to the output file for reference packed data')
|
||||
|
||||
options, _ = parser.parse_args()
|
||||
|
||||
for output in [options.unpacked_output, options.packed_output]:
|
||||
directory = os.path.dirname(output)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
PackArmLibraryRelocations(options.android_pack_relocations,
|
||||
options.android_objcopy,
|
||||
options.added_section,
|
||||
options.test_file,
|
||||
options.packed_output)
|
||||
|
||||
UnpackArmLibraryRelocations(options.android_pack_relocations,
|
||||
options.packed_output,
|
||||
options.unpacked_output)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
35
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
Executable file
35
tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
# Generates elf_file_unittest_relocs_arm{32,64}{,_packed}.so test data files
|
||||
# from elf_file_unittest_relocs.cc. Run once to create these test data
|
||||
# files; the files are checked into the source tree.
|
||||
#
|
||||
# To use:
|
||||
# ./generate_elf_file_unittest_relocs.sh
|
||||
# git add elf_file_unittest_relocs_arm{32,64}{,_packed}.so
|
||||
|
||||
function main() {
|
||||
local '-r' test_data_directory="$(pwd)"
|
||||
cd '../../..'
|
||||
|
||||
source tools/cr/cr-bash-helpers.sh
|
||||
local arch
|
||||
for arch in 'arm32' 'arm64'; do
|
||||
cr 'init' '--platform=android' '--type=Debug' '--architecture='"${arch}"
|
||||
cr 'build' 'relocation_packer_unittests_test_data'
|
||||
done
|
||||
|
||||
local '-r' packer='out_android/Debug/obj/tools/relocation_packer'
|
||||
local '-r' gen="${packer}/relocation_packer_unittests_test_data.gen"
|
||||
|
||||
cp "${gen}/elf_file_unittest_relocs_arm"{32,64}{,_packed}'.so' \
|
||||
"${test_data_directory}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main
|
Loading…
Reference in New Issue
Block a user