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

Commit 5b555a4c authored by Christopher Ferris's avatar Christopher Ferris Committed by android-build-merger
Browse files

Merge "Add .gnu_debugdata support." am: 9302daeb am: 8af60711 am: 8a35b333

am: b3de868d

Change-Id: Icc90f7a90845b631502d32473aa3076221408d8a
parents 67af242c b3de868d
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -57,16 +57,17 @@ cc_defaults {
        "ElfInterface.cpp",
        "ElfInterfaceArm.cpp",
        "Log.cpp",
        "Regs.cpp",
        "MapInfo.cpp",
        "Maps.cpp",
        "Memory.cpp",
        "Regs.cpp",
        "Symbols.cpp",
    ],

    shared_libs: [
        "libbase",
        "liblog",
        "liblzma",
    ],
}

@@ -128,6 +129,7 @@ cc_defaults {
    shared_libs: [
        "libbase",
        "liblog",
        "liblzma",
    ],

    static_libs: [
@@ -151,6 +153,11 @@ cc_test {
    shared_libs: [
        "libunwindstack",
    ],

    data: [
        "tests/elf32.xz",
        "tests/elf64.xz",
    ],
}

// These unit tests run against the static debug library.
@@ -161,6 +168,11 @@ cc_test {
    static_libs: [
        "libunwindstack_debug",
    ],

    data: [
        "tests/elf32.xz",
        "tests/elf64.xz",
    ],
}

//-------------------------------------------------------------------------
@@ -173,6 +185,7 @@ cc_defaults {
    shared_libs: [
        "libunwindstack",
        "libbase",
        "liblzma",
    ],

    static_libs: [
@@ -190,3 +203,19 @@ cc_binary {
        "unwind_info.cpp",
    ],
}

// Generates the elf data for use in the tests for .gnu_debugdata frames.
// Once these files are generated, use the xz command to compress the data.
cc_binary_host {
    name: "gen_gnudebugdata",

    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],

    srcs: [
        "tests/GenGnuDebugdata.cpp",
    ],
}
+22 −0
Original line number Diff line number Diff line
@@ -49,6 +49,28 @@ bool Elf::Init() {
  return valid_;
}

// It is expensive to initialize the .gnu_debugdata section. Provide a method
// to initialize this data separately.
void Elf::InitGnuDebugdata() {
  if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
    return;
  }

  gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
  gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
  ElfInterface* gnu = gnu_debugdata_interface_.get();
  if (gnu == nullptr) {
    return;
  }
  if (gnu->Init()) {
    gnu->InitHeaders();
  } else {
    // Free all of the memory associated with the gnu_debugdata section.
    gnu_debugdata_memory_.reset(nullptr);
    gnu_debugdata_interface_.reset(nullptr);
  }
}

bool Elf::IsValidElf(Memory* memory) {
  if (memory == nullptr) {
    return false;
+11 −2
Original line number Diff line number Diff line
@@ -46,11 +46,15 @@ class Elf {
  }

  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
    return valid_ && interface_->GetFunctionName(addr, name, func_offset);
    return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
                      (gnu_debugdata_interface_ &&
                       gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
  }

  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
    return valid_ && interface_->Step(rel_pc, regs, process_memory);
    return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
                      (gnu_debugdata_interface_ &&
                       gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
  }

  ElfInterface* CreateInterfaceFromMemory(Memory* memory);
@@ -65,6 +69,8 @@ class Elf {

  ElfInterface* interface() { return interface_.get(); }

  ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }

  static bool IsValidElf(Memory* memory);

 protected:
@@ -73,6 +79,9 @@ class Elf {
  std::unique_ptr<Memory> memory_;
  uint32_t machine_type_;
  uint8_t class_type_;

  std::unique_ptr<Memory> gnu_debugdata_memory_;
  std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
};

#endif  // _LIBUNWINDSTACK_ELF_H
+58 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@
#include <memory>
#include <string>

#include <7zCrc.h>
#include <Xz.h>
#include <XzCrc64.h>

#include "DwarfDebugFrame.h"
#include "DwarfEhFrame.h"
#include "DwarfSection.h"
@@ -35,6 +39,60 @@ ElfInterface::~ElfInterface() {
  }
}

Memory* ElfInterface::CreateGnuDebugdataMemory() {
  if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
    return nullptr;
  }

  // TODO: Only call these initialization functions once.
  CrcGenerateTable();
  Crc64GenerateTable();

  std::vector<uint8_t> src(gnu_debugdata_size_);
  if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
    gnu_debugdata_offset_ = 0;
    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
    return nullptr;
  }

  ISzAlloc alloc;
  CXzUnpacker state;
  alloc.Alloc = [](void*, size_t size) { return malloc(size); };
  alloc.Free = [](void*, void* ptr) { return free(ptr); };

  XzUnpacker_Construct(&state, &alloc);

  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
  int return_val;
  size_t src_offset = 0;
  size_t dst_offset = 0;
  ECoderStatus status;
  dst->Resize(5 * gnu_debugdata_size_);
  do {
    size_t src_remaining = src.size() - src_offset;
    size_t dst_remaining = dst->Size() - dst_offset;
    if (dst_remaining < 2 * gnu_debugdata_size_) {
      dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
      dst_remaining += 2 * gnu_debugdata_size_;
    }
    return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
                                 &src_remaining, CODER_FINISH_ANY, &status);
    src_offset += src_remaining;
    dst_offset += dst_remaining;
  } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
  XzUnpacker_Free(&state);
  if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
    gnu_debugdata_offset_ = 0;
    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
    return nullptr;
  }

  // Shrink back down to the exact size.
  dst->Resize(dst_offset);

  return dst.release();
}

template <typename AddressType>
void ElfInterface::InitHeadersWithTemplate() {
  if (eh_frame_offset_ != 0) {
+170 −14
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@
 */

#include <elf.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <gtest/gtest.h>

@@ -26,10 +30,6 @@
#define PT_ARM_EXIDX 0x70000001
#endif

#if !defined(EM_AARCH64)
#define EM_AARCH64 183
#endif

class ElfTest : public ::testing::Test {
 protected:
  void SetUp() override {
@@ -43,17 +43,18 @@ class ElfTest : public ::testing::Test {
    ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
    ehdr->e_ident[EI_VERSION] = EV_CURRENT;
    ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;

    ehdr->e_type = ET_DYN;
    ehdr->e_version = EV_CURRENT;
  }

  void InitElf32(uint32_t type) {
  void InitElf32(uint32_t machine) {
    Elf32_Ehdr ehdr;

    InitEhdr<Elf32_Ehdr>(&ehdr);
    ehdr.e_ident[EI_CLASS] = ELFCLASS32;

    ehdr.e_type = ET_DYN;
    ehdr.e_machine = type;
    ehdr.e_version = EV_CURRENT;
    ehdr.e_machine = machine;
    ehdr.e_entry = 0;
    ehdr.e_phoff = 0x100;
    ehdr.e_shoff = 0;
@@ -64,7 +65,7 @@ class ElfTest : public ::testing::Test {
    ehdr.e_shentsize = sizeof(Elf32_Shdr);
    ehdr.e_shnum = 0;
    ehdr.e_shstrndx = 0;
    if (type == EM_ARM) {
    if (machine == EM_ARM) {
      ehdr.e_flags = 0x5000200;
      ehdr.e_phnum = 2;
    }
@@ -82,7 +83,7 @@ class ElfTest : public ::testing::Test {
    phdr.p_align = 0x1000;
    memory_->SetMemory(0x100, &phdr, sizeof(phdr));

    if (type == EM_ARM) {
    if (machine == EM_ARM) {
      memset(&phdr, 0, sizeof(phdr));
      phdr.p_type = PT_ARM_EXIDX;
      phdr.p_offset = 0x30000;
@@ -96,15 +97,13 @@ class ElfTest : public ::testing::Test {
    }
  }

  void InitElf64(uint32_t type) {
  void InitElf64(uint32_t machine) {
    Elf64_Ehdr ehdr;

    InitEhdr<Elf64_Ehdr>(&ehdr);
    ehdr.e_ident[EI_CLASS] = ELFCLASS64;

    ehdr.e_type = ET_DYN;
    ehdr.e_machine = type;
    ehdr.e_version = EV_CURRENT;
    ehdr.e_machine = machine;
    ehdr.e_entry = 0;
    ehdr.e_phoff = 0x100;
    ehdr.e_shoff = 0;
@@ -130,6 +129,12 @@ class ElfTest : public ::testing::Test {
    memory_->SetMemory(0x100, &phdr, sizeof(phdr));
  }

  template <typename Ehdr, typename Shdr>
  void GnuDebugdataInitFail(Ehdr* ehdr);

  template <typename Ehdr, typename Shdr>
  void GnuDebugdataInit(Ehdr* ehdr);

  MemoryFake* memory_;
};

@@ -208,3 +213,154 @@ TEST_F(ElfTest, elf_x86_64) {
  ASSERT_EQ(ELFCLASS64, elf.class_type());
  ASSERT_TRUE(elf.interface() != nullptr);
}

template <typename Ehdr, typename Shdr>
void ElfTest::GnuDebugdataInitFail(Ehdr* ehdr) {
  Elf elf(memory_);

  uint64_t offset = 0x2000;

  ehdr->e_shoff = offset;
  ehdr->e_shnum = 3;
  ehdr->e_shentsize = sizeof(Shdr);
  ehdr->e_shstrndx = 2;
  memory_->SetMemory(0, ehdr, sizeof(*ehdr));

  Shdr shdr;
  memset(&shdr, 0, sizeof(shdr));
  shdr.sh_type = SHT_NULL;
  memory_->SetMemory(offset, &shdr, sizeof(shdr));
  offset += ehdr->e_shentsize;

  memset(&shdr, 0, sizeof(shdr));
  shdr.sh_type = SHT_PROGBITS;
  shdr.sh_name = 0x100;
  shdr.sh_addr = 0x5000;
  shdr.sh_offset = 0x5000;
  shdr.sh_entsize = 0x100;
  shdr.sh_size = 0x800;
  memory_->SetMemory(offset, &shdr, sizeof(shdr));
  offset += ehdr->e_shentsize;

  memset(&shdr, 0, sizeof(shdr));
  shdr.sh_type = SHT_STRTAB;
  shdr.sh_name = 0x200000;
  shdr.sh_offset = 0xf000;
  shdr.sh_size = 0x1000;
  memory_->SetMemory(offset, &shdr, sizeof(shdr));
  offset += ehdr->e_shentsize;

  memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata"));

  ASSERT_TRUE(elf.Init());
  ASSERT_TRUE(elf.interface() != nullptr);
  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
  EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset());
  EXPECT_EQ(0x800U, elf.interface()->gnu_debugdata_size());

  elf.InitGnuDebugdata();
}

TEST_F(ElfTest, gnu_debugdata_init_fail32) {
  Elf32_Ehdr ehdr;
  InitEhdr<Elf32_Ehdr>(&ehdr);
  ehdr.e_ident[EI_CLASS] = ELFCLASS32;
  ehdr.e_machine = EM_ARM;

  GnuDebugdataInitFail<Elf32_Ehdr, Elf32_Shdr>(&ehdr);
}

TEST_F(ElfTest, gnu_debugdata_init_fail64) {
  Elf64_Ehdr ehdr;
  InitEhdr<Elf64_Ehdr>(&ehdr);
  ehdr.e_ident[EI_CLASS] = ELFCLASS64;
  ehdr.e_machine = EM_AARCH64;

  GnuDebugdataInitFail<Elf64_Ehdr, Elf64_Shdr>(&ehdr);
}

template <typename Ehdr, typename Shdr>
void ElfTest::GnuDebugdataInit(Ehdr* ehdr) {
  Elf elf(memory_);

  uint64_t offset = 0x2000;

  ehdr->e_shoff = offset;
  ehdr->e_shnum = 3;
  ehdr->e_shentsize = sizeof(Shdr);
  ehdr->e_shstrndx = 2;
  memory_->SetMemory(0, ehdr, sizeof(*ehdr));

  Shdr shdr;
  memset(&shdr, 0, sizeof(shdr));
  shdr.sh_type = SHT_NULL;
  memory_->SetMemory(offset, &shdr, sizeof(shdr));
  offset += ehdr->e_shentsize;

  uint64_t gnu_offset = offset;
  offset += ehdr->e_shentsize;

  memset(&shdr, 0, sizeof(shdr));
  shdr.sh_type = SHT_STRTAB;
  shdr.sh_name = 0x200000;
  shdr.sh_offset = 0xf000;
  shdr.sh_size = 0x1000;
  memory_->SetMemory(offset, &shdr, sizeof(shdr));
  offset += ehdr->e_shentsize;

  memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata"));

  // Read in the compressed elf data and put it in our fake memory.
  std::string name("tests/");
  if (sizeof(Ehdr) == sizeof(Elf32_Ehdr)) {
    name += "elf32.xz";
  } else {
    name += "elf64.xz";
  }
  int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
  ASSERT_NE(-1, fd) << "Cannot open " + name;
  // Assumes the file is less than 1024 bytes.
  std::vector<uint8_t> buf(1024);
  ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
  ASSERT_GT(bytes, 0);
  // Make sure the file isn't too big.
  ASSERT_NE(static_cast<size_t>(bytes), buf.size())
      << "File " + name + " is too big, increase buffer size.";
  close(fd);
  buf.resize(bytes);
  memory_->SetMemory(0x5000, buf);

  memset(&shdr, 0, sizeof(shdr));
  shdr.sh_type = SHT_PROGBITS;
  shdr.sh_name = 0x100;
  shdr.sh_addr = 0x5000;
  shdr.sh_offset = 0x5000;
  shdr.sh_size = bytes;
  memory_->SetMemory(gnu_offset, &shdr, sizeof(shdr));

  ASSERT_TRUE(elf.Init());
  ASSERT_TRUE(elf.interface() != nullptr);
  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
  EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset());

  elf.InitGnuDebugdata();
  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
}

TEST_F(ElfTest, gnu_debugdata_init32) {
  Elf32_Ehdr ehdr;
  InitEhdr<Elf32_Ehdr>(&ehdr);
  ehdr.e_ident[EI_CLASS] = ELFCLASS32;
  ehdr.e_machine = EM_ARM;

  GnuDebugdataInit<Elf32_Ehdr, Elf32_Shdr>(&ehdr);
}

TEST_F(ElfTest, gnu_debugdata_init64) {
  Elf64_Ehdr ehdr;
  InitEhdr<Elf64_Ehdr>(&ehdr);
  ehdr.e_ident[EI_CLASS] = ELFCLASS64;
  ehdr.e_machine = EM_AARCH64;

  GnuDebugdataInit<Elf64_Ehdr, Elf64_Shdr>(&ehdr);
}
Loading