summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2017-06-20 13:54:08 -0700
committerChristopher Ferris <cferris@google.com>2017-06-23 10:51:28 -0700
commit3400159c4d96ba9a02d04fdef79b0f90b302460e (patch)
treec4e13b762355ac9a356c0563f07ccca07b8f4ac0
parentd3b457640ceb9d330b2c550aa908e4eeba73a9a8 (diff)
downloadunwinding-3400159c4d96ba9a02d04fdef79b0f90b302460e.tar.gz
Add section parsing and function name handling.
Add the code to parse the Elf section headers. Add the plumbing through of all the symbol handling code. Add tests for all of this new functionality. Bug: 23762183 Test: Pass new unit tests. Change-Id: Ie2d90cbb3d7653c53251dbcf34d9e5d241278377
-rw-r--r--libunwindstack/Elf.h4
-rw-r--r--libunwindstack/ElfInterface.cpp108
-rw-r--r--libunwindstack/ElfInterface.h16
-rw-r--r--libunwindstack/tests/ElfInterfaceTest.cpp203
4 files changed, 311 insertions, 20 deletions
diff --git a/libunwindstack/Elf.h b/libunwindstack/Elf.h
index 7bf45b8..f9db541 100644
--- a/libunwindstack/Elf.h
+++ b/libunwindstack/Elf.h
@@ -45,8 +45,8 @@ class Elf {
return valid_ && interface_->GetSoname(name);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+ return valid_ && interface_->GetFunctionName(addr, name, func_offset);
}
bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index bfa7944..3a7f7cb 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -22,9 +22,18 @@
#include "DwarfDebugFrame.h"
#include "DwarfEhFrame.h"
+#include "DwarfSection.h"
#include "ElfInterface.h"
+#include "Log.h"
#include "Memory.h"
#include "Regs.h"
+#include "Symbols.h"
+
+ElfInterface::~ElfInterface() {
+ for (auto symbol : symbols_) {
+ delete symbol;
+ }
+}
template <typename AddressType>
void ElfInterface::InitHeadersWithTemplate() {
@@ -57,7 +66,13 @@ bool ElfInterface::ReadAllHeaders() {
if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
return false;
}
- return ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+
+ // We could still potentially unwind without the section header
+ // information, so ignore any errors.
+ if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
+ log(0, "Malformed section header found, ignoring...");
+ }
+ return true;
}
template <typename EhdrType, typename PhdrType>
@@ -147,12 +162,39 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
}
// Skip the first header, it's always going to be NULL.
+ offset += ehdr.e_shentsize;
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
return false;
}
- if (shdr.sh_type == SHT_PROGBITS) {
+ if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
+ if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ // Need to go get the information about the section that contains
+ // the string terminated names.
+ ShdrType str_shdr;
+ if (shdr.sh_link >= ehdr.e_shnum) {
+ return false;
+ }
+ uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+ return false;
+ }
+ if (str_shdr.sh_type != SHT_STRTAB) {
+ return false;
+ }
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
+ sizeof(str_shdr.sh_offset))) {
+ return false;
+ }
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+ return false;
+ }
+ symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
+ str_shdr.sh_offset, str_shdr.sh_size));
+ } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
// Look for the .debug_frame and .gnu_debugdata.
if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
return false;
@@ -160,18 +202,20 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ uint64_t* offset_ptr = nullptr;
+ uint64_t* size_ptr = nullptr;
if (name == ".debug_frame") {
- if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
- memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
- debug_frame_offset_ = shdr.sh_offset;
- debug_frame_size_ = shdr.sh_size;
- }
+ offset_ptr = &debug_frame_offset_;
+ size_ptr = &debug_frame_size_;
} else if (name == ".gnu_debugdata") {
- if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
- memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
- gnu_debugdata_offset_ = shdr.sh_offset;
- gnu_debugdata_size_ = shdr.sh_size;
- }
+ offset_ptr = &gnu_debugdata_offset_;
+ size_ptr = &gnu_debugdata_size_;
+ }
+ if (offset_ptr != nullptr &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ *offset_ptr = shdr.sh_offset;
+ *size_ptr = shdr.sh_size;
}
}
}
@@ -228,7 +272,40 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
return true;
}
-bool ElfInterface::Step(uint64_t, Regs*, Memory*) {
+template <typename SymType>
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+ uint64_t* func_offset) {
+ if (symbols_.empty()) {
+ return false;
+ }
+
+ for (const auto symbol : symbols_) {
+ if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+ // Need to subtract off the load_bias to get the correct pc.
+ if (pc < load_bias_) {
+ return false;
+ }
+ pc -= load_bias_;
+
+ // Try the eh_frame first.
+ DwarfSection* eh_frame = eh_frame_.get();
+ if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) {
+ return true;
+ }
+
+ // Try the debug_frame next.
+ DwarfSection* debug_frame = debug_frame_.get();
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
+ return true;
+ }
+
return false;
}
@@ -247,3 +324,8 @@ template bool ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
+ uint64_t*);
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+ uint64_t*);
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h
index 1cc8aa0..d0d0d28 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/ElfInterface.h
@@ -30,6 +30,7 @@
// Forward declarations.
class Memory;
class Regs;
+class Symbols;
struct LoadInfo {
uint64_t offset;
@@ -46,7 +47,7 @@ enum : uint8_t {
class ElfInterface {
public:
ElfInterface(Memory* memory) : memory_(memory) {}
- virtual ~ElfInterface() = default;
+ virtual ~ElfInterface();
virtual bool Init() = 0;
@@ -94,6 +95,9 @@ class ElfInterface {
template <typename DynType>
bool GetSonameWithTemplate(std::string* soname);
+ template <typename SymType>
+ bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
virtual bool HandleType(uint64_t, uint32_t) { return false; }
Memory* memory_;
@@ -118,6 +122,8 @@ class ElfInterface {
std::unique_ptr<DwarfSection> eh_frame_;
std::unique_ptr<DwarfSection> debug_frame_;
+
+ std::vector<Symbols*> symbols_;
};
class ElfInterface32 : public ElfInterface {
@@ -135,8 +141,8 @@ class ElfInterface32 : public ElfInterface {
return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
}
};
@@ -155,8 +161,8 @@ class ElfInterface64 : public ElfInterface {
return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
}
};
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 81cdaf5..0f56ba8 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -79,9 +79,37 @@ class ElfInterfaceTest : public ::testing::Test {
template <typename ElfType>
void InitHeadersDebugFrameFail();
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersMalformed();
+
+ template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+ void InitSectionHeaders(uint64_t entry_size);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsets();
+
+ template <typename Sym>
+ void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name);
+
MemoryFake memory_;
};
+template <typename Sym>
+void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name) {
+ Sym sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_info = STT_FUNC;
+ sym.st_value = value;
+ sym.st_size = size;
+ sym.st_name = name_offset;
+ sym.st_shndx = SHN_COMMON;
+
+ memory_.SetMemory(offset, &sym, sizeof(sym));
+ memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1);
+}
+
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
void ElfInterfaceTest::SinglePtLoad() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
@@ -718,3 +746,178 @@ TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
InitHeadersDebugFrameFail<MockElfInterface64>();
}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformed() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = 0x1000;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(elf->Init());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+ InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+ InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x1000;
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = entry_size;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0xa000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for the entries.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
+ InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
+
+ ASSERT_TRUE(elf->Init());
+ EXPECT_EQ(0U, elf->debug_frame_offset());
+ EXPECT_EQ(0U, elf->debug_frame_size());
+ EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+ // Look in the first symbol table.
+ std::string name;
+ uint64_t name_offset;
+ ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+ EXPECT_EQ("function_one", name);
+ EXPECT_EQ(16U, name_offset);
+ ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+ EXPECT_EQ("function_two", name);
+ EXPECT_EQ(32U, name_offset);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsets() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ 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;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+ memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+ ASSERT_TRUE(elf->Init());
+ EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+ EXPECT_EQ(0x500U, elf->debug_frame_size());
+ EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
+ InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+ InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}