Merge "Add proper support for embedded elf files."
am: 537c68c8ed
Change-Id: Ia28dbb1bbd02d54602a6256295cccf2def9caf04
This commit is contained in:
commit
863fcdb0ca
8 changed files with 210 additions and 40 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ struct MapInfo {
|
||||||
// instead of a portion of the file.
|
// instead of a portion of the file.
|
||||||
uint64_t elf_offset;
|
uint64_t elf_offset;
|
||||||
|
|
||||||
|
Memory* GetFileMemory();
|
||||||
Memory* CreateMemory(pid_t pid);
|
Memory* CreateMemory(pid_t pid);
|
||||||
// This function guarantees it will never return nullptr.
|
// This function guarantees it will never return nullptr.
|
||||||
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
|
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
|
||||||
|
|
|
||||||
|
|
@ -38,30 +38,50 @@ namespace unwindstack {
|
||||||
|
|
||||||
class MapInfoCreateMemoryTest : public ::testing::Test {
|
class MapInfoCreateMemoryTest : public ::testing::Test {
|
||||||
protected:
|
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() {
|
static void SetUpTestCase() {
|
||||||
std::vector<uint8_t> buffer(1024);
|
std::vector<uint8_t> buffer(1024);
|
||||||
|
memset(buffer.data(), 0, buffer.size());
|
||||||
memcpy(buffer.data(), ELFMAG, SELFMAG);
|
memcpy(buffer.data(), ELFMAG, SELFMAG);
|
||||||
for (size_t i = SELFMAG; i < buffer.size(); i++) {
|
buffer[EI_CLASS] = ELFCLASS32;
|
||||||
buffer[i] = i / 256 + 1;
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
|
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
|
||||||
|
|
||||||
for (size_t i = 0; i < 0x100; i++) {
|
memset(buffer.data(), 0, buffer.size());
|
||||||
buffer[i] = i / 256 + 1;
|
|
||||||
}
|
|
||||||
memcpy(&buffer[0x100], ELFMAG, SELFMAG);
|
memcpy(&buffer[0x100], ELFMAG, SELFMAG);
|
||||||
for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
|
buffer[0x100 + EI_CLASS] = ELFCLASS64;
|
||||||
buffer[i] = i / 256 + 1;
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
|
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_;
|
||||||
|
|
||||||
static TemporaryFile elf_at_100_;
|
static TemporaryFile elf_at_100_;
|
||||||
|
|
||||||
|
static TemporaryFile elf32_at_map_;
|
||||||
|
static TemporaryFile elf64_at_map_;
|
||||||
};
|
};
|
||||||
TemporaryFile MapInfoCreateMemoryTest::elf_;
|
TemporaryFile MapInfoCreateMemoryTest::elf_;
|
||||||
TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
|
TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
|
||||||
|
TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
|
||||||
|
TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
|
||||||
|
|
||||||
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
|
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
|
||||||
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
|
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);
|
std::vector<uint8_t> buffer(1024);
|
||||||
ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
|
ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
|
||||||
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
|
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
|
||||||
for (size_t i = SELFMAG; i < buffer.size(); i++) {
|
ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
|
||||||
ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
|
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));
|
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);
|
std::vector<uint8_t> buffer(0x100);
|
||||||
ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
|
ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
|
||||||
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
|
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
|
||||||
for (size_t i = SELFMAG; i < buffer.size(); i++) {
|
ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
|
||||||
ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
|
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));
|
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.
|
// Verify that device file names will never result in Memory object creation.
|
||||||
TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
|
TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
|
||||||
// Set up some memory so that a valid local memory object would
|
// Set up some memory so that a valid local memory object would
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,11 @@
|
||||||
#include <unwindstack/Memory.h>
|
#include <unwindstack/Memory.h>
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
if (argc != 2) {
|
if (argc != 2 && argc != 3) {
|
||||||
printf("Need to pass the name of an elf file to the program.\n");
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,6 +46,16 @@ int main(int argc, char** argv) {
|
||||||
return 1;
|
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.
|
// Send all log messages to stdout.
|
||||||
unwindstack::log_to_stdout(true);
|
unwindstack::log_to_stdout(true);
|
||||||
|
|
||||||
|
|
@ -76,9 +89,24 @@ int main(int argc, char** argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a crude way to get the symbols in order.
|
|
||||||
std::string name;
|
std::string name;
|
||||||
uint64_t load_bias = elf.interface()->load_bias();
|
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()) {
|
for (const auto& entry : elf.interface()->pt_loads()) {
|
||||||
uint64_t start = entry.second.offset + load_bias;
|
uint64_t start = entry.second.offset + load_bias;
|
||||||
uint64_t end = entry.second.table_size + load_bias;
|
uint64_t end = entry.second.table_size + load_bias;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue