diff options
author | Christopher Ferris <cferris@google.com> | 2018-12-10 11:13:23 -0800 |
---|---|---|
committer | Christopher Ferris <cferris@google.com> | 2018-12-11 19:16:29 -0800 |
commit | 01040b10b2393014c2d49265b6ef75a1a2a459a7 (patch) | |
tree | 3b73550aec21a98a8e4ae302f9d567f9d2541c65 /libunwindstack/tests/MapInfoCreateMemoryTest.cpp | |
parent | 1fcf7f7d1f6e064ff2a129394d26b9d5a257206f (diff) |
Fix handling of ro segments for embedded libs.
When a shared library is loaded directly from an apk, the new way the
linker splits a shared library into a read-only and execute segment
broke unwinding. Modify the code to handle this case.
Other changes:
- Modify the algorithm for finding read-only map entries. Before, the code
would search the entire map for the closest offset. Now it simply looks
at the previous map. I did this because the old code was too lenient and
might still work even if the linker changes. I want this to break if the
linker behavior changes so that I can analyze the change.
- Update the tools to use PTRACE_SEIZE instead of PTRACE_ATTACH since
PTRACE_ATTACH doesn't work in all cases.
- Small refactor of the GetFileMemory function.
- Add new unit test cases and new offline unwind test cases.
Bug: 120618231
Test: Ran new unit tests, ran original failing test.
Change-Id: I4bade55cf33220d52f1d5e9b0cbbbcc8419669d4
Diffstat (limited to 'libunwindstack/tests/MapInfoCreateMemoryTest.cpp')
-rw-r--r-- | libunwindstack/tests/MapInfoCreateMemoryTest.cpp | 57 |
1 files changed, 48 insertions, 9 deletions
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp index 0987bc126..943b3c9dc 100644 --- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp @@ -59,16 +59,16 @@ class MapInfoCreateMemoryTest : public ::testing::Test { } static void SetUpTestCase() { - std::vector<uint8_t> buffer(1024); - memset(buffer.data(), 0, buffer.size()); + std::vector<uint8_t> buffer(12288, 0); memcpy(buffer.data(), ELFMAG, SELFMAG); buffer[EI_CLASS] = ELFCLASS32; - ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size())); + ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), 1024)); memset(buffer.data(), 0, buffer.size()); - memcpy(&buffer[0x100], ELFMAG, SELFMAG); - buffer[0x100 + EI_CLASS] = ELFCLASS64; - ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size())); + memcpy(&buffer[0x1000], ELFMAG, SELFMAG); + buffer[0x1000 + EI_CLASS] = ELFCLASS64; + buffer[0x2000] = 0xff; + ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, buffer.data(), buffer.size())); InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32); InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64); @@ -84,13 +84,13 @@ class MapInfoCreateMemoryTest : public ::testing::Test { static TemporaryFile elf_; - static TemporaryFile elf_at_100_; + static TemporaryFile elf_at_1000_; static TemporaryFile elf32_at_map_; static TemporaryFile elf64_at_map_; }; TemporaryFile MapInfoCreateMemoryTest::elf_; -TemporaryFile MapInfoCreateMemoryTest::elf_at_100_; +TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_; TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_; TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_; @@ -134,7 +134,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) { // Verify that if the offset is non-zero and there is an elf at that // offset, that only part of the file is used. TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) { - MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path); + MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path); std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); @@ -312,4 +312,43 @@ TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) { } } +TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) { + Maps maps; + maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0); + maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0); + maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0); + + MapInfo* map_info = maps.Find(0x2000); + ASSERT_TRUE(map_info != nullptr); + + // Set up the size + Elf64_Ehdr ehdr; + ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET)); + ASSERT_TRUE(android::base::ReadFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr))); + + // Will not give the elf memory, because the read-only entry does not + // extend over the executable segment. + std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_)); + ASSERT_TRUE(memory.get() != nullptr); + std::vector<uint8_t> buffer(0x100); + EXPECT_EQ(0x2000U, map_info->offset); + EXPECT_EQ(0U, map_info->elf_offset); + ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100)); + EXPECT_EQ(0xffU, buffer[0]); + + // Now init the elf data enough so that the file memory object will be used. + ehdr.e_shoff = 0x4000; + ehdr.e_shnum = 1; + ehdr.e_shentsize = 0x100; + ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET)); + ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr))); + + memory.reset(map_info->CreateMemory(process_memory_)); + EXPECT_EQ(0x2000U, map_info->offset); + EXPECT_EQ(0x1000U, map_info->elf_offset); + Elf64_Ehdr ehdr_mem; + ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem))); + EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0); +} + } // namespace unwindstack |