Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 583ce2de authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Fix handling of ro segments for embedded libs."

parents 78f94a39 01040b10
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -235,6 +235,8 @@ cc_test {
        "tests/files/offline/jit_map_arm/*",
        "tests/files/offline/gnu_debugdata_arm/*",
        "tests/files/offline/offset_arm/*",
        "tests/files/offline/shared_lib_in_apk_arm64/*",
        "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
        "tests/files/offline/straddle_arm/*",
        "tests/files/offline/straddle_arm64/*",
    ],
+82 −34
Original line number Diff line number Diff line
@@ -29,6 +29,38 @@

namespace unwindstack {

bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
  // One last attempt, see if the previous map is read-only with the
  // same name and stretches across this map.
  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
    if (*iter == this) {
      if (iter == maps_->begin()) {
        return false;
      }
      --iter;
      MapInfo* prev_map = *iter;
      // Make sure this is a read-only map.
      if (prev_map->flags != PROT_READ) {
        return false;
      }
      uint64_t map_size = end - prev_map->end;
      if (!memory->Init(name, prev_map->offset, map_size)) {
        return false;
      }
      uint64_t max_size;
      if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
        return false;
      }
      if (!memory->Init(name, prev_map->offset, max_size)) {
        return false;
      }
      elf_offset = offset - prev_map->offset;
      return true;
    }
  }
  return false;
}

Memory* MapInfo::GetFileMemory() {
  std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
  if (offset == 0) {
@@ -38,8 +70,12 @@ Memory* MapInfo::GetFileMemory() {
    return nullptr;
  }

  // There are two possibilities when the offset is non-zero.
  // - There is an elf file embedded in a file.
  // These are the possibilities when the offset is non-zero.
  // - There is an elf file embedded in a file, and the offset is the
  //   the start of the elf in the file.
  // - There is an elf file embedded in a file, and the offset is the
  //   the start of the executable part of the file. The actual start
  //   of the elf is in the read-only segment preceeding this map.
  // - The whole file is an elf file, and the offset needs to be saved.
  //
  // Map in just the part of the file for the map. If this is not
@@ -53,28 +89,42 @@ Memory* MapInfo::GetFileMemory() {
    return nullptr;
  }

  uint64_t max_size;
  if (!Elf::GetInfo(memory.get(), &max_size)) {
    // Init as if the whole file is an elf.
    if (memory->Init(name, 0)) {
      elf_offset = offset;
  // Check if the start of this map is an embedded elf.
  uint64_t max_size = 0;
  uint64_t file_offset = offset;
  if (Elf::GetInfo(memory.get(), &max_size)) {
    if (max_size > map_size) {
      if (memory->Init(name, file_offset, max_size)) {
        return memory.release();
      }
      // Try to reinit using the default map_size.
      if (memory->Init(name, file_offset, map_size)) {
        return memory.release();
      }
      return nullptr;
    }
    return memory.release();
  }

  if (max_size > map_size) {
    if (memory->Init(name, offset, max_size)) {
  // No elf at offset, try to init as if the whole file is an elf.
  if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
    elf_offset = offset;
    return memory.release();
  }
    // Try to reinit using the default map_size.

  // See if the map previous to this one contains a read-only map
  // that represents the real start of the elf data.
  if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
    return memory.release();
  }

  // Failed to find elf at start of file or at read-only map, return
  // file object from the current map.
  if (memory->Init(name, offset, map_size)) {
    return memory.release();
  }
  return nullptr;
}
  return memory.release();
}

Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
  if (end <= start) {
@@ -110,29 +160,27 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
    return nullptr;
  }

  // Find the read-only map that has the same name and has an offset closest
  // to the current offset but less than the offset of the current map.
  // For shared libraries, there should be a r-x map that has a non-zero
  // offset and then a r-- map that has a zero offset.
  // For shared libraries loaded from an apk, there should be a r-x map that
  // has a non-zero offset and then a r-- map that has a non-zero offset less
  // than the offset from the r-x map.
  uint64_t closest_offset = 0;
  // Find the read-only map by looking at the previous map. The linker
  // doesn't guarantee that this invariant will always be true. However,
  // if that changes, there is likely something else that will change and
  // break something.
  MapInfo* ro_map_info = nullptr;
  for (auto map_info : *maps_) {
    if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
        map_info->offset >= closest_offset) {
      ro_map_info = map_info;
      closest_offset = ro_map_info->offset;
  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
    if (*iter == this) {
      if (iter != maps_->begin()) {
        --iter;
        ro_map_info = *iter;
      }
      break;
    }
  }

  if (ro_map_info == nullptr) {
  if (ro_map_info == nullptr || ro_map_info->name != name || ro_map_info->offset >= offset) {
    return nullptr;
  }

  // Make sure that relative pc values are corrected properly.
  elf_offset = offset - closest_offset;
  elf_offset = offset - ro_map_info->offset;

  MemoryRanges* ranges = new MemoryRanges;
  ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+1 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ struct MapInfo {
  void operator=(const MapInfo&) = delete;

  Memory* GetFileMemory();
  bool InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory);

  // Protect the creation of the elf object.
  std::mutex mutex_;
+48 −9
Original line number Diff line number Diff line
@@ -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
+75 −0
Original line number Diff line number Diff line
@@ -1239,4 +1239,79 @@ TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
  EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
}

TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));

  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
  unwinder.Unwind();

  std::string frame_info(DumpFrames(unwinder));
  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
  EXPECT_EQ(
      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
      "  #02 pc 00000000000008bc  vdso.so\n"
      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
      "  #05 pc 000000000003d5b4 (offset 0x40000)  ANGLEPrebuilt.apk (ANGLEGetUtilityAPI+56)\n"
      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
      frame_info);

  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
  EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
  // Ignore top frame since the test code was modified to end in __libc_init.
}

TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
  // Add the memory that represents the shared library.
  MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
  AddMemory(dir_ + "lib_mem.data", memory);

  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
  unwinder.Unwind();

  std::string frame_info(DumpFrames(unwinder));
  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
  EXPECT_EQ(
      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
      "  #02 pc 00000000000008bc  vdso.so\n"
      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
      "  #05 pc 000000000003d5b4 (offset 0x2211000)  ANGLEPrebuilt.apk\n"
      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
      frame_info);

  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
  EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
  // Ignore top frame since the test code was modified to end in __libc_init.
}

}  // namespace unwindstack
Loading