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

Commit 9d5712c1 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Implement support for linker rosegment option.

The rosegment linker option results in two maps containing the elf data
existing. One is an execute map where the code lives, and the other is the
read-only segment which contains the elf header information. If the file
backing a shared library in memory is not readable, then the new code
will attempt to find the read-only map that has the same name as the
current execute segment, and that is at offest zero in the file.

Add new unit tests for this functionality.

Add the missing MapInfoCreateMemoryTest.cpp to the list of tests.

Bug: 109657296

Test: Pass new unit tests.
Test: All unit libbacktrace/libunwindstack tests pass with rosegment enabled.
Change-Id: If8f69e4a067d77b3f2a7c31e2e5cd989a0702a8c
parent 15a5c9c4
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -116,10 +116,6 @@ cc_test_library {

    target: {
        linux_glibc: {
            // The host uses rosegment, which isn't supported yet.
            ldflags: [
                "-Wl,--no-rosegment",
            ],
            // This forces the creation of eh_frame with unwind information
            // for host.
            cflags: [
+2 −0
Original line number Diff line number Diff line
@@ -178,6 +178,7 @@ cc_test {
        "tests/JitDebugTest.cpp",
        "tests/LocalUnwinderTest.cpp",
        "tests/LogFake.cpp",
        "tests/MapInfoCreateMemoryTest.cpp",
        "tests/MapInfoGetElfTest.cpp",
        "tests/MapInfoGetLoadBiasTest.cpp",
        "tests/MapsTest.cpp",
@@ -188,6 +189,7 @@ cc_test {
        "tests/MemoryOfflineBufferTest.cpp",
        "tests/MemoryOfflineTest.cpp",
        "tests/MemoryRangeTest.cpp",
        "tests/MemoryRangesTest.cpp",
        "tests/MemoryRemoteTest.cpp",
        "tests/MemoryTest.cpp",
        "tests/RegsInfoTest.cpp",
+48 −1
Original line number Diff line number Diff line
@@ -102,7 +102,54 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
  if (!(flags & PROT_READ)) {
    return nullptr;
  }
  return new MemoryRange(process_memory, start, end - start, 0);

  // Need to verify that this elf is valid. It's possible that
  // only part of the elf file to be mapped into memory is in the executable
  // map. In this case, there will be another read-only map that includes the
  // first part of the elf file. This is done if the linker rosegment
  // option is used.
  std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
  bool valid;
  uint64_t max_size;
  Elf::GetInfo(memory.get(), &valid, &max_size);
  if (valid) {
    // Valid elf, we are done.
    return memory.release();
  }

  if (name.empty() || maps_ == nullptr) {
    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;
  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;
    }
  }

  if (ro_map_info != nullptr) {
    // Make sure that relative pc values are corrected properly.
    elf_offset = offset - closest_offset;

    MemoryRanges* ranges = new MemoryRanges;
    ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
                                   ro_map_info->end - ro_map_info->start, 0));
    ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));

    return ranges;
  }
  return nullptr;
}

Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+3 −3
Original line number Diff line number Diff line
@@ -66,13 +66,13 @@ bool Maps::Parse() {
        if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
        }
        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
      });
}

void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
               const std::string& name, uint64_t load_bias) {
  MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
  MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
  map_info->load_bias = load_bias;
  maps_.push_back(map_info);
}
@@ -97,7 +97,7 @@ bool BufferMaps::Parse() {
        if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
        }
        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
      });
}

+12 −0
Original line number Diff line number Diff line
@@ -316,6 +316,18 @@ size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
  return memory_->Read(read_addr, dst, read_length);
}

void MemoryRanges::Insert(MemoryRange* memory) {
  maps_.emplace(memory->offset() + memory->length(), memory);
}

size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
  auto entry = maps_.upper_bound(addr);
  if (entry != maps_.end()) {
    return entry->second->Read(addr, dst, size);
  }
  return 0;
}

bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
  auto memory_file = std::make_shared<MemoryFileAtOffset>();
  if (!memory_file->Init(file, offset)) {
Loading