diff --git a/src/ia64/Gstep-ia64.c b/src/ia64/Gstep-ia64.c index 20926e2c..408e472b 100644 --- a/src/ia64/Gstep-ia64.c +++ b/src/ia64/Gstep-ia64.c @@ -1,5 +1,5 @@ /* libunwind - a platform-independent unwind library - Copyright (C) 2001-2002 Hewlett-Packard Co + Copyright (C) 2001-2003 Hewlett-Packard Co Contributed by David Mosberger-Tang This file is part of libunwind. @@ -31,6 +31,7 @@ static inline int update_frame_state (struct cursor *c) { unw_word_t prev_ip, prev_sp, prev_bsp, ip, pr, num_regs, cfm; + unw_word_t bsp, bspstore, rnat_addr, ndirty, loadrs; int ret; prev_ip = c->ip; @@ -48,6 +49,8 @@ update_frame_state (struct cursor *c) if (ret < 0) return ret; + c->sigcontext_off = c->sigcontext_loc - c->sp; + if (c->ip_loc == c->sigcontext_loc + SIGCONTEXT_BR_OFF + 0*8) { /* Earlier kernels (before 2.4.19 and 2.5.10) had buggy @@ -64,6 +67,40 @@ update_frame_state (struct cursor *c) return ret; num_regs = cfm & 0x7f; /* size of frame */ + + /* When Linux delivers a signal on an alternate stack, it does + things a bit differently from what the unwind conventions + allow us to describe: instead of saving ar.rnat, ar.bsp, and + ar.bspstore, it saves the former two plus the "loadrs" value. + Because of this, we need to detect & record a potential + rbs-area switch here manually... */ + if (c->bsp_loc) + { + /* If ar.bsp has been saved already AND the current bsp is + not equal to the saved value, then we know for sure that + we're past the point where the backing store has been + switched (and before the point where it's restored). */ + ret = ia64_get (c, c->sigcontext_loc + SIGCONTEXT_AR_BSP_OFF, &bsp); + if (ret < 0) + return ret; + + if (bsp != c->bsp) + { + assert (c->rnat_loc); + + ret = ia64_get (c, c->sigcontext_loc + SIGCONTEXT_LOADRS_OFF, + &loadrs); + if (ret < 0) + return ret; + + loadrs >>= 16; + ndirty = ia64_rse_num_regs (c->bsp - loadrs, c->bsp); + bspstore = ia64_rse_skip_regs (bsp, -ndirty); + ret = rbs_record_switch (c, bsp, bspstore, c->rnat_loc); + if (ret < 0) + return ret; + } + } } else { @@ -72,7 +109,25 @@ update_frame_state (struct cursor *c) return ret; num_regs = (cfm >> 7) & 0x7f; /* size of locals */ } + + if (unlikely (c->pi.flags & UNW_PI_FLAG_IA64_RBS_SWITCH)) + { + if ((ret = ia64_get (c, c->bsp_loc, &bsp)) < 0 + || (ret = ia64_get (c, c->bspstore_loc, &bspstore)) < 0 + || (ret = ia64_get (c, c->rnat_loc, &rnat_addr)) < 0) + return ret; + + if (bsp != c->bsp) + { + ret = rbs_record_switch (c, bsp, bspstore, rnat_addr); + if (ret < 0) + return ret; + } + } + c->bsp = ia64_rse_skip_regs (c->bsp, -num_regs); + if (c->rbs_area[c->rbs_curr].end - c->bsp > c->rbs_area[c->rbs_curr].size) + rbs_underflow (c); /* update the IP cache: */ ret = ia64_get (c, c->ip_loc, &ip);