📄 unwind.c
字号:
/* * Copyright (C) 1999-2004 Hewlett-Packard Co * David Mosberger-Tang <davidm@hpl.hp.com> * Copyright (C) 2003 Fenghua Yu <fenghua.yu@intel.com> * - Change pt_regs_off() to make it less dependant on pt_regs structure. *//* * 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 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. */#ifdef XEN#include <xen/types.h>#include <xen/elf.h>#include <xen/kernel.h>#include <xen/sched.h>#include <xen/xmalloc.h>#include <xen/spinlock.h>#include <xen/errno.h>// work around#ifdef CONFIG_SMP#define write_trylock(lock) _raw_write_trylock(lock)#else#define write_trylock(lock) ({1;})#endif#else#include <linux/module.h>#include <linux/bootmem.h>#include <linux/elf.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#endif#include <asm/unwind.h>#include <asm/delay.h>#include <asm/page.h>#include <asm/ptrace.h>#include <asm/ptrace_offsets.h>#include <asm/rse.h>#include <asm/sections.h>#include <asm/system.h>#include <asm/uaccess.h>#include "entry.h"#include "unwind_i.h"#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_STATS 0 /* WARNING: this disabled interrupts for long time-spans!! */#ifdef UNW_DEBUG static unsigned int unw_debug_level = UNW_DEBUG;# define UNW_DEBUG_ON(n) unw_debug_level >= n /* Do not code a printk level, not all debug lines end in newline */# define UNW_DPRINT(n, ...) if (UNW_DEBUG_ON(n)) printk(__VA_ARGS__)# define inline#else /* !UNW_DEBUG */# define UNW_DEBUG_ON(n) 0# define UNW_DPRINT(n, ...)#endif /* UNW_DEBUG */#if UNW_STATS# define STAT(x...) x#else# define STAT(x...)#endif#ifdef XEN#define alloc_reg_state() xmalloc(struct unw_reg_state)#define free_reg_state(usr) xfree(usr)#define alloc_labeled_state() xmalloc(struct unw_labeled_state)#define free_labeled_state(usr) xfree(usr)#else#define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC)#define free_reg_state(usr) kfree(usr)#define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC)#define free_labeled_state(usr) kfree(usr)#endiftypedef unsigned long unw_word;typedef unsigned char unw_hash_index_t;static struct { spinlock_t lock; /* spinlock for unwind data */ /* list of unwind tables (one per load-module) */ struct unw_table *tables; unsigned long r0; /* constant 0 for r0 */ /* 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]; short pt_regs_offsets[32]; /* unwind table for the kernel: */ struct unw_table kernel_table; /* unwind table describing the gate page (kernel code that is mapped into user space): */ size_t gate_table_size; unsigned long *gate_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];# ifdef 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 = { offsetof(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_GR */ offsetof(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_MEM */ offsetof(struct unw_frame_info, bsp_loc)/8, offsetof(struct unw_frame_info, bspstore_loc)/8, offsetof(struct unw_frame_info, pfs_loc)/8, offsetof(struct unw_frame_info, rnat_loc)/8, offsetof(struct unw_frame_info, psp)/8, offsetof(struct unw_frame_info, rp_loc)/8, offsetof(struct unw_frame_info, r4)/8, offsetof(struct unw_frame_info, r5)/8, offsetof(struct unw_frame_info, r6)/8, offsetof(struct unw_frame_info, r7)/8, offsetof(struct unw_frame_info, unat_loc)/8, offsetof(struct unw_frame_info, pr_loc)/8, offsetof(struct unw_frame_info, lc_loc)/8, offsetof(struct unw_frame_info, fpsr_loc)/8, offsetof(struct unw_frame_info, b1_loc)/8, offsetof(struct unw_frame_info, b2_loc)/8, offsetof(struct unw_frame_info, b3_loc)/8, offsetof(struct unw_frame_info, b4_loc)/8, offsetof(struct unw_frame_info, b5_loc)/8, offsetof(struct unw_frame_info, f2_loc)/8, offsetof(struct unw_frame_info, f3_loc)/8, offsetof(struct unw_frame_info, f4_loc)/8, offsetof(struct unw_frame_info, f5_loc)/8, offsetof(struct unw_frame_info, fr_loc[16 - 16])/8, offsetof(struct unw_frame_info, fr_loc[17 - 16])/8, offsetof(struct unw_frame_info, fr_loc[18 - 16])/8, offsetof(struct unw_frame_info, fr_loc[19 - 16])/8, offsetof(struct unw_frame_info, fr_loc[20 - 16])/8, offsetof(struct unw_frame_info, fr_loc[21 - 16])/8, offsetof(struct unw_frame_info, fr_loc[22 - 16])/8, offsetof(struct unw_frame_info, fr_loc[23 - 16])/8, offsetof(struct unw_frame_info, fr_loc[24 - 16])/8, offsetof(struct unw_frame_info, fr_loc[25 - 16])/8, offsetof(struct unw_frame_info, fr_loc[26 - 16])/8, offsetof(struct unw_frame_info, fr_loc[27 - 16])/8, offsetof(struct unw_frame_info, fr_loc[28 - 16])/8, offsetof(struct unw_frame_info, fr_loc[29 - 16])/8, offsetof(struct unw_frame_info, fr_loc[30 - 16])/8, offsetof(struct unw_frame_info, fr_loc[31 - 16])/8, }, .pt_regs_offsets = { [0] = -1, offsetof(struct pt_regs, r1), offsetof(struct pt_regs, r2), offsetof(struct pt_regs, r3), [4] = -1, [5] = -1, [6] = -1, [7] = -1, offsetof(struct pt_regs, r8), offsetof(struct pt_regs, r9), offsetof(struct pt_regs, r10), offsetof(struct pt_regs, r11), offsetof(struct pt_regs, r12), offsetof(struct pt_regs, r13), offsetof(struct pt_regs, r14), offsetof(struct pt_regs, r15), offsetof(struct pt_regs, r16), offsetof(struct pt_regs, r17), offsetof(struct pt_regs, r18), offsetof(struct pt_regs, r19), offsetof(struct pt_regs, r20), offsetof(struct pt_regs, r21), offsetof(struct pt_regs, r22), offsetof(struct pt_regs, r23), offsetof(struct pt_regs, r24), offsetof(struct pt_regs, r25), offsetof(struct pt_regs, r26), offsetof(struct pt_regs, r27), offsetof(struct pt_regs, r28), offsetof(struct pt_regs, r29), offsetof(struct pt_regs, r30), offsetof(struct pt_regs, r31), }, .hash = { [0 ... UNW_HASH_SIZE - 1] = -1 },#ifdef 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};static inline intread_only (void *addr){ return (unsigned long) ((char *) addr - (char *) &unw.r0) < sizeof(unw.r0);}/* * Returns offset of rREG in struct pt_regs. */static inline unsigned longpt_regs_off (unsigned long reg){ short off = -1; if (reg < ARRAY_SIZE(unw.pt_regs_offsets)) off = unw.pt_regs_offsets[reg]; if (off < 0) { UNW_DPRINT(0, "unwind.%s: bad scratch reg r%lu\n", __FUNCTION__, reg); off = 0; } return (unsigned long) off;}static inline struct pt_regs *get_scratch_regs (struct unw_frame_info *info){ if (!info->pt) { /* This should not happen with valid unwind info. */ UNW_DPRINT(0, "unwind.%s: bad unwind info: resetting info->pt\n", __FUNCTION__); if (info->flags & UNW_FLAG_INTERRUPT_FRAME) info->pt = (unsigned long) ((struct pt_regs *) info->psp - 1); else info->pt = info->sp - 16; } UNW_DPRINT(3, "unwind.%s: sp 0x%lx pt 0x%lx\n", __FUNCTION__, info->sp, info->pt); return (struct pt_regs *) info->pt;}/* Unwind accessors. */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) { if (regnum == 0 && !write) { *val = 0; /* read r0 always returns 0 */ *nat = 0; return 0; } UNW_DPRINT(0, "unwind.%s: trying to access non-existent r%u\n", __FUNCTION__, 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) { UNW_DPRINT(0, "unwind.%s: %p outside of regstk " "[0x%lx-0x%lx)\n", __FUNCTION__, (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 */ pt = get_scratch_regs(info); addr = (unsigned long *) ((unsigned long)pt + pt_regs_off(regnum)); if (info->pri_unat_loc) nat_addr = info->pri_unat_loc; else nat_addr = &info->sw->caller_unat; nat_mask = (1UL << ((long) addr & 0x1f8)/8); } } else { /* access a stacked register */ addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum - 32); nat_addr = ia64_rse_rnat_addr(addr); if ((unsigned long) addr < info->regstk.limit || (unsigned long) addr >= info->regstk.top) { UNW_DPRINT(0, "unwind.%s: ignoring attempt to access register outside " "of rbs\n", __FUNCTION__); 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) { if (read_only(addr)) { UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n", __FUNCTION__); } else { *addr = *val; if (*nat) *nat_addr |= nat_mask; else *nat_addr &= ~nat_mask; } } else { if ((*nat_addr & nat_mask) == 0) { *val = *addr; *nat = 0; } else { *val = 0; /* if register is a NaT, *addr may contain kernel data! */ *nat = 1; } } return 0;}EXPORT_SYMBOL(unw_access_gr);intunw_access_br (struct unw_frame_info *info, int regnum, unsigned long *val, int write){ unsigned long *addr; struct pt_regs *pt; switch (regnum) { /* scratch: */ case 0: pt = get_scratch_regs(info); addr = &pt->b0; break; case 6: pt = get_scratch_regs(info); addr = &pt->b6; break; case 7: pt = get_scratch_regs(info); 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: UNW_DPRINT(0, "unwind.%s: trying to access non-existent b%u\n", __FUNCTION__, regnum); return -1; } if (write) if (read_only(addr)) { UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n", __FUNCTION__); } else *addr = *val; else *val = *addr; return 0;}EXPORT_SYMBOL(unw_access_br);intunw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val, int write){ struct ia64_fpreg *addr = NULL; struct pt_regs *pt; if ((unsigned) (regnum - 2) >= 126) { UNW_DPRINT(0, "unwind.%s: trying to access non-existent f%u\n", __FUNCTION__, regnum); return -1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -