⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unwind.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -