diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index d941684f5..ca45c9b51 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -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); diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 5a180f1a9..20ffc13af 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -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); diff --git a/debuggerd/machine.h b/debuggerd/machine.h index f9ca6bd6a..6049b69d0 100644 --- a/debuggerd/machine.h +++ b/debuggerd/machine.h @@ -20,6 +20,6 @@ #include #include -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 diff --git a/debuggerd/utility.c b/debuggerd/utility.c index c0fb13a09..64e59809b 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -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 : ""; + const char* map_name = symbol->map_name ? symbol->map_name : ""; 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)) { diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 879c8b43f..39f91cba4 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -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 diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c index 57f51c831..2729c7edc 100644 --- a/debuggerd/x86/machine.c +++ b/debuggerd/x86/machine.c @@ -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); diff --git a/include/corkscrew/backtrace.h b/include/corkscrew/backtrace.h index cecb13db6..157d02934 100644 --- a/include/corkscrew/backtrace.h +++ b/include/corkscrew/backtrace.h @@ -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; diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h index a1c344f4d..c5cd8f81e 100644 --- a/include/corkscrew/map_info.h +++ b/include/corkscrew/map_info.h @@ -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 } diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h index 6acf9eb05..172e348d5 100644 --- a/include/corkscrew/ptrace.h +++ b/include/corkscrew/ptrace.h @@ -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(). diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c index 6bed2ae5e..cf9a5ab32 100644 --- a/libcorkscrew/arch-arm/backtrace-arm.c +++ b/libcorkscrew/arch-arm/backtrace-arm.c @@ -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 : "", 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); } diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c index fd155056f..868230ceb 100644 --- a/libcorkscrew/arch-arm/ptrace-arm.c +++ b/libcorkscrew/arch-arm/ptrace-arm.c @@ -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; } } diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c index 1324899e3..24fadcb03 100644 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -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); } diff --git a/libcorkscrew/backtrace-arch.h b/libcorkscrew/backtrace-arch.h index 80bca2bbb..a46f80b56 100644 --- a/libcorkscrew/backtrace-arch.h +++ b/libcorkscrew/backtrace-arch.h @@ -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, diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c index b03c43fb3..f9a49ec41 100644 --- a/libcorkscrew/backtrace.c +++ b/libcorkscrew/backtrace.c @@ -31,6 +31,7 @@ #include #include #include +#include #if HAVE_DLADDR #include @@ -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); } diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c index 60ed9b43d..f33378fce 100644 --- a/libcorkscrew/map_info.c +++ b/libcorkscrew/map_info.c @@ -26,6 +26,7 @@ #include #include #include +#include // 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); + } } diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c index a308bb5cc..cbea8ca82 100644 --- a/libcorkscrew/ptrace.c +++ b/libcorkscrew/ptrace.c @@ -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;