From 9f0c5b1487884f04a1e0e207c4d2c20f10d5d1d3 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 23 Jul 2013 17:20:20 -0600 Subject: [PATCH] fix x86_64 DWARF-based stack trace Commit 5917d4c added a guard on some pointer dereferences, but the guard is too strict for DWARF. Allow reading of TEXT segments, etc. --- racket/src/racket/src/jitstack.c | 8 +- racket/src/racket/src/unwind/libunwind.c | 132 ++++++++++++++++++--- racket/src/racket/src/unwind/libunwind.h | 4 +- racket/src/racket/src/unwind/libunwind_i.h | 32 ++--- 4 files changed, 136 insertions(+), 40 deletions(-) diff --git a/racket/src/racket/src/jitstack.c b/racket/src/racket/src/jitstack.c index a652f4eaac..eb6b538958 100644 --- a/racket/src/racket/src/jitstack.c +++ b/racket/src/racket/src/jitstack.c @@ -197,14 +197,10 @@ Scheme_Object *scheme_native_stack_trace(void) tail = scheme_null; } -#ifdef MZ_USE_DWARF_LIBUNWIND - unw_set_safe_pointer_range(stack_start, stack_end); - unw_reset_bad_ptr_flag(); -#endif - #ifdef MZ_USE_DWARF_LIBUNWIND unw_getcontext(&cx); unw_init_local(&c, &cx); + unw_set_safe_pointer_range(&c, stack_start, stack_end); use_unw = 1; p = NULL; #else @@ -473,7 +469,7 @@ Scheme_Object *scheme_native_stack_trace(void) unw_step(&c); q = (void *)unw_get_ip(&c); if ((q == prev_q) - || unw_reset_bad_ptr_flag()) + || unw_reset_bad_ptr_flag(&c)) break; } } diff --git a/racket/src/racket/src/unwind/libunwind.c b/racket/src/racket/src/unwind/libunwind.c index 9d59c216b5..3a84c083a7 100644 --- a/racket/src/racket/src/unwind/libunwind.c +++ b/racket/src/racket/src/unwind/libunwind.c @@ -2545,6 +2545,91 @@ callback (struct dl_phdr_info *info, size_t size, void *ptr) return 1; } +struct addrs_callback_data { + long size, count; + unw_word_t *starts, *ends; +}; + +static void add_address_range(struct addrs_callback_data *cb_data, + unw_word_t start, + unw_word_t end) { + if (cb_data->count == cb_data->size) { + long size = (cb_data->size ? (cb_data->size * 2) : 32); + unw_word_t *n; + + n = (unw_word_t *)malloc(sizeof(unw_word_t) * size); + memcpy(n, cb_data->starts, sizeof(unw_word_t) * cb_data->size); + if (cb_data->starts) free(cb_data->starts); + cb_data->starts = n; + + n = (unw_word_t *)malloc(sizeof(unw_word_t) * size); + memcpy(n, cb_data->ends, sizeof(unw_word_t) * cb_data->size); + if (cb_data->ends) free(cb_data->ends); + cb_data->ends = n; + + cb_data->size = size; + } + + cb_data->starts[cb_data->count] = start; + cb_data->ends[cb_data->count] = end; + cb_data->count++; +} + +static int +safe_addrs_callback (struct dl_phdr_info *info, size_t size, void *ptr) +{ + struct addrs_callback_data *cb_data = ptr; + const Elf_W(Phdr) *phdr; + Elf_W(Addr) load_base; + long n; + + /* Make sure struct dl_phdr_info is at least as big as we need. */ + if (size < offsetof (struct dl_phdr_info, dlpi_phnum) + + sizeof (info->dlpi_phnum)) + return -1; + + phdr = info->dlpi_phdr; + load_base = info->dlpi_addr; + + /* See if PC falls into one of the loaded segments. Find the + eh-header segment at the same time. */ + for (n = info->dlpi_phnum; --n >= 0; phdr++) + { + if (phdr->p_type == PT_LOAD) + { + Elf_W(Addr) vaddr = phdr->p_vaddr + load_base; + + add_address_range(cb_data, (unw_word_t)vaddr, (unw_word_t)(vaddr + phdr->p_memsz)); + } + } + + return 0; +} + +static struct addrs_callback_data safe_addrs; + +HIDDEN int +initialize_safe_addresses (unw_addr_space_t as) +{ + if (!safe_addrs.count) { + struct addrs_callback_data cb_data; + memset(&cb_data, 0, sizeof(cb_data)); +#ifndef UW_NO_SYNC + sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); +#endif + (void)dl_iterate_phdr (safe_addrs_callback, &cb_data); +#ifndef UW_NO_SYNC + sigprocmask (SIG_SETMASK, &saved_mask, NULL); +#endif + memcpy(&safe_addrs, &cb_data, sizeof(cb_data)); + } + + as->num_safe_addresses = safe_addrs.count; + as->safe_start_addresses = safe_addrs.starts; + as->safe_end_addresses = safe_addrs.ends; + return 0; +} + HIDDEN int dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, void *arg) @@ -3161,6 +3246,8 @@ int unw_init_local (unw_cursor_t *cursor, unw_context_t *uc) as->debug_frames = shared_debug_frames; #endif + initialize_safe_addresses(as); + c->dwarf.as = as; c->dwarf.as_arg = uc; return common_init (c); @@ -3183,29 +3270,37 @@ int unw_step (unw_cursor_t *c) return dwarf_step(&((struct cursor *)c)->dwarf); } -static int saw_bad_ptr = 0; static char safe_space[8]; -static unw_word_t safe_start_address, safe_end_address; -void unw_set_safe_pointer_range(unw_word_t s, unw_word_t e) +void unw_set_safe_pointer_range(unw_cursor_t *c, unw_word_t s, unw_word_t e) { - safe_start_address = s; - safe_end_address = e; + unw_addr_space_t as = ((struct dwarf_cursor *)c)->as; + as->safe_start_address = s; + as->safe_end_address = e; } -int unw_reset_bad_ptr_flag() +int unw_reset_bad_ptr_flag(unw_cursor_t *c) { - int v = saw_bad_ptr; - saw_bad_ptr = 0; + unw_addr_space_t as = ((struct dwarf_cursor *)c)->as; + int v = as->saw_bad_ptr; + as->saw_bad_ptr = 0; return v; } -static void *safe_pointer(unw_word_t p) +static void *safe_pointer(unw_addr_space_t as, unw_word_t p) { - if (safe_start_address != safe_end_address) - if ((p < safe_start_address) - || (p >= safe_end_address)) { - saw_bad_ptr = 1; + int i; + + for (i = as->num_safe_addresses; i--; ) { + if ((p >= as->safe_start_addresses[i]) + && (p <= as->safe_end_addresses[i])) + return (void *)p; + } + + if (as->safe_start_address != as->safe_end_address) + if ((p < as->safe_start_address) + || (p >= as->safe_end_address)) { + as->saw_bad_ptr = 1; return safe_space; } @@ -3228,7 +3323,8 @@ unw_word_t unw_get_frame_pointer(unw_cursor_t *c) #else # define JIT_FRAME_POINTER_ID 6 /* = BP */ #endif - return *(unw_word_t *)safe_pointer(((struct cursor *)c)->dwarf.loc[JIT_FRAME_POINTER_ID].val); + return *(unw_word_t *)safe_pointer(((struct cursor *)c)->dwarf.as, + ((struct cursor *)c)->dwarf.loc[JIT_FRAME_POINTER_ID].val); } #ifdef UNW_X86_64 @@ -3249,8 +3345,8 @@ void unw_manual_step(unw_cursor_t *_c, c->dwarf.loc[13].val = (unw_word_t)r13_addr; c->dwarf.loc[16].val = (unw_word_t)ip_addr; - c->dwarf.ip = *(unw_word_t *)safe_pointer((unw_word_t)ip_addr); - c->dwarf.cfa = *(unw_word_t *)safe_pointer((unw_word_t)sp_addr); + c->dwarf.ip = *(unw_word_t *)safe_pointer(c->dwarf.as, (unw_word_t)ip_addr); + c->dwarf.cfa = *(unw_word_t *)safe_pointer(c->dwarf.as, (unw_word_t)sp_addr); c->dwarf.ret_addr_column = UNW_TDEP_IP; c->dwarf.pi_valid = 0; c->dwarf.hint = 0; @@ -3280,8 +3376,8 @@ void unw_manual_step(unw_cursor_t *_c, c->dwarf.loc[UNW_ARM_RSP].val = (unw_word_t)sp_addr; c->dwarf.loc[UNW_ARM_R11].val = (unw_word_t)fp_addr; - c->dwarf.ip = *(unw_word_t *)safe_pointer((unw_word_t)ip_addr); - c->dwarf.cfa = *(unw_word_t *)safe_pointer((unw_word_t)sp_addr); + c->dwarf.ip = *(unw_word_t *)safe_pointer(c->dwarf.as, (unw_word_t)ip_addr); + c->dwarf.cfa = *(unw_word_t *)safe_pointer(c->dwarf.as, (unw_word_t)sp_addr); c->dwarf.ret_addr_column = UNW_TDEP_IP; c->dwarf.pi_valid = 0; c->dwarf.hint = 0; diff --git a/racket/src/racket/src/unwind/libunwind.h b/racket/src/racket/src/unwind/libunwind.h index e95d86358d..1b4ec2e7d4 100644 --- a/racket/src/racket/src/unwind/libunwind.h +++ b/racket/src/racket/src/unwind/libunwind.h @@ -585,8 +585,8 @@ void unw_manual_step(unw_cursor_t *_c, extern unw_addr_space_t unw_local_addr_space; -extern int unw_reset_bad_ptr_flag(); -extern void unw_set_safe_pointer_range(unw_word_t s, unw_word_t e); +extern int unw_reset_bad_ptr_flag(unw_cursor_t *c); +extern void unw_set_safe_pointer_range(unw_cursor_t *c, unw_word_t s, unw_word_t e); #define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) extern int unw_tdep_is_fpreg (int); diff --git a/racket/src/racket/src/unwind/libunwind_i.h b/racket/src/racket/src/unwind/libunwind_i.h index 708c8545ce..d7c7fe37ba 100644 --- a/racket/src/racket/src/unwind/libunwind_i.h +++ b/racket/src/racket/src/unwind/libunwind_i.h @@ -692,6 +692,10 @@ struct unw_addr_space #ifdef CONFIG_DEBUG_FRAME struct unw_debug_frame_list *debug_frames; #endif + unw_word_t safe_start_address, safe_end_address; + long num_safe_addresses; + unw_word_t *safe_start_addresses, *safe_end_addresses; + int saw_bad_ptr; }; struct cursor @@ -723,14 +727,14 @@ struct cursor # define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \ tdep_uc_addr((c)->as_arg, (r)), 0)) -static void *safe_pointer(unw_word_t); +static void *safe_pointer(unw_addr_space_t, unw_word_t); static inline int dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) { if (!DWARF_GET_LOC (loc)) return -1; - *val = *(unw_fpreg_t *) safe_pointer(DWARF_GET_LOC (loc)); + *val = *(unw_fpreg_t *) safe_pointer(c->as, DWARF_GET_LOC (loc)); return 0; } @@ -739,7 +743,7 @@ dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) { if (!DWARF_GET_LOC (loc)) return -1; - *(unw_fpreg_t *) safe_pointer(DWARF_GET_LOC (loc)) = val; + *(unw_fpreg_t *) safe_pointer(c->as, DWARF_GET_LOC (loc)) = val; return 0; } @@ -748,7 +752,7 @@ dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) { if (!DWARF_GET_LOC (loc)) return -1; - *val = *(unw_word_t *) safe_pointer(DWARF_GET_LOC (loc)); + *val = *(unw_word_t *) safe_pointer(c->as, DWARF_GET_LOC (loc)); return 0; } @@ -757,7 +761,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) { if (!DWARF_GET_LOC (loc)) return -1; - *(unw_word_t *) safe_pointer(DWARF_GET_LOC (loc)) = val; + *(unw_word_t *) safe_pointer(c->as, DWARF_GET_LOC (loc)) = val; return 0; } @@ -826,7 +830,7 @@ static inline int dwarf_reads8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int8_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->s8; *addr += sizeof (mvp->s8); @@ -837,7 +841,7 @@ static inline int dwarf_reads16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int16_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->s16; *addr += sizeof (mvp->s16); @@ -848,7 +852,7 @@ static inline int dwarf_reads32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int32_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->s32; *addr += sizeof (mvp->s32); @@ -859,7 +863,7 @@ static inline int dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int64_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->s64; *addr += sizeof (mvp->s64); @@ -870,7 +874,7 @@ static inline int dwarf_readu8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint8_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->u8; *addr += sizeof (mvp->u8); @@ -881,7 +885,7 @@ static inline int dwarf_readu16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint16_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->u16; *addr += sizeof (mvp->u16); @@ -892,7 +896,7 @@ static inline int dwarf_readu32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint32_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->u32; *addr += sizeof (mvp->u32); @@ -903,7 +907,7 @@ static inline int dwarf_readu64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint64_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->u64; *addr += sizeof (mvp->u64); @@ -914,7 +918,7 @@ static inline int dwarf_readw (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, unw_word_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *)safe_pointer(*addr); + dwarf_misaligned_value_t *mvp = (void *)safe_pointer(as, *addr); *val = mvp->w; *addr += sizeof (mvp->w);