📄 unwind.c
字号:
prev_ip = info->ip; prev_sp = info->sp; prev_bsp = info->bsp; /* restore the ip */ if (!info->rp_loc) { /* FIXME: should really be level 0 but it occurs too often. KAO */ UNW_DPRINT(1, "unwind.%s: failed to locate return link (ip=0x%lx)!\n", __FUNCTION__, info->ip); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } ip = info->ip = *info->rp_loc;#ifndef XEN if (ip < GATE_ADDR) {#else if (!is_hypervisor_virt(info->ip)) {#endif UNW_DPRINT(2, "unwind.%s: reached user-space (ip=0x%lx)\n", __FUNCTION__, ip); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); return -1; } /* restore the cfm: */ if (!info->pfs_loc) { UNW_DPRINT(0, "unwind.%s: failed to locate ar.pfs!\n", __FUNCTION__); 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)) { info->pt = info->sp + 16; if ((pr & (1UL << PRED_NON_SYSCALL)) != 0) num_regs = *info->cfm_loc & 0x7f; /* size of frame */ info->pfs_loc = (unsigned long *) (info->pt + offsetof(struct pt_regs, ar_pfs)); UNW_DPRINT(3, "unwind.%s: interrupt_frame pt 0x%lx\n", __FUNCTION__, info->pt); } 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) { UNW_DPRINT(0, "unwind.%s: bsp (0x%lx) out of range [0x%lx-0x%lx]\n", __FUNCTION__, 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) { UNW_DPRINT(0, "unwind.%s: sp (0x%lx) out of range [0x%lx-0x%lx]\n", __FUNCTION__, 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) { UNW_DPRINT(0, "unwind.%s: ip, sp, bsp unchanged; stopping here (ip=0x%lx)\n", __FUNCTION__, 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;}EXPORT_SYMBOL(unw_unwind);intunw_unwind_to_user (struct unw_frame_info *info){ unsigned long ip, sp, pr = 0; while (unw_unwind(info) >= 0) { unw_get_sp(info, &sp); if ((long)((unsigned long)info->task + IA64_STK_OFFSET - sp) < IA64_PT_REGS_SIZE) { UNW_DPRINT(0, "unwind.%s: ran off the top of the kernel stack\n", __FUNCTION__); break; }#ifndef XEN if (unw_is_intr_frame(info) && (pr & (1UL << PRED_USER_STACK))) return 0;#else if (unw_is_intr_frame(info) && !is_hvm_vcpu(info->task) && (pr & (1UL << PRED_USER_STACK))) return 0; /* * vmx fault handlers don't vcpu->on_stack and keep * (pr & (1UL << PRED_USER_STACK)) condition untouched. * we need to stop unwinding somehow. */ if (unw_is_intr_frame(info) && is_hvm_vcpu(info->task) && info->pr_loc == &vcpu_regs(info->task)->pr) return 0;#endif if (unw_get_pr (info, &pr) < 0) { unw_get_rp(info, &ip); UNW_DPRINT(0, "unwind.%s: failed to read " "predicate register (ip=0x%lx)\n", __FUNCTION__, ip); return -1; } } unw_get_ip(info, &ip); UNW_DPRINT(0, "unwind.%s: failed to unwind to user-level (ip=0x%lx)\n", __FUNCTION__, ip); return -1;}EXPORT_SYMBOL(unw_unwind_to_user);static voidinit_frame_info (struct unw_frame_info *info, struct task_struct *t, struct switch_stack *sw, unsigned long stktop){ unsigned long rbslimit, rbstop, stklimit; 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; 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 = stktop; info->pr = sw->pr; UNW_DPRINT(3, "unwind.%s:\n" " task 0x%lx\n" " rbs = [0x%lx-0x%lx)\n" " stk = [0x%lx-0x%lx)\n" " pr 0x%lx\n" " sw 0x%lx\n" " sp 0x%lx\n", __FUNCTION__, (unsigned long) t, rbslimit, rbstop, stktop, stklimit, info->pr, (unsigned long) info->sw, info->sp); STAT(unw.stat.api.init_time += ia64_get_itc() - start; local_irq_restore(flags));}voidunw_init_from_interruption (struct unw_frame_info *info, struct task_struct *t, struct pt_regs *pt, struct switch_stack *sw){ unsigned long sof; init_frame_info(info, t, sw, pt->r12); info->cfm_loc = &pt->cr_ifs; info->unat_loc = &pt->ar_unat; info->pfs_loc = &pt->ar_pfs; sof = *info->cfm_loc & 0x7f; info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sof); info->ip = pt->cr_iip + ia64_psr(pt)->ri; info->pt = (unsigned long) pt; UNW_DPRINT(3, "unwind.%s:\n" " bsp 0x%lx\n" " sof 0x%lx\n" " ip 0x%lx\n", __FUNCTION__, info->bsp, sof, info->ip); find_save_locs(info);}voidunw_init_frame_info (struct unw_frame_info *info, struct task_struct *t, struct switch_stack *sw){ unsigned long sol; init_frame_info(info, t, sw, (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; UNW_DPRINT(3, "unwind.%s:\n" " bsp 0x%lx\n" " sol 0x%lx\n" " ip 0x%lx\n", __FUNCTION__, info->bsp, sol, info->ip); find_save_locs(info);}EXPORT_SYMBOL(unw_init_frame_info);voidunw_init_from_blocked_task (struct unw_frame_info *info, struct task_struct *t){#ifdef XEN struct switch_stack *sw = (struct switch_stack *) (t->arch._thread.ksp + 16);#else struct switch_stack *sw = (struct switch_stack *) (t->thread.ksp + 16);#endif UNW_DPRINT(1, "unwind.%s\n", __FUNCTION__); unw_init_frame_info(info, t, sw);}EXPORT_SYMBOL(unw_init_from_blocked_task);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;}#ifndef XENvoid *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) { UNW_DPRINT(0, "unwind.%s: ignoring attempt to insert empty unwind table\n", __FUNCTION__); return NULL; } table = kmalloc(sizeof(*table), GFP_USER); if (!table) return NULL; 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) { UNW_DPRINT(0, "unwind.%s: ignoring attempt to remove non-existent unwind table\n", __FUNCTION__); return; } table = handle; if (table == &unw.kernel_table) { UNW_DPRINT(0, "unwind.%s: sorry, freeing the kernel's unwind table is a " "no-can-do!\n", __FUNCTION__); 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) { UNW_DPRINT(0, "unwind.%s: failed to find unwind table %p\n", __FUNCTION__, (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);}static int __initcreate_gate_table (void){ const struct unw_table_entry *entry, *start, *end; unsigned long *lp, segbase = GATE_ADDR; size_t info_size, size; char *info; Elf64_Phdr *punw = NULL, *phdr = (Elf64_Phdr *) (GATE_ADDR + GATE_EHDR->e_phoff); int i; for (i = 0; i < GATE_EHDR->e_phnum; ++i, ++phdr) if (phdr->p_type == PT_IA_64_UNWIND) { punw = phdr; break; } if (!punw) { printk("%s: failed to find gate DSO's unwind table!\n", __FUNCTION__); return 0; } start = (const struct unw_table_entry *) punw->p_vaddr; end = (struct unw_table_entry *) ((char *) start + punw->p_memsz); size = 0; unw_add_unwind_table("linux-gate.so", segbase, 0, start, end); for (entry = start; entry < 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 = kmalloc(size, GFP_KERNEL); if (!unw.gate_table) { unw.gate_table_size = 0; printk(KERN_ERR "%s: unable to create unwind data for gate page!\n", __FUNCTION__); return 0; } unw.gate_table_size = size; lp = unw.gate_table; info = (char *) unw.gate_table + size; for (entry = start; entry < 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] = segbase + entry->start_offset; /* start */ lp[1] = segbase + entry->end_offset; /* end */ lp[2] = info - (char *) unw.gate_table; /* info */ } *lp = 0; /* end-of-table marker */ return 0;}__initcall(create_gate_table);#endif // !XENvoid __initunw_init (void){ extern char __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(CALLER_UNAT); unw.sw_off[unw.preg_index[UNW_REG_BSPSTORE]] = SW(AR_BSPSTORE); unw.sw_off[unw.preg_index[UNW_REG_PFS]] = SW(AR_PFS); unw.sw_off[unw.preg_index[UNW_REG_RP]] = SW(B0); unw.sw_off[unw.preg_index[UNW_REG_UNAT]] = SW(CALLER_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; rwlock_init(&unw.cache[i].lock); } unw.lru_head = UNW_CACHE_SIZE - 1; unw.lru_tail = 0; init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp, __start_unwind, __end_unwind);}#ifndef XEN/* * DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED * * This system call has been deprecated. The new and improved way to get * at the kernel's unwind info is via the gate DSO. The address of the * ELF header for this DSO is passed to user-level via AT_SYSINFO_EHDR. * * DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED * * 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 __user *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;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -