Merge "Improve stack unwinder robustness."
This commit is contained in:
commit
8e4e894acf
|
@ -87,7 +87,7 @@ static void dump_memory_and_code(int tfd, pid_t tid, bool at_fault) {
|
|||
}
|
||||
}
|
||||
|
||||
void dump_registers(ptrace_context_t* context __attribute((unused)),
|
||||
void dump_registers(const ptrace_context_t* context __attribute((unused)),
|
||||
int tfd, pid_t tid, bool at_fault)
|
||||
{
|
||||
struct pt_regs r;
|
||||
|
@ -125,7 +125,7 @@ void dump_registers(ptrace_context_t* context __attribute((unused)),
|
|||
#endif
|
||||
}
|
||||
|
||||
void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
|
||||
void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
|
||||
dump_registers(context, tfd, tid, at_fault);
|
||||
|
||||
dump_backtrace_and_stack(context, tfd, tid, at_fault);
|
||||
|
|
|
@ -114,12 +114,12 @@ static const char *get_sigcode(int signo, int code)
|
|||
return "?";
|
||||
}
|
||||
|
||||
static void dump_fault_addr(int tfd, pid_t pid, int sig)
|
||||
static void dump_fault_addr(int tfd, pid_t tid, int sig)
|
||||
{
|
||||
siginfo_t si;
|
||||
|
||||
memset(&si, 0, sizeof(si));
|
||||
if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){
|
||||
if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
|
||||
_LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno));
|
||||
} else if (signal_has_address(sig)) {
|
||||
_LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n",
|
||||
|
@ -157,7 +157,7 @@ static void dump_crash_banner(int tfd, pid_t pid, pid_t tid, int sig)
|
|||
}
|
||||
|
||||
/* Return true if some thread is not detached cleanly */
|
||||
static bool dump_sibling_thread_report(ptrace_context_t* context,
|
||||
static bool dump_sibling_thread_report(const ptrace_context_t* context,
|
||||
int tfd, pid_t pid, pid_t tid) {
|
||||
char task_path[64];
|
||||
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
|
||||
|
@ -361,7 +361,7 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal,
|
|||
|
||||
dump_crash_banner(tfd, pid, tid, signal);
|
||||
|
||||
ptrace_context_t* context = load_ptrace_context(pid);
|
||||
ptrace_context_t* context = load_ptrace_context(tid);
|
||||
|
||||
dump_thread(context, tfd, tid, true);
|
||||
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
#include <corkscrew/backtrace.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
|
||||
void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
|
||||
|
||||
#endif // _DEBUGGERD_MACHINE_H
|
||||
|
|
|
@ -57,8 +57,8 @@ bool signal_has_address(int sig) {
|
|||
}
|
||||
}
|
||||
|
||||
static void dump_backtrace(ptrace_context_t* context __attribute((unused)),
|
||||
int tfd, int pid __attribute((unused)), bool at_fault,
|
||||
static void dump_backtrace(const ptrace_context_t* context __attribute((unused)),
|
||||
int tfd, pid_t tid __attribute((unused)), bool at_fault,
|
||||
const backtrace_frame_t* backtrace, size_t frames) {
|
||||
_LOG(tfd, !at_fault, "\nbacktrace:\n");
|
||||
|
||||
|
@ -66,7 +66,7 @@ static void dump_backtrace(ptrace_context_t* context __attribute((unused)),
|
|||
get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
const backtrace_symbol_t* symbol = &backtrace_symbols[i];
|
||||
const char* map_name = symbol->map_info ? symbol->map_info->name : "<unknown>";
|
||||
const char* map_name = symbol->map_name ? symbol->map_name : "<unknown>";
|
||||
const char* symbol_name = symbol->demangled_name ? symbol->demangled_name : symbol->name;
|
||||
if (symbol_name) {
|
||||
_LOG(tfd, !at_fault, " #%02d pc %08x %s (%s)\n",
|
||||
|
@ -79,11 +79,11 @@ static void dump_backtrace(ptrace_context_t* context __attribute((unused)),
|
|||
free_backtrace_symbols(backtrace_symbols, frames);
|
||||
}
|
||||
|
||||
static void dump_stack_segment(ptrace_context_t* context, int tfd, int pid,
|
||||
static void dump_stack_segment(const ptrace_context_t* context, int tfd, pid_t tid,
|
||||
bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
|
||||
for (size_t i = 0; i < words; i++) {
|
||||
uint32_t stack_content;
|
||||
if (!try_get_word(pid, *sp, &stack_content)) {
|
||||
if (!try_get_word_ptrace(tid, *sp, &stack_content)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ static void dump_stack_segment(ptrace_context_t* context, int tfd, int pid,
|
|||
}
|
||||
}
|
||||
|
||||
static void dump_stack(ptrace_context_t* context, int tfd, int pid, bool at_fault,
|
||||
static void dump_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault,
|
||||
const backtrace_frame_t* backtrace, size_t frames) {
|
||||
bool have_first = false;
|
||||
size_t first, last;
|
||||
|
@ -138,7 +138,7 @@ static void dump_stack(ptrace_context_t* context, int tfd, int pid, bool at_faul
|
|||
// Dump a few words before the first frame.
|
||||
bool only_in_tombstone = !at_fault;
|
||||
uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
|
||||
dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, STACK_WORDS, -1);
|
||||
dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, -1);
|
||||
|
||||
// Dump a few words from all successive frames.
|
||||
// Only log the first 3 frames, put the rest in the tombstone.
|
||||
|
@ -152,7 +152,7 @@ static void dump_stack(ptrace_context_t* context, int tfd, int pid, bool at_faul
|
|||
only_in_tombstone = true;
|
||||
}
|
||||
if (i == last) {
|
||||
dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, STACK_WORDS, i);
|
||||
dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, i);
|
||||
if (sp < frame->stack_top + frame->stack_size) {
|
||||
_LOG(tfd, only_in_tombstone, " ........ ........\n");
|
||||
}
|
||||
|
@ -163,12 +163,13 @@ static void dump_stack(ptrace_context_t* context, int tfd, int pid, bool at_faul
|
|||
} else if (words > STACK_WORDS) {
|
||||
words = STACK_WORDS;
|
||||
}
|
||||
dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, words, i);
|
||||
dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, words, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_backtrace_and_stack(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
|
||||
void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid,
|
||||
bool at_fault) {
|
||||
backtrace_frame_t backtrace[STACK_DEPTH];
|
||||
ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
|
||||
if (frames > 0) {
|
||||
|
@ -237,7 +238,7 @@ void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault) {
|
|||
}
|
||||
}
|
||||
|
||||
void dump_nearby_maps(ptrace_context_t* context, int tfd, pid_t tid) {
|
||||
void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid) {
|
||||
siginfo_t si;
|
||||
memset(&si, 0, sizeof(si));
|
||||
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
|
||||
|
|
|
@ -52,7 +52,7 @@ bool signal_has_address(int sig);
|
|||
/*
|
||||
* Dumps the backtrace and contents of the stack.
|
||||
*/
|
||||
void dump_backtrace_and_stack(ptrace_context_t* context, int tfd, pid_t pid, bool at_fault);
|
||||
void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
|
||||
|
||||
/*
|
||||
* Dumps a few bytes of memory, starting a bit before and ending a bit
|
||||
|
@ -66,7 +66,7 @@ void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault);
|
|||
*
|
||||
* This only makes sense to do on the thread that crashed.
|
||||
*/
|
||||
void dump_nearby_maps(ptrace_context_t* context, int tfd, pid_t tid);
|
||||
void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid);
|
||||
|
||||
|
||||
#endif // _DEBUGGERD_UTILITY_H
|
||||
|
|
|
@ -39,12 +39,12 @@
|
|||
#include "../machine.h"
|
||||
#include "../utility.h"
|
||||
|
||||
static void dump_registers(ptrace_context_t* context __attribute((unused)),
|
||||
int tfd, pid_t pid, bool at_fault) {
|
||||
static void dump_registers(const ptrace_context_t* context __attribute((unused)),
|
||||
int tfd, pid_t tid, bool at_fault) {
|
||||
struct pt_regs_x86 r;
|
||||
bool only_in_tombstone = !at_fault;
|
||||
|
||||
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
|
||||
if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
|
||||
_LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ static void dump_registers(ptrace_context_t* context __attribute((unused)),
|
|||
r.eip, r.ebp, r.esp, r.eflags);
|
||||
}
|
||||
|
||||
void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
|
||||
void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
|
||||
dump_registers(context, tfd, tid, at_fault);
|
||||
|
||||
dump_backtrace_and_stack(context, tfd, tid, at_fault);
|
||||
|
|
|
@ -43,8 +43,8 @@ typedef struct {
|
|||
typedef struct {
|
||||
uintptr_t relative_pc; /* relative PC offset from the start of the library,
|
||||
or the absolute PC if the library is unknown */
|
||||
const map_info_t* map_info; /* memory map of the library, or NULL if unknown */
|
||||
const char* name; /* symbol name, or NULL if unknown */
|
||||
char* map_name; /* executable or library name, or NULL if unknown */
|
||||
char* name; /* symbol name, or NULL if unknown */
|
||||
char* demangled_name; /* demangled symbol name, or NULL if unknown */
|
||||
} backtrace_symbol_t;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ typedef struct map_info {
|
|||
struct map_info* next;
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
bool is_readable;
|
||||
bool is_executable;
|
||||
void* data; // arbitrary data associated with the map by the user, initially NULL
|
||||
char name[];
|
||||
|
@ -44,8 +45,20 @@ void free_map_info_list(map_info_t* milist);
|
|||
/* Finds the memory map that contains the specified address. */
|
||||
const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr);
|
||||
|
||||
/* Gets the memory map for this process. (result is cached) */
|
||||
const map_info_t* my_map_info_list();
|
||||
/* Returns true if the addr is in an readable map. */
|
||||
bool is_readable_map(const map_info_t* milist, uintptr_t addr);
|
||||
|
||||
/* Returns true if the addr is in an executable map. */
|
||||
bool is_executable_map(const map_info_t* milist, uintptr_t addr);
|
||||
|
||||
/* Acquires a reference to the memory map for this process.
|
||||
* The result is cached and refreshed automatically.
|
||||
* Make sure to release the map info when done. */
|
||||
map_info_t* acquire_my_map_info_list();
|
||||
|
||||
/* Releases a reference to the map info for this process that was
|
||||
* previous acquired using acquire_my_map_info_list(). */
|
||||
void release_my_map_info_list(map_info_t* milist);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -35,6 +35,12 @@ typedef struct {
|
|||
map_info_t* map_info_list;
|
||||
} ptrace_context_t;
|
||||
|
||||
/* Describes how to access memory from a process. */
|
||||
typedef struct {
|
||||
pid_t tid;
|
||||
const map_info_t* map_info_list;
|
||||
} memory_t;
|
||||
|
||||
#if __i386__
|
||||
/* ptrace() register context. */
|
||||
typedef struct pt_regs_x86 {
|
||||
|
@ -59,11 +65,28 @@ typedef struct pt_regs_x86 {
|
|||
#endif
|
||||
|
||||
/*
|
||||
* Reads a word of memory safely.
|
||||
* Uses ptrace() if tid >= 0, local memory otherwise.
|
||||
* Returns false if the word could not be read.
|
||||
* Initializes a memory structure for accessing memory from this process.
|
||||
*/
|
||||
bool try_get_word(pid_t tid, uintptr_t ptr, uint32_t* out_value);
|
||||
void init_memory(memory_t* memory, const map_info_t* map_info_list);
|
||||
|
||||
/*
|
||||
* Initializes a memory structure for accessing memory from another process
|
||||
* using ptrace().
|
||||
*/
|
||||
void init_memory_ptrace(memory_t* memory, pid_t tid);
|
||||
|
||||
/*
|
||||
* Reads a word of memory safely.
|
||||
* If the memory is local, ensures that the address is readable before dereferencing it.
|
||||
* Returns false and a value of 0xffffffff if the word could not be read.
|
||||
*/
|
||||
bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value);
|
||||
|
||||
/*
|
||||
* Reads a word of memory safely using ptrace().
|
||||
* Returns false and a value of 0xffffffff if the word could not be read.
|
||||
*/
|
||||
bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value);
|
||||
|
||||
/*
|
||||
* Loads information needed for examining a remote process using ptrace().
|
||||
|
|
|
@ -120,35 +120,31 @@ static uintptr_t prel_to_absolute(uintptr_t place, uint32_t prel_offset) {
|
|||
return place + (((int32_t)(prel_offset << 1)) >> 1);
|
||||
}
|
||||
|
||||
static uintptr_t get_exception_handler(
|
||||
const ptrace_context_t* context, pid_t tid, uintptr_t pc) {
|
||||
static uintptr_t get_exception_handler(const memory_t* memory,
|
||||
const map_info_t* map_info_list, uintptr_t pc) {
|
||||
if (!pc) {
|
||||
ALOGV("get_exception_handler: pc is zero, no handler");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t exidx_start;
|
||||
size_t exidx_size;
|
||||
const map_info_t* mi;
|
||||
if (tid < 0) {
|
||||
if (memory->tid < 0) {
|
||||
mi = NULL;
|
||||
exidx_start = find_exidx(pc, &exidx_size);
|
||||
} else {
|
||||
mi = find_map_info(context->map_info_list, pc);
|
||||
mi = find_map_info(map_info_list, pc);
|
||||
if (mi && mi->data) {
|
||||
const map_info_data_t* data = (const map_info_data_t*)mi->data;
|
||||
exidx_start = data->exidx_start;
|
||||
exidx_size = data->exidx_size / 8;
|
||||
exidx_size = data->exidx_size;
|
||||
} else {
|
||||
exidx_start = 0;
|
||||
exidx_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The PC points to the instruction following the branch.
|
||||
// We want to find the exception handler entry that corresponds to the branch itself,
|
||||
// so we offset the PC backwards into the previous instruction.
|
||||
// ARM instructions are 4 bytes, Thumb are 2, so we just subtract two so we either
|
||||
// end up in the middle (ARM) or at the beginning of the instruction (Thumb).
|
||||
if (pc >= 2) {
|
||||
pc -= 2;
|
||||
}
|
||||
|
||||
uintptr_t handler = 0;
|
||||
if (exidx_start) {
|
||||
uint32_t low = 0;
|
||||
|
@ -157,7 +153,7 @@ static uintptr_t get_exception_handler(
|
|||
uint32_t index = (low + high) / 2;
|
||||
uintptr_t entry = exidx_start + index * 8;
|
||||
uint32_t entry_prel_pc;
|
||||
if (!try_get_word(tid, entry, &entry_prel_pc)) {
|
||||
if (!try_get_word(memory, entry, &entry_prel_pc)) {
|
||||
break;
|
||||
}
|
||||
uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc);
|
||||
|
@ -168,7 +164,7 @@ static uintptr_t get_exception_handler(
|
|||
if (index + 1 < exidx_size) {
|
||||
uintptr_t next_entry = entry + 8;
|
||||
uint32_t next_entry_prel_pc;
|
||||
if (!try_get_word(tid, next_entry, &next_entry_prel_pc)) {
|
||||
if (!try_get_word(memory, next_entry, &next_entry_prel_pc)) {
|
||||
break;
|
||||
}
|
||||
uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc);
|
||||
|
@ -180,7 +176,7 @@ static uintptr_t get_exception_handler(
|
|||
|
||||
uintptr_t entry_handler_ptr = entry + 4;
|
||||
uint32_t entry_handler;
|
||||
if (!try_get_word(tid, entry_handler_ptr, &entry_handler)) {
|
||||
if (!try_get_word(memory, entry_handler_ptr, &entry_handler)) {
|
||||
break;
|
||||
}
|
||||
if (entry_handler & (1L << 31)) {
|
||||
|
@ -191,10 +187,15 @@ static uintptr_t get_exception_handler(
|
|||
break;
|
||||
}
|
||||
}
|
||||
ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, "
|
||||
"exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
|
||||
pc, mi ? mi->name : "<unknown>", mi ? mi->start : 0,
|
||||
exidx_start, exidx_size, handler);
|
||||
if (mi) {
|
||||
ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, "
|
||||
"exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
|
||||
pc, mi->name, mi->start, exidx_start, exidx_size, handler);
|
||||
} else {
|
||||
ALOGV("get_exception_handler: pc=0x%08x, "
|
||||
"exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
|
||||
pc, exidx_start, exidx_size, handler);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
@ -203,11 +204,11 @@ typedef struct {
|
|||
uint32_t word;
|
||||
} byte_stream_t;
|
||||
|
||||
static bool try_next_byte(pid_t tid, byte_stream_t* stream, uint8_t* out_value) {
|
||||
static bool try_next_byte(const memory_t* memory, byte_stream_t* stream, uint8_t* out_value) {
|
||||
uint8_t result;
|
||||
switch (stream->ptr & 3) {
|
||||
case 0:
|
||||
if (!try_get_word(tid, stream->ptr, &stream->word)) {
|
||||
if (!try_get_word(memory, stream->ptr, &stream->word)) {
|
||||
*out_value = 0;
|
||||
return false;
|
||||
}
|
||||
|
@ -237,13 +238,13 @@ static void set_reg(unwind_state_t* state, uint32_t reg, uint32_t value) {
|
|||
state->gregs[reg] = value;
|
||||
}
|
||||
|
||||
static bool try_pop_registers(pid_t tid, unwind_state_t* state, uint32_t mask) {
|
||||
static bool try_pop_registers(const memory_t* memory, unwind_state_t* state, uint32_t mask) {
|
||||
uint32_t sp = state->gregs[R_SP];
|
||||
bool sp_updated = false;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (mask & (1 << i)) {
|
||||
uint32_t value;
|
||||
if (!try_get_word(tid, sp, &value)) {
|
||||
if (!try_get_word(memory, sp, &value)) {
|
||||
return false;
|
||||
}
|
||||
if (i == R_SP) {
|
||||
|
@ -272,8 +273,8 @@ static bool try_pop_registers(pid_t tid, unwind_state_t* state, uint32_t mask) {
|
|||
* virtual register state (including the stack pointer) such that
|
||||
* the call frame is unwound and the PC register points to the call site.
|
||||
*/
|
||||
static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
||||
byte_stream_t* stream, int pr_index) {
|
||||
static bool execute_personality_routine(const memory_t* memory,
|
||||
unwind_state_t* state, byte_stream_t* stream, int pr_index) {
|
||||
size_t size;
|
||||
switch (pr_index) {
|
||||
case 0: // Personality routine #0, short frame, descriptors have 16-bit scope.
|
||||
|
@ -282,7 +283,7 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
case 1: // Personality routine #1, long frame, descriptors have 16-bit scope.
|
||||
case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope.
|
||||
uint8_t size_byte;
|
||||
if (!try_next_byte(tid, stream, &size_byte)) {
|
||||
if (!try_next_byte(memory, stream, &size_byte)) {
|
||||
return false;
|
||||
}
|
||||
size = (uint32_t)size_byte * sizeof(uint32_t) + 2;
|
||||
|
@ -295,7 +296,7 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
bool pc_was_set = false;
|
||||
while (size--) {
|
||||
uint8_t op;
|
||||
if (!try_next_byte(tid, stream, &op)) {
|
||||
if (!try_next_byte(memory, stream, &op)) {
|
||||
return false;
|
||||
}
|
||||
if ((op & 0xc0) == 0x00) {
|
||||
|
@ -306,13 +307,13 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4);
|
||||
} else if ((op & 0xf0) == 0x80) {
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4);
|
||||
if (mask) {
|
||||
// "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}"
|
||||
if (!try_pop_registers(tid, state, mask)) {
|
||||
if (!try_pop_registers(memory, state, mask)) {
|
||||
return false;
|
||||
}
|
||||
if (mask & (1 << R_PC)) {
|
||||
|
@ -334,13 +335,13 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
} else if ((op & 0xf8) == 0xa0) {
|
||||
// "Pop r4-r[4+nnn]"
|
||||
uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0;
|
||||
if (!try_pop_registers(tid, state, mask)) {
|
||||
if (!try_pop_registers(memory, state, mask)) {
|
||||
return false;
|
||||
}
|
||||
} else if ((op & 0xf8) == 0xa8) {
|
||||
// "Pop r4-r[4+nnn], r14"
|
||||
uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000;
|
||||
if (!try_pop_registers(tid, state, mask)) {
|
||||
if (!try_pop_registers(memory, state, mask)) {
|
||||
return false;
|
||||
}
|
||||
} else if (op == 0xb0) {
|
||||
|
@ -348,12 +349,12 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
break;
|
||||
} else if (op == 0xb1) {
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
|
||||
// "Pop integer registers under mask {r3, r2, r1, r0}"
|
||||
if (!try_pop_registers(tid, state, op2)) {
|
||||
if (!try_pop_registers(memory, state, op2)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -366,7 +367,7 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
uint32_t shift = 0;
|
||||
uint8_t op2;
|
||||
do {
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
value |= (op2 & 0x7f) << shift;
|
||||
|
@ -376,7 +377,7 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
} else if (op == 0xb3) {
|
||||
// "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12);
|
||||
|
@ -389,13 +390,13 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
} else if (op == 0xc6) {
|
||||
// "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
|
||||
} else if (op == 0xc7) {
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
|
||||
|
@ -409,14 +410,14 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
// "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc]
|
||||
// saved (as if) by FSTMFD"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
|
||||
} else if (op == 0xc9) {
|
||||
// "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
if (!(size--) || !try_next_byte(memory, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
|
||||
|
@ -434,52 +435,86 @@ static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
|||
return true;
|
||||
}
|
||||
|
||||
static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* context,
|
||||
static bool try_get_half_word(const memory_t* memory, uint32_t pc, uint16_t* out_value) {
|
||||
uint32_t word;
|
||||
if (try_get_word(memory, pc & ~2, &word)) {
|
||||
*out_value = pc & 2 ? word >> 16 : word & 0xffff;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
|
||||
if (pc & 1) {
|
||||
/* Thumb mode - need to check whether the bl(x) has long offset or not.
|
||||
* Examples:
|
||||
*
|
||||
* arm blx in the middle of thumb:
|
||||
* 187ae: 2300 movs r3, #0
|
||||
* 187b0: f7fe ee1c blx 173ec
|
||||
* 187b4: 2c00 cmp r4, #0
|
||||
*
|
||||
* arm bl in the middle of thumb:
|
||||
* 187d8: 1c20 adds r0, r4, #0
|
||||
* 187da: f136 fd15 bl 14f208
|
||||
* 187de: 2800 cmp r0, #0
|
||||
*
|
||||
* pure thumb:
|
||||
* 18894: 189b adds r3, r3, r2
|
||||
* 18896: 4798 blx r3
|
||||
* 18898: b001 add sp, #4
|
||||
*/
|
||||
pc &= ~1;
|
||||
uint16_t prev1, prev2;
|
||||
if (try_get_half_word(memory, pc - 4, &prev1)
|
||||
&& ((prev1 & 0xf000) == 0xf000)
|
||||
&& try_get_half_word(memory, pc - 2, &prev2)
|
||||
&& ((prev2 & 0xe000) == 0xe000)) {
|
||||
pc -= 4; // long offset
|
||||
} else {
|
||||
pc -= 2;
|
||||
}
|
||||
} else {
|
||||
/* ARM mode, all instructions are 32bit. Yay! */
|
||||
pc -= 4;
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
static ssize_t unwind_backtrace_common(const memory_t* memory,
|
||||
const map_info_t* map_info_list,
|
||||
unwind_state_t* state, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth) {
|
||||
size_t ignored_frames = 0;
|
||||
size_t returned_frames = 0;
|
||||
|
||||
uintptr_t handler = get_exception_handler(context, tid, state->gregs[R_PC]);
|
||||
if (!handler) {
|
||||
// If there is no handler for the PC, the program may have branched to
|
||||
// an invalid address. Check whether we have a handler for the LR
|
||||
// where we came from and use that instead.
|
||||
backtrace_frame_t* frame = add_backtrace_entry(state->gregs[R_PC], backtrace,
|
||||
ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
for (size_t index = 0; returned_frames < max_depth; index++) {
|
||||
uintptr_t pc = index ? rewind_pc_arch(memory, state->gregs[R_PC])
|
||||
: state->gregs[R_PC];
|
||||
backtrace_frame_t* frame = add_backtrace_entry(pc,
|
||||
backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
if (frame) {
|
||||
frame->stack_top = state->gregs[R_SP];
|
||||
}
|
||||
|
||||
handler = get_exception_handler(context, tid, state->gregs[R_LR]);
|
||||
uintptr_t handler = get_exception_handler(memory, map_info_list, pc);
|
||||
if (!handler) {
|
||||
// We don't have a handler here either. Unwinding will not be possible.
|
||||
// Return the PC and LR (if it looks sane) and call it good.
|
||||
if (state->gregs[R_LR] && state->gregs[R_LR] != state->gregs[R_PC]) {
|
||||
// Don't return the SP for this second frame because we don't
|
||||
// know how big the first one is so we don't know where this
|
||||
// one starts.
|
||||
add_backtrace_entry(state->gregs[R_LR], backtrace,
|
||||
ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
// If there is no handler for the PC and this is the first frame,
|
||||
// then the program may have branched to an invalid address.
|
||||
// Try starting from the LR instead, otherwise stop unwinding.
|
||||
if (index == 0 && state->gregs[R_LR]
|
||||
&& state->gregs[R_LR] != state->gregs[R_PC]) {
|
||||
set_reg(state, R_PC, state->gregs[R_LR]);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
return returned_frames;
|
||||
}
|
||||
|
||||
// Ok, continue from the LR.
|
||||
set_reg(state, R_PC, state->gregs[R_LR]);
|
||||
}
|
||||
|
||||
while (handler && returned_frames < max_depth) {
|
||||
backtrace_frame_t* frame = add_backtrace_entry(state->gregs[R_PC], backtrace,
|
||||
ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
if (frame) {
|
||||
frame->stack_top = state->gregs[R_SP];
|
||||
}
|
||||
|
||||
byte_stream_t stream;
|
||||
stream.ptr = handler;
|
||||
uint8_t pr;
|
||||
if (!try_next_byte(tid, &stream, &pr)) {
|
||||
if (!try_next_byte(memory, &stream, &pr)) {
|
||||
break;
|
||||
}
|
||||
if ((pr & 0xf0) != 0x80) {
|
||||
|
@ -490,19 +525,33 @@ static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* contex
|
|||
|
||||
// The first byte indicates the personality routine to execute.
|
||||
// Following bytes provide instructions to the personality routine.
|
||||
if (!execute_personality_routine(tid, state, &stream, pr & 0x0f)) {
|
||||
if (!execute_personality_routine(memory, state, &stream, pr & 0x0f)) {
|
||||
break;
|
||||
}
|
||||
if (frame && state->gregs[R_SP] > frame->stack_top) {
|
||||
frame->stack_size = state->gregs[R_SP] - frame->stack_top;
|
||||
}
|
||||
if (!state->gregs[R_PC]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler = get_exception_handler(context, tid, state->gregs[R_PC]);
|
||||
// Ran out of frames that we could unwind using handlers.
|
||||
// Add a final entry for the LR if it looks sane and call it good.
|
||||
if (returned_frames < max_depth
|
||||
&& state->gregs[R_LR]
|
||||
&& state->gregs[R_LR] != state->gregs[R_PC]
|
||||
&& is_executable_map(map_info_list, state->gregs[R_LR])) {
|
||||
// We don't know where the stack for this extra frame starts so we
|
||||
// don't return any stack information for it.
|
||||
add_backtrace_entry(rewind_pc_arch(memory, state->gregs[R_LR]),
|
||||
backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
}
|
||||
return returned_frames;
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
||||
const map_info_t* map_info_list,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
const ucontext_t* uc = (const ucontext_t*)sigcontext;
|
||||
|
||||
|
@ -511,7 +560,10 @@ ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
|||
state.gregs[i] = uc->uc_mcontext.gregs[i];
|
||||
}
|
||||
|
||||
return unwind_backtrace_common(-1, NULL, &state, backtrace, ignore_depth, max_depth);
|
||||
memory_t memory;
|
||||
init_memory(&memory, map_info_list);
|
||||
return unwind_backtrace_common(&memory, map_info_list, &state,
|
||||
backtrace, ignore_depth, max_depth);
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
||||
|
@ -526,5 +578,8 @@ ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
|||
state.gregs[i] = regs.uregs[i];
|
||||
}
|
||||
|
||||
return unwind_backtrace_common(tid, context, &state, backtrace, ignore_depth, max_depth);
|
||||
memory_t memory;
|
||||
init_memory_ptrace(&memory, tid);
|
||||
return unwind_backtrace_common(&memory, context->map_info_list, &state,
|
||||
backtrace, ignore_depth, max_depth);
|
||||
}
|
||||
|
|
|
@ -29,26 +29,31 @@
|
|||
static void load_exidx_header(pid_t pid, map_info_t* mi,
|
||||
uintptr_t* out_exidx_start, size_t* out_exidx_size) {
|
||||
uint32_t elf_phoff;
|
||||
uint32_t elf_phnum;
|
||||
if (try_get_word(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
|
||||
&& try_get_word(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_phnum)) {
|
||||
uint32_t elf_phentsize_phnum;
|
||||
if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
|
||||
&& try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
|
||||
&elf_phentsize_phnum)) {
|
||||
uint32_t elf_phentsize = elf_phentsize_phnum >> 16;
|
||||
uint32_t elf_phnum = elf_phentsize_phnum & 0xffff;
|
||||
for (uint32_t i = 0; i < elf_phnum; i++) {
|
||||
uintptr_t elf_phdr = mi->start + elf_phoff + i * sizeof(Elf32_Phdr);
|
||||
uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
|
||||
uint32_t elf_phdr_type;
|
||||
if (!try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
|
||||
if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
|
||||
break;
|
||||
}
|
||||
if (elf_phdr_type == PT_ARM_EXIDX) {
|
||||
uint32_t elf_phdr_offset;
|
||||
uint32_t elf_phdr_filesz;
|
||||
if (!try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
|
||||
if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
|
||||
&elf_phdr_offset)
|
||||
|| !try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz),
|
||||
|| !try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz),
|
||||
&elf_phdr_filesz)) {
|
||||
break;
|
||||
}
|
||||
*out_exidx_start = mi->start + elf_phdr_offset;
|
||||
*out_exidx_size = elf_phdr_filesz;
|
||||
*out_exidx_size = elf_phdr_filesz / 8;
|
||||
ALOGV("Parsed EXIDX header info for %s: start=0x%08x, size=%d", mi->name,
|
||||
*out_exidx_start, *out_exidx_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,14 +73,21 @@ typedef struct {
|
|||
uint32_t esp;
|
||||
} unwind_state_t;
|
||||
|
||||
static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* context,
|
||||
uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
|
||||
// TODO: Implement for x86.
|
||||
return pc;
|
||||
}
|
||||
|
||||
static ssize_t unwind_backtrace_common(const memory_t* memory,
|
||||
const map_info_t* map_info_list,
|
||||
unwind_state_t* state, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth) {
|
||||
size_t ignored_frames = 0;
|
||||
size_t returned_frames = 0;
|
||||
|
||||
while (state->ebp && returned_frames < max_depth) {
|
||||
backtrace_frame_t* frame = add_backtrace_entry(state->eip,
|
||||
for (size_t index = 0; state->ebp && returned_frames < max_depth; index++) {
|
||||
backtrace_frame_t* frame = add_backtrace_entry(
|
||||
index ? rewind_pc_arch(memory, state->eip) : state->eip,
|
||||
backtrace, ignore_depth, max_depth,
|
||||
&ignored_frames, &returned_frames);
|
||||
uint32_t next_esp = state->ebp + 8;
|
||||
|
@ -91,8 +98,8 @@ static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* contex
|
|||
}
|
||||
}
|
||||
state->esp = next_esp;
|
||||
if (!try_get_word(tid, state->ebp + 4, &state->eip)
|
||||
|| !try_get_word(tid, state->ebp, &state->ebp)
|
||||
if (!try_get_word(memory, state->ebp + 4, &state->eip)
|
||||
|| !try_get_word(memory, state->ebp, &state->ebp)
|
||||
|| !state->eip) {
|
||||
break;
|
||||
}
|
||||
|
@ -102,6 +109,7 @@ static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* contex
|
|||
}
|
||||
|
||||
ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
||||
const map_info_t* map_info_list,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
const ucontext_t* uc = (const ucontext_t*)sigcontext;
|
||||
|
||||
|
@ -110,7 +118,10 @@ ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
|||
state.eip = uc->uc_mcontext.eip;
|
||||
state.esp = uc->uc_mcontext.esp;
|
||||
|
||||
return unwind_backtrace_common(-1, NULL, &state, backtrace, ignore_depth, max_depth);
|
||||
memory_t memory;
|
||||
init_memory(&memory, map_info_list);
|
||||
return unwind_backtrace_common(&memory, map_info_list,
|
||||
&state, backtrace, ignore_depth, max_depth);
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
||||
|
@ -125,5 +136,8 @@ ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
|||
state.eip = regs.eip;
|
||||
state.esp = regs.esp;
|
||||
|
||||
return unwind_backtrace_common(tid, context, &state, backtrace, ignore_depth, max_depth);
|
||||
memory_t memory;
|
||||
init_memory_ptrace(&memory, tid);
|
||||
return unwind_backtrace_common(&memory, context->map_info_list,
|
||||
&state, backtrace, ignore_depth, max_depth);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Rewind the program counter by one instruction. */
|
||||
uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc);
|
||||
|
||||
ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
||||
const map_info_t* map_info_list,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
|
||||
|
||||
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <unwind.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/atomic.h>
|
||||
|
||||
#if HAVE_DLADDR
|
||||
#include <dlfcn.h>
|
||||
|
@ -42,6 +43,7 @@ typedef struct {
|
|||
size_t max_depth;
|
||||
size_t ignored_frames;
|
||||
size_t returned_frames;
|
||||
memory_t memory;
|
||||
} backtrace_state_t;
|
||||
|
||||
static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) {
|
||||
|
@ -52,7 +54,7 @@ static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* con
|
|||
// This will require a new architecture-specific function to query
|
||||
// the appropriate registers. Current callers of unwind_backtrace
|
||||
// don't need this information, so we won't bother collecting it just yet.
|
||||
add_backtrace_entry(pc, state->backtrace,
|
||||
add_backtrace_entry(rewind_pc_arch(&state->memory, pc), state->backtrace,
|
||||
state->ignore_depth, state->max_depth,
|
||||
&state->ignored_frames, &state->returned_frames);
|
||||
}
|
||||
|
@ -60,14 +62,22 @@ static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* con
|
|||
}
|
||||
|
||||
ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
ALOGV("Unwinding current thread %d.", gettid());
|
||||
|
||||
map_info_t* milist = acquire_my_map_info_list();
|
||||
|
||||
backtrace_state_t state;
|
||||
state.backtrace = backtrace;
|
||||
state.ignore_depth = ignore_depth;
|
||||
state.max_depth = max_depth;
|
||||
state.ignored_frames = 0;
|
||||
state.returned_frames = 0;
|
||||
init_memory(&state.memory, milist);
|
||||
|
||||
_Unwind_Reason_Code rc =_Unwind_Backtrace(unwind_backtrace_callback, &state);
|
||||
|
||||
release_my_map_info_list(milist);
|
||||
|
||||
if (state.returned_frames) {
|
||||
return state.returned_frames;
|
||||
}
|
||||
|
@ -77,28 +87,39 @@ ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size
|
|||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static volatile struct {
|
||||
int32_t tid;
|
||||
const map_info_t* map_info_list;
|
||||
backtrace_frame_t* backtrace;
|
||||
size_t ignore_depth;
|
||||
size_t max_depth;
|
||||
size_t returned_frames;
|
||||
bool done;
|
||||
} g_unwind_signal_state;
|
||||
|
||||
static void unwind_backtrace_thread_signal_handler(int n, siginfo_t* siginfo, void* sigcontext) {
|
||||
backtrace_frame_t* backtrace = g_unwind_signal_state.backtrace;
|
||||
if (backtrace) {
|
||||
g_unwind_signal_state.backtrace = NULL;
|
||||
int32_t tid = android_atomic_acquire_load(&g_unwind_signal_state.tid);
|
||||
if (tid == gettid()) {
|
||||
g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch(
|
||||
siginfo, sigcontext, backtrace,
|
||||
siginfo, sigcontext,
|
||||
g_unwind_signal_state.map_info_list,
|
||||
g_unwind_signal_state.backtrace,
|
||||
g_unwind_signal_state.ignore_depth,
|
||||
g_unwind_signal_state.max_depth);
|
||||
g_unwind_signal_state.done = true;
|
||||
android_atomic_release_store(-1, &g_unwind_signal_state.tid);
|
||||
} else {
|
||||
ALOGV("Received spurious SIGURG on thread %d that was intended for thread %d.",
|
||||
gettid(), tid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth) {
|
||||
if (tid == gettid()) {
|
||||
return unwind_backtrace(backtrace, ignore_depth + 1, max_depth);
|
||||
}
|
||||
|
||||
ALOGV("Unwinding thread %d from thread %d.", tid, gettid());
|
||||
|
||||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
struct sigaction act;
|
||||
struct sigaction oact;
|
||||
|
@ -108,26 +129,32 @@ ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
|
|||
sigemptyset(&act.sa_mask);
|
||||
|
||||
pthread_mutex_lock(&g_unwind_signal_mutex);
|
||||
|
||||
g_unwind_signal_state.backtrace = backtrace;
|
||||
g_unwind_signal_state.ignore_depth = ignore_depth;
|
||||
g_unwind_signal_state.max_depth = max_depth;
|
||||
g_unwind_signal_state.returned_frames = 0;
|
||||
g_unwind_signal_state.done = false;
|
||||
map_info_t* milist = acquire_my_map_info_list();
|
||||
|
||||
ssize_t frames = -1;
|
||||
if (!sigaction(SIGURG, &act, &oact)) {
|
||||
if (!kill(tid, SIGURG)) {
|
||||
while (!g_unwind_signal_state.done) {
|
||||
g_unwind_signal_state.map_info_list = milist;
|
||||
g_unwind_signal_state.backtrace = backtrace;
|
||||
g_unwind_signal_state.ignore_depth = ignore_depth;
|
||||
g_unwind_signal_state.max_depth = max_depth;
|
||||
g_unwind_signal_state.returned_frames = 0;
|
||||
android_atomic_release_store(tid, &g_unwind_signal_state.tid);
|
||||
|
||||
if (kill(tid, SIGURG)) {
|
||||
ALOGV("Failed to send SIGURG to thread %d.", tid);
|
||||
android_atomic_release_store(-1, &g_unwind_signal_state.tid);
|
||||
} else {
|
||||
while (android_atomic_acquire_load(&g_unwind_signal_state.tid) == tid) {
|
||||
ALOGV("Waiting for response from thread %d...", tid);
|
||||
usleep(1000);
|
||||
}
|
||||
frames = g_unwind_signal_state.returned_frames;
|
||||
}
|
||||
|
||||
sigaction(SIGURG, &oact, NULL);
|
||||
}
|
||||
|
||||
g_unwind_signal_state.backtrace = NULL;
|
||||
|
||||
release_my_map_info_list(milist);
|
||||
pthread_mutex_unlock(&g_unwind_signal_mutex);
|
||||
return frames;
|
||||
#else
|
||||
|
@ -146,14 +173,14 @@ ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context,
|
|||
|
||||
static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) {
|
||||
symbol->relative_pc = pc;
|
||||
symbol->map_info = NULL;
|
||||
symbol->map_name = NULL;
|
||||
symbol->name = NULL;
|
||||
symbol->demangled_name = NULL;
|
||||
}
|
||||
|
||||
void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
|
||||
backtrace_symbol_t* backtrace_symbols) {
|
||||
const map_info_t* milist = my_map_info_list();
|
||||
map_info_t* milist = acquire_my_map_info_list();
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
const backtrace_frame_t* frame = &backtrace[i];
|
||||
backtrace_symbol_t* symbol = &backtrace_symbols[i];
|
||||
|
@ -162,16 +189,19 @@ void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
|
|||
const map_info_t* mi = find_map_info(milist, frame->absolute_pc);
|
||||
if (mi) {
|
||||
symbol->relative_pc = frame->absolute_pc - mi->start;
|
||||
symbol->map_info = mi;
|
||||
if (mi->name[0]) {
|
||||
symbol->map_name = strdup(mi->name);
|
||||
}
|
||||
#if HAVE_DLADDR
|
||||
Dl_info info;
|
||||
if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) {
|
||||
symbol->name = info.dli_sname;
|
||||
symbol->name = strdup(info.dli_sname);
|
||||
symbol->demangled_name = demangle_symbol_name(symbol->name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
release_my_map_info_list(milist);
|
||||
}
|
||||
|
||||
void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
|
||||
|
@ -187,10 +217,12 @@ void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
|
|||
find_symbol_ptrace(context, frame->absolute_pc, &mi, &s);
|
||||
if (mi) {
|
||||
symbol->relative_pc = frame->absolute_pc - mi->start;
|
||||
symbol->map_info = mi;
|
||||
if (mi->name[0]) {
|
||||
symbol->map_name = strdup(mi->name);
|
||||
}
|
||||
}
|
||||
if (s) {
|
||||
symbol->name = s->name;
|
||||
symbol->name = strdup(s->name);
|
||||
symbol->demangled_name = demangle_symbol_name(symbol->name);
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +231,8 @@ void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
|
|||
void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) {
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
backtrace_symbol_t* symbol = &backtrace_symbols[i];
|
||||
free(symbol->map_name);
|
||||
free(symbol->name);
|
||||
free(symbol->demangled_name);
|
||||
init_backtrace_symbol(symbol, 0);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/log.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
|
@ -54,10 +55,14 @@ static map_info_t* parse_maps_line(const char* line)
|
|||
if (mi) {
|
||||
mi->start = start;
|
||||
mi->end = end;
|
||||
mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
|
||||
mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
|
||||
mi->data = NULL;
|
||||
memcpy(mi->name, name, name_len);
|
||||
mi->name[name_len] = '\0';
|
||||
ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
|
||||
"is_readable=%d, is_executable=%d, name=%s",
|
||||
mi->start, mi->end, mi->is_readable, mi->is_executable, mi->name);
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
@ -99,14 +104,87 @@ const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) {
|
|||
return mi;
|
||||
}
|
||||
|
||||
static pthread_once_t g_my_milist_once = PTHREAD_ONCE_INIT;
|
||||
static map_info_t* g_my_milist = NULL;
|
||||
|
||||
static void init_my_milist_once() {
|
||||
g_my_milist = load_map_info_list(getpid());
|
||||
bool is_readable_map(const map_info_t* milist, uintptr_t addr) {
|
||||
const map_info_t* mi = find_map_info(milist, addr);
|
||||
return mi && mi->is_readable;
|
||||
}
|
||||
|
||||
const map_info_t* my_map_info_list() {
|
||||
pthread_once(&g_my_milist_once, init_my_milist_once);
|
||||
return g_my_milist;
|
||||
bool is_executable_map(const map_info_t* milist, uintptr_t addr) {
|
||||
const map_info_t* mi = find_map_info(milist, addr);
|
||||
return mi && mi->is_executable;
|
||||
}
|
||||
|
||||
static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static map_info_t* g_my_map_info_list = NULL;
|
||||
|
||||
static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL;
|
||||
|
||||
typedef struct {
|
||||
uint32_t refs;
|
||||
int64_t timestamp;
|
||||
} my_map_info_data_t;
|
||||
|
||||
static int64_t now() {
|
||||
struct timespec t;
|
||||
t.tv_sec = t.tv_nsec = 0;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return t.tv_sec * 1000000000LL + t.tv_nsec;
|
||||
}
|
||||
|
||||
static void dec_ref(map_info_t* milist, my_map_info_data_t* data) {
|
||||
if (!--data->refs) {
|
||||
ALOGV("Freed my_map_info_list %p.", milist);
|
||||
free(data);
|
||||
free_map_info_list(milist);
|
||||
}
|
||||
}
|
||||
|
||||
map_info_t* acquire_my_map_info_list() {
|
||||
pthread_mutex_lock(&g_my_map_info_list_mutex);
|
||||
|
||||
int64_t time = now();
|
||||
if (g_my_map_info_list) {
|
||||
my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
|
||||
int64_t age = time - data->timestamp;
|
||||
if (age >= MAX_CACHE_AGE) {
|
||||
ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age);
|
||||
dec_ref(g_my_map_info_list, data);
|
||||
g_my_map_info_list = NULL;
|
||||
} else {
|
||||
ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age);
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_my_map_info_list) {
|
||||
my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t));
|
||||
g_my_map_info_list = load_map_info_list(getpid());
|
||||
if (g_my_map_info_list) {
|
||||
ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list);
|
||||
g_my_map_info_list->data = data;
|
||||
data->refs = 1;
|
||||
data->timestamp = time;
|
||||
} else {
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
map_info_t* milist = g_my_map_info_list;
|
||||
if (milist) {
|
||||
my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
|
||||
data->refs += 1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_my_map_info_list_mutex);
|
||||
return milist;
|
||||
}
|
||||
|
||||
void release_my_map_info_list(map_info_t* milist) {
|
||||
if (milist) {
|
||||
pthread_mutex_lock(&g_my_map_info_list_mutex);
|
||||
|
||||
my_map_info_data_t* data = (my_map_info_data_t*)milist->data;
|
||||
dec_ref(milist, data);
|
||||
|
||||
pthread_mutex_unlock(&g_my_map_info_list_mutex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,44 +34,55 @@ static const uint32_t ELF_MAGIC = 0x464C457f; // "ELF\0177"
|
|||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
#endif
|
||||
|
||||
bool try_get_word(pid_t tid, uintptr_t ptr, uint32_t* out_value) {
|
||||
void init_memory(memory_t* memory, const map_info_t* map_info_list) {
|
||||
memory->tid = -1;
|
||||
memory->map_info_list = map_info_list;
|
||||
}
|
||||
|
||||
void init_memory_ptrace(memory_t* memory, pid_t tid) {
|
||||
memory->tid = tid;
|
||||
memory->map_info_list = NULL;
|
||||
}
|
||||
|
||||
bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) {
|
||||
ALOGV("try_get_word: reading word at 0x%08x", ptr);
|
||||
if (ptr & 3) {
|
||||
ALOGV("try_get_word: invalid pointer 0x%08x", ptr);
|
||||
*out_value = 0;
|
||||
*out_value = 0xffffffffL;
|
||||
return false;
|
||||
}
|
||||
if (tid < 0) {
|
||||
#if 0 /*unreliable, unclear whether this is safe from a signal handler context*/
|
||||
// Determine whether the pointer is likely to be valid before dereferencing it.
|
||||
unsigned char vec[1];
|
||||
while (mincore((void*)(ptr & PAGE_MASK), sizeof(uint32_t), vec)) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
ALOGV("try_get_word: invalid pointer 0x%08x, mincore() errno=%d", ptr, errno);
|
||||
*out_value = 0;
|
||||
return false;
|
||||
}
|
||||
if (memory->tid < 0) {
|
||||
if (!is_readable_map(memory->map_info_list, ptr)) {
|
||||
ALOGV("try_get_word: pointer 0x%08x not in a readable map", ptr);
|
||||
*out_value = 0xffffffffL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
*out_value = *(uint32_t*)ptr;
|
||||
return true;
|
||||
} else {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*out_value = ptrace(PTRACE_PEEKTEXT, tid, (void*)ptr, NULL);
|
||||
*out_value = ptrace(PTRACE_PEEKTEXT, memory->tid, (void*)ptr, NULL);
|
||||
if (*out_value == 0xffffffffL && errno) {
|
||||
ALOGV("try_get_word: invalid pointer 0x%08x, ptrace() errno=%d", ptr, errno);
|
||||
*out_value = 0;
|
||||
ALOGV("try_get_word: invalid pointer 0x%08x reading from tid %d, "
|
||||
"ptrace() errno=%d", ptr, memory->tid, errno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value) {
|
||||
memory_t memory;
|
||||
init_memory_ptrace(&memory, tid);
|
||||
return try_get_word(&memory, ptr, out_value);
|
||||
}
|
||||
|
||||
static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) {
|
||||
if (mi->is_executable) {
|
||||
if (mi->is_executable && mi->is_readable) {
|
||||
uint32_t elf_magic;
|
||||
if (try_get_word(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) {
|
||||
if (try_get_word_ptrace(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) {
|
||||
map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t));
|
||||
if (data) {
|
||||
mi->data = data;
|
||||
|
|
Loading…
Reference in New Issue