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

Commit 3f805ac3 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Add proper support for embedded elf files.

- Add a method to get the max size of an elf file by reading the
  section header offset + size. This will properly map an elf
  file embedded into an apk, instead of just mapping in what is done
  by the dynamic linker. It does assume that the section headers are
  at the end of the elf file.
- Add new tests for the above functionality.
- Update the unwind_symbols tool to take an address for finding a
  function instead of dumping the entire symbol table.

Bug: 23762183

Test: Unit tests pass, unwind through the camera process and verify
Test: the GoogleCamera.apk shows some function names.
Change-Id: I00c021680fe1d43b60d652bf91bbf6667d9617be
parent e1f9a58c
Loading
Loading
Loading
Loading
+22 −0
Original line number Original line Diff line number Diff line
@@ -124,6 +124,28 @@ bool Elf::IsValidElf(Memory* memory) {
  return true;
  return true;
}
}


void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
  if (!IsValidElf(memory)) {
    *valid = false;
    return;
  }
  *size = 0;
  *valid = true;

  // Now read the section header information.
  uint8_t class_type;
  if (!memory->Read(EI_CLASS, &class_type, 1)) {
    return;
  }
  if (class_type == ELFCLASS32) {
    ElfInterface32::GetMaxSize(memory, size);
  } else if (class_type == ELFCLASS64) {
    ElfInterface64::GetMaxSize(memory, size);
  } else {
    *valid = false;
  }
}

ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
  if (!IsValidElf(memory)) {
  if (!IsValidElf(memory)) {
    return nullptr;
    return nullptr;
+19 −0
Original line number Original line Diff line number Diff line
@@ -370,6 +370,22 @@ bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
  return false;
  return false;
}
}


// This is an estimation of the size of the elf file using the location
// of the section headers and size. This assumes that the section headers
// are at the end of the elf file. If the elf has a load bias, the size
// will be too large, but this is acceptable.
template <typename EhdrType>
void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
  EhdrType ehdr;
  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
    return;
  }
  if (ehdr.e_shnum == 0) {
    return;
  }
  *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
}

// Instantiate all of the needed template functions.
// Instantiate all of the needed template functions.
template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
@@ -391,4 +407,7 @@ template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
                                                                   uint64_t*);
                                                                   uint64_t*);


template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);

}  // namespace unwindstack
}  // namespace unwindstack
+53 −24
Original line number Original line Diff line number Diff line
@@ -27,6 +27,55 @@


namespace unwindstack {
namespace unwindstack {


Memory* MapInfo::GetFileMemory() {
  std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
  if (offset == 0) {
    if (memory->Init(name, 0)) {
      return memory.release();
    }
    return nullptr;
  }

  // There are two possibilities when the offset is non-zero.
  // - There is an elf file embedded in a file.
  // - 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
  // a valid elf, then reinit as if the whole file is an elf file.
  // If the offset is a valid elf, then determine the size of the map
  // and reinit to that size. This is needed because the dynamic linker
  // only maps in a portion of the original elf, and never the symbol
  // file data.
  uint64_t map_size = end - start;
  if (!memory->Init(name, offset, map_size)) {
    return nullptr;
  }

  bool valid;
  uint64_t max_size;
  Elf::GetInfo(memory.get(), &valid, &max_size);
  if (!valid) {
    // Init as if the whole file is an elf.
    if (memory->Init(name, 0)) {
      elf_offset = offset;
      return memory.release();
    }
    return nullptr;
  }

  if (max_size > map_size) {
    if (memory->Init(name, offset, max_size)) {
      return memory.release();
    }
    // Try to reinit using the default map_size.
    if (memory->Init(name, offset, map_size)) {
      return memory.release();
    }
    return nullptr;
  }
  return memory.release();
}

Memory* MapInfo::CreateMemory(pid_t pid) {
Memory* MapInfo::CreateMemory(pid_t pid) {
  if (end <= start) {
  if (end <= start) {
    return nullptr;
    return nullptr;
@@ -40,33 +89,13 @@ Memory* MapInfo::CreateMemory(pid_t pid) {
    if (flags & MAPS_FLAGS_DEVICE_MAP) {
    if (flags & MAPS_FLAGS_DEVICE_MAP) {
      return nullptr;
      return nullptr;
    }
    }

    Memory* memory = GetFileMemory();
    std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
    if (memory != nullptr) {
    uint64_t map_size;
      return memory;
    if (offset != 0) {
      // Only map in a piece of the file.
      map_size = end - start;
    } else {
      map_size = UINT64_MAX;
    }
    if (file_memory->Init(name, offset, map_size)) {
      // It's possible that a non-zero offset might not be pointing to
      // valid elf data. Check if this is a valid elf, and if not assume
      // that this was meant to incorporate the entire file.
      if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
        // Don't bother checking the validity that will happen on the elf init.
        if (file_memory->Init(name, 0)) {
          elf_offset = offset;
          return file_memory.release();
        }
        // Fall through if the init fails.
      } else {
        return file_memory.release();
      }
    }
    }
  }
  }


  Memory* memory = nullptr;
  Memory* memory;
  if (pid == getpid()) {
  if (pid == getpid()) {
    memory = new MemoryLocal();
    memory = new MemoryLocal();
  } else {
  } else {
+2 −0
Original line number Original line Diff line number Diff line
@@ -70,6 +70,8 @@ class Elf {


  static bool IsValidElf(Memory* memory);
  static bool IsValidElf(Memory* memory);


  static void GetInfo(Memory* memory, bool* valid, uint64_t* size);

 protected:
 protected:
  bool valid_ = false;
  bool valid_ = false;
  std::unique_ptr<ElfInterface> interface_;
  std::unique_ptr<ElfInterface> interface_;
+11 −0
Original line number Original line Diff line number Diff line
@@ -102,6 +102,9 @@ class ElfInterface {


  virtual bool HandleType(uint64_t, uint32_t) { return false; }
  virtual bool HandleType(uint64_t, uint32_t) { return false; }


  template <typename EhdrType>
  static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);

  Memory* memory_;
  Memory* memory_;
  std::unordered_map<uint64_t, LoadInfo> pt_loads_;
  std::unordered_map<uint64_t, LoadInfo> pt_loads_;
  uint64_t load_bias_ = 0;
  uint64_t load_bias_ = 0;
@@ -146,6 +149,10 @@ class ElfInterface32 : public ElfInterface {
  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
  }
  }

  static void GetMaxSize(Memory* memory, uint64_t* size) {
    GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
  }
};
};


class ElfInterface64 : public ElfInterface {
class ElfInterface64 : public ElfInterface {
@@ -166,6 +173,10 @@ class ElfInterface64 : public ElfInterface {
  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
  }
  }

  static void GetMaxSize(Memory* memory, uint64_t* size) {
    GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
  }
};
};


}  // namespace unwindstack
}  // namespace unwindstack
Loading