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/Android.mk b/tools/relocation_packer/Android.mk
index 75dba71..94c946c 100644
--- a/tools/relocation_packer/Android.mk
+++ b/tools/relocation_packer/Android.mk
@@ -95,3 +95,9 @@
 $(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
 $(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
 $(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_ia32.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_ia32_packed.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_x64.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_x64_packed.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_mips32.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_mips32_packed.so))
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
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
index 6ce6d0c..5e339ae 100755
--- a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
index 6ac2eef..253dd97 100755
--- a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
index 945b450..d3d0194 100755
--- a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
index ed85ce1..269b975 100755
--- a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32.so
new file mode 100755
index 0000000..42db62c
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32_packed.so
new file mode 100755
index 0000000..27817cc
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32.so
new file mode 100755
index 0000000..6da324b
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32_packed.so
new file mode 100755
index 0000000..b11ca48
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64.so
new file mode 100755
index 0000000..6cb689e
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64_packed.so
new file mode 100755
index 0000000..60b9ad1
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64_packed.so
Binary files differ