diff options
24 files changed, 1542 insertions, 299 deletions
diff --git a/tools/relocation_packer/README.TXT b/tools/relocation_packer/README.TXT index 6beb5a9..f3ea83d 100644 --- a/tools/relocation_packer/README.TXT +++ b/tools/relocation_packer/README.TXT @@ -1,12 +1,13 @@ Introduction: ------------- -R_ARM_RELATIVE relocations are the bulk of dynamic relocations (the .rel.dyn -section) in libchrome.<version>.so. The ELF standard representation of them -is wasteful. +Relative relocations are the bulk of dynamic relocations (the .rel.dyn +or .rela.dyn sections) in libchrome.<version>.so. The ELF standard +representation of them is wasteful. -Packing uses run length encoding to store them more efficiently. Packed -relocations are placed in a new .android.rel.dyn section. Packing reduces +Packing uses a combination of run length encoding, delta encoding, and LEB128 +encoding to store them more efficiently. Packed relocations are placed in +a new .android.rel.dyn or .android.rela.dyn section. Packing reduces the footprint of libchrome.<version>.so in the filesystem, in APK downloads, and in memory when loaded on the device. @@ -29,24 +30,36 @@ 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 section data: +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 .android.rel.dyn data in libchrome.<version>.so + - The offset of packed relocation data in libchrome.<version>.so DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e) - - The size of .android.rel.dyn data in bytes + - The size of packed relocation data in bytes -The format of .android.rel.dyn data is: +32 bit ARM libraries use relocations without addends. 64 bit ARM libraries +use relocations with addends. The packing strategy necessarily differs for +the two relocation types. + +Where libchrome.<version>.so contains relocations without addends, the format +of .android.rel.dyn data is: "APR1" identifier N: the number of count-delta pairs in the encoding A: the initial offset N * C,D: N count-delta pairs +Where libchrome.<version>.so contains relocations with addends, the format +of .android.rela.dyn data is: + + "APA1" identifier + N: the number of addr-addend delta pairs in the encoding + N * A,V: N addr-addend delta pairs + All numbers in the encoding stream are stored as LEB128 values. For details see http://en.wikipedia.org/wiki/LEB128. -The streaming unpacking algorithm is: +The streaming unpacking algorithm for 32 bit ARM is: skip over "APR1" pairs, addr = next leb128 value, next leb128 value @@ -59,16 +72,34 @@ The streaming unpacking algorithm is: 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 and then run the tool: +To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and +then run the tool: echo -n 'NULL' >/tmp/small - arm-linux-gnueabi-objcopy \ - --add-section .android.rel.dyn=/tmp/small \ - libchrome.<version>.so libchrome.<version>.so.packed + if file libchrome.<version>.so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy + --add-section .android.rel.dyn=/tmp/small + libchrome.<version>.so libchrome.<version>.so.packed + else + aarch64-linux-android-objcopy + --add-section .android.rela.dyn=/tmp/small + libchrome.<version>.so libchrome.<version>.so.packed + fi rm /tmp/small relocation_packer libchrome.<version>.so.packed @@ -76,19 +107,21 @@ To unpack and restore the shared library to its original state: cp libchrome.<version>.so.packed unpackable relocation_packer -u unpackable - arm-linux-gnueabi-objcopy \ - --remove-section=.android.rel.dyn unpackable libchrome.<version>.so + if file libchrome.<version>.so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy \ + --remove-section=.android.rel.dyn unpackable libchrome.<version>.so + else + aarch64-linux-android-objcopy \ + --remove-section=.android.rela.dyn unpackable libchrome.<version>.so + endif rm unpackable Bugs & TODOs: ------------- -Currently only supports arm32. Support for arm64 requires some extension -and modification. - Requires two free slots in the .dynamic section. Uses these to add data that -tells the crazy linker where to find the packed .android.rel.dyn data. Fails +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). diff --git a/tools/relocation_packer/relocation_packer.gyp b/tools/relocation_packer/relocation_packer.gyp index 893cc4a..5d1574f 100644 --- a/tools/relocation_packer/relocation_packer.gyp +++ b/tools/relocation_packer/relocation_packer.gyp @@ -27,9 +27,11 @@ ], 'sources': [ 'src/debug.cc', + 'src/delta_encoder.cc', 'src/elf_file.cc', 'src/leb128.cc', 'src/packer.cc', + 'src/sleb128.cc', 'src/run_length_encoder.cc', ], }, @@ -67,9 +69,11 @@ ], 'sources': [ 'src/debug_unittest.cc', + 'src/delta_encoder_unittest.cc', 'src/elf_file_unittest.cc', 'src/leb128_unittest.cc', 'src/packer_unittest.cc', + 'src/sleb128_unittest.cc', 'src/run_length_encoder_unittest.cc', 'src/run_all_unittests.cc', ], @@ -77,8 +81,73 @@ { 'destination': '<(INTERMEDIATE_DIR)', 'files': [ - 'test_data/elf_file_unittest_relocs.so', - 'test_data/elf_file_unittest_relocs_packed.so', + 'test_data/elf_file_unittest_relocs_arm32.so', + 'test_data/elf_file_unittest_relocs_arm32_packed.so', + 'test_data/elf_file_unittest_relocs_arm64.so', + 'test_data/elf_file_unittest_relocs_arm64_packed.so', + ], + }, + ], + }, + + # Targets to build test data. These participate only in building test + # data for use with elf_file_unittest.cc, and are not part of the main + # relocation packer build. Unit test data files are checked in to the + # source tree as 'golden' data, and are not generated 'on the fly' by + # the build. + # + # See test_data/generate_elf_file_unittest_relocs.sh for instructions. + { + 'target_name': 'relocation_packer_test_data', + 'toolsets': ['target'], + 'type': 'shared_library', + 'cflags': [ + '-O0', + '-g0', + ], + 'sources': [ + 'test_data/elf_file_unittest_relocs.cc', + ], + }, + { + 'target_name': 'relocation_packer_unittests_test_data', + 'toolsets': ['target'], + 'type': 'none', + 'actions': [ + { + 'variables': { + 'test_file': '<(SHARED_LIB_DIR)/librelocation_packer_test_data.so', + 'conditions': [ + [ 'target_arch == "arm"', { + 'added_section': '.android.rel.dyn', + 'unpacked_output': 'elf_file_unittest_relocs_arm32.so', + 'packed_output': 'elf_file_unittest_relocs_arm32_packed.so', + }], + [ 'target_arch == "arm64"', { + 'added_section': '.android.rela.dyn', + 'unpacked_output': 'elf_file_unittest_relocs_arm64.so', + 'packed_output': 'elf_file_unittest_relocs_arm64_packed.so', + }], + ], + }, + 'action_name': 'generate_relocation_packer_test_data', + 'inputs': [ + 'test_data/generate_elf_file_unittest_relocs.py', + '<(PRODUCT_DIR)/relocation_packer', + '<(test_file)', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(unpacked_output)', + '<(INTERMEDIATE_DIR)/<(packed_output)', + ], + 'action': [ + 'python', 'test_data/generate_elf_file_unittest_relocs.py', + '--android-pack-relocations=<(PRODUCT_DIR)/relocation_packer', + '--android-objcopy=<(android_objcopy)', + '--added-section=<(added_section)', + '--test-file=<(test_file)', + '--unpacked-output=<(INTERMEDIATE_DIR)/<(unpacked_output)', + '--packed-output=<(INTERMEDIATE_DIR)/<(packed_output)', ], }, ], diff --git a/tools/relocation_packer/src/delta_encoder.cc b/tools/relocation_packer/src/delta_encoder.cc new file mode 100644 index 0000000..31142ad --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder.cc @@ -0,0 +1,72 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "delta_encoder.h" + +#include <vector> + +#include "debug.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode relative relocations with addends into a delta encoded (packed) +// representation. Represented as simple r_offset and r_addend delta pairs, +// with an implicit neutral element at the start. +void RelocationDeltaCodec::Encode(const std::vector<ELF::Rela>& relocations, + std::vector<ELF::Sxword>* packed) { + // One relocation is sufficient for delta encoding. + if (relocations.size() < 1) + return; + + // Start with the element count, then append the delta pairs. + packed->push_back(relocations.size()); + + ELF::Addr offset = 0; + ELF::Sxword addend = 0; + + for (size_t i = 0; i < relocations.size(); ++i) { + const ELF::Rela* relocation = &relocations[i]; + CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode); + + packed->push_back(relocation->r_offset - offset); + offset = relocation->r_offset; + packed->push_back(relocation->r_addend - addend); + addend = relocation->r_addend; + } +} + +// Decode relative relocations with addends from a delta encoded (packed) +// representation. +void RelocationDeltaCodec::Decode(const std::vector<ELF::Sxword>& packed, + std::vector<ELF::Rela>* relocations) { + // We need at least one packed pair after the packed pair count to be + // able to unpack. + if (packed.size() < 3) + return; + + // Ensure that the packed data offers enough pairs. There may be zero + // padding on it that we ignore. + CHECK(packed[0] <= (packed.size() - 1) >> 1); + + ELF::Addr offset = 0; + ELF::Sxword addend = 0; + + // The first packed vector element is the pairs count. Start uncondensing + // pairs at the second, and finish at the end of the pairs data. + const size_t pairs_count = packed[0]; + for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) { + offset += packed[i]; + addend += packed[i + 1]; + + // Generate a relocation for this offset and addend pair. + ELF::Rela relocation; + relocation.r_offset = offset; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); + } +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/delta_encoder.h b/tools/relocation_packer/src/delta_encoder.h new file mode 100644 index 0000000..6b5d49f --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder.h @@ -0,0 +1,80 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Delta encode and decode relative relocations with addends. +// +// Relative relocations are the bulk of dynamic relocations (the +// .rel.dyn or .rela.dyn sections) in libchrome<version>.so, and the ELF +// standard representation of them is wasteful. .rel.dyn contains +// relocations without addends, .rela.dyn relocations with addends. +// +// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes +// on 64 bit plaforms. It is split into offset, info, and addend fields. +// Offsets strictly increase, and each is commonly a few bytes different +// from its predecessor. Addends are less well behaved. The info field is +// constant. Example, from 'readelf -x4 libchrome.<version>.so' 64 bit: +// +// offset info +// 80949303 00000000 03040000 00000000 ................ +// addend offset +// fc015b00 00000000 88949303 00000000 ..[............. +// info addend +// 03040000 00000000 24025b00 00000000 ........$.[..... +// offset info +// 90949303 00000000 03040000 00000000 ................ +// addend offset +// 3c025b00 00000000 98949303 00000000 <.[............. +// info addend +// 03040000 00000000 50025b00 00000000 ........P.[..... +// +// The offset strictly increases, but the addend is unpredictable, so run +// length encoding will not work well with this data. We can however pack +// with delta encoding. The upper four bytes of the eight byte offset and +// addend are invariably zeroes. The difference between adjacent offsets +// is almost always small, and between adjacent addends is often small. And +// info is constant and can be eliminated. +// +// Delta encoding reduces the size of the data modestly, so that the first +// three relocations above can be represented as: +// +// initial offset initial addend offset delta addend delta +// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028 +// offset delta addend delta ... +// 00000000 00000008 00000000 0000009f +// +// The addend delta can be negative as well as positive, but overall the +// deltas have a much smaller range than the input data. When encoded as +// signed LEB128 the total data reduction becomes useful. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ + +#include <vector> + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationDeltaCodec packs vectors of relative relocations with +// addends into more compact forms, and unpacks them to reproduce the +// pre-packed data. +class RelocationDeltaCodec { + public: + // Encode relative relocations with addends into a more compact form. + // |relocations| is a vector of relative relocation with addend structs. + // |packed| is the vector of packed words into which relocations are packed. + static void Encode(const std::vector<ELF::Rela>& relocations, + std::vector<ELF::Sxword>* packed); + + // Decode relative relocations with addends from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relative relocations. + static void Decode(const std::vector<ELF::Sxword>& packed, + std::vector<ELF::Rela>* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ 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 0000000..b9bf39ad --- /dev/null +++ b/tools/relocation_packer/src/delta_encoder_unittest.cc @@ -0,0 +1,150 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "delta_encoder.h" + +#include <vector> +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, + ELF::Sxword addend, + std::vector<ELF::Rela>* relocations) { + ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, + ELF::Sxword addend, + const ELF::Rela& relocation) { + return relocation.r_offset == addr && + ELF_R_SYM(relocation.r_info) == 0 && + ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode && + relocation.r_addend == addend; +} + +} // namespace + +namespace relocation_packer { + +TEST(Delta, Encode) { + std::vector<ELF::Rela> relocations; + std::vector<ELF::Sxword> packed; + + RelocationDeltaCodec codec; + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(0, packed.size()); + + // Initial relocation. + AddRelocation(0xf00d0000, 10000, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(3, packed.size()); + // One pair present. + EXPECT_EQ(1, packed[0]); + // Delta from the neutral element is the initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + + // Add a second relocation, 4 byte offset delta, 12 byte addend delta. + AddRelocation(0xf00d0004, 10012, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(5, packed.size()); + // Two pairs present. + EXPECT_EQ(2, packed[0]); + // Delta from the neutral element is the initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + // 4 byte offset delta, 12 byte addend delta. + EXPECT_EQ(4, packed[3]); + EXPECT_EQ(12, packed[4]); + + // Add a third relocation, 4 byte offset delta, 12 byte addend delta. + AddRelocation(0xf00d0008, 10024, &relocations); + + // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas. + AddRelocation(0xf00d0010, 10000, &relocations); + AddRelocation(0xf00d0018, 9976, &relocations); + AddRelocation(0xf00d0020, 9952, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(13, packed.size()); + // Six pairs present. + EXPECT_EQ(6, packed[0]); + // Initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_EQ(4, packed[3]); + EXPECT_EQ(12, packed[4]); + EXPECT_EQ(4, packed[5]); + EXPECT_EQ(12, packed[6]); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_EQ(8, packed[7]); + EXPECT_EQ(-24, packed[8]); + EXPECT_EQ(8, packed[9]); + EXPECT_EQ(-24, packed[10]); + EXPECT_EQ(8, packed[11]); + EXPECT_EQ(-24, packed[12]); +} + +TEST(Delta, Decode) { + std::vector<ELF::Sxword> packed; + std::vector<ELF::Rela> relocations; + + RelocationDeltaCodec codec; + codec.Decode(packed, &relocations); + + EXPECT_EQ(0, relocations.size()); + + // Six pairs. + packed.push_back(6); + // Initial relocation. + packed.push_back(0xc0de0000); + packed.push_back(10000); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + packed.push_back(4); + packed.push_back(12); + packed.push_back(4); + packed.push_back(12); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + packed.push_back(8); + packed.push_back(-24); + packed.push_back(8); + packed.push_back(-24); + packed.push_back(8); + packed.push_back(-24); + + relocations.clear(); + codec.Decode(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xc0de0000, 10000, relocations[0])); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0004, 10012, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xc0de0008, 10024, relocations[2])); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0010, 10000, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xc0de0018, 9976, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xc0de0020, 9952, relocations[5])); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index 0fd0f89..d9243b0 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -21,7 +21,7 @@ namespace relocation_packer { static const uint32_t kStubIdentifier = 0x4c4c554eu; // Out-of-band dynamic tags used to indicate the offset and size of the -// .android.rel.dyn section. +// android packed relocations section. static const ELF::Sword DT_ANDROID_REL_OFFSET = DT_LOOS; static const ELF::Sword DT_ANDROID_REL_SIZE = DT_LOOS + 1; @@ -109,8 +109,8 @@ void VerboseLogSectionData(const Elf_Data* data) { } // namespace // Load the complete ELF file into a memory image in libelf, and identify -// the .rel.dyn, .dynamic, and .android.rel.dyn sections. No-op if the -// ELF file has already been loaded. +// 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. bool ElfFile::Load() { if (elf_) return true; @@ -164,12 +164,18 @@ bool ElfFile::Load() { size_t string_index; elf_getshdrstrndx(elf, &string_index); - // Notes of the .rel.dyn, .android.rel.dyn, and .dynamic sections. Found - // while iterating sections, and later stored in class attributes. - Elf_Scn* found_rel_dyn_section = NULL; - Elf_Scn* found_android_rel_dyn_section = NULL; + // 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 = NULL; + Elf_Scn* found_android_relocations_section = NULL; Elf_Scn* found_dynamic_section = NULL; + // 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; + // Flag set if we encounter any .debug* section. We do not adjust any // offsets or addresses of any debug data, so if we find one of these then // the resulting output shared object should still run, but might not be @@ -183,12 +189,20 @@ bool ElfFile::Load() { 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) { + has_rel_relocations = true; + } + if (section_header->sh_type == SHT_RELA) { + has_rela_relocations = true; + } + // Note special sections as we encounter them. - if (name == ".rel.dyn") { - found_rel_dyn_section = section; + if (name == ".rel.dyn" || name == ".rela.dyn") { + found_relocations_section = section; } - if (name == ".android.rel.dyn") { - found_android_rel_dyn_section = section; + if (name == ".android.rel.dyn" || name == ".android.rela.dyn") { + found_android_relocations_section = section; } if (section_header->sh_offset == dynamic_program_header->p_offset) { found_dynamic_section = section; @@ -210,29 +224,41 @@ bool ElfFile::Load() { } // Loading failed if we did not find the required special sections. - if (!found_rel_dyn_section) { - LOG(ERROR) << "Missing .rel.dyn section"; + if (!found_relocations_section) { + LOG(ERROR) << "Missing .rel.dyn or .rela.dyn section"; return false; } if (!found_dynamic_section) { LOG(ERROR) << "Missing .dynamic section"; return false; } - if (!found_android_rel_dyn_section) { - LOG(ERROR) << "Missing .android.rel.dyn section " + if (!found_android_relocations_section) { + LOG(ERROR) << "Missing .android.rel.dyn or .android.rela.dyn section " << "(to fix, run with --help and follow the pre-packing " << "instructions)"; 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; + } + if (has_debug_section) { LOG(WARNING) << "Found .debug section(s), and ignored them"; } elf_ = elf; - rel_dyn_section_ = found_rel_dyn_section; + relocations_section_ = found_relocations_section; dynamic_section_ = found_dynamic_section; - android_rel_dyn_section_ = found_android_rel_dyn_section; + android_relocations_section_ = found_android_relocations_section; + relocations_type_ = has_rel_relocations ? REL : RELA; return true; } @@ -318,8 +344,9 @@ void AdjustSectionHeadersForHole(Elf* elf, } // Helper for ResizeSection(). Adjust the .dynamic section for the hole. +template <typename Rel> void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, - bool is_rel_dyn_resize, + bool is_relocations_resize, ELF::Off hole_start, ssize_t hole_size) { Elf_Data* data = GetSectionData(dynamic_section); @@ -351,10 +378,10 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, << " d_ptr adjusted to " << dynamic->d_un.d_ptr; } - // If we are specifically resizing .rel.dyn, we need to make some added - // adjustments to tags that indicate the counts of ARM relative + // If we are specifically resizing dynamic relocations, we need to make + // some added adjustments to tags that indicate the counts of relative // relocations in the shared object. - if (!is_rel_dyn_resize) + if (!is_relocations_resize) continue; // DT_RELSZ is the overall size of relocations. Adjust by hole size. @@ -370,7 +397,7 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, if (tag == DT_RELCOUNT) { // Cast sizeof to a signed type to avoid the division result being // promoted into an unsigned size_t. - const ssize_t sizeof_rel = static_cast<ssize_t>(sizeof(ELF::Rel)); + const ssize_t sizeof_rel = static_cast<ssize_t>(sizeof(Rel)); dynamic->d_un.d_val += hole_size / sizeof_rel; VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag << " d_val adjusted to " << dynamic->d_un.d_val; @@ -378,7 +405,7 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, // DT_RELENT doesn't change, but make sure it is what we expect. if (tag == DT_RELENT) { - CHECK(dynamic->d_un.d_val == sizeof(ELF::Rel)); + CHECK(dynamic->d_un.d_val == sizeof(Rel)); } } @@ -420,21 +447,22 @@ void AdjustDynSymSectionForHole(Elf_Scn* dynsym_section, RewriteSectionData(data, section_data, bytes); } -// Helper for ResizeSection(). Adjust the .rel.plt section for the hole. -// We need to adjust the offset of every relocation inside it that falls -// beyond the hole start. +// Helper for ResizeSection(). Adjust the plt relocations section for the +// hole. We need to adjust the offset of every relocation inside it that +// falls beyond the hole start. +template <typename Rel> void AdjustRelPltSectionForHole(Elf_Scn* relplt_section, ELF::Off hole_start, ssize_t hole_size) { Elf_Data* data = GetSectionData(relplt_section); - const ELF::Rel* relplt_base = reinterpret_cast<ELF::Rel*>(data->d_buf); - std::vector<ELF::Rel> relplts( + const Rel* relplt_base = reinterpret_cast<Rel*>(data->d_buf); + std::vector<Rel> relplts( relplt_base, relplt_base + data->d_size / sizeof(relplts[0])); for (size_t i = 0; i < relplts.size(); ++i) { - ELF::Rel* relplt = &relplts[i]; + Rel* relplt = &relplts[i]; if (relplt->r_offset > hole_start) { relplt->r_offset += hole_size; VLOG(1) << "relplt[" << i @@ -476,19 +504,21 @@ void AdjustSymTabSectionForHole(Elf_Scn* symtab_section, // Resize a section. If the new size is larger than the current size, open // up a hole by increasing file offsets that come after the hole. If smaller // than the current size, remove the hole by decreasing those offsets. +template <typename Rel> void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) { ELF::Shdr* section_header = ELF::getshdr(section); if (section_header->sh_size == new_size) return; - // Note if we are resizing the real .rel.dyn. If yes, then we have to - // massage d_un.d_val in the dynamic section where d_tag is DT_RELSZ and + // Note if we are resizing the real dyn relocations. If yes, then we have + // to massage d_un.d_val in the dynamic section where d_tag is DT_RELSZ and // DT_RELCOUNT. size_t string_index; elf_getshdrstrndx(elf, &string_index); const std::string section_name = elf_strptr(elf, string_index, section_header->sh_name); - const bool is_rel_dyn_resize = section_name == ".rel.dyn"; + const bool is_relocations_resize = + (section_name == ".rel.dyn" || section_name == ".rela.dyn"); // Require that the section size and the data size are the same. True // in practice for all sections we resize when packing or unpacking. @@ -541,14 +571,15 @@ void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) { } CHECK(dynamic_program_header); - // Sections requiring special attention, and the .android.rel.dyn offset. + // Sections requiring special attention, and the packed android + // relocations offset. Elf_Scn* dynamic_section = NULL; Elf_Scn* dynsym_section = NULL; - Elf_Scn* relplt_section = NULL; + Elf_Scn* plt_relocations_section = NULL; Elf_Scn* symtab_section = NULL; - ELF::Off android_rel_dyn_offset = 0; + ELF::Off android_relocations_offset = 0; - // Find these sections, and the .android.rel.dyn offset. + // Find these sections, and the packed android relocations offset. section = NULL; while ((section = elf_nextscn(elf, section)) != NULL) { ELF::Shdr* section_header = ELF::getshdr(section); @@ -560,36 +591,38 @@ void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) { if (name == ".dynsym") { dynsym_section = section; } - if (name == ".rel.plt") { - relplt_section = section; + if (name == ".rel.plt" || name == ".rela.plt") { + plt_relocations_section = section; } if (name == ".symtab") { symtab_section = section; } - // Note .android.rel.dyn offset. - if (name == ".android.rel.dyn") { - android_rel_dyn_offset = section_header->sh_offset; + // Note packed android relocations offset. + if (name == ".android.rel.dyn" || name == ".android.rela.dyn") { + android_relocations_offset = section_header->sh_offset; } } CHECK(dynamic_section != NULL); CHECK(dynsym_section != NULL); - CHECK(relplt_section != NULL); - CHECK(android_rel_dyn_offset != 0); + CHECK(plt_relocations_section != NULL); + CHECK(android_relocations_offset != 0); // Adjust the .dynamic section for the hole. Because we have to edit the // current contents of .dynamic we disallow resizing it. CHECK(section != dynamic_section); - AdjustDynamicSectionForHole(dynamic_section, - is_rel_dyn_resize, - hole_start, - hole_size); + AdjustDynamicSectionForHole<Rel>(dynamic_section, + is_relocations_resize, + hole_start, + hole_size); // Adjust the .dynsym section for the hole. AdjustDynSymSectionForHole(dynsym_section, hole_start, hole_size); - // Adjust the .rel.plt section for the hole. - AdjustRelPltSectionForHole(relplt_section, hole_start, hole_size); + // Adjust the plt relocations section for the hole. + AdjustRelPltSectionForHole<Rel>(plt_relocations_section, + hole_start, + hole_size); // If present, adjust the .symtab section for the hole. If the shared // library was stripped then .symtab will be absent. @@ -647,13 +680,14 @@ void RemoveDynamicEntry(ELF::Sword tag, CHECK(dynamics->at(dynamics->size() - 1).d_tag == DT_NULL); } -// Apply ARM relative relocations to the file data to which they refer. +// Apply relative relocations to the file data to which they refer. // This relocates data into the area it will occupy after the hole in -// .rel.dyn is added or removed. +// the dynamic relocations is added or removed. +template <typename Rel> void AdjustRelocationTargets(Elf* elf, ELF::Off hole_start, - size_t hole_size, - const std::vector<ELF::Rel>& relocations) { + ssize_t hole_size, + const std::vector<Rel>& relocations) { Elf_Scn* section = NULL; while ((section = elf_nextscn(elf, section)) != NULL) { const ELF::Shdr* section_header = ELF::getshdr(section); @@ -672,7 +706,7 @@ void AdjustRelocationTargets(Elf* elf, uint8_t* area = reinterpret_cast<uint8_t*>(data->d_buf); for (size_t i = 0; i < relocations.size(); ++i) { - const ELF::Rel* relocation = &relocations[i]; + const Rel* relocation = &relocations[i]; CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode); // See if this relocation points into the current section. @@ -706,8 +740,12 @@ void AdjustRelocationTargets(Elf* elf, } // Pad relocations with a given number of null relocations. -void PadRelocations(size_t count, - std::vector<ELF::Rel>* relocations) { +template <typename Rel> +void PadRelocations(size_t count, std::vector<Rel>* relocations); + +template <> +void PadRelocations<ELF::Rel>(size_t count, + std::vector<ELF::Rel>* relocations) { ELF::Rel null_relocation; null_relocation.r_offset = 0; null_relocation.r_info = ELF_R_INFO(0, ELF::kNoRelocationCode); @@ -715,14 +753,26 @@ void PadRelocations(size_t count, relocations->insert(relocations->end(), padding.begin(), padding.end()); } +template <> +void PadRelocations<ELF::Rela>(size_t count, + std::vector<ELF::Rela>* relocations) { + ELF::Rela null_relocation; + null_relocation.r_offset = 0; + null_relocation.r_info = ELF_R_INFO(0, ELF::kNoRelocationCode); + null_relocation.r_addend = 0; + std::vector<ELF::Rela> padding(count, null_relocation); + relocations->insert(relocations->end(), padding.begin(), padding.end()); +} + // Adjust relocations so that the offset that they indicate will be correct -// after the hole in .rel.dyn is added or removed (in effect, relocate the -// relocations). +// after the hole in the dynamic relocations is added or removed (in effect, +// relocate the relocations). +template <typename Rel> void AdjustRelocations(ELF::Off hole_start, - size_t hole_size, - std::vector<ELF::Rel>* relocations) { + ssize_t hole_size, + std::vector<Rel>* relocations) { for (size_t i = 0; i < relocations->size(); ++i) { - ELF::Rel* relocation = &relocations->at(i); + Rel* relocation = &relocations->at(i); if (relocation->r_offset > hole_start) { relocation->r_offset += hole_size; VLOG(1) << "relocation[" << i @@ -733,8 +783,8 @@ void AdjustRelocations(ELF::Off hole_start, } // namespace -// Remove ARM relative entries from .rel.dyn and write as packed data -// into .android.rel.dyn. +// Remove relative entries from dynamic relocations and write as packed +// data into android packed relocations. bool ElfFile::PackRelocations() { // Load the ELF file into libelf. if (!Load()) { @@ -742,21 +792,46 @@ bool ElfFile::PackRelocations() { return false; } - // Retrieve the current .rel.dyn section data. - Elf_Data* data = GetSectionData(rel_dyn_section_); + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); - // Convert data to a vector of Elf32 relocations. - const ELF::Rel* relocations_base = reinterpret_cast<ELF::Rel*>(data->d_buf); - std::vector<ELF::Rel> relocations( - relocations_base, - relocations_base + data->d_size / sizeof(relocations[0])); + if (relocations_type_ == REL) { + // Convert data to a vector of relocations. + const ELF::Rel* relocations_base = reinterpret_cast<ELF::Rel*>(data->d_buf); + std::vector<ELF::Rel> relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + LOG(INFO) << "Relocations : REL"; + return PackTypedRelocations<ELF::Rel>(relocations, data); + } + + if (relocations_type_ == RELA) { + // Convert data to a vector of relocations with addends. + const ELF::Rela* relocations_base = + reinterpret_cast<ELF::Rela*>(data->d_buf); + std::vector<ELF::Rela> relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); - std::vector<ELF::Rel> relative_relocations; - std::vector<ELF::Rel> other_relocations; + LOG(INFO) << "Relocations : RELA"; + return PackTypedRelocations<ELF::Rela>(relocations, data); + } + + NOTREACHED(); + return false; +} + +// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template <typename Rel> +bool ElfFile::PackTypedRelocations(const std::vector<Rel>& relocations, + Elf_Data* data) { + // Filter relocations into those that are relative and others. + std::vector<Rel> relative_relocations; + std::vector<Rel> other_relocations; - // Filter relocations into those that are ARM relative and others. for (size_t i = 0; i < relocations.size(); ++i) { - const ELF::Rel& relocation = relocations[i]; + const Rel& relocation = relocations[i]; if (ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode) { CHECK(ELF_R_SYM(relocation.r_info) == 0); relative_relocations.push_back(relocation); @@ -775,19 +850,22 @@ bool ElfFile::PackRelocations() { return false; } - // Unless padding, pre-apply ARM relative relocations to account for the + // Unless padding, pre-apply relative relocations to account for the // hole, and pre-adjust all relocation offsets accordingly. - if (!is_padding_rel_dyn_) { + if (!is_padding_relocations_) { // Pre-calculate the size of the hole we will close up when we rewrite - // .rel.dyn. We have to adjust relocation addresses to account for this. - ELF::Shdr* section_header = ELF::getshdr(rel_dyn_section_); + // dynamic relocations. We have to adjust relocation addresses to + // account for this. + ELF::Shdr* section_header = ELF::getshdr(relocations_section_); const ELF::Off hole_start = section_header->sh_offset; - size_t hole_size = + ssize_t hole_size = relative_relocations.size() * sizeof(relative_relocations[0]); - const size_t unaligned_hole_size = hole_size; + const ssize_t unaligned_hole_size = hole_size; - // Adjust the actual hole size to preserve alignment. - hole_size -= hole_size % kPreserveAlignment; + // Adjust the actual hole size to preserve alignment. We always adjust + // by a whole number of NONE-type relocations. + while (hole_size % kPreserveAlignment) + hole_size -= sizeof(relative_relocations[0]); LOG(INFO) << "Compaction : " << hole_size << " bytes"; // Adjusting for alignment may have removed any packing benefit. @@ -803,12 +881,13 @@ bool ElfFile::PackRelocations() { PadRelocations(required, &other_relocations); LOG(INFO) << "Alignment pad : " << required << " relocations"; - // Apply relocations to all ARM relative data to relocate it into the - // area it will occupy once the hole in .rel.dyn is removed. - AdjustRelocationTargets(elf_, hole_start, -hole_size, relative_relocations); + // Apply relocations to all relative data to relocate it into the + // area it will occupy once the hole in the dynamic relocations is removed. + AdjustRelocationTargets<Rel>( + elf_, hole_start, -hole_size, relative_relocations); // Relocate the relocations. - AdjustRelocations(hole_start, -hole_size, &relative_relocations); - AdjustRelocations(hole_start, -hole_size, &other_relocations); + AdjustRelocations<Rel>(hole_start, -hole_size, &relative_relocations); + AdjustRelocations<Rel>(hole_start, -hole_size, &other_relocations); } else { // If padding, add NONE-type relocations to other_relocations to make it // the same size as the the original relocations we read in. This makes @@ -817,7 +896,7 @@ bool ElfFile::PackRelocations() { PadRelocations(required, &other_relocations); } - // Pack ARM relative relocations. + // Pack relative relocations. const size_t initial_bytes = relative_relocations.size() * sizeof(relative_relocations[0]); LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes"; @@ -828,7 +907,7 @@ bool ElfFile::PackRelocations() { const size_t packed_bytes = packed.size() * sizeof(packed[0]); LOG(INFO) << "Packed relative: " << packed_bytes << " bytes"; - // If we have insufficient ARM relative relocations to form a run then + // If we have insufficient relative relocations to form a run then // packing fails. if (packed.empty()) { LOG(INFO) << "Too few relative relocations to pack"; @@ -836,7 +915,7 @@ bool ElfFile::PackRelocations() { } // Run a loopback self-test as a check that packing is lossless. - std::vector<ELF::Rel> unpacked; + std::vector<Rel> unpacked; packer.UnpackRelativeRelocations(packed, &unpacked); CHECK(unpacked.size() == relative_relocations.size()); CHECK(!memcmp(&unpacked[0], @@ -849,27 +928,28 @@ bool ElfFile::PackRelocations() { return false; } - // Rewrite the current .rel.dyn section to be only the ARM non-relative - // relocations, then shrink it to size. + // Rewrite the current dynamic relocations section to be only the ARM + // non-relative relocations, then shrink it to size. const void* section_data = &other_relocations[0]; const size_t bytes = other_relocations.size() * sizeof(other_relocations[0]); - ResizeSection(elf_, rel_dyn_section_, bytes); + ResizeSection<Rel>(elf_, relocations_section_, bytes); RewriteSectionData(data, section_data, bytes); - // Rewrite the current .android.rel.dyn section to hold the packed - // ARM relative relocations. - data = GetSectionData(android_rel_dyn_section_); - ResizeSection(elf_, android_rel_dyn_section_, packed_bytes); + // Rewrite the current packed android relocations section to hold the packed + // relative relocations. + data = GetSectionData(android_relocations_section_); + ResizeSection<Rel>(elf_, android_relocations_section_, packed_bytes); RewriteSectionData(data, packed_data, packed_bytes); - // Rewrite .dynamic to include two new tags describing .android.rel.dyn. + // Rewrite .dynamic to include two new tags describing the packed android + // relocations. data = GetSectionData(dynamic_section_); const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf); std::vector<ELF::Dyn> dynamics( dynamic_base, dynamic_base + data->d_size / sizeof(dynamics[0])); - // Use two of the spare slots to describe the .android.rel.dyn section. - ELF::Shdr* section_header = ELF::getshdr(android_rel_dyn_section_); + // Use two of the spare slots to describe the packed section. + ELF::Shdr* section_header = ELF::getshdr(android_relocations_section_); const ELF::Dyn offset_dyn = {DT_ANDROID_REL_OFFSET, {section_header->sh_offset}}; AddDynamicEntry(offset_dyn, &dynamics); @@ -884,8 +964,9 @@ bool ElfFile::PackRelocations() { return true; } -// Find packed ARM relative relocations in .android.rel.dyn, unpack them, -// and rewrite the .rel.dyn section in so_file to contain unpacked data. +// Find packed relative relocations in the packed android relocations +// section, unpack them, and rewrite the dynamic relocations section to +// contain unpacked data. bool ElfFile::UnpackRelocations() { // Load the ELF file into libelf. if (!Load()) { @@ -893,8 +974,8 @@ bool ElfFile::UnpackRelocations() { return false; } - // Retrieve the current .android.rel.dyn section data. - Elf_Data* data = GetSectionData(android_rel_dyn_section_); + // Retrieve the current packed android relocations section data. + Elf_Data* data = GetSectionData(android_relocations_section_); // Convert data to a vector of bytes. const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf); @@ -902,40 +983,58 @@ bool ElfFile::UnpackRelocations() { packed_base, packed_base + data->d_size / sizeof(packed[0])); - // Properly packed data must begin with "APR1". - if (packed.empty() || - packed[0] != 'A' || packed[1] != 'P' || - packed[2] != 'R' || packed[3] != '1') { - LOG(ERROR) << "Packed relative relocations not found (not packed?)"; - return false; + if (packed.size() > 3 && + packed[0] == 'A' && packed[1] == 'P' && + packed[2] == 'R' && packed[3] == '1') { + // Signature is APR1, unpack relocations. + CHECK(relocations_type_ == REL); + LOG(INFO) << "Relocations : REL"; + return UnpackTypedRelocations<ELF::Rel>(packed, data); } - // Unpack the data to re-materialize the ARM relative relocations. + if (packed.size() > 3 && + packed[0] == 'A' && packed[1] == 'P' && + packed[2] == 'A' && packed[3] == '1') { + // Signature is APA1, unpack relocations with addends. + CHECK(relocations_type_ == RELA); + LOG(INFO) << "Relocations : RELA"; + return UnpackTypedRelocations<ELF::Rela>(packed, data); + } + + LOG(ERROR) << "Packed relative relocations not found (not packed?)"; + return false; +} + +// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template <typename Rel> +bool ElfFile::UnpackTypedRelocations(const std::vector<uint8_t>& packed, + Elf_Data* data) { + // Unpack the data to re-materialize the relative relocations. const size_t packed_bytes = packed.size() * sizeof(packed[0]); LOG(INFO) << "Packed relative: " << packed_bytes << " bytes"; - std::vector<ELF::Rel> relative_relocations; + std::vector<Rel> relative_relocations; RelocationPacker packer; packer.UnpackRelativeRelocations(packed, &relative_relocations); const size_t unpacked_bytes = relative_relocations.size() * sizeof(relative_relocations[0]); LOG(INFO) << "Unpacked relative: " << unpacked_bytes << " bytes"; - // Retrieve the current .rel.dyn section data. - data = GetSectionData(rel_dyn_section_); + // Retrieve the current dynamic relocations section data. + data = GetSectionData(relocations_section_); // Interpret data as Elf32 relocations. - const ELF::Rel* relocations_base = reinterpret_cast<ELF::Rel*>(data->d_buf); - std::vector<ELF::Rel> relocations( + const Rel* relocations_base = reinterpret_cast<Rel*>(data->d_buf); + std::vector<Rel> relocations( relocations_base, relocations_base + data->d_size / sizeof(relocations[0])); - std::vector<ELF::Rel> other_relocations; + std::vector<Rel> other_relocations; size_t padding = 0; // Filter relocations to locate any that are NONE-type. These will occur // if padding was turned on for packing. for (size_t i = 0; i < relocations.size(); ++i) { - const ELF::Rel& relocation = relocations[i]; + const Rel& relocation = relocations[i]; if (ELF_R_TYPE(relocation.r_info) != ELF::kNoRelocationCode) { other_relocations.push_back(relocation); } else { @@ -945,55 +1044,59 @@ bool ElfFile::UnpackRelocations() { LOG(INFO) << "Relative : " << relative_relocations.size() << " entries"; LOG(INFO) << "Other : " << other_relocations.size() << " entries"; - // If we found the same number of null relocation entries in .rel.dyn as we - // hold as unpacked relative relocations, then this is a padded file. + // 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 = padding == relative_relocations.size(); - // Unless padded, pre-apply ARM relative relocations to account for the + // Unless padded, pre-apply relative relocations to account for the // hole, and pre-adjust all relocation offsets accordingly. if (!is_padded) { // Pre-calculate the size of the hole we will open up when we rewrite - // .rel.dyn. We have to adjust relocation addresses to account for this. - ELF::Shdr* section_header = ELF::getshdr(rel_dyn_section_); + // dynamic relocations. We have to adjust relocation addresses to + // account for this. + ELF::Shdr* section_header = ELF::getshdr(relocations_section_); const ELF::Off hole_start = section_header->sh_offset; - size_t hole_size = + ssize_t hole_size = relative_relocations.size() * sizeof(relative_relocations[0]); // Adjust the hole size for the padding added to preserve alignment. hole_size -= padding * sizeof(other_relocations[0]); LOG(INFO) << "Expansion : " << hole_size << " bytes"; - // Apply relocations to all ARM relative data to relocate it into the - // area it will occupy once the hole in .rel.dyn is opened. - AdjustRelocationTargets(elf_, hole_start, hole_size, relative_relocations); + // Apply relocations to all relative data to relocate it into the + // area it will occupy once the hole in dynamic relocations is opened. + AdjustRelocationTargets<Rel>( + elf_, hole_start, hole_size, relative_relocations); // Relocate the relocations. - AdjustRelocations(hole_start, hole_size, &relative_relocations); - AdjustRelocations(hole_start, hole_size, &other_relocations); + AdjustRelocations<Rel>(hole_start, hole_size, &relative_relocations); + AdjustRelocations<Rel>(hole_start, hole_size, &other_relocations); } - // Rewrite the current .rel.dyn section to be the ARM relative relocations - // followed by other relocations. This is the usual order in which we find - // them after linking, so this action will normally put the entire .rel.dyn - // section back to its pre-split-and-packed state. + // Rewrite the current dynamic relocations section to be the relative + // relocations followed by other relocations. This is the usual order in + // which we find them after linking, so this action will normally put the + // entire dynamic relocations section back to its pre-split-and-packed state. relocations.assign(relative_relocations.begin(), relative_relocations.end()); relocations.insert(relocations.end(), other_relocations.begin(), other_relocations.end()); const void* section_data = &relocations[0]; const size_t bytes = relocations.size() * sizeof(relocations[0]); LOG(INFO) << "Total : " << relocations.size() << " entries"; - ResizeSection(elf_, rel_dyn_section_, bytes); + ResizeSection<Rel>(elf_, relocations_section_, bytes); RewriteSectionData(data, section_data, bytes); - // Nearly empty the current .android.rel.dyn section. Leaves a four-byte - // stub so that some data remains allocated to the section. This is a - // convenience which allows us to re-pack this file again without + // Nearly empty the current packed android relocations section. Leaves a + // four-byte stub so that some data remains allocated to the section. + // This is a convenience which allows us to re-pack this file again without // having to remove the section and then add a new small one with objcopy. // The way we resize sections relies on there being some data in a section. - data = GetSectionData(android_rel_dyn_section_); - ResizeSection(elf_, android_rel_dyn_section_, sizeof(kStubIdentifier)); + data = GetSectionData(android_relocations_section_); + ResizeSection<Rel>( + elf_, android_relocations_section_, sizeof(kStubIdentifier)); RewriteSectionData(data, &kStubIdentifier, sizeof(kStubIdentifier)); - // Rewrite .dynamic to remove two tags describing .android.rel.dyn. + // Rewrite .dynamic to remove two tags describing packed android relocations. data = GetSectionData(dynamic_section_); const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf); std::vector<ELF::Dyn> dynamics( diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h index 3778e01..cdc4e47 100644 --- a/tools/relocation_packer/src/elf_file.h +++ b/tools/relocation_packer/src/elf_file.h @@ -4,18 +4,24 @@ // ELF shared object file updates handler. // -// Provides functions to remove ARM relative relocations from the .rel.dyn -// section and pack them in .android.rel.dyn, and unpack to return the file -// to its pre-packed state. +// 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 -// section. A standard libchrome.<version>.so will not contain this section, -// so the following can be used to add one: +// or android.rela.dyn section. A standard libchrome.<version>.so will not +// contain this section, so the following can be used to add one: // // echo -n 'NULL' >/tmp/small -// arm-linux-gnueabi-objcopy -// --add-section .android.rel.dyn=/tmp/small -// libchrome.<version>.so libchrome.<version>.so.packed +// if file libchrome.<version>.so | grep -q 'ELF 32'; then +// arm-linux-androideabi-objcopy +// --add-section .android.rel.dyn=/tmp/small +// libchrome.<version>.so libchrome.<version>.so.packed +// else +// aarch64-linux-android-objcopy +// --add-section .android.rela.dyn=/tmp/small +// libchrome.<version>.so libchrome.<version>.so.packed +// fi // rm /tmp/small // // To use, open the file and pass the file descriptor to the constructor, @@ -31,24 +37,26 @@ // status = elf_file.UnpackRelocations(); // close(fd); // -// SetPadding() causes PackRelocations() to pad .rel.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. +// 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 ARM relative relocations -// removed from .rel.dyn, and replaced as packed data in .android.rel.dyn. -// The resulting file is shorter than its non-packed original. +// 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, combining the ARM relative -// relocations with the data already in .rel.dyn, and then writing back the -// now expanded .rel.dyn section. +// expanding the packed data in .android.rel.dyn or .android.rela.dyn, +// combining the relative relocations with the data already in .rel.dyn +// or .rela.dyn, and then writing back the now expanded section. #ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ #define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ #include <string.h> +#include <vector> #include "elf.h" #include "libelf.h" @@ -56,25 +64,28 @@ namespace relocation_packer { -// An ElfFile reads shared objects, and shuttles ARM relative relocations -// between .rel.dyn and .android.rel.dyn sections. +// An ElfFile reads shared objects, and shuttles relative relocations +// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn +// sections. class ElfFile { public: explicit ElfFile(int fd) { memset(this, 0, sizeof(*this)); fd_ = fd; } ~ElfFile() {} // Set padding mode. When padding, PackRelocations() will not shrink - // the .rel.dyn section, but instead replace ARM relative with + // the .rel.dyn or .rela.dyn section, but instead replace relative with // NONE-type entries. - // |flag| is true to pad .rel.dyn, false to shrink it. - inline void SetPadding(bool flag) { is_padding_rel_dyn_ = flag; } + // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it. + inline void SetPadding(bool flag) { is_padding_relocations_ = flag; } - // Transfer ARM relative relocations from .rel.dyn to a packed - // representation in .android.rel.dyn. Returns true on success. + // 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 ARM relative relocations from a packed representation in - // .android.rel.dyn to .rel.dyn. Returns true on success. + // 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: @@ -83,12 +94,24 @@ class ElfFile { // |fd| is an open file descriptor for the shared object. bool Load(); + // Templated packer, helper for PackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + template <typename Rel> + bool PackTypedRelocations(const std::vector<Rel>& relocations, + Elf_Data* data); + + // Templated unpacker, helper for UnpackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + template <typename Rel> + bool UnpackTypedRelocations(const std::vector<uint8_t>& packed, + Elf_Data* data); + // Write ELF file changes. void Flush(); - // If set, pad rather than shrink .rel.dyn. Primarily for debugging, - // allows packing to be checked without affecting load addresses. - bool is_padding_rel_dyn_; + // 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_; // File descriptor opened on the shared object. int fd_; @@ -97,9 +120,12 @@ class ElfFile { Elf* elf_; // Sections that we manipulate, assigned by Load(). - Elf_Scn* rel_dyn_section_; + Elf_Scn* relocations_section_; Elf_Scn* dynamic_section_; - Elf_Scn* android_rel_dyn_section_; + Elf_Scn* android_relocations_section_; + + // Relocation type found, assigned by Load(). + enum { NONE = 0, REL, RELA } relocations_type_; }; } // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc index d84928e..37abd0d 100644 --- a/tools/relocation_packer/src/elf_file_unittest.cc +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -10,6 +10,7 @@ #include <string> #include <vector> #include "debug.h" +#include "elf_traits.h" #include "testing/gtest/include/gtest/gtest.h" // Macro stringification. @@ -70,8 +71,20 @@ void OpenRelocsTestFile(const char* name, FILE** stream) { } void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) { - OpenRelocsTestFile("elf_file_unittest_relocs.so", relocs_so); - OpenRelocsTestFile("elf_file_unittest_relocs_packed.so", packed_relocs_so); + const char* arch = NULL; + if (ELF::kMachine == EM_ARM) { + arch = "arm32"; + } else if (ELF::kMachine == EM_AARCH64) { + arch = "arm64"; + } + ASSERT_FALSE(arch == NULL); + + const std::string base = std::string("elf_file_unittest_relocs_") + arch; + const std::string relocs = base + ".so"; + const std::string packed_relocs = base + "_packed.so"; + + OpenRelocsTestFile(relocs.c_str(), relocs_so); + OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so); } void CloseRelocsTestFile(FILE* temporary) { diff --git a/tools/relocation_packer/src/elf_traits.h b/tools/relocation_packer/src/elf_traits.h index 20e89e7..1004767 100644 --- a/tools/relocation_packer/src/elf_traits.h +++ b/tools/relocation_packer/src/elf_traits.h @@ -19,6 +19,17 @@ # error "Define one of TARGET_ARM or TARGET_ARM64, but not both" #endif +// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h. +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#endif +#ifndef R_AARCH64_RELATIVE +#define R_AARCH64_RELATIVE 1027 +#endif +#ifndef R_AARCH64_NONE +#define R_AARCH64_NONE 0 +#endif + // ELF is a traits structure used to provide convenient aliases for // 32/64 bit Elf types and functions, depending on the target specified. @@ -30,6 +41,7 @@ struct ELF { 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; @@ -62,6 +74,7 @@ struct ELF { 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; @@ -73,17 +86,6 @@ struct ELF { static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); } static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); } -// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h. -# ifndef EM_AARCH64 -# define EM_AARCH64 183 -# endif -# ifndef R_AARCH64_RELATIVE -# define R_AARCH64_RELATIVE 1027 -# endif -# ifndef R_AARCH64_NONE -# define R_AARCH64_NONE 0 -# endif - enum { kMachine = EM_AARCH64 }; enum { kFileClass = ELFCLASS64 }; enum { kRelativeRelocationCode = R_AARCH64_RELATIVE }; diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h index 500845e..6cc2d7c 100644 --- a/tools/relocation_packer/src/leb128.h +++ b/tools/relocation_packer/src/leb128.h @@ -2,9 +2,9 @@ // 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 ARM relative relocations. +// LEB128 encoder and decoder for packed relative relocations. // -// Run-length encoded ARM relative relocations consist of a large number +// Run-length encoded relative relocations consist of a large number // of pairs of relatively small positive integer values. Encoding these as // LEB128 saves space. // diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc index 08a5a34..28f5b04 100644 --- a/tools/relocation_packer/src/main.cc +++ b/tools/relocation_packer/src/main.cc @@ -2,9 +2,9 @@ // 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 ARM relative relocations in a shared library. +// Tool to pack and unpack relative relocations in a shared library. // -// Packing removes ARM relative relocations from .rel.dyn and writes them +// 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. @@ -38,30 +38,62 @@ void PrintUsage(const char* argv0) { const char* basename = temporary.c_str(); printf( - "Usage: %s [-u] [-v] [-p] file\n" - "Pack or unpack ARM relative relocations in a shared library.\n\n" - " -u, --unpack unpack previously packed ARM relative relocations\n" + "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 .rel.dyn, but pad (for debugging)\n\n" - "Extracts ARM relative relocations from the .rel.dyn section, packs\n" - "them into a more compact format, and stores the packed relocations in\n" - ".android.rel.dyn. Expands .android.rel.dyn to hold the packed data,\n" - "and shrinks .rel.dyn by the amount of unpacked data removed from it.\n\n" - "Before being packed, a shared library needs to be prepared by adding\n" - "a null .android.rel.dyn section. A complete packing process is:\n\n" - " echo -n 'NULL' >/tmp/small\n" - " arm-linux-gnueabi-objcopy \\\n" - " --add-section .android.rel.dyn=/tmp/small \\\n" - " libchrome.<version>.so\n" - " rm /tmp/small\n" - " %s libchrome.<version>.so\n\n" - "To unpack and restore the shared library to its original state:\n\n" - " %s -u libchrome.<version>.so\n" - " arm-linux-gnueabi-objcopy \\\n" - " --remove-section=.android.rel.dyn libchrome.<version>.so\n\n" + " -p, --pad do not shrink relocations, but pad (for debugging)\n\n", + basename); + + if (ELF::kMachine == EM_ARM) { + printf( + "Extracts relative relocations from the .rel.dyn section, packs them\n" + "into a more compact format, and stores the packed relocations in\n" + ".android.rel.dyn. Expands .android.rel.dyn to hold the packed\n" + "data, and shrinks .rel.dyn by the amount of unpacked data removed\n" + "from it.\n\n" + "Before being packed, a shared library needs to be prepared by adding\n" + "a null .android.rel.dyn section.\n\n" + "To pack relocations in a shared library:\n\n" + " echo -n 'NULL' >/tmp/small\n" + " arm-linux-androideabi-objcopy \\\n" + " --add-section .android.rel.dyn=/tmp/small \\\n" + " libchrome.<version>.so\n" + " rm /tmp/small\n" + " %s libchrome.<version>.so\n\n" + "To unpack and restore the shared library to its original state:\n\n" + " %s -u libchrome.<version>.so\n" + " arm-linux-androideabi-objcopy \\\n" + " --remove-section=.android.rel.dyn libchrome.<version>.so\n\n", + basename, basename); + } else if (ELF::kMachine == EM_AARCH64) { + printf( + "Extracts relative relocations from the .rela.dyn section, packs them\n" + "into a more compact format, and stores the packed relocations in\n" + ".android.rela.dyn. Expands .android.rela.dyn to hold the packed\n" + "data, and shrinks .rela.dyn by the amount of unpacked data removed\n" + "from it.\n\n" + "Before being packed, a shared library needs to be prepared by adding\n" + "a null .android.rela.dyn section.\n\n" + "To pack relocations in a shared library:\n\n" + " echo -n 'NULL' >/tmp/small\n" + " aarch64-linux-android-objcopy \\\n" + " --add-section .android.rela.dyn=/tmp/small \\\n" + " libchrome.<version>.so\n" + " rm /tmp/small\n" + " %s libchrome.<version>.so\n\n" + "To unpack and restore the shared library to its original state:\n\n" + " %s -u libchrome.<version>.so\n" + " aarch64-linux-android-objcopy \\\n" + " --remove-section=.android.rela.dyn libchrome.<version>.so\n\n", + basename, basename); + } else { + NOTREACHED(); + } + + printf( "Debug sections are not handled, so packing should not be used on\n" - "shared libraries compiled for debugging or otherwise unstripped.\n", - basename, basename, basename); + "shared libraries compiled for debugging or otherwise unstripped.\n"); } } // namespace @@ -99,6 +131,7 @@ int main(int argc, char* argv[]) { break; default: NOTREACHED(); + return 1; } } if (optind != argc - 1) { diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc index c1d9862..29bec1e 100644 --- a/tools/relocation_packer/src/packer.cc +++ b/tools/relocation_packer/src/packer.cc @@ -7,18 +7,19 @@ #include <vector> #include "debug.h" +#include "delta_encoder.h" #include "elf_traits.h" #include "leb128.h" #include "run_length_encoder.h" +#include "sleb128.h" namespace relocation_packer { -// Pack ARM relative relocations into a run-length encoded packed +// Pack relative relocations into a run-length encoded packed // representation. void RelocationPacker::PackRelativeRelocations( const std::vector<ELF::Rel>& relocations, std::vector<uint8_t>* packed) { - // Run-length encode. std::vector<ELF::Xword> packed_words; RelocationRunLengthCodec codec; @@ -45,17 +46,18 @@ void RelocationPacker::PackRelativeRelocations( packed->push_back(0); } -// Unpack ARM relative relocations from a run-length encoded packed +// Unpack relative relocations from a run-length encoded packed // representation. void RelocationPacker::UnpackRelativeRelocations( const std::vector<uint8_t>& packed, std::vector<ELF::Rel>* relocations) { - // LEB128 decode, after checking and stripping "APR1" prefix. std::vector<ELF::Xword> packed_words; Leb128Decoder decoder(packed); - CHECK(decoder.Dequeue() == 'A' && decoder.Dequeue() == 'P' && - decoder.Dequeue() == 'R' && decoder.Dequeue() == '1'); + CHECK(decoder.Dequeue() == 'A' && + decoder.Dequeue() == 'P' && + decoder.Dequeue() == 'R' && + decoder.Dequeue() == '1'); decoder.DequeueAll(&packed_words); // Run-length decode. @@ -63,4 +65,60 @@ void RelocationPacker::UnpackRelativeRelocations( codec.Decode(packed_words, relocations); } +// Pack relative relocations with addends into a delta encoded packed +// representation. +void RelocationPacker::PackRelativeRelocations( + const std::vector<ELF::Rela>& relocations, + std::vector<uint8_t>* packed) { + // Delta encode. + std::vector<ELF::Sxword> packed_words; + RelocationDeltaCodec codec; + codec.Encode(relocations, &packed_words); + + // If insufficient data to delta encode, do nothing. + if (packed_words.empty()) + return; + + // Signed LEB128 encode, with "APA1" prefix. ASCII does not encode as + // itself under signed LEB128, so we have to treat it specially. + Sleb128Encoder encoder; + encoder.EnqueueAll(packed_words); + std::vector<uint8_t> encoded; + encoder.GetEncoding(&encoded); + + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('A'); + packed->push_back('1'); + packed->insert(packed->end(), encoded.begin(), encoded.end()); + + // Pad packed to a whole number of words. This padding will decode as + // signed LEB128 zeroes. Delta decoding ignores it because encoding + // embeds the pairs count in the stream itself. + while (packed->size() % sizeof(ELF::Word)) + packed->push_back(0); +} + +// Unpack relative relocations with addends from a delta encoded +// packed representation. +void RelocationPacker::UnpackRelativeRelocations( + const std::vector<uint8_t>& packed, + std::vector<ELF::Rela>* relocations) { + // Check "APA1" prefix. + CHECK(packed.at(0) == 'A' && + packed.at(1) == 'P' && + packed.at(2) == 'A' && + packed.at(3) == '1'); + + // Signed LEB128 decode, after stripping "APA1" prefix. + std::vector<ELF::Sxword> packed_words; + std::vector<uint8_t> stripped(packed.begin() + 4, packed.end()); + Sleb128Decoder decoder(stripped); + decoder.DequeueAll(&packed_words); + + // Delta decode. + RelocationDeltaCodec codec; + codec.Decode(packed_words, relocations); +} + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h index 21e4de1..db09ce8 100644 --- a/tools/relocation_packer/src/packer.h +++ b/tools/relocation_packer/src/packer.h @@ -2,21 +2,44 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Pack ARM relative relocations into a more compact form. +// 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 ARM relative relocations into a much smaller set +// 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 compacts the result of run-length encoding. +// 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 might look something like: +// 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_ @@ -29,21 +52,25 @@ namespace relocation_packer { -// A RelocationPacker packs vectors of ARM relative relocations into more +// A RelocationPacker packs vectors of relative relocations into more // compact forms, and unpacks them to reproduce the pre-packed data. class RelocationPacker { public: - // Pack ARM relative relocations into a more compact form. - // |relocations| is a vector of ARM relative relocation structs. + // Pack relative relocations into a more compact form. + // |relocations| is a vector of relative relocation structs. // |packed| is the vector of packed bytes into which relocations are packed. static void PackRelativeRelocations(const std::vector<ELF::Rel>& relocations, std::vector<uint8_t>* packed); + static void PackRelativeRelocations(const std::vector<ELF::Rela>& relocations, + std::vector<uint8_t>* packed); - // Unpack ARM relative relocations from their more compact form. + // Unpack relative relocations from their more compact form. // |packed| is the vector of packed relocations. - // |relocations| is a vector of unpacked ARM relative relocation structs. + // |relocations| is a vector of unpacked relative relocation structs. static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed, std::vector<ELF::Rel>* relocations); + static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed, + std::vector<ELF::Rela>* relocations); }; } // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc index ac70caa3..de5be79 100644 --- a/tools/relocation_packer/src/packer_unittest.cc +++ b/tools/relocation_packer/src/packer_unittest.cc @@ -24,11 +24,30 @@ bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) { ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode; } +void AddRelocation(ELF::Addr addr, + ELF::Sxword addend, + std::vector<ELF::Rela>* relocations) { + ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, + ELF::Sxword addend, + const ELF::Rela& relocation) { + return relocation.r_offset == addr && + ELF_R_SYM(relocation.r_info) == 0 && + ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode && + relocation.r_addend == addend; +} + } // namespace namespace relocation_packer { -TEST(Packer, Pack) { +TEST(Packer, PackRel) { std::vector<ELF::Rel> relocations; std::vector<uint8_t> packed; @@ -72,7 +91,7 @@ TEST(Packer, Pack) { EXPECT_EQ(0, packed[15]); } -TEST(Packer, Unpack) { +TEST(Packer, UnpackRel) { std::vector<uint8_t> packed; std::vector<ELF::Rel> relocations; @@ -116,4 +135,116 @@ TEST(Packer, Unpack) { EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5])); } +TEST(Packer, PackRela) { + std::vector<ELF::Rela> relocations; + std::vector<uint8_t> packed; + + RelocationPacker packer; + + // Initial relocation. + AddRelocation(0xd1ce0000, 10000, &relocations); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + AddRelocation(0xd1ce0004, 10012, &relocations); + AddRelocation(0xd1ce0008, 10024, &relocations); + // Three more relocations, 8 byte deltas, -24 byte addend deltas. + AddRelocation(0xd1ce0010, 10000, &relocations); + AddRelocation(0xd1ce0018, 9976, &relocations); + AddRelocation(0xd1ce0020, 9952, &relocations); + + packed.clear(); + packer.PackRelativeRelocations(relocations, &packed); + + EXPECT_EQ(24, packed.size()); + // Identifier. + EXPECT_EQ('A', packed[0]); + EXPECT_EQ('P', packed[1]); + EXPECT_EQ('A', packed[2]); + EXPECT_EQ('1', packed[3]); + // Delta pairs count. + EXPECT_EQ(6, packed[4]); + // 0xd1ce0000 + EXPECT_EQ(128, packed[5]); + EXPECT_EQ(128, packed[6]); + EXPECT_EQ(184, packed[7]); + EXPECT_EQ(142, packed[8]); + EXPECT_EQ(13, packed[9]); + // 10000 + EXPECT_EQ(144, packed[10]); + EXPECT_EQ(206, packed[11]); + EXPECT_EQ(0, packed[12]); + // 4, 12 + EXPECT_EQ(4, packed[13]); + EXPECT_EQ(12, packed[14]); + // 4, 12 + EXPECT_EQ(4, packed[15]); + EXPECT_EQ(12, packed[16]); + // 8, -24 + EXPECT_EQ(8, packed[17]); + EXPECT_EQ(104, packed[18]); + // 8, -24 + EXPECT_EQ(8, packed[19]); + EXPECT_EQ(104, packed[20]); + // 8, -24 + EXPECT_EQ(8, packed[21]); + EXPECT_EQ(104, packed[22]); + // Padding. + EXPECT_EQ(0, packed[23]); +} + +TEST(Packer, UnpackRela) { + std::vector<uint8_t> packed; + std::vector<ELF::Rela> relocations; + + RelocationPacker packer; + + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('A'); + packed.push_back('1'); + // Delta pairs count. + packed.push_back(6); + // 0xd1ce0000 + packed.push_back(128); + packed.push_back(128); + packed.push_back(184); + packed.push_back(142); + packed.push_back(13); + // 10000 + packed.push_back(144); + packed.push_back(206); + packed.push_back(0); + // 4, 12 + packed.push_back(4); + packed.push_back(12); + // 4, 12 + packed.push_back(4); + packed.push_back(12); + // 8, -24 + packed.push_back(8); + packed.push_back(104); + // 8, -24 + packed.push_back(8); + packed.push_back(104); + // 8, -24 + packed.push_back(8); + packed.push_back(104); + // Padding. + packed.push_back(0); + + relocations.clear(); + packer.UnpackRelativeRelocations(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 10000, relocations[0])); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 10012, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 10024, relocations[2])); + // Three more relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 10000, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 9976, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 9952, relocations[5])); +} + } // namespace relocation_packer diff --git a/tools/relocation_packer/src/run_length_encoder.cc b/tools/relocation_packer/src/run_length_encoder.cc index 2d68bf4..2f2e1c3 100644 --- a/tools/relocation_packer/src/run_length_encoder.cc +++ b/tools/relocation_packer/src/run_length_encoder.cc @@ -14,18 +14,22 @@ namespace relocation_packer { namespace { // Generate a vector of deltas between the r_offset fields of adjacent -// ARM relative relocations. +// relative relocations. void GetDeltas(const std::vector<ELF::Rel>& relocations, std::vector<ELF::Addr>* deltas) { CHECK(relocations.size() >= 2); for (size_t i = 0; i < relocations.size() - 1; ++i) { - const ELF::Addr first = relocations[i].r_offset; - const ELF::Addr second = relocations[i + 1].r_offset; + const ELF::Rel* first = &relocations[i]; + CHECK(ELF_R_TYPE(first->r_info) == ELF::kRelativeRelocationCode); + + const ELF::Rel* second = &relocations[i + 1]; + CHECK(ELF_R_TYPE(second->r_info) == ELF::kRelativeRelocationCode); + // Requires that offsets are 'strictly increasing'. The packing // algorithm fails if this does not hold. - CHECK(second > first); - deltas->push_back(second - first); + CHECK(second->r_offset > first->r_offset); + deltas->push_back(second->r_offset - first->r_offset); } } @@ -92,7 +96,7 @@ void Uncondense(ELF::Addr addr, } // namespace -// Encode ARM relative relocations into a run-length encoded (packed) +// Encode relative relocations into a run-length encoded (packed) // representation. void RelocationRunLengthCodec::Encode(const std::vector<ELF::Rel>& relocations, std::vector<ELF::Xword>* packed) { @@ -116,7 +120,7 @@ void RelocationRunLengthCodec::Encode(const std::vector<ELF::Rel>& relocations, packed->at(0) = (packed->size() - 2) >> 1; } -// Decode ARM relative relocations from a run-length encoded (packed) +// Decode relative relocations from a run-length encoded (packed) // representation. void RelocationRunLengthCodec::Decode(const std::vector<ELF::Xword>& packed, std::vector<ELF::Rel>* relocations) { diff --git a/tools/relocation_packer/src/run_length_encoder.h b/tools/relocation_packer/src/run_length_encoder.h index cfb95f9..c7b165b 100644 --- a/tools/relocation_packer/src/run_length_encoder.h +++ b/tools/relocation_packer/src/run_length_encoder.h @@ -2,17 +2,18 @@ // 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 ARM relative relocations. +// Run-length encode and decode relative relocations. // -// ARM relative relocations are the bulk of dynamic relocations (the -// .rel.dyn section) in libchrome<version>.so, and the ELF standard -// representation of them is wasteful. +// Relative relocations are the bulk of dynamic relocations (the +// .rel.dyn or .rela.dyn sections) in libchrome<version>.so, and the ELF +// standard representation of them is wasteful. .rel.dyn contains +// relocations without addends, .rela.dyn relocations with addends. // -// A relocation is 8 bytes (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 always 0x17. Example, -// from 'readelf -x4 libchrome.<version>.so': +// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes +// on 64 bit plaforms, split into offset and info fields. Offsets strictly +// increase, and each is commonly a few bytes different from its predecessor. +// There are long runs where the difference does not change. The info field +// is constant. Example, from 'readelf -x4 libchrome.<version>.so' 32 bit: // // offset info offset info // 808fef01 17000000 848fef01 17000000 ................ @@ -25,26 +26,26 @@ // by an encoded count of 6 and offset difference of 4: // // start count diff -// 808fef01 00000006 00000004 +// 01ef8f80 00000006 00000004 // -// Because ARM relative relocation offsets strictly increase, the complete -// set of ARM relative relocations in libchrome.<version>.so can be +// Because relative relocation offsets strictly increase, the complete +// set of relative relocations in libchrome.<version>.so can be // represented by a single start address followed by one or more difference // and count encoded word pairs: // // start run1 count run1 diff run2 count run2 diff -// 808fef01 00000006 00000004 00000010 00000008 ... +// 01ef8f80 00000006 00000004 00000010 00000008 ... // -// Decoding regenerates ARM relative relocations beginning at address +// 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 ARM relative relocation. +// 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 ARM relative relocations vector +// count pairs. A final run-length encoded relative relocations vector // might therefore look something like: // // pairs start run 1 run 2 ... run 15 -// 0000000f 808fef01 00000006 00000004 00000010 00000008 ... +// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ... // Interpreted as: // pairs=15 start=.. count=6,delta=4 count=16,delta=8 @@ -58,19 +59,19 @@ namespace relocation_packer { -// A RelocationRunLengthCodec packs vectors of ARM relative relocations +// A RelocationRunLengthCodec packs vectors of relative relocations // into more compact forms, and unpacks them to reproduce the pre-packed data. class RelocationRunLengthCodec { public: - // Encode ARM relative relocations into a more compact form. - // |relocations| is a vector of ARM relative relocation structs. + // Encode relative relocations into a more compact form. + // |relocations| is a vector of relative relocation structs. // |packed| is the vector of packed words into which relocations are packed. static void Encode(const std::vector<ELF::Rel>& relocations, std::vector<ELF::Xword>* packed); - // Decode ARM relative relocations from their more compact form. + // Decode relative relocations from their more compact form. // |packed| is the vector of packed relocations. - // |relocations| is a vector of unpacked ARM relative relocation structs. + // |relocations| is a vector of unpacked relative relocation structs. static void Decode(const std::vector<ELF::Xword>& packed, std::vector<ELF::Rel>* relocations); }; diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc new file mode 100644 index 0000000..a10bd79 --- /dev/null +++ b/tools/relocation_packer/src/sleb128.cc @@ -0,0 +1,95 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sleb128.h" + +#include <limits.h> +#include <stdint.h> +#include <vector> + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +Sleb128Encoder::Sleb128Encoder() { } +Sleb128Encoder::~Sleb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. The +// value is sign extended up to a multiple of 7 bits (ensuring that the +// most significant bit is zero for a positive number and one for a +// negative number). +void Sleb128Encoder::Enqueue(ELF::Sxword value) { + static const size_t size = CHAR_BIT * sizeof(value); + + bool more = true; + const bool negative = value < 0; + + while (more) { + uint8_t byte = value & 127; + value >>= 7; + + // Sign extend if encoding a -ve value. + if (negative) + value |= -(static_cast<ELF::Sxword>(1) << (size - 7)); + + // The sign bit of byte is second high order bit. + const bool sign_bit = byte & 64; + if ((value == 0 && !sign_bit) || (value == -1 && sign_bit)) + more = false; + else + byte |= 128; + encoding_.push_back(byte); + } +} + +// Add a vector of values to the encoding. +void Sleb128Encoder::EnqueueAll(const std::vector<ELF::Sxword>& values) { + for (size_t i = 0; i < values.size(); ++i) + Enqueue(values[i]); +} + +// Create a new decoder for the given encoded stream. +Sleb128Decoder::Sleb128Decoder(const std::vector<uint8_t>& encoding) { + encoding_ = encoding; + cursor_ = 0; +} + +// Empty destructor to silence chromium-style. +Sleb128Decoder::~Sleb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Consume bytes +// until one without its most significant bit is found, and re-form the +// value from the 7 bit fields of the bytes consumed. +ELF::Sxword Sleb128Decoder::Dequeue() { + ELF::Sxword value = 0; + static const size_t size = CHAR_BIT * sizeof(value); + + size_t shift = 0; + uint8_t byte; + + // Loop until we reach a byte with its high order bit clear. + do { + byte = encoding_[cursor_++]; + value |= (static_cast<ELF::Sxword>(byte & 127) << shift); + shift += 7; + } while (byte & 128); + + // The sign bit is second high order bit of the final byte decoded. + // Sign extend if value is -ve and we did not shift all of it. + if (shift < size && (byte & 64)) + value |= -(static_cast<ELF::Sxword>(1) << shift); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +void Sleb128Decoder::DequeueAll(std::vector<ELF::Sxword>* values) { + while (cursor_ < encoding_.size()) + values->push_back(Dequeue()); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h new file mode 100644 index 0000000..3544543 --- /dev/null +++ b/tools/relocation_packer/src/sleb128.h @@ -0,0 +1,75 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// SLEB128 encoder and decoder for packed relative relocations. +// +// Delta encoded relative relocations consist of a large number +// of pairs signed integer values, many with small values. Encoding these +// as signed LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ + +#include <stdint.h> +#include <unistd.h> +#include <vector> + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a signed LEB128 byte stream. +class Sleb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Sleb128Encoder(); + ~Sleb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the signed int to add. + void Enqueue(ELF::Sxword value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of signed ints to add. + void EnqueueAll(const std::vector<ELF::Sxword>& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector<uint8_t> encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +class Sleb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Sleb128Decoder(const std::vector<uint8_t>& encoding); + + // Explicit (but empty) destructor, for chromium-style. + ~Sleb128Decoder(); + + // Retrieve the next value from the encoded stream. + ELF::Sxword Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector<ELF::Sxword>* values); + + private: + // Encoded LEB128 stream. + std::vector<uint8_t> encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ diff --git a/tools/relocation_packer/src/sleb128_unittest.cc b/tools/relocation_packer/src/sleb128_unittest.cc new file mode 100644 index 0000000..60a5d0d --- /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 <vector> +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Sleb128, Encoder) { + std::vector<ELF::Sxword> values; + values.push_back(624485); + values.push_back(0); + values.push_back(1); + values.push_back(63); + values.push_back(64); + values.push_back(-1); + values.push_back(-624485); + + Sleb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(2147483647); + encoder.Enqueue(-2147483648); + encoder.Enqueue(9223372036854775807ll); + encoder.Enqueue(-9223372036854775807ll - 1); + + std::vector<uint8_t> encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(42u, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 63 + EXPECT_EQ(0x3f, encoding[5]); + // 64 + EXPECT_EQ(0xc0, encoding[6]); + EXPECT_EQ(0x00, encoding[7]); + // -1 + EXPECT_EQ(0x7f, encoding[8]); + // -624485 + EXPECT_EQ(0x9b, encoding[9]); + EXPECT_EQ(0xf1, encoding[10]); + EXPECT_EQ(0x59, encoding[11]); + // 2147483647 + EXPECT_EQ(0xff, encoding[12]); + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0x07, encoding[16]); + // -2147483648 + EXPECT_EQ(0x80, encoding[17]); + EXPECT_EQ(0x80, encoding[18]); + EXPECT_EQ(0x80, encoding[19]); + EXPECT_EQ(0x80, encoding[20]); + EXPECT_EQ(0x78, encoding[21]); + // 9223372036854775807 + EXPECT_EQ(0xff, encoding[22]); + EXPECT_EQ(0xff, encoding[23]); + EXPECT_EQ(0xff, encoding[24]); + EXPECT_EQ(0xff, encoding[25]); + EXPECT_EQ(0xff, encoding[26]); + EXPECT_EQ(0xff, encoding[27]); + EXPECT_EQ(0xff, encoding[28]); + EXPECT_EQ(0xff, encoding[29]); + EXPECT_EQ(0xff, encoding[30]); + EXPECT_EQ(0x00, encoding[31]); + // -9223372036854775808 + EXPECT_EQ(0x80, encoding[32]); + EXPECT_EQ(0x80, encoding[33]); + EXPECT_EQ(0x80, encoding[34]); + EXPECT_EQ(0x80, encoding[35]); + EXPECT_EQ(0x80, encoding[36]); + EXPECT_EQ(0x80, encoding[37]); + EXPECT_EQ(0x80, encoding[38]); + EXPECT_EQ(0x80, encoding[39]); + EXPECT_EQ(0x80, encoding[40]); + EXPECT_EQ(0x7f, encoding[41]); +} + +TEST(Sleb128, Decoder) { + std::vector<uint8_t> encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 63 + encoding.push_back(0x3f); + // 64 + encoding.push_back(0xc0); + encoding.push_back(0x00); + // -1 + encoding.push_back(0x7f); + // -624485 + encoding.push_back(0x9b); + encoding.push_back(0xf1); + encoding.push_back(0x59); + // 2147483647 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x07); + // -2147483648 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x78); + // 9223372036854775807 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x00); + // -9223372036854775808 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x7f); + + Sleb128Decoder decoder(encoding); + + EXPECT_EQ(624485, decoder.Dequeue()); + + std::vector<ELF::Sxword> dequeued; + decoder.DequeueAll(&dequeued); + + EXPECT_EQ(10u, dequeued.size()); + EXPECT_EQ(0, dequeued[0]); + EXPECT_EQ(1, dequeued[1]); + EXPECT_EQ(63, dequeued[2]); + EXPECT_EQ(64, dequeued[3]); + EXPECT_EQ(-1, dequeued[4]); + EXPECT_EQ(-624485, dequeued[5]); + EXPECT_EQ(2147483647, dequeued[6]); + EXPECT_EQ(-2147483648, dequeued[7]); + EXPECT_EQ(9223372036854775807ll, dequeued[8]); + EXPECT_EQ(-9223372036854775807ll - 1, dequeued[9]); +} + +} // namespace relocation_packer diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc index 794c284..ddb4add2 100644 --- a/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc +++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc @@ -5,26 +5,8 @@ // Test data for packing/unpacking. When compiled, creates a run of // relative relocations. // -// #!/bin/bash -// -// # Compile as an arm shared library. -// /usr/bin/arm-linux-gnueabi-g++ -shared -o /tmp/testdata.so \ -// elf_file_unittest_relocs.cc -// -// # Add a new null .android.rel.dyn section, needed for packing. -// echo 'NULL' >/tmp/small -// /usr/bin/arm-linux-gnueabi-objcopy \ -// --add-section .android.rel.dyn=/tmp/small /tmp/testdata.so -// -// # Create packed and unpacked reference files. -// packer="../../../out_android/Debug/relocation_packer" -// cp /tmp/testdata.so elf_file_unittest_relocs_packed.so -// $packer elf_file_unittest_relocs_packed.so -// cp elf_file_unittest_relocs_packed.so elf_file_unittest_relocs.so -// $packer -u elf_file_unittest_relocs.so -// -// # Clean up. -// rm /tmp/testdata.so /tmp/small +// See generate_elf_file_unittest_relocs.sh for instructions on how to build +// unit test data from this source file. const int i = 0; diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs.so Binary files differdeleted file mode 100755 index ed93f5b..0000000 --- a/tools/relocation_packer/test_data/elf_file_unittest_relocs.so +++ /dev/null diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_packed.so Binary files differdeleted file mode 100755 index 1c4943e1..0000000 --- a/tools/relocation_packer/test_data/elf_file_unittest_relocs_packed.so +++ /dev/null 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 0000000..e71b5cb --- /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 0000000..f90a2f6 --- /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 |