Merge "Add proper support for embedded elf files."
am: 537c68c8ed
Change-Id: Ia28dbb1bbd02d54602a6256295cccf2def9caf04
This commit is contained in:
commit
863fcdb0ca
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue