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

Commit 1b080f45 authored by Christopher Ferris's avatar Christopher Ferris Committed by android-build-merger
Browse files

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

am: 583ce2de

Change-Id: I525d51e3f5aab094f4ce374d7eca27d02bf444ca
parents 96cf6ece 583ce2de
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