📄 unwind.c
字号:
pop(&sr); STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start); script_finalize(script, &sr); STAT(unw.stat.script.build_time += ia64_get_itc() - start); return script;}/* * Apply the unwinding actions represented by OPS and update SR to * reflect the state that existed upon entry to the function that this * unwinder represents. */static inline voidrun_script (struct unw_script *script, struct unw_frame_info *state){ struct unw_insn *ip, *limit, next_insn; unsigned long opc, dst, val, off; unsigned long *s = (unsigned long *) state; STAT(unsigned long start;) STAT(++unw.stat.script.runs; start = ia64_get_itc()); state->flags = script->flags; ip = script->insn; limit = script->insn + script->count; next_insn = *ip; while (ip++ < limit) { opc = next_insn.opc; dst = next_insn.dst; val = next_insn.val; next_insn = *ip; redo: switch (opc) { case UNW_INSN_ADD: s[dst] += val; break; case UNW_INSN_MOVE2: if (!s[val]) goto lazy_init; s[dst+1] = s[val+1]; s[dst] = s[val]; break; case UNW_INSN_MOVE: 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] & (local_cpu_data->unimpl_va_mask | 0x7)) != 0 || 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 & (local_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));}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); unw_init_frame_info(info, t, sw);}static voidinit_unwind_table (struct unw_table *table, const char *name, unsigned long segment_base, unsigned long gp, const void *table_start, const void *table_end){ const 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, const void *table_start, const void *table_end){ const 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);}voidunw_create_gate_table (void){ extern char __start_gate_section[], __stop_gate_section[]; unsigned long *lp, start, end, segbase = unw.kernel_table.segment_base; const struct unw_table_entry *entry, *first; size_t info_size, size; char *info; start = (unsigned long) __start_gate_section - segbase; end = (unsigned long) __stop_gate_section - segbase; size = 0; first = lookup(&unw.kernel_table, start); for (entry = first; entry->start_offset < end; ++entry) size += 3*8 + 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset)); size += 8; /* reserve space for "end of table" marker */ unw.gate_table = alloc_bootmem(size); if (!unw.gate_table) { unw.gate_table_size = 0; printk("unwind: unable to create unwind data for gate page!\n"); return; } unw.gate_table_size = size; lp = unw.gate_table; info = (char *) unw.gate_table + size; for (entry = first; entry->start_offset < end; ++entry, lp += 3) { info_size = 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset)); info -= info_size; memcpy(info, (char *) segbase + entry->info_offset, info_size); lp[0] = entry->start_offset - start + GATE_ADDR; /* start */ lp[1] = entry->end_offset - start + GATE_ADDR; /* end */ lp[2] = info - (char *) unw.gate_table; /* info */ } *lp = 0; /* end-of-table marker */}voidunw_init (void){ 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);}/* * This system call copies the unwind data into the buffer pointed to by BUF and returns * the size of the unwind data. If BUF_SIZE is smaller than the size of the unwind data * or if BUF is NULL, nothing is copied, but the system call still returns the size of the * unwind data. * * The first portion of the unwind data contains an unwind table and rest contains the * associated unwind info (in no particular order). The unwind table consists of a table * of entries of the form: * * u64 start; (64-bit address of start of function) * u64 end; (64-bit address of start of function) * u64 info; (BUF-relative offset to unwind info) * * The end of the unwind table is indicated by an entry with a START address of zero. * * Please see the IA-64 Software Conventions and Runtime Architecture manual for details * on the format of the unwind info. * * ERRORS * EFAULT BUF points outside your accessible address space. */asmlinkage longsys_getunwind (void *buf, size_t buf_size){ if (buf && buf_size >= unw.gate_table_size) if (copy_to_user(buf, unw.gate_table, unw.gate_table_size) != 0) return -EFAULT; return unw.gate_table_size;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -