diff --git a/tools/relocation_packer/Android.mk b/tools/relocation_packer/Android.mk index 75dba7143..94c946cda 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.so)) $(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so)) $(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so)) $(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so)) +$(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 102d61423..400423921 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -302,13 +302,75 @@ static void AdjustSectionHeadersForHole(Elf* elf, } } -// 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 -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 +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 +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(program_header); + } else if (hole_size > 0) { + status = RestoreLoadSegmentAlignment(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 +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 @@ static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers, } 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(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 @@ static void RewriteProgramHeadersForHole(Elf* elf, target_load_header->p_memsz += hole_size; // Adjust the offsets and p_vaddrs - AdjustProgramHeaderOffsets(elf_program_header, - program_header_count, - hole_start, - hole_size); + AdjustProgramHeaderFields(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 32f79683f..d5c891890 100644 --- a/tools/relocation_packer/src/elf_file_unittest.cc +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -183,6 +183,18 @@ TEST(ElfFile, PackRelocationsArm64) { 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 @@ TEST(ElfFile, UnpackRelocationsArm64) { 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 6ce6d0cd2..5e339ae4b 100755 Binary files a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so differ diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so index 6ac2eef05..253dd97c1 100755 Binary files a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so differ diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so index 945b450e3..d3d0194da 100755 Binary files a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so differ diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so index ed85ce1e7..269b97554 100755 Binary files a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so 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 000000000..42db62c24 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32.so 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 000000000..27817cc38 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_ia32_packed.so 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 000000000..6da324bb7 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32.so 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 000000000..b11ca4883 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_mips32_packed.so 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 000000000..6cb689ec5 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64.so 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 000000000..60b9ad179 Binary files /dev/null and b/tools/relocation_packer/test_data/elf_file_unittest_relocs_x64_packed.so differ