Merge "Add proper support for embedded elf files."
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 @@
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 @@
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<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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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;