Generalize compression tool
1. One binary for all architectures
2. Generalize (and slightly improve) compression
2.1 works on all relocation types (rela?.dyn section only so far)
2.2 Uses same format to encode ElfW(Rel) as well as ElfW(Rela) tables
Bug: 18051137
Change-Id: I66c95d9076954ca115816fc577d0f5ef274e5e72
diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc
index 3ffccec..20b25ef 100644
--- a/tools/relocation_packer/src/elf_file.cc
+++ b/tools/relocation_packer/src/elf_file.cc
@@ -5,26 +5,10 @@
// Implementation notes:
//
// We need to remove a piece from the ELF shared library. However, we also
-// want to ensure that code and data loads at the same addresses as before
-// packing, so that tools like breakpad can still match up addresses found
-// in any crash dumps with data extracted from the pre-packed version of
-// the shared library.
-//
-// Arranging this means that we have to split one of the LOAD segments into
-// two. Unfortunately, the program headers are located at the very start
-// of the shared library file, so expanding the program header section
-// would cause a lot of consequent changes to files offsets that we don't
-// really want to have to handle.
-//
-// Luckily, though, there is a segment that is always present and always
-// unused on Android; the GNU_STACK segment. What we do is to steal that
-// and repurpose it to be one of the split LOAD segments. We then have to
-// sort LOAD segments by offset to keep the crazy linker happy.
-//
-// All of this takes place in SplitProgramHeadersForHole(), used on packing,
-// and is unraveled on unpacking in CoalesceProgramHeadersForHole(). See
-// commentary on those functions for an example of this segment stealing
-// in action.
+// want to avoid fixing DWARF cfi data and relative relocation addresses.
+// So after packing we shift offets and starting address of the RX segment
+// while preserving code/data vaddrs location.
+// This requires some fixups for symtab/hash/gnu_hash dynamic section addresses.
#include "elf_file.h"
@@ -42,28 +26,29 @@
namespace relocation_packer {
-// Stub identifier written to 'null out' packed data, "NULL".
-static const uint32_t kStubIdentifier = 0x4c4c554eu;
-
// Out-of-band dynamic tags used to indicate the offset and size of the
// 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;
+static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2;
+static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3;
+
+static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4;
+static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5;
+
+static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1;
+static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2;
// Alignment to preserve, in bytes. This must be at least as large as the
// largest d_align and sh_addralign values found in the loaded file.
// Out of caution for RELRO page alignment, we preserve to a complete target
// page. See http://www.airs.com/blog/archives/189.
-static const size_t kPreserveAlignment = 4096;
-
-namespace {
+static constexpr size_t kPreserveAlignment = 4096;
// Get section data. Checks that the section has exactly one data entry,
// so that the section size and the data size are the same. True in
// practice for all sections we resize when packing or unpacking. Done
// by ensuring that a call to elf_getdata(section, data) returns NULL as
// the next data entry.
-Elf_Data* GetSectionData(Elf_Scn* section) {
+static Elf_Data* GetSectionData(Elf_Scn* section) {
Elf_Data* data = elf_getdata(section, NULL);
CHECK(data && elf_getdata(section, data) == NULL);
return data;
@@ -71,9 +56,9 @@
// Rewrite section data. Allocates new data and makes it the data element's
// buffer. Relies on program exit to free allocated data.
-void RewriteSectionData(Elf_Scn* section,
- const void* section_data,
- size_t size) {
+static void RewriteSectionData(Elf_Scn* section,
+ const void* section_data,
+ size_t size) {
Elf_Data* data = GetSectionData(section);
CHECK(size == data->d_size);
uint8_t* area = new uint8_t[size];
@@ -82,7 +67,8 @@
}
// Verbose ELF header logging.
-void VerboseLogElfHeader(const ELF::Ehdr* elf_header) {
+template <typename Ehdr>
+static void VerboseLogElfHeader(const Ehdr* elf_header) {
VLOG(1) << "e_phoff = " << elf_header->e_phoff;
VLOG(1) << "e_shoff = " << elf_header->e_shoff;
VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
@@ -93,8 +79,9 @@
}
// Verbose ELF program header logging.
-void VerboseLogProgramHeader(size_t program_header_index,
- const ELF::Phdr* program_header) {
+template <typename Phdr>
+static void VerboseLogProgramHeader(size_t program_header_index,
+ const Phdr* program_header) {
std::string type;
switch (program_header->p_type) {
case PT_NULL: type = "NULL"; break;
@@ -118,17 +105,19 @@
}
// Verbose ELF section header logging.
-void VerboseLogSectionHeader(const std::string& section_name,
- const ELF::Shdr* section_header) {
+template <typename Shdr>
+static void VerboseLogSectionHeader(const std::string& section_name,
+ const Shdr* section_header) {
VLOG(1) << "section " << section_name;
VLOG(1) << " sh_addr = " << section_header->sh_addr;
VLOG(1) << " sh_offset = " << section_header->sh_offset;
VLOG(1) << " sh_size = " << section_header->sh_size;
+ VLOG(1) << " sh_entsize = " << section_header->sh_entsize;
VLOG(1) << " sh_addralign = " << section_header->sh_addralign;
}
// Verbose ELF section data logging.
-void VerboseLogSectionData(const Elf_Data* data) {
+static void VerboseLogSectionData(const Elf_Data* data) {
VLOG(1) << " data";
VLOG(1) << " d_buf = " << data->d_buf;
VLOG(1) << " d_off = " << data->d_off;
@@ -136,12 +125,11 @@
VLOG(1) << " d_align = " << data->d_align;
}
-} // namespace
-
// Load the complete ELF file into a memory image in libelf, and identify
// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or
// .android.rela.dyn sections. No-op if the ELF file has already been loaded.
-bool ElfFile::Load() {
+template <typename ELF>
+bool ElfFile<ELF>::Load() {
if (elf_)
return true;
@@ -153,15 +141,12 @@
return false;
}
- ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ auto elf_header = ELF::getehdr(elf);
if (!elf_header) {
LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno());
return false;
}
- if (elf_header->e_machine != ELF::kMachine) {
- LOG(ERROR) << "ELF file architecture is not " << ELF::Machine();
- return false;
- }
+
if (elf_header->e_type != ET_DYN) {
LOG(ERROR) << "ELF file is not a shared object";
return false;
@@ -173,19 +158,16 @@
CHECK(endian == ELFDATA2LSB);
CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
- // Also require that the file class is as expected.
const int file_class = elf_header->e_ident[EI_CLASS];
- CHECK(file_class == ELF::kFileClass);
-
VLOG(1) << "endian = " << endian << ", file class = " << file_class;
VerboseLogElfHeader(elf_header);
- const ELF::Phdr* elf_program_header = ELF::getphdr(elf);
- CHECK(elf_program_header);
+ auto elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header != nullptr);
- const ELF::Phdr* dynamic_program_header = NULL;
+ const typename ELF::Phdr* dynamic_program_header = NULL;
for (size_t i = 0; i < elf_header->e_phnum; ++i) {
- const ELF::Phdr* program_header = &elf_program_header[i];
+ auto program_header = &elf_program_header[i];
VerboseLogProgramHeader(i, program_header);
if (program_header->p_type == PT_DYNAMIC) {
@@ -193,7 +175,7 @@
dynamic_program_header = program_header;
}
}
- CHECK(dynamic_program_header != NULL);
+ CHECK(dynamic_program_header != nullptr);
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
@@ -201,9 +183,8 @@
// 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;
+ Elf_Scn* found_relocations_section = nullptr;
+ Elf_Scn* found_dynamic_section = nullptr;
// Notes of relocation section types seen. We require one or the other of
// these; both is unsupported.
@@ -211,16 +192,16 @@
bool has_rela_relocations = false;
Elf_Scn* section = NULL;
- while ((section = elf_nextscn(elf, section)) != NULL) {
- const ELF::Shdr* section_header = ELF::getshdr(section);
+ while ((section = elf_nextscn(elf, section)) != nullptr) {
+ auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
VerboseLogSectionHeader(name, section_header);
// Note relocation section types.
- if (section_header->sh_type == SHT_REL) {
+ if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) {
has_rel_relocations = true;
}
- if (section_header->sh_type == SHT_RELA) {
+ if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) {
has_rela_relocations = true;
}
@@ -229,10 +210,7 @@
section_header->sh_size > 0) {
found_relocations_section = section;
}
- if ((name == ".android.rel.dyn" || name == ".android.rela.dyn") &&
- section_header->sh_size > 0) {
- found_android_relocations_section = section;
- }
+
if (section_header->sh_offset == dynamic_program_header->p_offset) {
found_dynamic_section = section;
}
@@ -252,12 +230,6 @@
LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section";
return false;
}
- if (!found_android_relocations_section) {
- LOG(ERROR) << "Missing or empty .android.rel.dyn or .android.rela.dyn "
- << "section (to fix, run with --help and follow the "
- << "pre-packing instructions)";
- return false;
- }
if (!found_dynamic_section) {
LOG(ERROR) << "Missing .dynamic section";
return false;
@@ -277,17 +249,15 @@
elf_ = elf;
relocations_section_ = found_relocations_section;
dynamic_section_ = found_dynamic_section;
- android_relocations_section_ = found_android_relocations_section;
relocations_type_ = has_rel_relocations ? REL : RELA;
return true;
}
-namespace {
-
// Helper for ResizeSection(). Adjust the main ELF header for the hole.
-void AdjustElfHeaderForHole(ELF::Ehdr* elf_header,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
if (elf_header->e_phoff > hole_start) {
elf_header->e_phoff += hole_size;
VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
@@ -299,437 +269,119 @@
}
// Helper for ResizeSection(). Adjust all section headers for the hole.
-void AdjustSectionHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+static void AdjustSectionHeadersForHole(Elf* elf,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
size_t string_index;
elf_getshdrstrndx(elf, &string_index);
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != NULL) {
- ELF::Shdr* section_header = ELF::getshdr(section);
+ auto section_header = ELF::getshdr(section);
std::string name = elf_strptr(elf, string_index, section_header->sh_name);
if (section_header->sh_offset > hole_start) {
section_header->sh_offset += hole_size;
VLOG(1) << "section " << name
<< " sh_offset adjusted to " << section_header->sh_offset;
+ } else {
+ section_header->sh_addr -= hole_size;
+ VLOG(1) << "section " << name
+ << " sh_addr adjusted to " << section_header->sh_addr;
}
}
}
// Helper for ResizeSection(). Adjust the offsets of any program headers
// that have offsets currently beyond the hole start.
-void AdjustProgramHeaderOffsets(ELF::Phdr* program_headers,
- size_t count,
- ELF::Phdr* ignored_1,
- ELF::Phdr* ignored_2,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header == ignored_1 || program_header == ignored_2)
- continue;
+ typename ELF::Phdr* program_header = &program_headers[i];
if (program_header->p_offset > hole_start) {
// The hole start is past this segment, so adjust offset.
program_header->p_offset += hole_size;
VLOG(1) << "phdr[" << i
<< "] p_offset adjusted to "<< program_header->p_offset;
+ } else {
+ program_header->p_vaddr -= hole_size;
+ program_header->p_paddr -= hole_size;
+ VLOG(1) << "phdr[" << i
+ << "] p_vaddr adjusted to "<< program_header->p_vaddr
+ << "; p_paddr adjusted to "<< program_header->p_paddr;
}
}
}
// Helper for ResizeSection(). Find the first loadable segment in the
// file. We expect it to map from file offset zero.
-ELF::Phdr* FindFirstLoadSegment(ELF::Phdr* program_headers,
- size_t count) {
- ELF::Phdr* first_loadable_segment = NULL;
-
+template <typename ELF>
+static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start) {
for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
+ typename ELF::Phdr* program_header = &program_headers[i];
if (program_header->p_type == PT_LOAD &&
- program_header->p_offset == 0 &&
- program_header->p_vaddr == 0 &&
- program_header->p_paddr == 0) {
- first_loadable_segment = program_header;
+ program_header->p_offset <= hole_start &&
+ (program_header->p_offset + program_header->p_filesz) >= hole_start ) {
+ return program_header;
}
}
- LOG_IF(FATAL, !first_loadable_segment)
- << "Cannot locate a LOAD segment with address and offset zero";
+ LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start;
+ NOTREACHED();
- return first_loadable_segment;
+ return nullptr;
}
-// Helper for ResizeSection(). Find the PT_GNU_STACK segment, and check
-// that it contains what we expect so we can restore it on unpack if needed.
-ELF::Phdr* FindUnusedGnuStackSegment(ELF::Phdr* program_headers,
- size_t count) {
- ELF::Phdr* unused_segment = NULL;
-
- for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header->p_type == PT_GNU_STACK &&
- program_header->p_offset == 0 &&
- program_header->p_vaddr == 0 &&
- program_header->p_paddr == 0 &&
- program_header->p_filesz == 0 &&
- program_header->p_memsz == 0 &&
- program_header->p_flags == (PF_R | PF_W) &&
- program_header->p_align == ELF::kGnuStackSegmentAlignment) {
- unused_segment = program_header;
- }
- }
- LOG_IF(FATAL, !unused_segment)
- << "Cannot locate the expected GNU_STACK segment";
-
- return unused_segment;
-}
-
-// Helper for ResizeSection(). Find the segment that was the first loadable
-// one before we split it into two. This is the one into which we coalesce
-// the split segments on unpacking.
-ELF::Phdr* FindOriginalFirstLoadSegment(ELF::Phdr* program_headers,
- size_t count) {
- const ELF::Phdr* first_loadable_segment =
- FindFirstLoadSegment(program_headers, count);
-
- ELF::Phdr* original_first_loadable_segment = NULL;
-
- for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- // The original first loadable segment is the one that follows on from
- // the one we wrote on split to be the current first loadable segment.
- if (program_header->p_type == PT_LOAD &&
- program_header->p_offset == first_loadable_segment->p_filesz) {
- original_first_loadable_segment = program_header;
- }
- }
- LOG_IF(FATAL, !original_first_loadable_segment)
- << "Cannot locate the LOAD segment that follows a LOAD at offset zero";
-
- return original_first_loadable_segment;
-}
-
-// Helper for ResizeSection(). Find the segment that contains the hole.
-Elf_Scn* FindSectionContainingHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- Elf_Scn* section = NULL;
- Elf_Scn* last_unholed_section = NULL;
-
- while ((section = elf_nextscn(elf, section)) != NULL) {
- const ELF::Shdr* section_header = ELF::getshdr(section);
-
- // Because we get here after section headers have been adjusted for the
- // hole, we need to 'undo' that adjustment to give a view of the original
- // sections layout.
- ELF::Off offset = section_header->sh_offset;
- if (section_header->sh_offset >= hole_start) {
- offset -= hole_size;
- }
-
- if (offset <= hole_start) {
- last_unholed_section = section;
- }
- }
- LOG_IF(FATAL, !last_unholed_section)
- << "Cannot identify the section before the one containing the hole";
-
- // The section containing the hole is the one after the last one found
- // by the loop above.
- Elf_Scn* holed_section = elf_nextscn(elf, last_unholed_section);
- LOG_IF(FATAL, !holed_section)
- << "Cannot identify the section containing the hole";
-
- return holed_section;
-}
-
-// Helper for ResizeSection(). Find the last section contained in a segment.
-Elf_Scn* FindLastSectionInSegment(Elf* elf,
- ELF::Phdr* program_header,
- ELF::Off hole_start,
- ssize_t hole_size) {
- const ELF::Off segment_end =
- program_header->p_offset + program_header->p_filesz;
-
- Elf_Scn* section = NULL;
- Elf_Scn* last_section = NULL;
-
- while ((section = elf_nextscn(elf, section)) != NULL) {
- const ELF::Shdr* section_header = ELF::getshdr(section);
-
- // As above, 'undo' any section offset adjustment to give a view of the
- // original sections layout.
- ELF::Off offset = section_header->sh_offset;
- if (section_header->sh_offset >= hole_start) {
- offset -= hole_size;
- }
-
- if (offset < segment_end) {
- last_section = section;
- }
- }
- LOG_IF(FATAL, !last_section)
- << "Cannot identify the last section in the given segment";
-
- return last_section;
-}
-
-// Helper for ResizeSection(). Order loadable segments by their offsets.
-// The crazy linker contains assumptions about loadable segment ordering,
-// and it is better if we do not break them.
-void SortOrderSensitiveProgramHeaders(ELF::Phdr* program_headers,
- size_t count) {
- std::vector<ELF::Phdr*> orderable;
-
- // Collect together orderable program headers. These are all the LOAD
- // segments, and any GNU_STACK that may be present (removed on packing,
- // but replaced on unpacking).
- for (size_t i = 0; i < count; ++i) {
- ELF::Phdr* program_header = &program_headers[i];
-
- if (program_header->p_type == PT_LOAD ||
- program_header->p_type == PT_GNU_STACK) {
- orderable.push_back(program_header);
- }
- }
-
- // Order these program headers so that any PT_GNU_STACK is last, and
- // the LOAD segments that precede it appear in offset order. Uses
- // insertion sort.
- for (size_t i = 1; i < orderable.size(); ++i) {
- for (size_t j = i; j > 0; --j) {
- ELF::Phdr* first = orderable[j - 1];
- ELF::Phdr* second = orderable[j];
-
- if (!(first->p_type == PT_GNU_STACK ||
- first->p_offset > second->p_offset)) {
- break;
- }
- std::swap(*first, *second);
- }
- }
-}
-
-// Helper for ResizeSection(). The GNU_STACK program header is unused in
-// Android, so we can repurpose it here. Before packing, the program header
-// table contains something like:
-//
-// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-// LOAD 0x000000 0x00000000 0x00000000 0x1efc818 0x1efc818 R E 0x1000
-// LOAD 0x1efd008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000
-// DYNAMIC 0x205ec50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4
-// GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
-//
-// The hole in the file is in the first of these. In order to preserve all
-// load addresses, what we do is to turn the GNU_STACK into a new LOAD entry
-// that maps segments up to where we created the hole, adjust the first LOAD
-// entry so that it maps segments after that, adjust any other program
-// headers whose offset is after the hole start, and finally order the LOAD
-// segments by offset, to give:
-//
-// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-// LOAD 0x000000 0x00000000 0x00000000 0x14ea4 0x14ea4 R E 0x1000
-// LOAD 0x014ea4 0x00212ea4 0x00212ea4 0x1cea164 0x1cea164 R E 0x1000
-// DYNAMIC 0x1e60c50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4
-// LOAD 0x1cff008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000
-//
-// We work out the split points by finding the .rel.dyn or .rela.dyn section
-// that contains the hole, and by finding the last section in a given segment.
-//
-// To unpack, we reverse the above to leave the file as it was originally.
-void SplitProgramHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- CHECK(hole_size < 0);
- const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+// Helper for ResizeSection(). Rewrite program headers.
+template <typename ELF>
+static void RewriteProgramHeadersForHole(Elf* elf,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
CHECK(elf_header);
- ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header);
const size_t program_header_count = elf_header->e_phnum;
// Locate the segment that we can overwrite to form the new LOAD entry,
// and the segment that we are going to split into two parts.
- ELF::Phdr* spliced_header =
- FindUnusedGnuStackSegment(elf_program_header, program_header_count);
- ELF::Phdr* split_header =
- FindFirstLoadSegment(elf_program_header, program_header_count);
+ typename ELF::Phdr* target_load_header =
+ FindLoadSegmentForHole<ELF>(elf_program_header, program_header_count, hole_start);
- VLOG(1) << "phdr[" << split_header - elf_program_header << "] split";
- VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] new LOAD";
+ VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust";
+ // Adjust PT_LOAD program header memsz and filesz
+ target_load_header->p_filesz += hole_size;
+ target_load_header->p_memsz += hole_size;
- // Find the section that contains the hole. We split on the section that
- // follows it.
- Elf_Scn* holed_section =
- FindSectionContainingHole(elf, hole_start, hole_size);
-
- size_t string_index;
- elf_getshdrstrndx(elf, &string_index);
-
- ELF::Shdr* section_header = ELF::getshdr(holed_section);
- std::string name = elf_strptr(elf, string_index, section_header->sh_name);
- VLOG(1) << "section " << name << " split after";
-
- // Find the last section in the segment we are splitting.
- Elf_Scn* last_section =
- FindLastSectionInSegment(elf, split_header, hole_start, hole_size);
-
- section_header = ELF::getshdr(last_section);
- name = elf_strptr(elf, string_index, section_header->sh_name);
- VLOG(1) << "section " << name << " split end";
-
- // Split on the section following the holed one, and up to (but not
- // including) the section following the last one in the split segment.
- Elf_Scn* split_section = elf_nextscn(elf, holed_section);
- LOG_IF(FATAL, !split_section)
- << "No section follows the section that contains the hole";
- Elf_Scn* end_section = elf_nextscn(elf, last_section);
- LOG_IF(FATAL, !end_section)
- << "No section follows the last section in the segment being split";
-
- // Split the first portion of split_header into spliced_header.
- const ELF::Shdr* split_section_header = ELF::getshdr(split_section);
- spliced_header->p_type = split_header->p_type;
- spliced_header->p_offset = split_header->p_offset;
- spliced_header->p_vaddr = split_header->p_vaddr;
- spliced_header->p_paddr = split_header->p_paddr;
- CHECK(split_header->p_filesz == split_header->p_memsz);
- spliced_header->p_filesz = split_section_header->sh_offset;
- spliced_header->p_memsz = split_section_header->sh_offset;
- spliced_header->p_flags = split_header->p_flags;
- spliced_header->p_align = split_header->p_align;
-
- // Now rewrite split_header to remove the part we spliced from it.
- const ELF::Shdr* end_section_header = ELF::getshdr(end_section);
- split_header->p_offset = spliced_header->p_filesz;
- CHECK(split_header->p_vaddr == split_header->p_paddr);
- split_header->p_vaddr = split_section_header->sh_addr;
- split_header->p_paddr = split_section_header->sh_addr;
- CHECK(split_header->p_filesz == split_header->p_memsz);
- split_header->p_filesz =
- end_section_header->sh_offset - spliced_header->p_filesz;
- split_header->p_memsz =
- end_section_header->sh_offset - spliced_header->p_filesz;
-
- // Adjust the offsets of all program headers that are not one of the pair
- // we just created by splitting.
- AdjustProgramHeaderOffsets(elf_program_header,
- program_header_count,
- spliced_header,
- split_header,
- hole_start,
- hole_size);
-
- // Finally, order loadable segments by offset/address. The crazy linker
- // contains assumptions about loadable segment ordering.
- SortOrderSensitiveProgramHeaders(elf_program_header,
- program_header_count);
-}
-
-// Helper for ResizeSection(). Undo the work of SplitProgramHeadersForHole().
-void CoalesceProgramHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- CHECK(hole_size > 0);
- const ELF::Ehdr* elf_header = ELF::getehdr(elf);
- CHECK(elf_header);
-
- ELF::Phdr* elf_program_header = ELF::getphdr(elf);
- CHECK(elf_program_header);
-
- const size_t program_header_count = elf_header->e_phnum;
-
- // Locate the segment that we overwrote to form the new LOAD entry, and
- // the segment that we split into two parts on packing.
- ELF::Phdr* spliced_header =
- FindFirstLoadSegment(elf_program_header, program_header_count);
- ELF::Phdr* split_header =
- FindOriginalFirstLoadSegment(elf_program_header, program_header_count);
-
- VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] stack";
- VLOG(1) << "phdr[" << split_header - elf_program_header << "] coalesce";
-
- // Find the last section in the second segment we are coalescing.
- Elf_Scn* last_section =
- FindLastSectionInSegment(elf, split_header, hole_start, hole_size);
-
- size_t string_index;
- elf_getshdrstrndx(elf, &string_index);
-
- const ELF::Shdr* section_header = ELF::getshdr(last_section);
- std::string name = elf_strptr(elf, string_index, section_header->sh_name);
- VLOG(1) << "section " << name << " coalesced";
-
- // Rewrite the coalesced segment into split_header.
- const ELF::Shdr* last_section_header = ELF::getshdr(last_section);
- split_header->p_offset = spliced_header->p_offset;
- CHECK(split_header->p_vaddr == split_header->p_paddr);
- split_header->p_vaddr = spliced_header->p_vaddr;
- split_header->p_paddr = spliced_header->p_vaddr;
- CHECK(split_header->p_filesz == split_header->p_memsz);
- split_header->p_filesz =
- last_section_header->sh_offset + last_section_header->sh_size;
- split_header->p_memsz =
- last_section_header->sh_offset + last_section_header->sh_size;
-
- // Reconstruct the original GNU_STACK segment into spliced_header.
- spliced_header->p_type = PT_GNU_STACK;
- spliced_header->p_offset = 0;
- spliced_header->p_vaddr = 0;
- spliced_header->p_paddr = 0;
- spliced_header->p_filesz = 0;
- spliced_header->p_memsz = 0;
- spliced_header->p_flags = PF_R | PF_W;
- spliced_header->p_align = ELF::kGnuStackSegmentAlignment;
-
- // Adjust the offsets of all program headers that are not one of the pair
- // we just coalesced.
- AdjustProgramHeaderOffsets(elf_program_header,
- program_header_count,
- spliced_header,
- split_header,
- hole_start,
- hole_size);
-
- // Finally, order loadable segments by offset/address. The crazy linker
- // contains assumptions about loadable segment ordering.
- SortOrderSensitiveProgramHeaders(elf_program_header,
- program_header_count);
-}
-
-// Helper for ResizeSection(). Rewrite program headers.
-void RewriteProgramHeadersForHole(Elf* elf,
- ELF::Off hole_start,
- ssize_t hole_size) {
- // If hole_size is negative then we are removing a piece of the file, and
- // we want to split program headers so that we keep the same addresses
- // for text and data. If positive, then we are putting that piece of the
- // file back in, so we coalesce the previously split program headers.
- if (hole_size < 0)
- SplitProgramHeadersForHole(elf, hole_start, hole_size);
- else if (hole_size > 0)
- CoalesceProgramHeadersForHole(elf, hole_start, hole_size);
+ // Adjust the offsets and p_vaddrs
+ AdjustProgramHeaderOffsets<ELF>(elf_program_header,
+ program_header_count,
+ hole_start,
+ hole_size);
}
// Helper for ResizeSection(). Locate and return the dynamic section.
-Elf_Scn* GetDynamicSection(Elf* elf) {
- const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+template <typename ELF>
+static Elf_Scn* GetDynamicSection(Elf* elf) {
+ const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
CHECK(elf_header);
- const ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
CHECK(elf_program_header);
// Find the program header that describes the dynamic section.
- const ELF::Phdr* dynamic_program_header = NULL;
+ const typename ELF::Phdr* dynamic_program_header = NULL;
for (size_t i = 0; i < elf_header->e_phnum; ++i) {
- const ELF::Phdr* program_header = &elf_program_header[i];
+ const typename ELF::Phdr* program_header = &elf_program_header[i];
if (program_header->p_type == PT_DYNAMIC) {
dynamic_program_header = program_header;
@@ -741,7 +393,7 @@
Elf_Scn* dynamic_section = NULL;
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf, section)) != NULL) {
- ELF::Shdr* section_header = ELF::getshdr(section);
+ typename ELF::Shdr* section_header = ELF::getshdr(section);
if (section_header->sh_offset == dynamic_program_header->p_offset) {
dynamic_section = section;
@@ -753,47 +405,61 @@
}
// Helper for ResizeSection(). Adjust the .dynamic section for the hole.
-template <typename Rel>
-void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
- ELF::Off hole_start,
- ssize_t hole_size) {
+template <typename ELF>
+void ElfFile<ELF>::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+ typename ELF::Off hole_start,
+ ssize_t hole_size,
+ relocations_type_t relocations_type) {
+ CHECK(relocations_type != NONE);
Elf_Data* data = GetSectionData(dynamic_section);
- const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
- std::vector<ELF::Dyn> dynamics(
+ auto dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
+ if (hole_size > 0) { // expanding
+ hole_start += hole_size;
+ }
+
for (size_t i = 0; i < dynamics.size(); ++i) {
- ELF::Dyn* dynamic = &dynamics[i];
- const ELF::Sword tag = dynamic->d_tag;
+ typename ELF::Dyn* dynamic = &dynamics[i];
+ const typename ELF::Sword tag = dynamic->d_tag;
+
+ // Any tags that hold offsets are adjustment candidates.
+ const bool is_adjustable = (tag == DT_PLTGOT ||
+ tag == DT_HASH ||
+ tag == DT_GNU_HASH ||
+ tag == DT_STRTAB ||
+ tag == DT_SYMTAB ||
+ tag == DT_RELA ||
+ tag == DT_INIT ||
+ tag == DT_FINI ||
+ tag == DT_REL ||
+ tag == DT_JMPREL ||
+ tag == DT_INIT_ARRAY ||
+ tag == DT_FINI_ARRAY ||
+ tag == DT_ANDROID_REL||
+ tag == DT_ANDROID_RELA);
+
+ if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) {
+ dynamic->d_un.d_ptr -= hole_size;
+ VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+ << " d_ptr adjusted to " << dynamic->d_un.d_ptr;
+ }
// DT_RELSZ or DT_RELASZ indicate the overall size of relocations.
// Only one will be present. Adjust by hole size.
- if (tag == DT_RELSZ || tag == DT_RELASZ) {
+ if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) {
dynamic->d_un.d_val += hole_size;
VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
<< " d_val adjusted to " << dynamic->d_un.d_val;
}
- // DT_RELCOUNT or DT_RELACOUNT hold the count of relative relocations.
- // Only one will be present. Packing reduces it to the alignment
- // padding, if any; unpacking restores it to its former value. The
- // crazy linker does not use it, but we update it anyway.
- if (tag == DT_RELCOUNT || tag == DT_RELACOUNT) {
- // 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(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;
- }
+ // Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and
+ // technically (2) the relative relocation count is not changed.
- // DT_RELENT and DT_RELAENT do not change, but make sure they are what
- // we expect. Only one will be present.
- if (tag == DT_RELENT || tag == DT_RELAENT) {
- CHECK(dynamic->d_un.d_val == sizeof(Rel));
- }
+ // DT_RELENT and DT_RELAENT don't change, ignore them as well.
}
void* section_data = &dynamics[0];
@@ -804,19 +470,19 @@
// 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;
+template <typename ELF>
+void ElfFile<ELF>::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
+ typename ELF::Word new_sh_type,
+ relocations_type_t relocations_type) {
- // Note if we are resizing the real dyn relocations.
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_relocations_resize =
- (section_name == ".rel.dyn" || section_name == ".rela.dyn");
+ auto section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+ if (section_header->sh_size == new_size) {
+ return;
+ }
// Require that the section size and the data size are the same. True
// in practice for all sections we resize when packing or unpacking.
@@ -827,61 +493,72 @@
// data that we can validly expand).
CHECK(data->d_size && data->d_buf);
- const ELF::Off hole_start = section_header->sh_offset;
+ const auto hole_start = section_header->sh_offset;
const ssize_t hole_size = new_size - data->d_size;
- VLOG_IF(1, (hole_size > 0)) << "expand section size = " << data->d_size;
- VLOG_IF(1, (hole_size < 0)) << "shrink section size = " << data->d_size;
+ VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " <<
+ data->d_size << " -> " << (data->d_size + hole_size);
+ VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " <<
+ data->d_size << " -> " << (data->d_size + hole_size);
+
+ // libelf overrides sh_entsize for known sh_types, so it does not matter what we set
+ // for SHT_REL/SHT_RELA.
+ typename ELF::Xword new_entsize =
+ (new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0;
+
+ VLOG(1) << "Update section (" << name << ") entry size: " <<
+ section_header->sh_entsize << " -> " << new_entsize;
// Resize the data and the section header.
data->d_size += hole_size;
section_header->sh_size += hole_size;
+ section_header->sh_entsize = new_entsize;
+ section_header->sh_type = new_sh_type;
// Add the hole size to all offsets in the ELF file that are after the
// start of the hole. If the hole size is positive we are expanding the
// section to create a new hole; if negative, we are closing up a hole.
// Start with the main ELF header.
- ELF::Ehdr* elf_header = ELF::getehdr(elf);
- AdjustElfHeaderForHole(elf_header, hole_start, hole_size);
+ typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ AdjustElfHeaderForHole<ELF>(elf_header, hole_start, hole_size);
// Adjust all section headers.
- AdjustSectionHeadersForHole(elf, hole_start, hole_size);
+ AdjustSectionHeadersForHole<ELF>(elf, hole_start, hole_size);
- // If resizing the dynamic relocations, rewrite the program headers to
- // either split or coalesce segments, and adjust dynamic entries to match.
- if (is_relocations_resize) {
- RewriteProgramHeadersForHole(elf, hole_start, hole_size);
+ // Rewrite the program headers to either split or coalesce segments,
+ // and adjust dynamic entries to match.
+ RewriteProgramHeadersForHole<ELF>(elf, hole_start, hole_size);
- Elf_Scn* dynamic_section = GetDynamicSection(elf);
- AdjustDynamicSectionForHole<Rel>(dynamic_section, hole_start, hole_size);
- }
+ Elf_Scn* dynamic_section = GetDynamicSection<ELF>(elf);
+ AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type);
}
// Find the first slot in a dynamics array with the given tag. The array
// always ends with a free (unused) element, and which we exclude from the
// search. Returns dynamics->size() if not found.
-size_t FindDynamicEntry(ELF::Sword tag,
- std::vector<ELF::Dyn>* dynamics) {
+template <typename ELF>
+static size_t FindDynamicEntry(typename ELF::Sword tag,
+ std::vector<typename ELF::Dyn>* dynamics) {
// Loop until the penultimate entry. We exclude the end sentinel.
for (size_t i = 0; i < dynamics->size() - 1; ++i) {
- if (dynamics->at(i).d_tag == tag)
+ if (dynamics->at(i).d_tag == tag) {
return i;
+ }
}
// The tag was not found.
return dynamics->size();
}
-// Replace the first free (unused) slot in a dynamics vector with the given
-// value. The vector always ends with a free (unused) element, so the slot
-// found cannot be the last one in the vector.
-void AddDynamicEntry(const ELF::Dyn& dyn,
- std::vector<ELF::Dyn>* dynamics) {
- const size_t slot = FindDynamicEntry(DT_NULL, dynamics);
+// Replace dynamic entry.
+template <typename ELF>
+static void ReplaceDynamicEntry(typename ELF::Sword tag,
+ const typename ELF::Dyn& dyn,
+ std::vector<typename ELF::Dyn>* dynamics) {
+ const size_t slot = FindDynamicEntry<ELF>(tag, dynamics);
if (slot == dynamics->size()) {
- LOG(FATAL) << "No spare dynamic array slots found "
- << "(to fix, increase gold's --spare-dynamic-tags value)";
+ LOG(FATAL) << "Dynamic slot is not found for tag=" << tag;
}
// Replace this entry with the one supplied.
@@ -889,53 +566,10 @@
VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag;
}
-// Remove the element in the dynamics vector that matches the given tag with
-// unused slot data. Shuffle the following elements up, and ensure that the
-// last is the null sentinel.
-void RemoveDynamicEntry(ELF::Sword tag,
- std::vector<ELF::Dyn>* dynamics) {
- const size_t slot = FindDynamicEntry(tag, dynamics);
- CHECK(slot != dynamics->size());
-
- // Remove this entry by shuffling up everything that follows.
- for (size_t i = slot; i < dynamics->size() - 1; ++i) {
- dynamics->at(i) = dynamics->at(i + 1);
- VLOG(1) << "dynamic[" << i
- << "] overwritten with dynamic[" << i + 1 << "]";
- }
-
- // Ensure that the end sentinel is still present.
- CHECK(dynamics->at(dynamics->size() - 1).d_tag == DT_NULL);
-}
-
-// Construct a null relocation without addend.
-void NullRelocation(ELF::Rel* relocation) {
- relocation->r_offset = 0;
- relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
-}
-
-// Construct a null relocation with addend.
-void NullRelocation(ELF::Rela* relocation) {
- relocation->r_offset = 0;
- relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
- relocation->r_addend = 0;
-}
-
-// Pad relocations with the given number of null entries. Generates its
-// null entry with the appropriate NullRelocation() invocation.
-template <typename Rel>
-void PadRelocations(size_t count, std::vector<Rel>* relocations) {
- Rel null_relocation;
- NullRelocation(&null_relocation);
- std::vector<Rel> padding(count, null_relocation);
- relocations->insert(relocations->end(), padding.begin(), padding.end());
-}
-
-} // namespace
-
// Remove relative entries from dynamic relocations and write as packed
// data into android packed relocations.
-bool ElfFile::PackRelocations() {
+template <typename ELF>
+bool ElfFile<ELF>::PackRelocations() {
// Load the ELF file into libelf.
if (!Load()) {
LOG(ERROR) << "Failed to load as ELF";
@@ -944,175 +578,129 @@
// Retrieve the current dynamic relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
+ // we always pack rela, because packed format is pretty much the same
+ std::vector<typename ELF::Rela> relocations;
if (relocations_type_ == REL) {
// Convert data to a vector of relocations.
- const 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]));
-
+ const typename ELF::Rel* relocations_base = reinterpret_cast<typename ELF::Rel*>(data->d_buf);
+ ConvertRelArrayToRelaVector(relocations_base,
+ data->d_size / sizeof(typename ELF::Rel), &relocations);
LOG(INFO) << "Relocations : REL";
- return PackTypedRelocations<ELF::Rel>(relocations);
- }
-
- if (relocations_type_ == RELA) {
+ } else 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(
+ const typename ELF::Rela* relocations_base = reinterpret_cast<typename ELF::Rela*>(data->d_buf);
+ relocations = std::vector<typename ELF::Rela>(
relocations_base,
relocations_base + data->d_size / sizeof(relocations[0]));
LOG(INFO) << "Relocations : RELA";
- return PackTypedRelocations<ELF::Rela>(relocations);
+ } else {
+ NOTREACHED();
}
- NOTREACHED();
- return false;
+ return PackTypedRelocations(&relocations);
}
// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
-template <typename Rel>
-bool ElfFile::PackTypedRelocations(const std::vector<Rel>& relocations) {
- // Filter relocations into those that are relative and others.
- std::vector<Rel> relative_relocations;
- std::vector<Rel> other_relocations;
+template <typename ELF>
+bool ElfFile<ELF>::PackTypedRelocations(std::vector<typename ELF::Rela>* relocations) {
+ typedef typename ELF::Rela Rela;
- for (size_t i = 0; i < relocations.size(); ++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);
- } else {
- other_relocations.push_back(relocation);
- }
- }
- LOG(INFO) << "Relative : " << relative_relocations.size() << " entries";
- LOG(INFO) << "Other : " << other_relocations.size() << " entries";
- LOG(INFO) << "Total : " << relocations.size() << " entries";
-
- // If no relative relocations then we have nothing packable. Perhaps
+ // If no relocations then we have nothing packable. Perhaps
// the shared object has already been packed?
- if (relative_relocations.empty()) {
- LOG(ERROR) << "No relative relocations found (already packed?)";
+ if (relocations->empty()) {
+ LOG(ERROR) << "No relocations found (already packed?)";
return false;
}
- // If not padding fully, apply only enough padding to preserve alignment.
- // Otherwise, pad so that we do not shrink the relocations section at all.
- if (!is_padding_relocations_) {
- // Calculate the size of the hole we will close up when we rewrite
- // dynamic relocations.
- ssize_t hole_size =
- relative_relocations.size() * sizeof(relative_relocations[0]);
- const ssize_t unaligned_hole_size = hole_size;
+ const size_t rel_size =
+ relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel);
+ const size_t initial_bytes = relocations->size() * rel_size;
- // 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.
- if (hole_size == 0) {
- LOG(INFO) << "Too few relative relocations to pack after alignment";
- return false;
- }
-
- // Find the padding needed in other_relocations to preserve alignment.
- // Ensure that we never completely empty the real relocations section.
- size_t padding_bytes = unaligned_hole_size - hole_size;
- if (padding_bytes == 0 && other_relocations.size() == 0) {
- do {
- padding_bytes += sizeof(relative_relocations[0]);
- } while (padding_bytes % kPreserveAlignment);
- }
- CHECK(padding_bytes % sizeof(other_relocations[0]) == 0);
- const size_t padding = padding_bytes / sizeof(other_relocations[0]);
-
- // Padding may have removed any packing benefit.
- if (padding >= relative_relocations.size()) {
- LOG(INFO) << "Too few relative relocations to pack after padding";
- return false;
- }
-
- // Add null relocations to other_relocations to preserve alignment.
- PadRelocations<Rel>(padding, &other_relocations);
- LOG(INFO) << "Alignment pad : " << padding << " 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
- // the ResizeSection() below a no-op.
- const size_t padding = relocations.size() - other_relocations.size();
- PadRelocations<Rel>(padding, &other_relocations);
- }
-
- // Pack relative relocations.
- const size_t initial_bytes =
- relative_relocations.size() * sizeof(relative_relocations[0]);
- LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes";
+ LOG(INFO) << "Unpacked : " << initial_bytes << " bytes";
std::vector<uint8_t> packed;
- RelocationPacker packer;
- packer.PackRelativeRelocations(relative_relocations, &packed);
- const void* packed_data = &packed[0];
- const size_t packed_bytes = packed.size() * sizeof(packed[0]);
- LOG(INFO) << "Packed relative: " << packed_bytes << " bytes";
+ RelocationPacker<ELF> packer;
- // If we have insufficient relative relocations to form a run then
- // packing fails.
+ // Pack relocations: dry run to estimate memory savings.
+ packer.PackRelocations(*relocations, &packed);
+ const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]);
+ LOG(INFO) << "Packed (no padding): " << packed_bytes_estimate << " bytes";
+
if (packed.empty()) {
- LOG(INFO) << "Too few relative relocations to pack";
+ LOG(INFO) << "Too few relocations to pack";
return false;
}
+ // Pre-calculate the size of the hole we will close up when we rewrite
+ // dynamic relocations. We have to adjust relocation addresses to
+ // account for this.
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+ ssize_t hole_size = initial_bytes - packed_bytes_estimate;
+
+ // hole_size needs to be page_aligned.
+ hole_size -= hole_size % kPreserveAlignment;
+
+ LOG(INFO) << "Compaction : " << hole_size << " bytes";
+
+ // Adjusting for alignment may have removed any packing benefit.
+ if (hole_size == 0) {
+ LOG(INFO) << "Too few relocations to pack after alignment";
+ return false;
+ }
+
+ if (hole_size <= 0) {
+ LOG(INFO) << "Packing relocations saves no space";
+ return false;
+ }
+
+ size_t data_padding_bytes = is_padding_relocations_ ?
+ initial_bytes - packed_bytes_estimate :
+ initial_bytes - hole_size - packed_bytes_estimate;
+
+ // pad data
+ std::vector<uint8_t> padding(data_padding_bytes, 0);
+ packed.insert(packed.end(), padding.begin(), padding.end());
+
+ const void* packed_data = &packed[0];
+
// Run a loopback self-test as a check that packing is lossless.
- std::vector<Rel> unpacked;
- packer.UnpackRelativeRelocations(packed, &unpacked);
- CHECK(unpacked.size() == relative_relocations.size());
+ std::vector<Rela> unpacked;
+ packer.UnpackRelocations(packed, &unpacked);
+ CHECK(unpacked.size() == relocations->size());
CHECK(!memcmp(&unpacked[0],
- &relative_relocations[0],
+ &relocations->at(0),
unpacked.size() * sizeof(unpacked[0])));
- // Make sure packing saved some space.
- if (packed_bytes >= initial_bytes) {
- LOG(INFO) << "Packing relative relocations saves no space";
- return false;
- }
+ // Rewrite the current dynamic relocations section with packed one then shrink it to size.
+ const size_t bytes = packed.size() * sizeof(packed[0]);
+ ResizeSection(elf_, relocations_section_, bytes,
+ relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_);
+ RewriteSectionData(relocations_section_, packed_data, bytes);
- // 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<Rel>(elf_, relocations_section_, bytes);
- RewriteSectionData(relocations_section_, section_data, bytes);
+ // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt
- // Rewrite the current packed android relocations section to hold the packed
- // relative relocations.
- ResizeSection<Rel>(elf_, android_relocations_section_, packed_bytes);
- RewriteSectionData(android_relocations_section_, packed_data, packed_bytes);
-
- // Rewrite .dynamic to include two new tags describing the packed android
+ // Rewrite .dynamic and rename relocation tags describing the packed android
// relocations.
Elf_Data* data = GetSectionData(dynamic_section_);
- const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
- std::vector<ELF::Dyn> dynamics(
+ const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
- // Use two of the spare slots to describe the packed section.
- ELF::Shdr* section_header = ELF::getshdr(android_relocations_section_);
+ section_header = ELF::getshdr(relocations_section_);
{
- ELF::Dyn dyn;
- dyn.d_tag = DT_ANDROID_REL_OFFSET;
- dyn.d_un.d_ptr = section_header->sh_offset;
- AddDynamicEntry(dyn, &dynamics);
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA;
+ dyn.d_un.d_ptr = section_header->sh_addr;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics);
}
{
- ELF::Dyn dyn;
- dyn.d_tag = DT_ANDROID_REL_SIZE;
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ;
dyn.d_un.d_val = section_header->sh_size;
- AddDynamicEntry(dyn, &dynamics);
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics);
}
+
const void* dynamics_data = &dynamics[0];
const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
@@ -1124,15 +712,17 @@
// 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() {
+template <typename ELF>
+bool ElfFile<ELF>::UnpackRelocations() {
// Load the ELF file into libelf.
if (!Load()) {
LOG(ERROR) << "Failed to load as ELF";
return false;
}
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
// Retrieve the current packed android relocations section data.
- Elf_Data* data = GetSectionData(android_relocations_section_);
+ Elf_Data* data = GetSectionData(relocations_section_);
// Convert data to a vector of bytes.
const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf);
@@ -1140,118 +730,94 @@
packed_base,
packed_base + data->d_size / sizeof(packed[0]));
- if (packed.size() > 3 &&
+ if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) &&
+ packed.size() > 3 &&
packed[0] == 'A' &&
packed[1] == 'P' &&
- packed[2] == 'R' &&
- packed[3] == '1') {
- // Signature is APR1, unpack relocations.
- CHECK(relocations_type_ == REL);
- LOG(INFO) << "Relocations : REL";
- return UnpackTypedRelocations<ELF::Rel>(packed);
+ (packed[2] == 'U' || packed[2] == 'S') &&
+ packed[3] == '2') {
+ LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA");
+ } else {
+ LOG(ERROR) << "Packed relocations not found (not packed?)";
+ return false;
}
- 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);
- }
-
- LOG(ERROR) << "Packed relative relocations not found (not packed?)";
- return false;
+ return UnpackTypedRelocations(packed);
}
// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
-template <typename Rel>
-bool ElfFile::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
+template <typename ELF>
+bool ElfFile<ELF>::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
// Unpack the data to re-materialize the relative relocations.
const size_t packed_bytes = packed.size() * sizeof(packed[0]);
- LOG(INFO) << "Packed relative: " << packed_bytes << " bytes";
- 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";
+ LOG(INFO) << "Packed : " << packed_bytes << " bytes";
+ std::vector<typename ELF::Rela> unpacked_relocations;
+ RelocationPacker<ELF> packer;
+ packer.UnpackRelocations(packed, &unpacked_relocations);
+
+ const size_t relocation_entry_size =
+ relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela);
+ const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size;
+ LOG(INFO) << "Unpacked : " << unpacked_bytes << " bytes";
// Retrieve the current dynamic relocations section data.
Elf_Data* data = GetSectionData(relocations_section_);
- // Interpret data as 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<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 Rel& relocation = relocations[i];
- if (ELF_R_TYPE(relocation.r_info) != ELF::kNoRelocationCode) {
- other_relocations.push_back(relocation);
- } else {
- ++padding;
- }
- }
- LOG(INFO) << "Relative : " << relative_relocations.size() << " entries";
- LOG(INFO) << "Other : " << other_relocations.size() << " entries";
+ LOG(INFO) << "Relocations : " << unpacked_relocations.size() << " entries";
// If we found the same number of null relocation entries in the dynamic
// relocations section as we hold as unpacked relative relocations, then
// this is a padded file.
- const bool is_padded = padding == relative_relocations.size();
- // Unless padded, report by how much we expand the file.
+ const bool is_padded = packed_bytes == unpacked_bytes;
+
+ // Unless padded, pre-apply relative relocations to account for the
+ // hole, and pre-adjust all relocation offsets accordingly.
+ typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+
if (!is_padded) {
- // Calculate the size of the hole we will open up when we rewrite
- // dynamic relocations.
- 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";
+ LOG(INFO) << "Expansion : " << unpacked_bytes - packed_bytes << " bytes";
}
- // 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<Rel>(elf_, relocations_section_, bytes);
- RewriteSectionData(relocations_section_, section_data, bytes);
+ // Rewrite the current dynamic relocations section with unpacked version of
+ // relocations.
+ const void* section_data = nullptr;
+ std::vector<typename ELF::Rel> unpacked_rel_relocations;
+ if (relocations_type_ == RELA) {
+ section_data = &unpacked_relocations[0];
+ } else if (relocations_type_ == REL) {
+ ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations);
+ section_data = &unpacked_rel_relocations[0];
+ } else {
+ NOTREACHED();
+ }
- // 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.
- ResizeSection<Rel>(
- elf_, android_relocations_section_, sizeof(kStubIdentifier));
- RewriteSectionData(
- android_relocations_section_, &kStubIdentifier, sizeof(kStubIdentifier));
+ ResizeSection(elf_, relocations_section_, unpacked_bytes,
+ relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_);
+ RewriteSectionData(relocations_section_, section_data, unpacked_bytes);
// Rewrite .dynamic to remove two tags describing packed android relocations.
data = GetSectionData(dynamic_section_);
- const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
- std::vector<ELF::Dyn> dynamics(
+ const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+ std::vector<typename ELF::Dyn> dynamics(
dynamic_base,
dynamic_base + data->d_size / sizeof(dynamics[0]));
- RemoveDynamicEntry(DT_ANDROID_REL_OFFSET, &dynamics);
- RemoveDynamicEntry(DT_ANDROID_REL_SIZE, &dynamics);
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA;
+ dyn.d_un.d_ptr = section_header->sh_addr;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA,
+ dyn, &dynamics);
+ }
+
+ {
+ typename ELF::Dyn dyn;
+ dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ;
+ dyn.d_un.d_val = section_header->sh_size;
+ ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ,
+ dyn, &dynamics);
+ }
+
const void* dynamics_data = &dynamics[0];
const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
@@ -1261,7 +827,8 @@
}
// Flush rewritten shared object file data.
-void ElfFile::Flush() {
+template <typename ELF>
+void ElfFile<ELF>::Flush() {
// Flag all ELF data held in memory as needing to be written back to the
// file, and tell libelf that we have controlled the file layout.
elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY);
@@ -1269,6 +836,10 @@
// Write ELF data back to disk.
const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
+ if (file_bytes == -1) {
+ LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno());
+ }
+
CHECK(file_bytes > 0);
VLOG(1) << "elf_update returned: " << file_bytes;
@@ -1280,4 +851,32 @@
CHECK(truncate == 0);
}
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array,
+ size_t rel_array_size,
+ std::vector<typename ELF::Rela>* rela_vector) {
+ for (size_t i = 0; i<rel_array_size; ++i) {
+ typename ELF::Rela rela;
+ rela.r_offset = rel_array[i].r_offset;
+ rela.r_info = rel_array[i].r_info;
+ rela.r_addend = 0;
+ rela_vector->push_back(rela);
+ }
+}
+
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
+ std::vector<typename ELF::Rel>* rel_vector) {
+ for (auto rela : rela_vector) {
+ typename ELF::Rel rel;
+ rel.r_offset = rela.r_offset;
+ rel.r_info = rela.r_info;
+ CHECK(rela.r_addend == 0);
+ rel_vector->push_back(rel);
+ }
+}
+
+template class ElfFile<ELF32_traits>;
+template class ElfFile<ELF64_traits>;
+
} // namespace relocation_packer