Fix unit tests, and extend for other architectures
On unpacking, reinstate any p_align values that packing reduced to
page size. Ensures a round-trip pack and unpack is bit-equivalent
to the original input.
https://android-review.googlesource.com/#/c/148492/
Extend unit tests to include ia32, x64, and mips32.
Recreate test data for arm32 and arm64. Generate new test data for
ia32, x64, and mips32.
Bug: http://b/20687795
Bug: http://b/18051137
Change-Id: Ifbca8e206ef447297ba4f19272b813702be27a35
diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc
index 102d614..4004239 100644
--- a/tools/relocation_packer/src/elf_file.cc
+++ b/tools/relocation_packer/src/elf_file.cc
@@ -302,13 +302,75 @@
}
}
-// Helper for ResizeSection(). Adjust the offsets of any program headers
-// that have offsets currently beyond the hole start.
+// Helpers for ResizeSection(). On packing, reduce p_align for LOAD segments
+// to 4kb if larger. On unpacking, restore p_align for LOAD segments if
+// packing reduced it to 4kb. Return true if p_align was changed.
template <typename ELF>
-static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers,
+static bool ClampLoadSegmentAlignment(typename ELF::Phdr* program_header) {
+ CHECK(program_header->p_type == PT_LOAD);
+
+ // If large, reduce p_align for a LOAD segment to page size on packing.
+ if (program_header->p_align > kPageSize) {
+ program_header->p_align = kPageSize;
+ return true;
+ }
+ return false;
+}
+
+template <typename ELF>
+static bool RestoreLoadSegmentAlignment(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Phdr* program_header) {
+ CHECK(program_header->p_type == PT_LOAD);
+
+ // If p_align was reduced on packing, restore it to its previous value
+ // on unpacking. We do this by searching for a different LOAD segment
+ // and setting p_align to that of the other LOAD segment found.
+ //
+ // Relies on the following observations:
+ // - a packable ELF executable has more than one LOAD segment;
+ // - before packing all LOAD segments have the same p_align;
+ // - on packing we reduce only one LOAD segment's p_align.
+ if (program_header->p_align == kPageSize) {
+ for (size_t i = 0; i < count; ++i) {
+ typename ELF::Phdr* other_header = &program_headers[i];
+ if (other_header->p_type == PT_LOAD && other_header != program_header) {
+ program_header->p_align = other_header->p_align;
+ return true;
+ }
+ }
+ LOG(WARNING) << "Cannot find a LOAD segment from which to restore p_align";
+ }
+ return false;
+}
+
+template <typename ELF>
+static bool AdjustLoadSegmentAlignment(typename ELF::Phdr* program_headers,
size_t count,
- typename ELF::Off hole_start,
+ typename ELF::Phdr* program_header,
ssize_t hole_size) {
+ CHECK(program_header->p_type == PT_LOAD);
+
+ bool status = false;
+ if (hole_size < 0) {
+ status = ClampLoadSegmentAlignment<ELF>(program_header);
+ } else if (hole_size > 0) {
+ status = RestoreLoadSegmentAlignment<ELF>(program_headers,
+ count,
+ program_header);
+ }
+ return status;
+}
+
+// Helper for ResizeSection(). Adjust the offsets of any program headers
+// that have offsets currently beyond the hole start, and adjust the
+// virtual and physical addrs (and perhaps alignment) of the others.
+template <typename ELF>
+static void AdjustProgramHeaderFields(typename ELF::Phdr* program_headers,
+ size_t count,
+ typename ELF::Off hole_start,
+ ssize_t hole_size) {
+ int alignment_changes = 0;
for (size_t i = 0; i < count; ++i) {
typename ELF::Phdr* program_header = &program_headers[i];
@@ -327,9 +389,20 @@
} else {
program_header->p_vaddr -= hole_size;
program_header->p_paddr -= hole_size;
- if (program_header->p_align > kPageSize) {
- program_header->p_align = kPageSize;
+
+ // If packing, clamp LOAD segment alignment to 4kb to prevent strip
+ // from adjusting it unnecessarily if run on a packed file. If
+ // unpacking, attempt to restore a reduced alignment to its previous
+ // value. Ensure that we do this on at most one LOAD segment.
+ if (program_header->p_type == PT_LOAD) {
+ alignment_changes += AdjustLoadSegmentAlignment<ELF>(program_headers,
+ count,
+ program_header,
+ hole_size);
+ LOG_IF(FATAL, alignment_changes > 1)
+ << "Changed p_align on more than one LOAD segment";
}
+
VLOG(1) << "phdr[" << i
<< "] p_vaddr adjusted to "<< program_header->p_vaddr
<< "; p_paddr adjusted to "<< program_header->p_paddr
@@ -383,10 +456,10 @@
target_load_header->p_memsz += hole_size;
// Adjust the offsets and p_vaddrs
- AdjustProgramHeaderOffsets<ELF>(elf_program_header,
- program_header_count,
- hole_start,
- hole_size);
+ AdjustProgramHeaderFields<ELF>(elf_program_header,
+ program_header_count,
+ hole_start,
+ hole_size);
}
// Helper for ResizeSection(). Locate and return the dynamic section.
diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc
index 32f7968..d5c8918 100644
--- a/tools/relocation_packer/src/elf_file_unittest.cc
+++ b/tools/relocation_packer/src/elf_file_unittest.cc
@@ -183,6 +183,18 @@
RunPackRelocationsTestFor("arm64");
}
+TEST(ElfFile, PackRelocationsMips32) {
+ RunPackRelocationsTestFor("mips32");
+}
+
+TEST(ElfFile, PackRelocationsIa32) {
+ RunPackRelocationsTestFor("ia32");
+}
+
+TEST(ElfFile, PackRelocationsX64) {
+ RunPackRelocationsTestFor("x64");
+}
+
TEST(ElfFile, UnpackRelocationsArm32) {
RunUnpackRelocationsTestFor("arm32");
}
@@ -191,4 +203,16 @@
RunUnpackRelocationsTestFor("arm64");
}
+TEST(ElfFile, UnpackRelocationsMips32) {
+ RunUnpackRelocationsTestFor("mips32");
+}
+
+TEST(ElfFile, UnpackRelocationsIa32) {
+ RunUnpackRelocationsTestFor("ia32");
+}
+
+TEST(ElfFile, UnpackRelocationsX64) {
+ RunUnpackRelocationsTestFor("x64");
+}
+
} // namespace relocation_packer