📄 unwind.c
字号:
if (!s[val]) goto lazy_init; s[dst] = s[val]; break; case UNW_INSN_MOVE_STACKED: s[dst] = (unsigned long) ia64_rse_skip_regs((unsigned long *)state->bsp, val); break; case UNW_INSN_ADD_PSP: s[dst] = state->psp + val; break; case UNW_INSN_ADD_SP: s[dst] = state->sp + val; break; case UNW_INSN_SETNAT_MEMSTK: if (!state->pri_unat_loc) state->pri_unat_loc = &state->sw->ar_unat; /* register off. is a multiple of 8, so the least 3 bits (type) are 0 */ s[dst+1] = (*state->pri_unat_loc - s[dst]) | UNW_NAT_MEMSTK; break; case UNW_INSN_SETNAT_TYPE: s[dst+1] = val; break; case UNW_INSN_LOAD:#if UNW_DEBUG if ((s[val] & (my_cpu_data.unimpl_va_mask | 0x7)) || s[val] < TASK_SIZE) { debug(1, "unwind: rejecting bad psp=0x%lx\n", s[val]); break; }#endif s[dst] = *(unsigned long *) s[val]; break; } } STAT(unw.stat.script.run_time += ia64_get_itc() - start); return; lazy_init: off = unw.sw_off[val]; s[val] = (unsigned long) state->sw + off; if (off >= struct_offset(struct switch_stack, r4) && off <= struct_offset(struct switch_stack, r7)) /* * We're initializing a general register: init NaT info, too. Note that * the offset is a multiple of 8 which gives us the 3 bits needed for * the type field. */ s[val+1] = (struct_offset(struct switch_stack, ar_unat) - off) | UNW_NAT_MEMSTK; goto redo;}static intfind_save_locs (struct unw_frame_info *info){ int have_write_lock = 0; struct unw_script *scr; if ((info->ip & (my_cpu_data.unimpl_va_mask | 0xf)) || info->ip < TASK_SIZE) { /* don't let obviously bad addresses pollute the cache */ debug(1, "unwind: rejecting bad ip=0x%lx\n", info->ip); info->rp_loc = 0; return -1; } scr = script_lookup(info); if (!scr) { scr = build_script(info); if (!scr) { dprintk("unwind: failed to locate/build unwind script for ip %lx\n", info->ip); return -1; } have_write_lock = 1; } info->hint = scr->hint; info->prev_script = scr - unw.cache; run_script(scr, info); if (have_write_lock) write_unlock(&scr->lock); else read_unlock(&scr->lock); return 0;}intunw_unwind (struct unw_frame_info *info){ unsigned long prev_ip, prev_sp, prev_bsp; unsigned long ip, pr, num_regs; STAT(unsigned long start, flags;) int retval; STAT(local_irq_save(flags); ++unw.stat.api.unwinds; start = ia64_get_itc()); prev_ip = info->ip; prev_sp = info->sp; prev_bsp = info->bsp; /* restore the ip */ if (!info->rp_loc) { debug(1, "unwind: failed to locate return link (ip=0x%lx)!\n", info->ip); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } ip = info->ip = *info->rp_loc; if (ip < GATE_ADDR + PAGE_SIZE) { /* * We don't have unwind info for the gate page, so we consider that part * of user-space for the purpose of unwinding. */ debug(1, "unwind: reached user-space (ip=0x%lx)\n", ip); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } /* restore the cfm: */ if (!info->pfs_loc) { dprintk("unwind: failed to locate ar.pfs!\n"); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } info->cfm_loc = info->pfs_loc; /* restore the bsp: */ pr = info->pr; num_regs = 0; if ((info->flags & UNW_FLAG_INTERRUPT_FRAME)) { if ((pr & (1UL << pNonSys)) != 0) num_regs = *info->cfm_loc & 0x7f; /* size of frame */ info->pfs_loc = (unsigned long *) (info->sp + 16 + struct_offset(struct pt_regs, ar_pfs)); } else num_regs = (*info->cfm_loc >> 7) & 0x7f; /* size of locals */ info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->bsp, -num_regs); if (info->bsp < info->regstk.limit || info->bsp > info->regstk.top) { dprintk("unwind: bsp (0x%lx) out of range [0x%lx-0x%lx]\n", info->bsp, info->regstk.limit, info->regstk.top); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } /* restore the sp: */ info->sp = info->psp; if (info->sp < info->memstk.top || info->sp > info->memstk.limit) { dprintk("unwind: sp (0x%lx) out of range [0x%lx-0x%lx]\n", info->sp, info->memstk.top, info->memstk.limit); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } if (info->ip == prev_ip && info->sp == prev_sp && info->bsp == prev_bsp) { dprintk("unwind: ip, sp, bsp remain unchanged; stopping here (ip=0x%lx)\n", ip); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } /* as we unwind, the saved ar.unat becomes the primary unat: */ info->pri_unat_loc = info->unat_loc; /* finally, restore the predicates: */ unw_get_pr(info, &info->pr); retval = find_save_locs(info); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return retval;}intunw_unwind_to_user (struct unw_frame_info *info){ unsigned long ip; while (unw_unwind(info) >= 0) { if (unw_get_rp(info, &ip) < 0) { unw_get_ip(info, &ip); dprintk("unwind: failed to read return pointer (ip=0x%lx)\n", ip); return -1; } /* * We don't have unwind info for the gate page, so we consider that part * of user-space for the purpose of unwinding. */ if (ip < GATE_ADDR + PAGE_SIZE) return 0; } unw_get_ip(info, &ip); dprintk("unwind: failed to unwind to user-level (ip=0x%lx)\n", ip); return -1;}voidunw_init_frame_info (struct unw_frame_info *info, struct task_struct *t, struct switch_stack *sw){ unsigned long rbslimit, rbstop, stklimit, stktop, sol; STAT(unsigned long start, flags;) STAT(local_irq_save(flags); ++unw.stat.api.inits; start = ia64_get_itc()); /* * Subtle stuff here: we _could_ unwind through the * switch_stack frame but we don't want to do that because it * would be slow as each preserved register would have to be * processed. Instead, what we do here is zero out the frame * info and start the unwind process at the function that * created the switch_stack frame. When a preserved value in * switch_stack needs to be accessed, run_script() will * initialize the appropriate pointer on demand. */ memset(info, 0, sizeof(*info)); rbslimit = (unsigned long) t + IA64_RBS_OFFSET; rbstop = sw->ar_bspstore; if (rbstop - (unsigned long) t >= IA64_STK_OFFSET) rbstop = rbslimit; stklimit = (unsigned long) t + IA64_STK_OFFSET; stktop = (unsigned long) sw - 16; if (stktop <= rbstop) stktop = rbstop; info->regstk.limit = rbslimit; info->regstk.top = rbstop; info->memstk.limit = stklimit; info->memstk.top = stktop; info->task = t; info->sw = sw; info->sp = info->psp = (unsigned long) (sw + 1) - 16; info->cfm_loc = &sw->ar_pfs; sol = (*info->cfm_loc >> 7) & 0x7f; info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol); info->ip = sw->b0; info->pr = sw->pr; find_save_locs(info); STAT(unw.stat.api.init_time += ia64_get_itc() - start; local_irq_restore(flags));}#endif /* CONFIG_IA64_NEW_UNWIND */voidunw_init_from_blocked_task (struct unw_frame_info *info, struct task_struct *t){ struct switch_stack *sw = (struct switch_stack *) (t->thread.ksp + 16);#ifdef CONFIG_IA64_NEW_UNWIND unw_init_frame_info(info, t, sw);#else unsigned long sol, limit, top; memset(info, 0, sizeof(*info)); sol = (sw->ar_pfs >> 7) & 0x7f; /* size of locals */ limit = (unsigned long) t + IA64_RBS_OFFSET; top = sw->ar_bspstore; if (top - (unsigned long) t >= IA64_STK_OFFSET) top = limit; info->regstk.limit = limit; info->regstk.top = top; info->sw = sw; info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol); info->cfm_loc = &sw->ar_pfs; info->ip = sw->b0;#endif}voidunw_init_from_current (struct unw_frame_info *info, struct pt_regs *regs){#ifdef CONFIG_IA64_NEW_UNWIND struct switch_stack *sw = (struct switch_stack *) regs - 1; unw_init_frame_info(info, current, sw); /* skip over interrupt frame: */ unw_unwind(info);#else struct switch_stack *sw = (struct switch_stack *) regs - 1; unsigned long sol, sof, *bsp, limit, top; limit = (unsigned long) current + IA64_RBS_OFFSET; top = sw->ar_bspstore; if (top - (unsigned long) current >= IA64_STK_OFFSET) top = limit; memset(info, 0, sizeof(*info)); sol = (sw->ar_pfs >> 7) & 0x7f; /* size of frame */ /* this gives us the bsp top level frame (kdb interrupt frame): */ bsp = ia64_rse_skip_regs((unsigned long *) top, -sol); /* now skip past the interrupt frame: */ sof = regs->cr_ifs & 0x7f; /* size of frame */ info->regstk.limit = limit; info->regstk.top = top; info->sw = sw; info->bsp = (unsigned long) ia64_rse_skip_regs(bsp, -sof); info->cfm_loc = ®s->cr_ifs; info->ip = regs->cr_iip;#endif}#ifndef CONFIG_IA64_NEW_UNWINDstatic unsigned longread_reg (struct unw_frame_info *info, int regnum, int *is_nat){ unsigned long *addr, *rnat_addr, rnat; addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum); if ((unsigned long) addr < info->regstk.limit || (unsigned long) addr >= info->regstk.top || ((long) addr & 0x7) != 0) { *is_nat = 1; return 0xdeadbeefdeadbeef; } rnat_addr = ia64_rse_rnat_addr(addr); if ((unsigned long) rnat_addr >= info->regstk.top) rnat = info->sw->ar_rnat; else rnat = *rnat_addr; *is_nat = (rnat & (1UL << ia64_rse_slot_num(addr))) != 0; return *addr;}/* * On entry, info->regstk.top should point to the register backing * store for r32. */intunw_unwind (struct unw_frame_info *info){ unsigned long sol, cfm = *info->cfm_loc; int is_nat; sol = (cfm >> 7) & 0x7f; /* size of locals */ /* * In general, we would have to make use of unwind info to * unwind an IA-64 stack, but for now gcc uses a special * convention that makes this possible without full-fledged * unwindo info. Specifically, we expect "rp" in the second * last, and "ar.pfs" in the last local register, so the * number of locals in a frame must be at least two. If it's * less than that, we reached the end of the C call stack. */ if (sol < 2) return -1; info->ip = read_reg(info, sol - 2, &is_nat); if (is_nat || (info->ip & (my_cpu_data.unimpl_va_mask | 0xf))) /* reject let obviously bad addresses */ return -1; info->cfm_loc = ia64_rse_skip_regs((unsigned long *) info->bsp, sol - 1); cfm = read_reg(info, sol - 1, &is_nat); if (is_nat) return -1; sol = (cfm >> 7) & 0x7f; info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->bsp, -sol); return 0;}#endif /* !CONFIG_IA64_NEW_UNWIND */#ifdef CONFIG_IA64_NEW_UNWINDstatic voidinit_unwind_table (struct unw_table *table, const char *name, unsigned long segment_base, unsigned long gp, void *table_start, void *table_end){ struct unw_table_entry *start = table_start, *end = table_end; table->name = name; table->segment_base = segment_base; table->gp = gp; table->start = segment_base + start[0].start_offset; table->end = segment_base + end[-1].end_offset; table->array = start; table->length = end - start;}void *unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp, void *table_start, void *table_end){ struct unw_table_entry *start = table_start, *end = table_end; struct unw_table *table; unsigned long flags; if (end - start <= 0) { dprintk("unwind: ignoring attempt to insert empty unwind table\n"); return 0; } table = kmalloc(sizeof(*table), GFP_USER); if (!table) return 0; init_unwind_table(table, name, segment_base, gp, table_start, table_end); spin_lock_irqsave(&unw.lock, flags); { /* keep kernel unwind table at the front (it's searched most commonly): */ table->next = unw.tables->next; unw.tables->next = table; } spin_unlock_irqrestore(&unw.lock, flags); return table;}voidunw_remove_unwind_table (void *handle){ struct unw_table *table, *prev; struct unw_script *tmp; unsigned long flags; long index; if (!handle) { dprintk("unwind: ignoring attempt to remove non-existent unwind table\n"); return; } table = handle; if (table == &unw.kernel_table) { dprintk("unwind: sorry, freeing the kernel's unwind table is a no-can-do!\n"); return; } spin_lock_irqsave(&unw.lock, flags); { /* first, delete the table: */ for (prev = (struct unw_table *) &unw.tables; prev; prev = prev->next) if (prev->next == table) break; if (!prev) { dprintk("unwind: failed to find unwind table %p\n", (void *) table); spin_unlock_irqrestore(&unw.lock, flags); return; } prev->next = table->next; } spin_unlock_irqrestore(&unw.lock, flags); /* next, remove hash table entries for this table */ for (index = 0; index <= UNW_HASH_SIZE; ++index) { tmp = unw.cache + unw.hash[index]; if (unw.hash[index] >= UNW_CACHE_SIZE || tmp->ip < table->start || tmp->ip >= table->end) continue; write_lock(&tmp->lock); { if (tmp->ip >= table->start && tmp->ip < table->end) { unw.hash[index] = tmp->coll_chain; tmp->ip = 0; } } write_unlock(&tmp->lock); } kfree(table);}#endif /* CONFIG_IA64_NEW_UNWIND */voidunw_init (void){#ifdef CONFIG_IA64_NEW_UNWIND extern int ia64_unw_start, ia64_unw_end, __gp; extern void unw_hash_index_t_is_too_narrow (void); long i, off; if (8*sizeof(unw_hash_index_t) < UNW_LOG_HASH_SIZE) unw_hash_index_t_is_too_narrow(); unw.sw_off[unw.preg_index[UNW_REG_PRI_UNAT_GR]] = SW(AR_UNAT); unw.sw_off[unw.preg_index[UNW_REG_BSPSTORE]] = SW(AR_BSPSTORE); unw.sw_off[unw.preg_index[UNW_REG_PFS]] = SW(AR_UNAT); unw.sw_off[unw.preg_index[UNW_REG_RP]] = SW(B0); unw.sw_off[unw.preg_index[UNW_REG_UNAT]] = SW(AR_UNAT); unw.sw_off[unw.preg_index[UNW_REG_PR]] = SW(PR); unw.sw_off[unw.preg_index[UNW_REG_LC]] = SW(AR_LC); unw.sw_off[unw.preg_index[UNW_REG_FPSR]] = SW(AR_FPSR); for (i = UNW_REG_R4, off = SW(R4); i <= UNW_REG_R7; ++i, off += 8) unw.sw_off[unw.preg_index[i]] = off; for (i = UNW_REG_B1, off = SW(B1); i <= UNW_REG_B5; ++i, off += 8) unw.sw_off[unw.preg_index[i]] = off; for (i = UNW_REG_F2, off = SW(F2); i <= UNW_REG_F5; ++i, off += 16) unw.sw_off[unw.preg_index[i]] = off; for (i = UNW_REG_F16, off = SW(F16); i <= UNW_REG_F31; ++i, off += 16) unw.sw_off[unw.preg_index[i]] = off; for (i = 0; i < UNW_CACHE_SIZE; ++i) { if (i > 0) unw.cache[i].lru_chain = (i - 1); unw.cache[i].coll_chain = -1; unw.cache[i].lock = RW_LOCK_UNLOCKED; } unw.lru_head = UNW_CACHE_SIZE - 1; unw.lru_tail = 0; init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) &__gp, &ia64_unw_start, &ia64_unw_end);#endif /* CONFIG_IA64_NEW_UNWIND */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -