summaryrefslogtreecommitdiff
path: root/libunwindstack
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2017-08-30 13:15:19 -0700
committerChristopher Ferris <cferris@google.com>2017-08-30 15:50:11 -0700
commit33f2ad27e04995e087503074feacafe1f1d40366 (patch)
treeb1ee3daae03df46f5d17c57391451505b67f47d7 /libunwindstack
parent0c643c8199f0a89cb8b49d6296fce995b3d25bdb (diff)
downloadunwinding-33f2ad27e04995e087503074feacafe1f1d40366.tar.gz
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
Diffstat (limited to 'libunwindstack')
-rw-r--r--libunwindstack/Elf.cpp22
-rw-r--r--libunwindstack/ElfInterface.cpp19
-rw-r--r--libunwindstack/MapInfo.cpp77
-rw-r--r--libunwindstack/include/unwindstack/Elf.h2
-rw-r--r--libunwindstack/include/unwindstack/ElfInterface.h11
-rw-r--r--libunwindstack/include/unwindstack/MapInfo.h1
-rw-r--r--libunwindstack/tests/MapInfoCreateMemoryTest.cpp84
-rw-r--r--libunwindstack/tools/unwind_symbols.cpp34
8 files changed, 210 insertions, 40 deletions
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4fc7c67..4f7476d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -124,6 +124,28 @@ bool Elf::IsValidElf(Memory* memory) {
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) {
if (!IsValidElf(memory)) {
return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 75abc85..be4f88a 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -370,6 +370,22 @@ bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
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.
template void ElfInterface::InitHeadersWithTemplate<uint32_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*,
uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
+
} // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index d0e1216..3272215 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -27,6 +27,55 @@
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) {
if (end <= start) {
return nullptr;
@@ -40,33 +89,13 @@ Memory* MapInfo::CreateMemory(pid_t pid) {
if (flags & MAPS_FLAGS_DEVICE_MAP) {
return nullptr;
}
-
- std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
- uint64_t map_size;
- 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 = GetFileMemory();
+ if (memory != nullptr) {
+ return memory;
}
}
- Memory* memory = nullptr;
+ Memory* memory;
if (pid == getpid()) {
memory = new MemoryLocal();
} else {
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index d89a746..4e7eb34 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -70,6 +70,8 @@ class Elf {
static bool IsValidElf(Memory* memory);
+ static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+
protected:
bool valid_ = false;
std::unique_ptr<ElfInterface> interface_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5cac0d3..142a625 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -102,6 +102,9 @@ class ElfInterface {
virtual bool HandleType(uint64_t, uint32_t) { return false; }
+ template <typename EhdrType>
+ static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+
Memory* memory_;
std::unordered_map<uint64_t, LoadInfo> pt_loads_;
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 {
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 {
@@ -166,6 +173,10 @@ class ElfInterface64 : public ElfInterface {
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
}
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
+ }
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 1854767..b8ba925 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -40,6 +40,7 @@ struct MapInfo {
// instead of a portion of the file.
uint64_t elf_offset;
+ Memory* GetFileMemory();
Memory* CreateMemory(pid_t pid);
// This function guarantees it will never return nullptr.
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 9e45e78..2aab9c6 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -38,30 +38,50 @@ namespace unwindstack {
class MapInfoCreateMemoryTest : public ::testing::Test {
protected:
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
+ std::vector<uint8_t> buffer(20000);
+ memset(buffer.data(), 0, buffer.size());
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
+ }
+
static void SetUpTestCase() {
std::vector<uint8_t> buffer(1024);
+ memset(buffer.data(), 0, buffer.size());
memcpy(buffer.data(), ELFMAG, SELFMAG);
- for (size_t i = SELFMAG; i < buffer.size(); i++) {
- buffer[i] = i / 256 + 1;
- }
+ buffer[EI_CLASS] = ELFCLASS32;
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- for (size_t i = 0; i < 0x100; i++) {
- buffer[i] = i / 256 + 1;
- }
+ memset(buffer.data(), 0, buffer.size());
memcpy(&buffer[0x100], ELFMAG, SELFMAG);
- for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
- buffer[i] = i / 256 + 1;
- }
+ buffer[0x100 + EI_CLASS] = ELFCLASS64;
ASSERT_TRUE(android::base::WriteFully(elf_at_100_.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);
}
static TemporaryFile elf_;
static TemporaryFile elf_at_100_;
+
+ static TemporaryFile elf32_at_map_;
+ static TemporaryFile elf64_at_map_;
};
TemporaryFile MapInfoCreateMemoryTest::elf_;
TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
+TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
@@ -93,8 +113,9 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
std::vector<uint8_t> buffer(1024);
ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
- for (size_t i = SELFMAG; i < buffer.size(); i++) {
- ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+ ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
@@ -113,13 +134,50 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
std::vector<uint8_t> buffer(0x100);
ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
- for (size_t i = SELFMAG; i < buffer.size(); i++) {
- ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+ ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
}
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
// Verify that device file names will never result in Memory object creation.
TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
// Set up some memory so that a valid local memory object would
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index b757c1e..dc9ae5a 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -28,8 +28,11 @@
#include <unwindstack/Memory.h>
int main(int argc, char** argv) {
- if (argc != 2) {
- printf("Need to pass the name of an elf file to the program.\n");
+ if (argc != 2 && argc != 3) {
+ printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
+ printf(" Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
+ printf(" specified, then get the function at that address.\n");
+ printf(" FUNC_ADDRESS must be a hex number.\n");
return 1;
}
@@ -43,6 +46,16 @@ int main(int argc, char** argv) {
return 1;
}
+ uint64_t func_addr;
+ if (argc == 3) {
+ char* name;
+ func_addr = strtoull(argv[2], &name, 16);
+ if (*name != '\0') {
+ printf("%s is not a hex number.\n", argv[2]);
+ return 1;
+ }
+ }
+
// Send all log messages to stdout.
unwindstack::log_to_stdout(true);
@@ -76,9 +89,24 @@ int main(int argc, char** argv) {
return 1;
}
- // This is a crude way to get the symbols in order.
std::string name;
uint64_t load_bias = elf.interface()->load_bias();
+ if (argc == 3) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
+ printf("No known function at 0x%" PRIx64 "\n", func_addr);
+ return 1;
+ }
+ printf("<0x%" PRIx64 ">", func_addr - func_offset);
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(": %s\n", cur_name.c_str());
+ return 0;
+ }
+
+ // This is a crude way to get the symbols in order.
for (const auto& entry : elf.interface()->pt_loads()) {
uint64_t start = entry.second.offset + load_bias;
uint64_t end = entry.second.table_size + load_bias;