📄 unwind.c
字号:
/* * Copyright (C) 1999-2000 Hewlett-Packard Co * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> *//* * This file implements call frame unwind support for the Linux * kernel. Parsing and processing the unwind information is * time-consuming, so this implementation translates the the unwind * descriptors into unwind scripts. These scripts are very simple * (basically a sequence of assignments) and efficient to execute. * They are cached for later re-use. Each script is specific for a * given instruction pointer address and the set of predicate values * that the script depends on (most unwind descriptors are * unconditional and scripts often do not depend on predicates at * all). This code is based on the unwind conventions described in * the "IA-64 Software Conventions and Runtime Architecture" manual. * * SMP conventions: * o updates to the global unwind data (in structure "unw") are serialized * by the unw.lock spinlock * o each unwind script has its own read-write lock; a thread must acquire * a read lock before executing a script and must acquire a write lock * before modifying a script * o if both the unw.lock spinlock and a script's read-write lock must be * acquired, then the read-write lock must be acquired first. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <asm/unwind.h>#ifdef CONFIG_IA64_NEW_UNWIND#include <asm/delay.h>#include <asm/page.h>#include <asm/ptrace.h>#include <asm/ptrace_offsets.h>#include <asm/rse.h>#include <asm/system.h>#include "entry.h"#include "unwind_i.h"#define MIN(a,b) ((a) < (b) ? (a) : (b))#define p5 5#define UNW_LOG_CACHE_SIZE 7 /* each unw_script is ~256 bytes in size */#define UNW_CACHE_SIZE (1 << UNW_LOG_CACHE_SIZE)#define UNW_LOG_HASH_SIZE (UNW_LOG_CACHE_SIZE + 1)#define UNW_HASH_SIZE (1 << UNW_LOG_HASH_SIZE)#define UNW_DEBUG 0#define UNW_STATS 0 /* WARNING: this disabled interrupts for long time-spans!! */#if UNW_DEBUG static long unw_debug_level = 255;# define debug(level,format...) if (unw_debug_level > level) printk(format)# define dprintk(format...) printk(format)# define inline#else# define debug(level,format...)# define dprintk(format...)#endif#if UNW_STATS# define STAT(x...) x#else# define STAT(x...)#endif#define alloc_reg_state() kmalloc(sizeof(struct unw_state_record), GFP_ATOMIC)#define free_reg_state(usr) kfree(usr)typedef unsigned long unw_word;typedef unsigned char unw_hash_index_t;#define struct_offset(str,fld) ((char *)&((str *)NULL)->fld - (char *) 0)static struct { spinlock_t lock; /* spinlock for unwind data */ /* list of unwind tables (one per load-module) */ struct unw_table *tables; /* table of registers that prologues can save (and order in which they're saved): */ const unsigned char save_order[8]; /* maps a preserved register index (preg_index) to corresponding switch_stack offset: */ unsigned short sw_off[sizeof(struct unw_frame_info) / 8]; unsigned short lru_head; /* index of lead-recently used script */ unsigned short lru_tail; /* index of most-recently used script */ /* index into unw_frame_info for preserved register i */ unsigned short preg_index[UNW_NUM_REGS]; /* unwind table for the kernel: */ struct unw_table kernel_table; /* hash table that maps instruction pointer to script index: */ unsigned short hash[UNW_HASH_SIZE]; /* script cache: */ struct unw_script cache[UNW_CACHE_SIZE];# if UNW_DEBUG const char *preg_name[UNW_NUM_REGS];# endif# if UNW_STATS struct { struct { int lookups; int hinted_hits; int normal_hits; int collision_chain_traversals; } cache; struct { unsigned long build_time; unsigned long run_time; unsigned long parse_time; int builds; int news; int collisions; int runs; } script; struct { unsigned long init_time; unsigned long unwind_time; int inits; int unwinds; } api; } stat;# endif} unw = { tables: &unw.kernel_table, lock: SPIN_LOCK_UNLOCKED, save_order: { UNW_REG_RP, UNW_REG_PFS, UNW_REG_PSP, UNW_REG_PR, UNW_REG_UNAT, UNW_REG_LC, UNW_REG_FPSR, UNW_REG_PRI_UNAT_GR }, preg_index: { struct_offset(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_GR */ struct_offset(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_MEM */ struct_offset(struct unw_frame_info, bsp_loc)/8, struct_offset(struct unw_frame_info, bspstore_loc)/8, struct_offset(struct unw_frame_info, pfs_loc)/8, struct_offset(struct unw_frame_info, rnat_loc)/8, struct_offset(struct unw_frame_info, psp)/8, struct_offset(struct unw_frame_info, rp_loc)/8, struct_offset(struct unw_frame_info, r4)/8, struct_offset(struct unw_frame_info, r5)/8, struct_offset(struct unw_frame_info, r6)/8, struct_offset(struct unw_frame_info, r7)/8, struct_offset(struct unw_frame_info, unat_loc)/8, struct_offset(struct unw_frame_info, pr_loc)/8, struct_offset(struct unw_frame_info, lc_loc)/8, struct_offset(struct unw_frame_info, fpsr_loc)/8, struct_offset(struct unw_frame_info, b1_loc)/8, struct_offset(struct unw_frame_info, b2_loc)/8, struct_offset(struct unw_frame_info, b3_loc)/8, struct_offset(struct unw_frame_info, b4_loc)/8, struct_offset(struct unw_frame_info, b5_loc)/8, struct_offset(struct unw_frame_info, f2_loc)/8, struct_offset(struct unw_frame_info, f3_loc)/8, struct_offset(struct unw_frame_info, f4_loc)/8, struct_offset(struct unw_frame_info, f5_loc)/8, struct_offset(struct unw_frame_info, fr_loc[16 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[17 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[18 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[19 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[20 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[21 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[22 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[23 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[24 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[25 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[26 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[27 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[28 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[29 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[30 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[31 - 16])/8, }, hash : { [0 ... UNW_HASH_SIZE - 1] = -1 },#if UNW_DEBUG preg_name: { "pri_unat_gr", "pri_unat_mem", "bsp", "bspstore", "ar.pfs", "ar.rnat", "psp", "rp", "r4", "r5", "r6", "r7", "ar.unat", "pr", "ar.lc", "ar.fpsr", "b1", "b2", "b3", "b4", "b5", "f2", "f3", "f4", "f5", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" }#endif};/* Unwind accessors. *//* * Returns offset of rREG in struct pt_regs. */static inline unsigned longpt_regs_off (unsigned long reg){ unsigned long off =0; if (reg >= 1 && reg <= 3) off = struct_offset(struct pt_regs, r1) + 8*(reg - 1); else if (reg <= 11) off = struct_offset(struct pt_regs, r8) + 8*(reg - 8); else if (reg <= 15) off = struct_offset(struct pt_regs, r12) + 8*(reg - 12); else if (reg <= 31) off = struct_offset(struct pt_regs, r16) + 8*(reg - 16); else dprintk("unwind: bad scratch reg r%lu\n", reg); return off;}intunw_access_gr (struct unw_frame_info *info, int regnum, unsigned long *val, char *nat, int write){ unsigned long *addr, *nat_addr, nat_mask = 0, dummy_nat; struct unw_ireg *ireg; struct pt_regs *pt; if ((unsigned) regnum - 1 >= 127) { dprintk("unwind: trying to access non-existent r%u\n", regnum); return -1; } if (regnum < 32) { if (regnum >= 4 && regnum <= 7) { /* access a preserved register */ ireg = &info->r4 + (regnum - 4); addr = ireg->loc; if (addr) { nat_addr = addr + ireg->nat.off; switch (ireg->nat.type) { case UNW_NAT_VAL: /* simulate getf.sig/setf.sig */ if (write) { if (*nat) { /* write NaTVal and be done with it */ addr[0] = 0; addr[1] = 0x1fffe; return 0; } addr[1] = 0x1003e; } else { if (addr[0] == 0 && addr[1] == 0x1ffe) { /* return NaT and be done with it */ *val = 0; *nat = 1; return 0; } } /* fall through */ case UNW_NAT_NONE: dummy_nat = 0; nat_addr = &dummy_nat; break; case UNW_NAT_MEMSTK: nat_mask = (1UL << ((long) addr & 0x1f8)/8); break; case UNW_NAT_REGSTK: nat_addr = ia64_rse_rnat_addr(addr); if ((unsigned long) addr < info->regstk.limit || (unsigned long) addr >= info->regstk.top) { dprintk("unwind: %p outside of regstk " "[0x%lx-0x%lx)\n", (void *) addr, info->regstk.limit, info->regstk.top); return -1; } if ((unsigned long) nat_addr >= info->regstk.top) nat_addr = &info->sw->ar_rnat; nat_mask = (1UL << ia64_rse_slot_num(addr)); break; } } else { addr = &info->sw->r4 + (regnum - 4); nat_addr = &info->sw->ar_unat; nat_mask = (1UL << ((long) addr & 0x1f8)/8); } } else { /* access a scratch register */ if (info->flags & UNW_FLAG_INTERRUPT_FRAME) pt = (struct pt_regs *) info->psp - 1; else pt = (struct pt_regs *) info->sp - 1; addr = (unsigned long *) ((long) pt + pt_regs_off(regnum)); if (info->pri_unat_loc) nat_addr = info->pri_unat_loc; else nat_addr = &info->sw->ar_unat; nat_mask = (1UL << ((long) addr & 0x1f8)/8); } } else { /* access a stacked register */ addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum); nat_addr = ia64_rse_rnat_addr(addr); if ((unsigned long) addr < info->regstk.limit || (unsigned long) addr >= info->regstk.top) { dprintk("unwind: ignoring attempt to access register outside of rbs\n"); return -1; } if ((unsigned long) nat_addr >= info->regstk.top) nat_addr = &info->sw->ar_rnat; nat_mask = (1UL << ia64_rse_slot_num(addr)); } if (write) { *addr = *val; if (*nat) *nat_addr |= nat_mask; else *nat_addr &= ~nat_mask; } else { *val = *addr; *nat = (*nat_addr & nat_mask) != 0; } return 0;}intunw_access_br (struct unw_frame_info *info, int regnum, unsigned long *val, int write){ unsigned long *addr; struct pt_regs *pt; if (info->flags & UNW_FLAG_INTERRUPT_FRAME) pt = (struct pt_regs *) info->psp - 1; else pt = (struct pt_regs *) info->sp - 1; switch (regnum) { /* scratch: */ case 0: addr = &pt->b0; break; case 6: addr = &pt->b6; break; case 7: addr = &pt->b7; break; /* preserved: */ case 1: case 2: case 3: case 4: case 5: addr = *(&info->b1_loc + (regnum - 1)); if (!addr) addr = &info->sw->b1 + (regnum - 1); break; default: dprintk("unwind: trying to access non-existent b%u\n", regnum); return -1; } if (write) *addr = *val; else *val = *addr; return 0;}intunw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val, int write){ struct ia64_fpreg *addr = 0; struct pt_regs *pt; if ((unsigned) (regnum - 2) >= 126) { dprintk("unwind: trying to access non-existent f%u\n", regnum); return -1; } if (info->flags & UNW_FLAG_INTERRUPT_FRAME) pt = (struct pt_regs *) info->psp - 1; else pt = (struct pt_regs *) info->sp - 1; if (regnum <= 5) { addr = *(&info->f2_loc + (regnum - 2)); if (!addr) addr = &info->sw->f2 + (regnum - 2); } else if (regnum <= 15) { if (regnum <= 9) addr = &pt->f6 + (regnum - 6); else addr = &info->sw->f10 + (regnum - 10); } else if (regnum <= 31) { addr = info->fr_loc[regnum - 16]; if (!addr) addr = &info->sw->f16 + (regnum - 16); } else { struct task_struct *t = info->task; if (write) ia64_sync_fph(t); else ia64_flush_fph(t); addr = t->thread.fph + (regnum - 32); } if (write) *addr = *val; else *val = *addr; return 0;}intunw_access_ar (struct unw_frame_info *info, int regnum, unsigned long *val, int write){ unsigned long *addr; struct pt_regs *pt; if (info->flags & UNW_FLAG_INTERRUPT_FRAME) pt = (struct pt_regs *) info->psp - 1; else pt = (struct pt_regs *) info->sp - 1; switch (regnum) { case UNW_AR_BSP: addr = info->bsp_loc; if (!addr) addr = &info->sw->ar_bspstore; break; case UNW_AR_BSPSTORE: addr = info->bspstore_loc; if (!addr) addr = &info->sw->ar_bspstore; break; case UNW_AR_PFS: addr = info->pfs_loc; if (!addr) addr = &info->sw->ar_pfs; break; case UNW_AR_RNAT: addr = info->rnat_loc; if (!addr) addr = &info->sw->ar_rnat; break; case UNW_AR_UNAT: addr = info->unat_loc; if (!addr) addr = &info->sw->ar_unat; break; case UNW_AR_LC: addr = info->lc_loc; if (!addr) addr = &info->sw->ar_lc; break; case UNW_AR_EC: if (!info->cfm_loc) return -1; if (write) *info->cfm_loc = (*info->cfm_loc & ~(0x3fUL << 52)) | ((*val & 0x3f) << 52); else *val = (*info->cfm_loc >> 52) & 0x3f; return 0; case UNW_AR_FPSR: addr = info->fpsr_loc; if (!addr) addr = &info->sw->ar_fpsr; break; case UNW_AR_RSC: addr = &pt->ar_rsc; break; case UNW_AR_CCV: addr = &pt->ar_ccv; break; default: dprintk("unwind: trying to access non-existent ar%u\n", regnum); return -1; } if (write) *addr = *val; else *val = *addr; return 0;}inline intunw_access_pr (struct unw_frame_info *info, unsigned long *val, int write){ unsigned long *addr; addr = info->pr_loc; if (!addr) addr = &info->sw->pr; if (write) *addr = *val; else *val = *addr; return 0;}/* Unwind decoder routines */static inline voidpush (struct unw_state_record *sr){ struct unw_reg_state *rs; rs = alloc_reg_state(); if (!rs) { printk("unwind: cannot stack reg state!\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -