/* libunwind - a platform-independent unwind library
|
Copyright (C) 2003-2005 Hewlett-Packard Co
|
Copyright (C) 2007 David Mosberger-Tang
|
Contributed by David Mosberger-Tang <dmosberger@gmail.com>
|
|
This file is part of libunwind.
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
a copy of this software and associated documentation files (the
|
"Software"), to deal in the Software without restriction, including
|
without limitation the rights to use, copy, modify, merge, publish,
|
distribute, sublicense, and/or sell copies of the Software, and to
|
permit persons to whom the Software is furnished to do so, subject to
|
the following conditions:
|
|
The above copyright notice and this permission notice shall be
|
included in all copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
#include "libunwind_i.h"
|
|
#include <stdio.h>
|
#include <sys/param.h>
|
|
#if HAVE_LZMA
|
#include <7zCrc.h>
|
#include <Xz.h>
|
#include <XzCrc64.h>
|
#endif /* HAVE_LZMA */
|
|
// --------------------------------------------------------------------------
|
// Functions to read elf data from memory.
|
// --------------------------------------------------------------------------
|
extern size_t elf_w (memory_read) (
|
struct elf_image* ei, unw_word_t addr, uint8_t* buffer, size_t bytes, bool string_read) {
|
uintptr_t end = ei->u.memory.end;
|
unw_accessors_t* a = unw_get_accessors (ei->u.memory.as);
|
if (end - addr < bytes) {
|
bytes = end - addr;
|
}
|
size_t bytes_read = 0;
|
unw_word_t data_word;
|
size_t align_bytes = addr & (sizeof(unw_word_t) - 1);
|
if (align_bytes != 0) {
|
if ((*a->access_mem) (ei->u.memory.as, addr & ~(sizeof(unw_word_t) - 1), &data_word,
|
0, ei->u.memory.as_arg) != 0) {
|
return 0;
|
}
|
size_t copy_bytes = MIN(sizeof(unw_word_t) - align_bytes, bytes);
|
memcpy (buffer, (uint8_t*) (&data_word) + align_bytes, copy_bytes);
|
if (string_read) {
|
// Check for nul terminator.
|
uint8_t* nul_terminator = memchr (buffer, '\0', copy_bytes);
|
if (nul_terminator != NULL) {
|
return nul_terminator - buffer;
|
}
|
}
|
|
addr += copy_bytes;
|
bytes_read += copy_bytes;
|
bytes -= copy_bytes;
|
buffer += copy_bytes;
|
}
|
|
size_t num_words = bytes / sizeof(unw_word_t);
|
size_t i;
|
for (i = 0; i < num_words; i++) {
|
if ((*a->access_mem) (ei->u.memory.as, addr, &data_word, 0, ei->u.memory.as_arg) != 0) {
|
return bytes_read;
|
}
|
|
memcpy (buffer, &data_word, sizeof(unw_word_t));
|
if (string_read) {
|
// Check for nul terminator.
|
uint8_t* nul_terminator = memchr (buffer, '\0', sizeof(unw_word_t));
|
if (nul_terminator != NULL) {
|
return nul_terminator - buffer + bytes_read;
|
}
|
}
|
|
addr += sizeof(unw_word_t);
|
bytes_read += sizeof(unw_word_t);
|
buffer += sizeof(unw_word_t);
|
}
|
|
size_t left_over = bytes & (sizeof(unw_word_t) - 1);
|
if (left_over) {
|
if ((*a->access_mem) (ei->u.memory.as, addr, &data_word, 0, ei->u.memory.as_arg) != 0) {
|
return bytes_read;
|
}
|
|
memcpy (buffer, &data_word, left_over);
|
if (string_read) {
|
// Check for nul terminator.
|
uint8_t* nul_terminator = memchr (buffer, '\0', sizeof(unw_word_t));
|
if (nul_terminator != NULL) {
|
return nul_terminator - buffer + bytes_read;
|
}
|
}
|
|
bytes_read += left_over;
|
}
|
return bytes_read;
|
}
|
|
static bool elf_w (section_table_offset) (struct elf_image* ei, Elf_W(Ehdr)* ehdr, Elf_W(Off)* offset) {
|
GET_EHDR_FIELD(ei, ehdr, e_shoff, true);
|
GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
|
GET_EHDR_FIELD(ei, ehdr, e_shnum, true);
|
|
uintptr_t size = ei->u.memory.end - ei->u.memory.start;
|
if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size) {
|
Debug (1, "section table outside of image? (%lu > %lu)\n",
|
(unsigned long) (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize),
|
(unsigned long) size);
|
return false;
|
}
|
|
*offset = ehdr->e_shoff;
|
return true;
|
}
|
|
static bool elf_w (string_table_offset) (
|
struct elf_image* ei, int section, Elf_W(Ehdr)* ehdr, Elf_W(Off)* offset) {
|
GET_EHDR_FIELD(ei, ehdr, e_shoff, true);
|
GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
|
unw_word_t str_soff = ehdr->e_shoff + (section * ehdr->e_shentsize);
|
uintptr_t size = ei->u.memory.end - ei->u.memory.start;
|
if (str_soff + ehdr->e_shentsize > size) {
|
Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
|
(unsigned long) (str_soff + ehdr->e_shentsize),
|
(unsigned long) size);
|
return false;
|
}
|
|
Elf_W(Shdr) shdr;
|
GET_SHDR_FIELD(ei, str_soff, &shdr, sh_offset);
|
GET_SHDR_FIELD(ei, str_soff, &shdr, sh_size);
|
if (shdr.sh_offset + shdr.sh_size > size) {
|
Debug (1, "string table outside of image? (%lu > %lu)\n",
|
(unsigned long) (shdr.sh_offset + shdr.sh_size),
|
(unsigned long) size);
|
return false;
|
}
|
|
Debug (16, "strtab=0x%lx\n", (long) shdr.sh_offset);
|
*offset = shdr.sh_offset;
|
return true;
|
}
|
|
static bool elf_w (lookup_symbol_memory) (
|
unw_addr_space_t as, unw_word_t ip, struct elf_image* ei, Elf_W(Addr) load_offset,
|
char* buf, size_t buf_len, unw_word_t* offp, Elf_W(Ehdr)* ehdr) {
|
Elf_W(Off) shdr_offset;
|
if (!elf_w (section_table_offset) (ei, ehdr, &shdr_offset)) {
|
return false;
|
}
|
|
GET_EHDR_FIELD(ei, ehdr, e_shnum, true);
|
GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
|
int i;
|
for (i = 0; i < ehdr->e_shnum; ++i) {
|
Elf_W(Shdr) shdr;
|
GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_type);
|
switch (shdr.sh_type) {
|
case SHT_SYMTAB:
|
case SHT_DYNSYM:
|
{
|
GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_link);
|
|
Elf_W(Off) strtab_offset;
|
if (!elf_w (string_table_offset) (ei, shdr.sh_link, ehdr, &strtab_offset)) {
|
continue;
|
}
|
|
GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_offset);
|
GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_size);
|
GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_entsize);
|
|
Debug (16, "symtab=0x%lx[%d]\n", (long) shdr.sh_offset, shdr.sh_type);
|
|
unw_word_t sym_offset;
|
unw_word_t symtab_end = shdr.sh_offset + shdr.sh_size;
|
for (sym_offset = shdr.sh_offset;
|
sym_offset < symtab_end;
|
sym_offset += shdr.sh_entsize) {
|
Elf_W(Sym) sym;
|
GET_SYM_FIELD(ei, sym_offset, &sym, st_info);
|
GET_SYM_FIELD(ei, sym_offset, &sym, st_shndx);
|
|
if (ELF_W (ST_TYPE) (sym.st_info) == STT_FUNC && sym.st_shndx != SHN_UNDEF) {
|
GET_SYM_FIELD(ei, sym_offset, &sym, st_value);
|
Elf_W(Addr) val;
|
if (tdep_get_func_addr (as, sym.st_value, &val) < 0) {
|
continue;
|
}
|
if (sym.st_shndx != SHN_ABS) {
|
val += load_offset;
|
}
|
Debug (16, "0x%016lx info=0x%02x\n", (long) val, sym.st_info);
|
|
GET_SYM_FIELD(ei, sym_offset, &sym, st_size);
|
if (ip >= val && (Elf_W(Addr)) (ip - val) < sym.st_size) {
|
GET_SYM_FIELD(ei, sym_offset, &sym, st_name);
|
uintptr_t size = ei->u.memory.end - ei->u.memory.start;
|
Elf_W(Off) strname_offset = strtab_offset + sym.st_name;
|
if (strname_offset > size || strname_offset < strtab_offset) {
|
// Malformed elf symbol table.
|
break;
|
}
|
|
size_t bytes_read = elf_w (memory_read) (
|
ei, ei->u.memory.start + strname_offset,
|
(uint8_t*) buf, buf_len, true);
|
if (bytes_read == 0) {
|
// Empty name, so keep checking the other symbol tables
|
// for a possible match.
|
break;
|
}
|
// Ensure the string is nul terminated, it is assumed that
|
// sizeof(buf) >= buf_len + 1.
|
buf[buf_len] = '\0';
|
|
if (offp != NULL) {
|
*offp = ip - val;
|
}
|
return true;
|
}
|
}
|
}
|
break;
|
}
|
|
default:
|
break;
|
}
|
shdr_offset += ehdr->e_shentsize;
|
}
|
return false;
|
}
|
|
static bool elf_w (get_load_offset_memory) (
|
struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
|
Elf_W(Ehdr)* ehdr, Elf_W(Addr)* load_offset) {
|
GET_EHDR_FIELD(ei, ehdr, e_phoff, true);
|
GET_EHDR_FIELD(ei, ehdr, e_phnum, true);
|
|
unw_word_t offset = ehdr->e_phoff;
|
int i;
|
for (i = 0; i < ehdr->e_phnum; ++i) {
|
Elf_W(Phdr) phdr;
|
GET_PHDR_FIELD(ei, offset, &phdr, p_type);
|
if (phdr.p_type == PT_LOAD) {
|
GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
|
if (phdr.p_offset == mapoff) {
|
GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
|
*load_offset = segbase - phdr.p_vaddr;
|
return true;
|
}
|
}
|
offset += sizeof(Elf_W(Phdr));
|
}
|
return false;
|
}
|
|
// --------------------------------------------------------------------------
|
// Functions to read elf data from the mapped elf image.
|
// --------------------------------------------------------------------------
|
static Elf_W(Shdr)* elf_w (section_table) (struct elf_image* ei) {
|
Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
|
Elf_W(Off) soff = ehdr->e_shoff;
|
if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->u.mapped.size) {
|
Debug (1, "section table outside of image? (%lu > %lu)\n",
|
(unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
|
(unsigned long) ei->u.mapped.size);
|
return NULL;
|
}
|
|
return (Elf_W(Shdr) *) ((char *) ei->u.mapped.image + soff);
|
}
|
|
static char* elf_w (string_table) (struct elf_image* ei, int section) {
|
Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
|
Elf_W(Off) str_soff = ehdr->e_shoff + (section * ehdr->e_shentsize);
|
if (str_soff + ehdr->e_shentsize > ei->u.mapped.size) {
|
Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
|
(unsigned long) (str_soff + ehdr->e_shentsize),
|
(unsigned long) ei->u.mapped.size);
|
return NULL;
|
}
|
Elf_W(Shdr)* str_shdr = (Elf_W(Shdr) *) ((char *) ei->u.mapped.image + str_soff);
|
|
if (str_shdr->sh_offset + str_shdr->sh_size > ei->u.mapped.size) {
|
Debug (1, "string table outside of image? (%lu > %lu)\n",
|
(unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
|
(unsigned long) ei->u.mapped.size);
|
return NULL;
|
}
|
|
Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
|
return (char*) ((uintptr_t) ei->u.mapped.image + str_shdr->sh_offset);
|
}
|
|
static bool elf_w (lookup_symbol_mapped) (
|
unw_addr_space_t as, unw_word_t ip, struct elf_image* ei, Elf_W(Addr) load_offset,
|
char* buf, size_t buf_len, unw_word_t* offp) {
|
|
Elf_W(Shdr)* shdr = elf_w (section_table) (ei);
|
if (!shdr) {
|
return false;
|
}
|
|
Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
|
int i;
|
for (i = 0; i < ehdr->e_shnum; ++i) {
|
switch (shdr->sh_type) {
|
case SHT_SYMTAB:
|
case SHT_DYNSYM:
|
{
|
Elf_W(Sym)* symtab = (Elf_W(Sym) *) ((char *) ei->u.mapped.image + shdr->sh_offset);
|
Elf_W(Sym)* symtab_end = (Elf_W(Sym) *) ((char *) symtab + shdr->sh_size);
|
|
char* strtab = elf_w (string_table) (ei, shdr->sh_link);
|
if (!strtab) {
|
continue;
|
}
|
|
Debug (16, "symtab=0x%lx[%d]\n", (long) shdr->sh_offset, shdr->sh_type);
|
|
Elf_W(Sym)* sym;
|
for (sym = symtab;
|
sym < symtab_end;
|
sym = (Elf_W(Sym) *) ((char *) sym + shdr->sh_entsize)) {
|
if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC && sym->st_shndx != SHN_UNDEF) {
|
Elf_W(Addr) val;
|
if (tdep_get_func_addr (as, sym->st_value, &val) < 0) {
|
continue;
|
}
|
if (sym->st_shndx != SHN_ABS) {
|
val += load_offset;
|
}
|
Debug (16, "0x%016lx info=0x%02x\n", (long) val, sym->st_info);
|
if (ip >= val && (Elf_W(Addr)) (ip - val) < sym->st_size) {
|
char* str_name = strtab + sym->st_name;
|
if (str_name > (char*) ei->u.mapped.image + ei->u.mapped.size ||
|
str_name < strtab) {
|
// Malformed elf symbol table.
|
break;
|
}
|
|
// Make sure we don't try and read past the end of the image.
|
uintptr_t max_size = (uintptr_t) str_name - (uintptr_t) ei->u.mapped.image;
|
if (buf_len > max_size) {
|
buf_len = max_size;
|
}
|
|
strncpy (buf, str_name, buf_len);
|
// Ensure the string is nul terminated, it is assumed that
|
// sizeof(buf) >= buf_len + 1.
|
buf[buf_len] = '\0';
|
if (buf[0] == '\0') {
|
// Empty name, so keep checking the other symbol tables
|
// for a possible match.
|
break;
|
}
|
if (offp != NULL) {
|
*offp = ip - val;
|
}
|
return true;
|
}
|
}
|
}
|
break;
|
}
|
|
default:
|
break;
|
}
|
shdr = (Elf_W(Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
|
}
|
return false;
|
}
|
|
static bool elf_w (get_load_offset_mapped) (
|
struct elf_image *ei, unsigned long segbase, unsigned long mapoff, Elf_W(Addr)* load_offset) {
|
Elf_W(Ehdr) *ehdr = ei->u.mapped.image;
|
Elf_W(Phdr) *phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
|
|
int i;
|
for (i = 0; i < ehdr->e_phnum; ++i) {
|
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) {
|
*load_offset = segbase - phdr[i].p_vaddr;
|
return true;
|
}
|
}
|
return false;
|
}
|
|
static Elf_W(Addr) elf_w (get_min_vaddr_mapped) (struct elf_image *ei) {
|
Elf_W(Ehdr) *ehdr = ei->u.mapped.image;
|
Elf_W(Phdr) *phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
|
Elf_W(Addr) min_vaddr = ~0u;
|
int i;
|
for (i = 0; i < ehdr->e_phnum; ++i) {
|
if (phdr[i].p_type == PT_LOAD && phdr[i].p_vaddr < min_vaddr) {
|
min_vaddr = phdr[i].p_vaddr;
|
}
|
}
|
return min_vaddr;
|
}
|
|
// --------------------------------------------------------------------------
|
|
static inline bool elf_w (lookup_symbol) (
|
unw_addr_space_t as, unw_word_t ip, struct elf_image *ei, Elf_W(Addr) load_offset,
|
char *buf, size_t buf_len, unw_word_t* offp, Elf_W(Ehdr)* ehdr) {
|
if (!ei->valid)
|
return false;
|
|
if (buf_len <= 1) {
|
Debug (1, "lookup_symbol called with a buffer too small to hold a name %zu\n", buf_len);
|
return false;
|
}
|
|
// Leave enough space for the nul terminator.
|
buf_len--;
|
|
if (ei->mapped) {
|
return elf_w (lookup_symbol_mapped) (as, ip, ei, load_offset, buf, buf_len, offp);
|
} else {
|
return elf_w (lookup_symbol_memory) (as, ip, ei, load_offset, buf, buf_len, offp, ehdr);
|
}
|
}
|
|
static bool elf_w (get_load_offset) (
|
struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
|
Elf_W(Ehdr)* ehdr, Elf_W(Addr)* load_offset) {
|
if (ei->mapped) {
|
return elf_w (get_load_offset_mapped) (ei, segbase, mapoff, load_offset);
|
} else {
|
return elf_w (get_load_offset_memory) (ei, segbase, mapoff, ehdr, load_offset);
|
}
|
}
|
|
/* ANDROID support update. */
|
static void* xz_alloc(void* p, size_t size) {
|
return malloc(size);
|
}
|
|
static void xz_free(void* p, void* address) {
|
free(address);
|
}
|
|
HIDDEN bool
|
elf_w (xz_decompress) (uint8_t* src, size_t src_size,
|
uint8_t** dst, size_t* dst_size) {
|
#if HAVE_LZMA
|
size_t src_offset = 0;
|
size_t dst_offset = 0;
|
size_t src_remaining;
|
size_t dst_remaining;
|
ISzAlloc alloc;
|
CXzUnpacker state;
|
ECoderStatus status;
|
alloc.Alloc = xz_alloc;
|
alloc.Free = xz_free;
|
XzUnpacker_Construct(&state, &alloc);
|
CrcGenerateTable();
|
Crc64GenerateTable();
|
*dst_size = 2 * src_size;
|
*dst = NULL;
|
do {
|
*dst_size *= 2;
|
*dst = realloc(*dst, *dst_size);
|
if (*dst == NULL) {
|
Debug (1, "LZMA decompression failed due to failed realloc.\n");
|
XzUnpacker_Free(&state);
|
return false;
|
}
|
src_remaining = src_size - src_offset;
|
dst_remaining = *dst_size - dst_offset;
|
int res = XzUnpacker_Code(&state,
|
*dst + dst_offset, &dst_remaining,
|
src + src_offset, &src_remaining,
|
CODER_FINISH_ANY, &status);
|
if (res != SZ_OK) {
|
Debug (1, "LZMA decompression failed with error %d\n", res);
|
free(*dst);
|
XzUnpacker_Free(&state);
|
return false;
|
}
|
src_offset += src_remaining;
|
dst_offset += dst_remaining;
|
} while (status == CODER_STATUS_NOT_FINISHED);
|
XzUnpacker_Free(&state);
|
if (!XzUnpacker_IsStreamWasFinished(&state)) {
|
Debug (1, "LZMA decompression failed due to incomplete stream.\n");
|
free(*dst);
|
return false;
|
}
|
*dst_size = dst_offset;
|
*dst = realloc(*dst, *dst_size);
|
return true;
|
#else
|
Debug (1, "Decompression failed - compiled without LZMA support.\n");
|
return false;
|
#endif // HAVE_LZMA
|
}
|
|
HIDDEN bool
|
elf_w (find_section_mapped) (struct elf_image *ei, const char* name,
|
uint8_t** section, size_t* size, Elf_W(Addr)* vaddr) {
|
Elf_W (Ehdr) *ehdr = ei->u.mapped.image;
|
Elf_W (Shdr) *shdr;
|
char *strtab;
|
int i;
|
|
if (!ei->valid || !ei->mapped) {
|
return false;
|
}
|
|
shdr = elf_w (section_table) (ei);
|
if (!shdr) {
|
return false;
|
}
|
|
strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
|
if (!strtab) {
|
return false;
|
}
|
|
for (i = 0; i < ehdr->e_shnum; ++i) {
|
if (strcmp (strtab + shdr->sh_name, name) == 0) {
|
if (section != NULL && size != NULL) {
|
if (shdr->sh_offset + shdr->sh_size > ei->u.mapped.size) {
|
Debug (1, "section %s outside image? (0x%lu > 0x%lu)\n", name,
|
(unsigned long) (shdr->sh_offset + shdr->sh_size),
|
(unsigned long) ei->u.mapped.size);
|
return false;
|
}
|
*section = ((uint8_t *) ei->u.mapped.image) + shdr->sh_offset;
|
*size = shdr->sh_size;
|
}
|
if (vaddr != NULL) {
|
*vaddr = shdr->sh_addr;
|
}
|
return true;
|
}
|
shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
|
}
|
return false;
|
}
|
/* ANDROID support update. */
|
|
// Find the ELF image that contains IP and return the procedure name from
|
// the symbol table that matches the IP.
|
HIDDEN bool elf_w (get_proc_name_in_image) (
|
unw_addr_space_t as, struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
|
unw_word_t ip, char* buf, size_t buf_len, unw_word_t* offp) {
|
Elf_W(Ehdr) ehdr;
|
memset(&ehdr, 0, sizeof(ehdr));
|
Elf_W(Addr) load_offset;
|
if (!elf_w (get_load_offset) (ei, segbase, mapoff, &ehdr, &load_offset)) {
|
return false;
|
}
|
|
if (elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, offp, &ehdr) != 0) {
|
return true;
|
}
|
|
// If the ELF image doesn't contain a match, look up the symbol in
|
// the MiniDebugInfo.
|
if (ei->mapped && ei->mini_debug_info_data) {
|
struct elf_image mdi;
|
mdi.mapped = true;
|
mdi.u.mapped.image = ei->mini_debug_info_data;
|
mdi.u.mapped.size = ei->mini_debug_info_size;
|
mdi.valid = elf_w (valid_object_mapped) (&mdi);
|
// The ELF file might have been relocated after the debug
|
// information has been compresses and embedded.
|
ElfW(Addr) ei_text_address, mdi_text_address;
|
if (elf_w (find_section_mapped) (ei, ".text", NULL, NULL, &ei_text_address) &&
|
elf_w (find_section_mapped) (&mdi, ".text", NULL, NULL, &mdi_text_address)) {
|
load_offset += ei_text_address - mdi_text_address;
|
}
|
bool ret_val = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf, buf_len, offp, &ehdr);
|
return ret_val;
|
}
|
return false;
|
}
|
|
HIDDEN bool elf_w (get_proc_name) (
|
unw_addr_space_t as, pid_t pid, unw_word_t ip, char* buf, size_t buf_len,
|
unw_word_t* offp, void* as_arg) {
|
unsigned long segbase, mapoff;
|
struct elf_image ei;
|
|
if (tdep_get_elf_image(as, &ei, pid, ip, &segbase, &mapoff, NULL, as_arg) < 0) {
|
return false;
|
}
|
|
return elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
|
}
|
|
HIDDEN bool elf_w (get_load_base) (struct elf_image* ei, unw_word_t mapoff, unw_word_t* load_base) {
|
if (!ei->valid) {
|
return false;
|
}
|
|
if (ei->mapped) {
|
Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
|
Elf_W(Phdr)* phdr = (Elf_W(Phdr)*) ((char*) ei->u.mapped.image + ehdr->e_phoff);
|
int i;
|
for (i = 0; i < ehdr->e_phnum; ++i) {
|
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) {
|
*load_base = phdr[i].p_vaddr;
|
return true;
|
}
|
}
|
return false;
|
} else {
|
Elf_W(Ehdr) ehdr;
|
GET_EHDR_FIELD(ei, &ehdr, e_phnum, false);
|
GET_EHDR_FIELD(ei, &ehdr, e_phoff, false);
|
int i;
|
unw_word_t offset = ehdr.e_phoff;
|
for (i = 0; i < ehdr.e_phnum; ++i) {
|
Elf_W(Phdr) phdr;
|
GET_PHDR_FIELD(ei, offset, &phdr, p_type);
|
GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
|
// Always use zero as the map offset for in memory maps.
|
// The dlopen of a shared library from an APK will result in a
|
// non-zero map offset which would mean we would never find the
|
// correct program header using the passed in map offset.
|
if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
|
GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
|
*load_base = phdr.p_vaddr;
|
return true;
|
}
|
offset += sizeof(phdr);
|
}
|
return false;
|
}
|
return false;
|
}
|