diff --git a/libc/include/elf.h b/libc/include/elf.h index a41a2441a..41680a521 100644 --- a/libc/include/elf.h +++ b/libc/include/elf.h @@ -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 diff --git a/linker/linker.cpp b/linker/linker.cpp index 5d2425f52..87fce95c2 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -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 -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 +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(reloc), - reinterpret_cast(base + addend)); - *reinterpret_cast(reloc) = (base + addend); + reinterpret_cast(load_bias + addend)); + *reinterpret_cast(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(reloc), - reinterpret_cast(base + addend)); - *reinterpret_cast(reloc) = call_ifunc_resolver(base + addend); + reinterpret_cast(load_bias + addend)); + *reinterpret_cast(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(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(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(load_bias + d->d_un.d_ptr); @@ -2085,6 +2109,22 @@ bool soinfo::prelink_image() { } break; + case DT_ANDROID_REL: + android_relocs_ = reinterpret_cast(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(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(packed_relocs, packed_relocs_size)), + global_group, local_group); + } else { // android_relocs_[2] == 'S' + relocated = relocate( + packed_reloc_iterator( + 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; } } diff --git a/linker/linker.h b/linker/linker.h index 2afbaf62f..f8640a0e5 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -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 - bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group); + template + 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(); }; diff --git a/linker/linker_leb128.h b/linker/linker_leb128.h new file mode 100644 index 000000000..d5c6488c0 --- /dev/null +++ b/linker/linker_leb128.h @@ -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 + +// 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(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(byte & 127) << shift); + shift += 7; + } while (byte & 128); + + if (shift < size && (byte & 64)) { + value |= -(static_cast(1) << shift); + } + + return value; + } + + private: + const uint8_t* current_; + const uint8_t* const end_; +}; + +#endif // __LINKER_LEB128_H + diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index 7fbde3d35..f0bde55cd 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -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&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template bool soinfo::relocate>( + packed_reloc_iterator&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template bool soinfo::relocate>( + packed_reloc_iterator&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); + +template +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); diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 91a2fb80d..38e62627a 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -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; diff --git a/linker/linker_reloc_iterators.h b/linker/linker_reloc_iterators.h new file mode 100644 index 000000000..5db31f9f6 --- /dev/null +++ b/linker/linker_reloc_iterators.h @@ -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 + +#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 +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 diff --git a/tools/Android.mk b/tools/Android.mk new file mode 100644 index 000000000..4dd66fe07 --- /dev/null +++ b/tools/Android.mk @@ -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) diff --git a/tools/relocation_packer/Android.mk b/tools/relocation_packer/Android.mk new file mode 100644 index 000000000..99a39c02b --- /dev/null +++ b/tools/relocation_packer/Android.mk @@ -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)) diff --git a/tools/relocation_packer/LICENSE b/tools/relocation_packer/LICENSE new file mode 100644 index 000000000..972bb2edb --- /dev/null +++ b/tools/relocation_packer/LICENSE @@ -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. diff --git a/tools/relocation_packer/README.TXT b/tools/relocation_packer/README.TXT new file mode 100644 index 000000000..071ab5df2 --- /dev/null +++ b/tools/relocation_packer/README.TXT @@ -0,0 +1,135 @@ +Introduction: +------------- + +Relative relocations are the bulk of dynamic relocations (the .rel.dyn +or .rela.dyn sections) in libchrome..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..so in the filesystem, in APK downloads, +and in memory when loaded on the device. + +A packed libchrome..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..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..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..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..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..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..so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy + --add-section .android.rel.dyn=/tmp/small + libchrome..so libchrome..so.packed + else + aarch64-linux-android-objcopy + --add-section .android.rela.dyn=/tmp/small + libchrome..so libchrome..so.packed + fi + rm /tmp/small + relocation_packer libchrome..so.packed + +To unpack and restore the shared library to its original state: + + cp libchrome..so.packed unpackable + relocation_packer -u unpackable + if file libchrome..so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy \ + --remove-section=.android.rel.dyn unpackable libchrome..so + else + aarch64-linux-android-objcopy \ + --remove-section=.android.rela.dyn unpackable libchrome..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. diff --git a/tools/relocation_packer/src/debug.cc b/tools/relocation_packer/src/debug.cc new file mode 100644 index 000000000..29d7ab067 --- /dev/null +++ b/tools/relocation_packer/src/debug.cc @@ -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 +#include +#include + +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 diff --git a/tools/relocation_packer/src/debug.h b/tools/relocation_packer/src/debug.h new file mode 100644 index 000000000..48be6c19b --- /dev/null +++ b/tools/relocation_packer/src/debug.h @@ -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 +#include +#include + +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_ diff --git a/tools/relocation_packer/src/debug_unittest.cc b/tools/relocation_packer/src/debug_unittest.cc new file mode 100644 index 000000000..b31e2ae7e --- /dev/null +++ b/tools/relocation_packer/src/debug_unittest.cc @@ -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 +#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 diff --git a/tools/relocation_packer/src/delta_encoder.cc b/tools/relocation_packer/src/delta_encoder.cc new file mode 100644 index 000000000..8349d7c64 --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder.cc @@ -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 + +#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 +void RelocationDeltaCodec::Encode(const std::vector& relocations, + std::vector* 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(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(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 +void RelocationDeltaCodec::Decode(const std::vector& packed, + std::vector* 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; ipush_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 +void RelocationDeltaCodec::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(reloc_two.r_offset) - + static_cast(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::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 +void RelocationDeltaCodec::DetectGroup(const std::vector& 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; +template class RelocationDeltaCodec; + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/delta_encoder.h b/tools/relocation_packer/src/delta_encoder.h new file mode 100644 index 000000000..46c324c49 --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder.h @@ -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 + +#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 +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& relocations, + std::vector* 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& packed, + std::vector* relocations); + + private: + static void DetectGroup(const std::vector& 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_ diff --git a/tools/relocation_packer/src/delta_encoder_unittest.cc b/tools/relocation_packer/src/delta_encoder_unittest.cc new file mode 100644 index 000000000..06d9c9673 --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder_unittest.cc @@ -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 +#include "elf.h" +#include "gtest/gtest.h" + +namespace { + +template +void AddRelocation(uint32_t addr, + uint32_t info, + int32_t addend, + std::vector* relocations) { + T relocation; + relocation.r_offset = addr; + relocation.r_info = info; + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +template +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 +static void encode() { + std::vector relocations; + std::vector packed; + + RelocationDeltaCodec 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(-24), packed[ndx++]); + EXPECT_EQ(static_cast(-24), packed[ndx++]); + EXPECT_EQ(static_cast(-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(); +} + +TEST(Delta, Encode64) { + encode(); +} + +template +static void decode() { + std::vector packed; + std::vector relocations; + + RelocationDeltaCodec 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(-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(-24)); // addend + packed.push_back(static_cast(-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(); +} + +TEST(Delta, Decode64) { + decode(); +} + +// 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 diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc new file mode 100644 index 000000000..20b25ef82 --- /dev/null +++ b/tools/relocation_packer/src/elf_file.cc @@ -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 +#include +#include +#include +#include +#include + +#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 +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 +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 +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 +bool ElfFile::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 +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 +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 +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 +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 +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_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_program_header, + program_header_count, + hole_start, + hole_size); +} + +// Helper for ResizeSection(). Locate and return the dynamic section. +template +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 +void ElfFile::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(data->d_buf); + std::vector 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 +void ElfFile::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_header, hole_start, hole_size); + + // Adjust all section headers. + AdjustSectionHeadersForHole(elf, hole_start, hole_size); + + // Rewrite the program headers to either split or coalesce segments, + // and adjust dynamic entries to match. + RewriteProgramHeadersForHole(elf, hole_start, hole_size); + + Elf_Scn* dynamic_section = GetDynamicSection(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 +static size_t FindDynamicEntry(typename ELF::Sword tag, + std::vector* 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 +static void ReplaceDynamicEntry(typename ELF::Sword tag, + const typename ELF::Dyn& dyn, + std::vector* dynamics) { + const size_t slot = FindDynamicEntry(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 +bool ElfFile::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 relocations; + + if (relocations_type_ == REL) { + // Convert data to a vector of relocations. + const typename ELF::Rel* relocations_base = reinterpret_cast(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(data->d_buf); + relocations = std::vector( + 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 +bool ElfFile::PackTypedRelocations(std::vector* 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 packed; + RelocationPacker 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 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 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(data->d_buf); + std::vector 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(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(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 +bool ElfFile::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(data->d_buf); + std::vector 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 +bool ElfFile::UnpackTypedRelocations(const std::vector& 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 unpacked_relocations; + RelocationPacker 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 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(data->d_buf); + std::vector 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(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(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 +void ElfFile::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 +void ElfFile::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, + size_t rel_array_size, + std::vector* rela_vector) { + for (size_t i = 0; ipush_back(rela); + } +} + +template +void ElfFile::ConvertRelaVectorToRelVector(const std::vector& rela_vector, + std::vector* 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; +template class ElfFile; + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h new file mode 100644 index 000000000..73c31923f --- /dev/null +++ b/tools/relocation_packer/src/elf_file.h @@ -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..so will not +// contain this section, so the following can be used to add one: +// +// echo -n 'NULL' >/tmp/small +// if file libchrome..so | grep -q 'ELF 32'; then +// arm-linux-androideabi-objcopy +// --add-section .android.rel.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// else +// aarch64-linux-android-objcopy +// --add-section .android.rela.dyn=/tmp/small +// libchrome..so libchrome..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 +#include + +#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 +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* relocations); + + // Templated unpacker, helper for UnpackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + bool UnpackTypedRelocations(const std::vector& packed); + + // Write ELF file changes. + void Flush(); + + void AdjustRelativeRelocationTargets(typename ELF::Off hole_start, + ssize_t hole_size, + std::vector* 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* rela_vector); + + static void ConvertRelaVectorToRelVector(const std::vector& rela_vector, + std::vector* 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_ diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc new file mode 100644 index 000000000..434f10102 --- /dev/null +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -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 +#include +#include +#include +#include +#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 +static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) { + relocation_packer::ElfFile elf_file(fileno(packed_relocs_so)); + + // Ensure packing fails (already packed). + EXPECT_FALSE(elf_file.PackRelocations()); + + // Unpack golden relocations, and check files are now identical. + EXPECT_TRUE(elf_file.UnpackRelocations()); + CheckFileContentsEqual(packed_relocs_so, relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +static void RunUnpackRelocationsTestFor(const std::string& arch) { + ASSERT_NE(static_cast(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(relocs_so, packed_relocs_so); + } else { + ProcessUnpack(relocs_so, packed_relocs_so); + } + } +} + +template +static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) { + relocation_packer::ElfFile elf_file(fileno(relocs_so)); + + // Ensure unpacking fails (not packed). + EXPECT_FALSE(elf_file.UnpackRelocations()); + + // Pack relocations, and check files are now identical. + EXPECT_TRUE(elf_file.PackRelocations()); + CheckFileContentsEqual(relocs_so, packed_relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +static void RunPackRelocationsTestFor(const std::string& arch) { + ASSERT_NE(static_cast(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(relocs_so, packed_relocs_so); + } else { + ProcessPack(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 diff --git a/tools/relocation_packer/src/elf_traits.h b/tools/relocation_packer/src/elf_traits.h new file mode 100644 index 000000000..41b06c857 --- /dev/null +++ b/tools/relocation_packer/src/elf_traits.h @@ -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_ diff --git a/tools/relocation_packer/src/leb128.cc b/tools/relocation_packer/src/leb128.cc new file mode 100644 index 000000000..101c55778 --- /dev/null +++ b/tools/relocation_packer/src/leb128.cc @@ -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 +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +template +Leb128Encoder::Leb128Encoder() { } + +template +Leb128Encoder::~Leb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. +template +void Leb128Encoder::Enqueue(uint_t value) { + uint_t uvalue = static_cast(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 +void Leb128Encoder::EnqueueAll(const std::vector& values) { + for (size_t i = 0; i < values.size(); ++i) { + Enqueue(values[i]); + } +} + +// Create a new decoder for the given encoded stream. +template +Leb128Decoder::Leb128Decoder(const std::vector& encoding, size_t start_with) { + encoding_ = encoding; + cursor_ = start_with; +} + +// Empty destructor to silence chromium-style. +template +Leb128Decoder::~Leb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Read forwards until +// a byte without its most significant bit is found, then read the 7 bit +// fields of the bytes spanned to re-form the value. +template +uint_t Leb128Decoder::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(byte & 127) << shift; + shift += 7; + } while (byte & 128); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +template +void Leb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) { + values->push_back(Dequeue()); + } +} + +template class Leb128Encoder; +template class Leb128Encoder; + +template class Leb128Decoder; +template class Leb128Decoder; + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h new file mode 100644 index 000000000..2c5b5d079 --- /dev/null +++ b/tools/relocation_packer/src/leb128.h @@ -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 +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a LEB128 byte stream. +template +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& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +template +class Leb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Leb128Decoder(const std::vector& 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* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ diff --git a/tools/relocation_packer/src/leb128_unittest.cc b/tools/relocation_packer/src/leb128_unittest.cc new file mode 100644 index 000000000..8a7028cbc --- /dev/null +++ b/tools/relocation_packer/src/leb128_unittest.cc @@ -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 +#include "gtest/gtest.h" + +namespace relocation_packer { + +TEST(Leb128, Encoder64) { + std::vector values; + values.push_back(624485); + values.push_back(0); + values.push_back(1); + values.push_back(127); + values.push_back(128); + + Leb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(4294967295); + encoder.Enqueue(18446744073709551615ul); + + std::vector 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 encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 127 + encoding.push_back(0x7f); + // 128 + encoding.push_back(0x80); + encoding.push_back(0x01); + // 4294967295 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x0f); + // 18446744073709551615 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x01); + + Leb128Decoder decoder(encoding, 0); + + EXPECT_EQ(624485U, decoder.Dequeue()); + + std::vector 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 diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc new file mode 100644 index 000000000..3f784e4f3 --- /dev/null +++ b/tools/relocation_packer/src/main.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 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 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; +} diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc new file mode 100644 index 000000000..8e30612e5 --- /dev/null +++ b/tools/relocation_packer/src/packer.cc @@ -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 + +#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 +void RelocationPacker::PackRelocations(const std::vector& relocations, + std::vector* packed) { + // Run-length encode. + std::vector packed_words; + RelocationDeltaCodec codec; + codec.Encode(relocations, &packed_words); + + // If insufficient data do nothing. + if (packed_words.empty()) + return; + + Sleb128Encoder sleb128_encoder; + Leb128Encoder leb128_encoder; + + std::vector leb128_packed; + std::vector 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 +void RelocationPacker::UnpackRelocations( + const std::vector& packed, + std::vector* relocations) { + + std::vector 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 decoder(packed, 4); + decoder.DequeueAll(&packed_words); + } else { + Sleb128Decoder decoder(packed, 4); + decoder.DequeueAll(&packed_words); + } + + RelocationDeltaCodec codec; + codec.Decode(packed_words, relocations); +} + +template class RelocationPacker; +template class RelocationPacker; + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h new file mode 100644 index 000000000..8a57e623b --- /dev/null +++ b/tools/relocation_packer/src/packer.h @@ -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 +#include + +#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 +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& relocations, + std::vector* 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& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc new file mode 100644 index 000000000..8dddd8b41 --- /dev/null +++ b/tools/relocation_packer/src/packer_unittest.cc @@ -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 +#include "elf.h" +#include "elf_traits.h" +#include "gtest/gtest.h" + + +template +static void AddRelocation(typename ELF::Addr addr, + typename ELF::Xword info, + typename ELF::Sxword addend, + std::vector* relocations) { + typename ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = info; + relocation.r_addend = addend; + + relocations->push_back(relocation); +} + +template +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 +static void DoPackNoAddend() { + std::vector relocations; + std::vector packed; + // Initial relocation. + AddRelocation(0xd1ce0000, 0x11, 0, &relocations); + // Two more relocations, 4 byte deltas. + AddRelocation(0xd1ce0004, 0x11, 0, &relocations); + AddRelocation(0xd1ce0008, 0x11, 0, &relocations); + // Three more relocations, 8 byte deltas. + AddRelocation(0xd1ce0010, 0x11, 0, &relocations); + AddRelocation(0xd1ce0018, 0x11, 0, &relocations); + AddRelocation(0xd1ce0020, 0x11, 0, &relocations); + + RelocationPacker 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(); + DoPackNoAddend(); +} + +template +static void DoUnpackNoAddend() { + std::vector relocations; + std::vector 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 packer; + packer.UnpackRelocations(packed, &relocations); + + size_t ndx = 0; + EXPECT_EQ(6U, relocations.size()); + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 0x11, 0, relocations[ndx++])); + + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 0x11, 0, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 0x11, 0, relocations[ndx++])); + + EXPECT_EQ(ndx, relocations.size()); +} + +TEST(Packer, UnpackNoAddend) { + DoUnpackNoAddend(); + DoUnpackNoAddend(); +} + +template +static void DoPackWithAddend() { + std::vector relocations; + + // Initial relocation. + AddRelocation(0xd1ce0000, 0x01, 10024, &relocations); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + AddRelocation(0xd1ce0004, 0x01, 10012, &relocations); + AddRelocation(0xd1ce0008, 0x01, 10024, &relocations); + // Three more relocations, 8 byte deltas, -24 byte addend deltas. + AddRelocation(0xd1ce0010, 0x01, 10000, &relocations); + AddRelocation(0xd1ce0018, 0x01, 9976, &relocations); + AddRelocation(0xd1ce0020, 0x01, 9952, &relocations); + + std::vector packed; + + RelocationPacker 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(); + DoPackWithAddend(); +} + +template +static void DoUnpackWithAddend() { + std::vector 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 relocations; + + RelocationPacker packer; + + relocations.clear(); + packer.UnpackRelocations(packed, &relocations); + + EXPECT_EQ(6U, relocations.size()); + size_t ndx = 0; + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 0x01, 10024, relocations[ndx++])); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 0x01, 10012, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 0x01, 10024, relocations[ndx++])); + // Three more relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 0x01, 10000, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 0x01, 9976, relocations[ndx++])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 0x01, 9952, relocations[ndx++])); + + EXPECT_EQ(ndx, relocations.size()); +} + +TEST(Packer, UnpackWithAddend) { + DoUnpackWithAddend(); + DoUnpackWithAddend(); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/run_length_encoder.h b/tools/relocation_packer/src/run_length_encoder.h new file mode 100644 index 000000000..f3a80e602 --- /dev/null +++ b/tools/relocation_packer/src/run_length_encoder.h @@ -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..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..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..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 + +#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& relocations, + std::vector* 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& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc new file mode 100644 index 000000000..12c21e364 --- /dev/null +++ b/tools/relocation_packer/src/sleb128.cc @@ -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 +#include +#include + +#include "elf_traits.h" + +namespace { + +template +class uint_traits {}; + +template <> +class uint_traits { + public: + typedef int64_t int_t; +}; + +template <> +class uint_traits { + public: + typedef int32_t int_t; +}; + +} + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +template +Sleb128Encoder::Sleb128Encoder() { } + +template +Sleb128Encoder::~Sleb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. The +// value is sign extended up to a multiple of 7 bits (ensuring that the +// most significant bit is zero for a positive number and one for a +// negative number). +template +void Sleb128Encoder::Enqueue(uint_t value) { + typedef typename uint_traits::int_t int_t; + static const size_t size = CHAR_BIT * sizeof(value); + + bool more = true; + const bool negative = static_cast(value) < 0; + + while (more) { + uint8_t byte = value & 127; + value >>= 7; + + // Sign extend if encoding a -ve value. + if (negative) + value |= -(static_cast(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(-1) && sign_bit)) + more = false; + else + byte |= 128; + encoding_.push_back(byte); + } +} + +// Add a vector of values to the encoding. +template +void Sleb128Encoder::EnqueueAll(const std::vector& values) { + for (size_t i = 0; i < values.size(); ++i) { + Enqueue(values[i]); + } +} + +// Create a new decoder for the given encoded stream. +template +Sleb128Decoder::Sleb128Decoder(const std::vector& encoding, size_t start_with) { + encoding_ = encoding; + cursor_ = start_with; +} + +// Empty destructor to silence chromium-style. +template +Sleb128Decoder::~Sleb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Consume bytes +// until one without its most significant bit is found, and re-form the +// value from the 7 bit fields of the bytes consumed. +template +uint_t Sleb128Decoder::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(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(1) << shift); + + return static_cast(value); +} + +// Decode and retrieve all remaining values from the encoding. +template +void Sleb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) { + values->push_back(Dequeue()); + } +} + +template class Sleb128Encoder; +template class Sleb128Encoder; +template class Sleb128Decoder; +template class Sleb128Decoder; + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h new file mode 100644 index 000000000..fa0a24610 --- /dev/null +++ b/tools/relocation_packer/src/sleb128.h @@ -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 +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a signed LEB128 byte stream. +template +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& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +template +class Sleb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Sleb128Decoder(const std::vector& 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* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ diff --git a/tools/relocation_packer/src/sleb128_unittest.cc b/tools/relocation_packer/src/sleb128_unittest.cc new file mode 100644 index 000000000..49a553c2f --- /dev/null +++ b/tools/relocation_packer/src/sleb128_unittest.cc @@ -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 +#include "elf_traits.h" +#include "gtest/gtest.h" + +namespace relocation_packer { + +TEST(Sleb128, Encoder64) { + std::vector 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(-1)); + values.push_back(static_cast(-624485)); + + Sleb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(2147483647U); + encoder.Enqueue(static_cast(-2147483648)); + encoder.Enqueue(9223372036854775807ULL); + encoder.Enqueue(static_cast(-9223372036854775807LL - 1)); + + std::vector 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 encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 63 + encoding.push_back(0x3f); + // 64 + encoding.push_back(0xc0); + encoding.push_back(0x00); + // -1 + encoding.push_back(0x7f); + // -624485 + encoding.push_back(0x9b); + encoding.push_back(0xf1); + encoding.push_back(0x59); + // 2147483647 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x07); + // -2147483648 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x78); + // 9223372036854775807 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x00); + // -9223372036854775808 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x7f); + + Sleb128Decoder decoder(encoding, 0); + + EXPECT_EQ(624485U, decoder.Dequeue()); + + std::vector 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(-1), dequeued[4]); + EXPECT_EQ(static_cast(-624485), dequeued[5]); + EXPECT_EQ(2147483647U, dequeued[6]); + EXPECT_EQ(static_cast(-2147483648), dequeued[7]); + EXPECT_EQ(9223372036854775807ULL, dequeued[8]); + EXPECT_EQ(static_cast(-9223372036854775807LL - 1), dequeued[9]); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc new file mode 100644 index 000000000..5e1fa747e --- /dev/null +++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc @@ -0,0 +1,1014 @@ +// 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. + +// Test data for packing/unpacking. When compiled, creates a run of +// relative relocations. +// +// See generate_elf_file_unittest_relocs.sh for instructions on how to build +// unit test data from this source file. + +const int i = 0; + +// Generator: +// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i' +const void* pointer_0 = &i; +const void* pointer_1 = &i; +const void* pointer_2 = &i; +const void* pointer_3 = &i; +const void* pointer_4 = &i; +const void* pointer_5 = &i; +const void* pointer_6 = &i; +const void* pointer_7 = &i; +const void* pointer_8 = &i; +const void* pointer_9 = &i; +const void* pointer_10 = &i; +const void* pointer_11 = &i; +const void* pointer_12 = &i; +const void* pointer_13 = &i; +const void* pointer_14 = &i; +const void* pointer_15 = &i; +const void* pointer_16 = &i; +const void* pointer_17 = &i; +const void* pointer_18 = &i; +const void* pointer_19 = &i; +const void* pointer_20 = &i; +const void* pointer_21 = &i; +const void* pointer_22 = &i; +const void* pointer_23 = &i; +const void* pointer_24 = &i; +const void* pointer_25 = &i; +const void* pointer_26 = &i; +const void* pointer_27 = &i; +const void* pointer_28 = &i; +const void* pointer_29 = &i; +const void* pointer_30 = &i; +const void* pointer_31 = &i; +const void* pointer_32 = &i; +const void* pointer_33 = &i; +const void* pointer_34 = &i; +const void* pointer_35 = &i; +const void* pointer_36 = &i; +const void* pointer_37 = &i; +const void* pointer_38 = &i; +const void* pointer_39 = &i; +const void* pointer_40 = &i; +const void* pointer_41 = &i; +const void* pointer_42 = &i; +const void* pointer_43 = &i; +const void* pointer_44 = &i; +const void* pointer_45 = &i; +const void* pointer_46 = &i; +const void* pointer_47 = &i; +const void* pointer_48 = &i; +const void* pointer_49 = &i; +const void* pointer_50 = &i; +const void* pointer_51 = &i; +const void* pointer_52 = &i; +const void* pointer_53 = &i; +const void* pointer_54 = &i; +const void* pointer_55 = &i; +const void* pointer_56 = &i; +const void* pointer_57 = &i; +const void* pointer_58 = &i; +const void* pointer_59 = &i; +const void* pointer_60 = &i; +const void* pointer_61 = &i; +const void* pointer_62 = &i; +const void* pointer_63 = &i; +const void* pointer_64 = &i; +const void* pointer_65 = &i; +const void* pointer_66 = &i; +const void* pointer_67 = &i; +const void* pointer_68 = &i; +const void* pointer_69 = &i; +const void* pointer_70 = &i; +const void* pointer_71 = &i; +const void* pointer_72 = &i; +const void* pointer_73 = &i; +const void* pointer_74 = &i; +const void* pointer_75 = &i; +const void* pointer_76 = &i; +const void* pointer_77 = &i; +const void* pointer_78 = &i; +const void* pointer_79 = &i; +const void* pointer_80 = &i; +const void* pointer_81 = &i; +const void* pointer_82 = &i; +const void* pointer_83 = &i; +const void* pointer_84 = &i; +const void* pointer_85 = &i; +const void* pointer_86 = &i; +const void* pointer_87 = &i; +const void* pointer_88 = &i; +const void* pointer_89 = &i; +const void* pointer_90 = &i; +const void* pointer_91 = &i; +const void* pointer_92 = &i; +const void* pointer_93 = &i; +const void* pointer_94 = &i; +const void* pointer_95 = &i; +const void* pointer_96 = &i; +const void* pointer_97 = &i; +const void* pointer_98 = &i; +const void* pointer_99 = &i; +const void* pointer_100 = &i; +const void* pointer_101 = &i; +const void* pointer_102 = &i; +const void* pointer_103 = &i; +const void* pointer_104 = &i; +const void* pointer_105 = &i; +const void* pointer_106 = &i; +const void* pointer_107 = &i; +const void* pointer_108 = &i; +const void* pointer_109 = &i; +const void* pointer_110 = &i; +const void* pointer_111 = &i; +const void* pointer_112 = &i; +const void* pointer_113 = &i; +const void* pointer_114 = &i; +const void* pointer_115 = &i; +const void* pointer_116 = &i; +const void* pointer_117 = &i; +const void* pointer_118 = &i; +const void* pointer_119 = &i; +const void* pointer_120 = &i; +const void* pointer_121 = &i; +const void* pointer_122 = &i; +const void* pointer_123 = &i; +const void* pointer_124 = &i; +const void* pointer_125 = &i; +const void* pointer_126 = &i; +const void* pointer_127 = &i; +const void* pointer_128 = &i; +const void* pointer_129 = &i; +const void* pointer_130 = &i; +const void* pointer_131 = &i; +const void* pointer_132 = &i; +const void* pointer_133 = &i; +const void* pointer_134 = &i; +const void* pointer_135 = &i; +const void* pointer_136 = &i; +const void* pointer_137 = &i; +const void* pointer_138 = &i; +const void* pointer_139 = &i; +const void* pointer_140 = &i; +const void* pointer_141 = &i; +const void* pointer_142 = &i; +const void* pointer_143 = &i; +const void* pointer_144 = &i; +const void* pointer_145 = &i; +const void* pointer_146 = &i; +const void* pointer_147 = &i; +const void* pointer_148 = &i; +const void* pointer_149 = &i; +const void* pointer_150 = &i; +const void* pointer_151 = &i; +const void* pointer_152 = &i; +const void* pointer_153 = &i; +const void* pointer_154 = &i; +const void* pointer_155 = &i; +const void* pointer_156 = &i; +const void* pointer_157 = &i; +const void* pointer_158 = &i; +const void* pointer_159 = &i; +const void* pointer_160 = &i; +const void* pointer_161 = &i; +const void* pointer_162 = &i; +const void* pointer_163 = &i; +const void* pointer_164 = &i; +const void* pointer_165 = &i; +const void* pointer_166 = &i; +const void* pointer_167 = &i; +const void* pointer_168 = &i; +const void* pointer_169 = &i; +const void* pointer_170 = &i; +const void* pointer_171 = &i; +const void* pointer_172 = &i; +const void* pointer_173 = &i; +const void* pointer_174 = &i; +const void* pointer_175 = &i; +const void* pointer_176 = &i; +const void* pointer_177 = &i; +const void* pointer_178 = &i; +const void* pointer_179 = &i; +const void* pointer_180 = &i; +const void* pointer_181 = &i; +const void* pointer_182 = &i; +const void* pointer_183 = &i; +const void* pointer_184 = &i; +const void* pointer_185 = &i; +const void* pointer_186 = &i; +const void* pointer_187 = &i; +const void* pointer_188 = &i; +const void* pointer_189 = &i; +const void* pointer_190 = &i; +const void* pointer_191 = &i; +const void* pointer_192 = &i; +const void* pointer_193 = &i; +const void* pointer_194 = &i; +const void* pointer_195 = &i; +const void* pointer_196 = &i; +const void* pointer_197 = &i; +const void* pointer_198 = &i; +const void* pointer_199 = &i; +const void* pointer_200 = &i; +const void* pointer_201 = &i; +const void* pointer_202 = &i; +const void* pointer_203 = &i; +const void* pointer_204 = &i; +const void* pointer_205 = &i; +const void* pointer_206 = &i; +const void* pointer_207 = &i; +const void* pointer_208 = &i; +const void* pointer_209 = &i; +const void* pointer_210 = &i; +const void* pointer_211 = &i; +const void* pointer_212 = &i; +const void* pointer_213 = &i; +const void* pointer_214 = &i; +const void* pointer_215 = &i; +const void* pointer_216 = &i; +const void* pointer_217 = &i; +const void* pointer_218 = &i; +const void* pointer_219 = &i; +const void* pointer_220 = &i; +const void* pointer_221 = &i; +const void* pointer_222 = &i; +const void* pointer_223 = &i; +const void* pointer_224 = &i; +const void* pointer_225 = &i; +const void* pointer_226 = &i; +const void* pointer_227 = &i; +const void* pointer_228 = &i; +const void* pointer_229 = &i; +const void* pointer_230 = &i; +const void* pointer_231 = &i; +const void* pointer_232 = &i; +const void* pointer_233 = &i; +const void* pointer_234 = &i; +const void* pointer_235 = &i; +const void* pointer_236 = &i; +const void* pointer_237 = &i; +const void* pointer_238 = &i; +const void* pointer_239 = &i; +const void* pointer_240 = &i; +const void* pointer_241 = &i; +const void* pointer_242 = &i; +const void* pointer_243 = &i; +const void* pointer_244 = &i; +const void* pointer_245 = &i; +const void* pointer_246 = &i; +const void* pointer_247 = &i; +const void* pointer_248 = &i; +const void* pointer_249 = &i; +const void* pointer_250 = &i; +const void* pointer_251 = &i; +const void* pointer_252 = &i; +const void* pointer_253 = &i; +const void* pointer_254 = &i; +const void* pointer_255 = &i; +const void* pointer_256 = &i; +const void* pointer_257 = &i; +const void* pointer_258 = &i; +const void* pointer_259 = &i; +const void* pointer_260 = &i; +const void* pointer_261 = &i; +const void* pointer_262 = &i; +const void* pointer_263 = &i; +const void* pointer_264 = &i; +const void* pointer_265 = &i; +const void* pointer_266 = &i; +const void* pointer_267 = &i; +const void* pointer_268 = &i; +const void* pointer_269 = &i; +const void* pointer_270 = &i; +const void* pointer_271 = &i; +const void* pointer_272 = &i; +const void* pointer_273 = &i; +const void* pointer_274 = &i; +const void* pointer_275 = &i; +const void* pointer_276 = &i; +const void* pointer_277 = &i; +const void* pointer_278 = &i; +const void* pointer_279 = &i; +const void* pointer_280 = &i; +const void* pointer_281 = &i; +const void* pointer_282 = &i; +const void* pointer_283 = &i; +const void* pointer_284 = &i; +const void* pointer_285 = &i; +const void* pointer_286 = &i; +const void* pointer_287 = &i; +const void* pointer_288 = &i; +const void* pointer_289 = &i; +const void* pointer_290 = &i; +const void* pointer_291 = &i; +const void* pointer_292 = &i; +const void* pointer_293 = &i; +const void* pointer_294 = &i; +const void* pointer_295 = &i; +const void* pointer_296 = &i; +const void* pointer_297 = &i; +const void* pointer_298 = &i; +const void* pointer_299 = &i; +const void* pointer_300 = &i; +const void* pointer_301 = &i; +const void* pointer_302 = &i; +const void* pointer_303 = &i; +const void* pointer_304 = &i; +const void* pointer_305 = &i; +const void* pointer_306 = &i; +const void* pointer_307 = &i; +const void* pointer_308 = &i; +const void* pointer_309 = &i; +const void* pointer_310 = &i; +const void* pointer_311 = &i; +const void* pointer_312 = &i; +const void* pointer_313 = &i; +const void* pointer_314 = &i; +const void* pointer_315 = &i; +const void* pointer_316 = &i; +const void* pointer_317 = &i; +const void* pointer_318 = &i; +const void* pointer_319 = &i; +const void* pointer_320 = &i; +const void* pointer_321 = &i; +const void* pointer_322 = &i; +const void* pointer_323 = &i; +const void* pointer_324 = &i; +const void* pointer_325 = &i; +const void* pointer_326 = &i; +const void* pointer_327 = &i; +const void* pointer_328 = &i; +const void* pointer_329 = &i; +const void* pointer_330 = &i; +const void* pointer_331 = &i; +const void* pointer_332 = &i; +const void* pointer_333 = &i; +const void* pointer_334 = &i; +const void* pointer_335 = &i; +const void* pointer_336 = &i; +const void* pointer_337 = &i; +const void* pointer_338 = &i; +const void* pointer_339 = &i; +const void* pointer_340 = &i; +const void* pointer_341 = &i; +const void* pointer_342 = &i; +const void* pointer_343 = &i; +const void* pointer_344 = &i; +const void* pointer_345 = &i; +const void* pointer_346 = &i; +const void* pointer_347 = &i; +const void* pointer_348 = &i; +const void* pointer_349 = &i; +const void* pointer_350 = &i; +const void* pointer_351 = &i; +const void* pointer_352 = &i; +const void* pointer_353 = &i; +const void* pointer_354 = &i; +const void* pointer_355 = &i; +const void* pointer_356 = &i; +const void* pointer_357 = &i; +const void* pointer_358 = &i; +const void* pointer_359 = &i; +const void* pointer_360 = &i; +const void* pointer_361 = &i; +const void* pointer_362 = &i; +const void* pointer_363 = &i; +const void* pointer_364 = &i; +const void* pointer_365 = &i; +const void* pointer_366 = &i; +const void* pointer_367 = &i; +const void* pointer_368 = &i; +const void* pointer_369 = &i; +const void* pointer_370 = &i; +const void* pointer_371 = &i; +const void* pointer_372 = &i; +const void* pointer_373 = &i; +const void* pointer_374 = &i; +const void* pointer_375 = &i; +const void* pointer_376 = &i; +const void* pointer_377 = &i; +const void* pointer_378 = &i; +const void* pointer_379 = &i; +const void* pointer_380 = &i; +const void* pointer_381 = &i; +const void* pointer_382 = &i; +const void* pointer_383 = &i; +const void* pointer_384 = &i; +const void* pointer_385 = &i; +const void* pointer_386 = &i; +const void* pointer_387 = &i; +const void* pointer_388 = &i; +const void* pointer_389 = &i; +const void* pointer_390 = &i; +const void* pointer_391 = &i; +const void* pointer_392 = &i; +const void* pointer_393 = &i; +const void* pointer_394 = &i; +const void* pointer_395 = &i; +const void* pointer_396 = &i; +const void* pointer_397 = &i; +const void* pointer_398 = &i; +const void* pointer_399 = &i; +const void* pointer_400 = &i; +const void* pointer_401 = &i; +const void* pointer_402 = &i; +const void* pointer_403 = &i; +const void* pointer_404 = &i; +const void* pointer_405 = &i; +const void* pointer_406 = &i; +const void* pointer_407 = &i; +const void* pointer_408 = &i; +const void* pointer_409 = &i; +const void* pointer_410 = &i; +const void* pointer_411 = &i; +const void* pointer_412 = &i; +const void* pointer_413 = &i; +const void* pointer_414 = &i; +const void* pointer_415 = &i; +const void* pointer_416 = &i; +const void* pointer_417 = &i; +const void* pointer_418 = &i; +const void* pointer_419 = &i; +const void* pointer_420 = &i; +const void* pointer_421 = &i; +const void* pointer_422 = &i; +const void* pointer_423 = &i; +const void* pointer_424 = &i; +const void* pointer_425 = &i; +const void* pointer_426 = &i; +const void* pointer_427 = &i; +const void* pointer_428 = &i; +const void* pointer_429 = &i; +const void* pointer_430 = &i; +const void* pointer_431 = &i; +const void* pointer_432 = &i; +const void* pointer_433 = &i; +const void* pointer_434 = &i; +const void* pointer_435 = &i; +const void* pointer_436 = &i; +const void* pointer_437 = &i; +const void* pointer_438 = &i; +const void* pointer_439 = &i; +const void* pointer_440 = &i; +const void* pointer_441 = &i; +const void* pointer_442 = &i; +const void* pointer_443 = &i; +const void* pointer_444 = &i; +const void* pointer_445 = &i; +const void* pointer_446 = &i; +const void* pointer_447 = &i; +const void* pointer_448 = &i; +const void* pointer_449 = &i; +const void* pointer_450 = &i; +const void* pointer_451 = &i; +const void* pointer_452 = &i; +const void* pointer_453 = &i; +const void* pointer_454 = &i; +const void* pointer_455 = &i; +const void* pointer_456 = &i; +const void* pointer_457 = &i; +const void* pointer_458 = &i; +const void* pointer_459 = &i; +const void* pointer_460 = &i; +const void* pointer_461 = &i; +const void* pointer_462 = &i; +const void* pointer_463 = &i; +const void* pointer_464 = &i; +const void* pointer_465 = &i; +const void* pointer_466 = &i; +const void* pointer_467 = &i; +const void* pointer_468 = &i; +const void* pointer_469 = &i; +const void* pointer_470 = &i; +const void* pointer_471 = &i; +const void* pointer_472 = &i; +const void* pointer_473 = &i; +const void* pointer_474 = &i; +const void* pointer_475 = &i; +const void* pointer_476 = &i; +const void* pointer_477 = &i; +const void* pointer_478 = &i; +const void* pointer_479 = &i; +const void* pointer_480 = &i; +const void* pointer_481 = &i; +const void* pointer_482 = &i; +const void* pointer_483 = &i; +const void* pointer_484 = &i; +const void* pointer_485 = &i; +const void* pointer_486 = &i; +const void* pointer_487 = &i; +const void* pointer_488 = &i; +const void* pointer_489 = &i; +const void* pointer_490 = &i; +const void* pointer_491 = &i; +const void* pointer_492 = &i; +const void* pointer_493 = &i; +const void* pointer_494 = &i; +const void* pointer_495 = &i; +const void* pointer_496 = &i; +const void* pointer_497 = &i; +const void* pointer_498 = &i; +const void* pointer_499 = &i; +const void* pointer_500 = &i; +const void* pointer_501 = &i; +const void* pointer_502 = &i; +const void* pointer_503 = &i; +const void* pointer_504 = &i; +const void* pointer_505 = &i; +const void* pointer_506 = &i; +const void* pointer_507 = &i; +const void* pointer_508 = &i; +const void* pointer_509 = &i; +const void* pointer_510 = &i; +const void* pointer_511 = &i; +const void* pointer_512 = &i; +const void* pointer_513 = &i; +const void* pointer_514 = &i; +const void* pointer_515 = &i; +const void* pointer_516 = &i; +const void* pointer_517 = &i; +const void* pointer_518 = &i; +const void* pointer_519 = &i; +const void* pointer_520 = &i; +const void* pointer_521 = &i; +const void* pointer_522 = &i; +const void* pointer_523 = &i; +const void* pointer_524 = &i; +const void* pointer_525 = &i; +const void* pointer_526 = &i; +const void* pointer_527 = &i; +const void* pointer_528 = &i; +const void* pointer_529 = &i; +const void* pointer_530 = &i; +const void* pointer_531 = &i; +const void* pointer_532 = &i; +const void* pointer_533 = &i; +const void* pointer_534 = &i; +const void* pointer_535 = &i; +const void* pointer_536 = &i; +const void* pointer_537 = &i; +const void* pointer_538 = &i; +const void* pointer_539 = &i; +const void* pointer_540 = &i; +const void* pointer_541 = &i; +const void* pointer_542 = &i; +const void* pointer_543 = &i; +const void* pointer_544 = &i; +const void* pointer_545 = &i; +const void* pointer_546 = &i; +const void* pointer_547 = &i; +const void* pointer_548 = &i; +const void* pointer_549 = &i; +const void* pointer_550 = &i; +const void* pointer_551 = &i; +const void* pointer_552 = &i; +const void* pointer_553 = &i; +const void* pointer_554 = &i; +const void* pointer_555 = &i; +const void* pointer_556 = &i; +const void* pointer_557 = &i; +const void* pointer_558 = &i; +const void* pointer_559 = &i; +const void* pointer_560 = &i; +const void* pointer_561 = &i; +const void* pointer_562 = &i; +const void* pointer_563 = &i; +const void* pointer_564 = &i; +const void* pointer_565 = &i; +const void* pointer_566 = &i; +const void* pointer_567 = &i; +const void* pointer_568 = &i; +const void* pointer_569 = &i; +const void* pointer_570 = &i; +const void* pointer_571 = &i; +const void* pointer_572 = &i; +const void* pointer_573 = &i; +const void* pointer_574 = &i; +const void* pointer_575 = &i; +const void* pointer_576 = &i; +const void* pointer_577 = &i; +const void* pointer_578 = &i; +const void* pointer_579 = &i; +const void* pointer_580 = &i; +const void* pointer_581 = &i; +const void* pointer_582 = &i; +const void* pointer_583 = &i; +const void* pointer_584 = &i; +const void* pointer_585 = &i; +const void* pointer_586 = &i; +const void* pointer_587 = &i; +const void* pointer_588 = &i; +const void* pointer_589 = &i; +const void* pointer_590 = &i; +const void* pointer_591 = &i; +const void* pointer_592 = &i; +const void* pointer_593 = &i; +const void* pointer_594 = &i; +const void* pointer_595 = &i; +const void* pointer_596 = &i; +const void* pointer_597 = &i; +const void* pointer_598 = &i; +const void* pointer_599 = &i; +const void* pointer_600 = &i; +const void* pointer_601 = &i; +const void* pointer_602 = &i; +const void* pointer_603 = &i; +const void* pointer_604 = &i; +const void* pointer_605 = &i; +const void* pointer_606 = &i; +const void* pointer_607 = &i; +const void* pointer_608 = &i; +const void* pointer_609 = &i; +const void* pointer_610 = &i; +const void* pointer_611 = &i; +const void* pointer_612 = &i; +const void* pointer_613 = &i; +const void* pointer_614 = &i; +const void* pointer_615 = &i; +const void* pointer_616 = &i; +const void* pointer_617 = &i; +const void* pointer_618 = &i; +const void* pointer_619 = &i; +const void* pointer_620 = &i; +const void* pointer_621 = &i; +const void* pointer_622 = &i; +const void* pointer_623 = &i; +const void* pointer_624 = &i; +const void* pointer_625 = &i; +const void* pointer_626 = &i; +const void* pointer_627 = &i; +const void* pointer_628 = &i; +const void* pointer_629 = &i; +const void* pointer_630 = &i; +const void* pointer_631 = &i; +const void* pointer_632 = &i; +const void* pointer_633 = &i; +const void* pointer_634 = &i; +const void* pointer_635 = &i; +const void* pointer_636 = &i; +const void* pointer_637 = &i; +const void* pointer_638 = &i; +const void* pointer_639 = &i; +const void* pointer_640 = &i; +const void* pointer_641 = &i; +const void* pointer_642 = &i; +const void* pointer_643 = &i; +const void* pointer_644 = &i; +const void* pointer_645 = &i; +const void* pointer_646 = &i; +const void* pointer_647 = &i; +const void* pointer_648 = &i; +const void* pointer_649 = &i; +const void* pointer_650 = &i; +const void* pointer_651 = &i; +const void* pointer_652 = &i; +const void* pointer_653 = &i; +const void* pointer_654 = &i; +const void* pointer_655 = &i; +const void* pointer_656 = &i; +const void* pointer_657 = &i; +const void* pointer_658 = &i; +const void* pointer_659 = &i; +const void* pointer_660 = &i; +const void* pointer_661 = &i; +const void* pointer_662 = &i; +const void* pointer_663 = &i; +const void* pointer_664 = &i; +const void* pointer_665 = &i; +const void* pointer_666 = &i; +const void* pointer_667 = &i; +const void* pointer_668 = &i; +const void* pointer_669 = &i; +const void* pointer_670 = &i; +const void* pointer_671 = &i; +const void* pointer_672 = &i; +const void* pointer_673 = &i; +const void* pointer_674 = &i; +const void* pointer_675 = &i; +const void* pointer_676 = &i; +const void* pointer_677 = &i; +const void* pointer_678 = &i; +const void* pointer_679 = &i; +const void* pointer_680 = &i; +const void* pointer_681 = &i; +const void* pointer_682 = &i; +const void* pointer_683 = &i; +const void* pointer_684 = &i; +const void* pointer_685 = &i; +const void* pointer_686 = &i; +const void* pointer_687 = &i; +const void* pointer_688 = &i; +const void* pointer_689 = &i; +const void* pointer_690 = &i; +const void* pointer_691 = &i; +const void* pointer_692 = &i; +const void* pointer_693 = &i; +const void* pointer_694 = &i; +const void* pointer_695 = &i; +const void* pointer_696 = &i; +const void* pointer_697 = &i; +const void* pointer_698 = &i; +const void* pointer_699 = &i; +const void* pointer_700 = &i; +const void* pointer_701 = &i; +const void* pointer_702 = &i; +const void* pointer_703 = &i; +const void* pointer_704 = &i; +const void* pointer_705 = &i; +const void* pointer_706 = &i; +const void* pointer_707 = &i; +const void* pointer_708 = &i; +const void* pointer_709 = &i; +const void* pointer_710 = &i; +const void* pointer_711 = &i; +const void* pointer_712 = &i; +const void* pointer_713 = &i; +const void* pointer_714 = &i; +const void* pointer_715 = &i; +const void* pointer_716 = &i; +const void* pointer_717 = &i; +const void* pointer_718 = &i; +const void* pointer_719 = &i; +const void* pointer_720 = &i; +const void* pointer_721 = &i; +const void* pointer_722 = &i; +const void* pointer_723 = &i; +const void* pointer_724 = &i; +const void* pointer_725 = &i; +const void* pointer_726 = &i; +const void* pointer_727 = &i; +const void* pointer_728 = &i; +const void* pointer_729 = &i; +const void* pointer_730 = &i; +const void* pointer_731 = &i; +const void* pointer_732 = &i; +const void* pointer_733 = &i; +const void* pointer_734 = &i; +const void* pointer_735 = &i; +const void* pointer_736 = &i; +const void* pointer_737 = &i; +const void* pointer_738 = &i; +const void* pointer_739 = &i; +const void* pointer_740 = &i; +const void* pointer_741 = &i; +const void* pointer_742 = &i; +const void* pointer_743 = &i; +const void* pointer_744 = &i; +const void* pointer_745 = &i; +const void* pointer_746 = &i; +const void* pointer_747 = &i; +const void* pointer_748 = &i; +const void* pointer_749 = &i; +const void* pointer_750 = &i; +const void* pointer_751 = &i; +const void* pointer_752 = &i; +const void* pointer_753 = &i; +const void* pointer_754 = &i; +const void* pointer_755 = &i; +const void* pointer_756 = &i; +const void* pointer_757 = &i; +const void* pointer_758 = &i; +const void* pointer_759 = &i; +const void* pointer_760 = &i; +const void* pointer_761 = &i; +const void* pointer_762 = &i; +const void* pointer_763 = &i; +const void* pointer_764 = &i; +const void* pointer_765 = &i; +const void* pointer_766 = &i; +const void* pointer_767 = &i; +const void* pointer_768 = &i; +const void* pointer_769 = &i; +const void* pointer_770 = &i; +const void* pointer_771 = &i; +const void* pointer_772 = &i; +const void* pointer_773 = &i; +const void* pointer_774 = &i; +const void* pointer_775 = &i; +const void* pointer_776 = &i; +const void* pointer_777 = &i; +const void* pointer_778 = &i; +const void* pointer_779 = &i; +const void* pointer_780 = &i; +const void* pointer_781 = &i; +const void* pointer_782 = &i; +const void* pointer_783 = &i; +const void* pointer_784 = &i; +const void* pointer_785 = &i; +const void* pointer_786 = &i; +const void* pointer_787 = &i; +const void* pointer_788 = &i; +const void* pointer_789 = &i; +const void* pointer_790 = &i; +const void* pointer_791 = &i; +const void* pointer_792 = &i; +const void* pointer_793 = &i; +const void* pointer_794 = &i; +const void* pointer_795 = &i; +const void* pointer_796 = &i; +const void* pointer_797 = &i; +const void* pointer_798 = &i; +const void* pointer_799 = &i; +const void* pointer_800 = &i; +const void* pointer_801 = &i; +const void* pointer_802 = &i; +const void* pointer_803 = &i; +const void* pointer_804 = &i; +const void* pointer_805 = &i; +const void* pointer_806 = &i; +const void* pointer_807 = &i; +const void* pointer_808 = &i; +const void* pointer_809 = &i; +const void* pointer_810 = &i; +const void* pointer_811 = &i; +const void* pointer_812 = &i; +const void* pointer_813 = &i; +const void* pointer_814 = &i; +const void* pointer_815 = &i; +const void* pointer_816 = &i; +const void* pointer_817 = &i; +const void* pointer_818 = &i; +const void* pointer_819 = &i; +const void* pointer_820 = &i; +const void* pointer_821 = &i; +const void* pointer_822 = &i; +const void* pointer_823 = &i; +const void* pointer_824 = &i; +const void* pointer_825 = &i; +const void* pointer_826 = &i; +const void* pointer_827 = &i; +const void* pointer_828 = &i; +const void* pointer_829 = &i; +const void* pointer_830 = &i; +const void* pointer_831 = &i; +const void* pointer_832 = &i; +const void* pointer_833 = &i; +const void* pointer_834 = &i; +const void* pointer_835 = &i; +const void* pointer_836 = &i; +const void* pointer_837 = &i; +const void* pointer_838 = &i; +const void* pointer_839 = &i; +const void* pointer_840 = &i; +const void* pointer_841 = &i; +const void* pointer_842 = &i; +const void* pointer_843 = &i; +const void* pointer_844 = &i; +const void* pointer_845 = &i; +const void* pointer_846 = &i; +const void* pointer_847 = &i; +const void* pointer_848 = &i; +const void* pointer_849 = &i; +const void* pointer_850 = &i; +const void* pointer_851 = &i; +const void* pointer_852 = &i; +const void* pointer_853 = &i; +const void* pointer_854 = &i; +const void* pointer_855 = &i; +const void* pointer_856 = &i; +const void* pointer_857 = &i; +const void* pointer_858 = &i; +const void* pointer_859 = &i; +const void* pointer_860 = &i; +const void* pointer_861 = &i; +const void* pointer_862 = &i; +const void* pointer_863 = &i; +const void* pointer_864 = &i; +const void* pointer_865 = &i; +const void* pointer_866 = &i; +const void* pointer_867 = &i; +const void* pointer_868 = &i; +const void* pointer_869 = &i; +const void* pointer_870 = &i; +const void* pointer_871 = &i; +const void* pointer_872 = &i; +const void* pointer_873 = &i; +const void* pointer_874 = &i; +const void* pointer_875 = &i; +const void* pointer_876 = &i; +const void* pointer_877 = &i; +const void* pointer_878 = &i; +const void* pointer_879 = &i; +const void* pointer_880 = &i; +const void* pointer_881 = &i; +const void* pointer_882 = &i; +const void* pointer_883 = &i; +const void* pointer_884 = &i; +const void* pointer_885 = &i; +const void* pointer_886 = &i; +const void* pointer_887 = &i; +const void* pointer_888 = &i; +const void* pointer_889 = &i; +const void* pointer_890 = &i; +const void* pointer_891 = &i; +const void* pointer_892 = &i; +const void* pointer_893 = &i; +const void* pointer_894 = &i; +const void* pointer_895 = &i; +const void* pointer_896 = &i; +const void* pointer_897 = &i; +const void* pointer_898 = &i; +const void* pointer_899 = &i; +const void* pointer_900 = &i; +const void* pointer_901 = &i; +const void* pointer_902 = &i; +const void* pointer_903 = &i; +const void* pointer_904 = &i; +const void* pointer_905 = &i; +const void* pointer_906 = &i; +const void* pointer_907 = &i; +const void* pointer_908 = &i; +const void* pointer_909 = &i; +const void* pointer_910 = &i; +const void* pointer_911 = &i; +const void* pointer_912 = &i; +const void* pointer_913 = &i; +const void* pointer_914 = &i; +const void* pointer_915 = &i; +const void* pointer_916 = &i; +const void* pointer_917 = &i; +const void* pointer_918 = &i; +const void* pointer_919 = &i; +const void* pointer_920 = &i; +const void* pointer_921 = &i; +const void* pointer_922 = &i; +const void* pointer_923 = &i; +const void* pointer_924 = &i; +const void* pointer_925 = &i; +const void* pointer_926 = &i; +const void* pointer_927 = &i; +const void* pointer_928 = &i; +const void* pointer_929 = &i; +const void* pointer_930 = &i; +const void* pointer_931 = &i; +const void* pointer_932 = &i; +const void* pointer_933 = &i; +const void* pointer_934 = &i; +const void* pointer_935 = &i; +const void* pointer_936 = &i; +const void* pointer_937 = &i; +const void* pointer_938 = &i; +const void* pointer_939 = &i; +const void* pointer_940 = &i; +const void* pointer_941 = &i; +const void* pointer_942 = &i; +const void* pointer_943 = &i; +const void* pointer_944 = &i; +const void* pointer_945 = &i; +const void* pointer_946 = &i; +const void* pointer_947 = &i; +const void* pointer_948 = &i; +const void* pointer_949 = &i; +const void* pointer_950 = &i; +const void* pointer_951 = &i; +const void* pointer_952 = &i; +const void* pointer_953 = &i; +const void* pointer_954 = &i; +const void* pointer_955 = &i; +const void* pointer_956 = &i; +const void* pointer_957 = &i; +const void* pointer_958 = &i; +const void* pointer_959 = &i; +const void* pointer_960 = &i; +const void* pointer_961 = &i; +const void* pointer_962 = &i; +const void* pointer_963 = &i; +const void* pointer_964 = &i; +const void* pointer_965 = &i; +const void* pointer_966 = &i; +const void* pointer_967 = &i; +const void* pointer_968 = &i; +const void* pointer_969 = &i; +const void* pointer_970 = &i; +const void* pointer_971 = &i; +const void* pointer_972 = &i; +const void* pointer_973 = &i; +const void* pointer_974 = &i; +const void* pointer_975 = &i; +const void* pointer_976 = &i; +const void* pointer_977 = &i; +const void* pointer_978 = &i; +const void* pointer_979 = &i; +const void* pointer_980 = &i; +const void* pointer_981 = &i; +const void* pointer_982 = &i; +const void* pointer_983 = &i; +const void* pointer_984 = &i; +const void* pointer_985 = &i; +const void* pointer_986 = &i; +const void* pointer_987 = &i; +const void* pointer_988 = &i; +const void* pointer_989 = &i; +const void* pointer_990 = &i; +const void* pointer_991 = &i; +const void* pointer_992 = &i; +const void* pointer_993 = &i; +const void* pointer_994 = &i; +const void* pointer_995 = &i; +const void* pointer_996 = &i; +const void* pointer_997 = &i; +const void* pointer_998 = &i; +const void* pointer_999 = &i; diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so new file mode 100755 index 000000000..6ce6d0cd2 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so differ diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so new file mode 100755 index 000000000..d97ef8252 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so differ diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so new file mode 100755 index 000000000..945b450e3 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so differ diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so new file mode 100755 index 000000000..e44e4597b Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so differ diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py new file mode 100755 index 000000000..e71b5cbbd --- /dev/null +++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py @@ -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()) diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh new file mode 100755 index 000000000..f90a2f658 --- /dev/null +++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh @@ -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