diff options
Diffstat (limited to 'runtime/regs.c')
-rw-r--r-- | runtime/regs.c | 394 |
1 files changed, 41 insertions, 353 deletions
diff --git a/runtime/regs.c b/runtime/regs.c index 2daeaa3c..5821f7e7 100644 --- a/runtime/regs.c +++ b/runtime/regs.c @@ -383,317 +383,60 @@ void _stp_print_regs(struct pt_regs * regs) #endif -/* - * (Theoretically) arch-independent scheme for binary lookup of register - * values (from pt_regs) by register name. A register may be called by - * more than one name. - */ -struct _stp_register_desc { - const char *name; - unsigned short size; // in bytes - unsigned short offset; // in bytes, from start of pt_regs -}; - -struct _stp_register_table { - struct _stp_register_desc *registers; - unsigned nr_registers; - unsigned nr_slots; // capacity -}; - -static DEFINE_SPINLOCK(_stp_register_table_lock); -static void _stp_populate_register_table(void); - -/* - * If the named register is in the list, return its slot number and *found=1. - * Else *found=0 and return the slot number where the name should be inserted. - */ -static int _stp_lookup_register(const char *name, - struct _stp_register_table *table, int *found) -{ - unsigned begin, mid, end; - - *found = 0; - end = table->nr_registers; - if (end == 0) - return 0; - begin = 0; - mid = -1; - for (;;) { - int cmp; - int prev_mid = mid; - mid = (begin + end) / 2; - if (mid == prev_mid) - break; - cmp = strcmp(name, table->registers[mid].name); - if (cmp == 0) { - *found = 1; - return mid; - } else if (cmp < 0) - end = mid; - else - begin = mid; - } - if (begin == 0 && strcmp(name, table->registers[0].name) < 0) - return 0; - return begin + 1; -} - -/* - * If found, return 1 and the size and/or offset in the pt_regs array. - * Else return 0. - */ -static int _stp_find_register(const char *name, - struct _stp_register_table *table, size_t *size, size_t *offset) -{ - int slot, found; - if (unlikely(table->nr_registers == 0)) { - unsigned long flags; - /* - * Should we do this at the beginning of time to avoid - * the possibility of spending too long in a handler? - */ - spin_lock_irqsave(&_stp_register_table_lock, flags); - if (table->nr_registers == 0) - _stp_populate_register_table(); - spin_unlock_irqrestore(&_stp_register_table_lock, flags); - } - slot = _stp_lookup_register(name, table, &found); - if (found) { - if (size) - *size = table->registers[slot].size; - if (offset) - *offset = table->registers[slot].offset; - return 1; - } - return 0; -} - -/* - * Add name to the register-lookup table. Note that the name pointer - * is merely copied, not strdup-ed. - */ -void _stp_add_register(const char *name, struct _stp_register_table *table, - size_t size, size_t offset) -{ - int idx, found; - struct _stp_register_desc *slot; - - idx = _stp_lookup_register(name, table, &found); - if (found) - _stp_error("stap runtime internal error: " - "register name %s used twice\n", name); - if (table->nr_registers >= table->nr_slots) - _stp_error("stap runtime internal error: " - "register table overflow\n"); - slot = &table->registers[idx]; - - // Move the slots later in the array out of the way. - if (idx < table->nr_registers) - memmove(slot+1, slot, - sizeof(*slot) * (table->nr_registers - idx)); - table->nr_registers++; - slot->name = name; - slot->size = size; - slot->offset = offset; -} - -#if defined(__i386__) || defined(__x86_64__) -/* - * This register set is used for i386 kernel and apps, and for 32-bit apps - * running on x86_64. For the latter case, this allows the user to use - * things like reg("eax") as well as the standard x86_64 pt_regs names. - */ - -/* - * x86_64 and i386 are especially ugly because the pt_reg member names - * changed as part of the x86 merge. We allow (and use, as needed) - * either the pre-merge name or the post-merge name. - */ - -// I count 32 different names, but add a fudge factor. -static struct _stp_register_desc i386_registers[32+8]; -static struct _stp_register_table i386_register_table = { - .registers = i386_registers, - .nr_slots = ARRAY_SIZE(i386_registers) -}; - -/* - * sizeof(long) is indeed what we want here, for both i386 and x86_64. - * Unlike function args, x86_64 pt_regs is the same even if the int3 - * was in an -m32 app. - */ -#define ADD_PT_REG(name, member) \ - _stp_add_register(name, &i386_register_table, \ - sizeof(long), offsetof(struct pt_regs, member)) -#define ADD2NAMES(nm1, nm2, member) \ - do { \ - ADD_PT_REG(nm1, member); \ - ADD_PT_REG(nm2, member); \ - } while (0) - -#ifdef STAPCONF_X86_UNIREGS -/* Map "ax" and "eax" to regs->ax, and "cs" and "xcs" to regs->cs */ -#define ADD_EREG(nm) ADD2NAMES(#nm, "e" #nm, nm) -#define ADD_XREG(nm) ADD2NAMES(#nm, "x" #nm, nm) -#define ADD_FLAGS_REG() ADD_EREG(flags) -#define EREG(nm, regs) ((regs)->nm) -#define RREG(nm, regs) ((regs)->nm) +/* Function arguments */ -#else /* ! STAPCONF_X86_UNIREGS */ +#define _STP_REGPARM 0x8000 +#define _STP_REGPARM_MASK ((_STP_REGPARM) - 1) -#ifdef __i386__ -#define ADD_EREG(nm) ADD2NAMES(#nm, "e" #nm, e##nm) -#define ADD_XREG(nm) ADD2NAMES(#nm, "x" #nm, x##nm) -#define ADD_FLAGS_REG() ADD_EREG(flags) -#define EREG(nm, regs) ((regs)->e##nm) -#else /* __x86_64__ */ /* - * Map "eax" to regs->rax and "xcs" to regs->cs. Other mappings are - * handled in x86_64_register_table. + * x86_64 and i386 are especially ugly because: + * 1) the pt_reg member names changed as part of the x86 merge. We use + * either the pre-merge name or the post-merge name, as needed. + * 2) -m32 apps on x86_64 look like i386 apps, so we need to support + * those semantics on both i386 and x86_64. */ -#define ADD_EREG(nm) ADD_PT_REG("e" #nm, r##nm) -#define ADD_XREG(nm) ADD_PT_REG("x" #nm, nm) -#define ADD_FLAGS_REG() ADD2NAMES("flags", "eflags", eflags) -/* Note: After a store to %eax, %rax holds the ZERO-extended %eax. */ -#define EREG(nm, regs) ((regs)->r##nm) -#define RREG(nm, regs) ((regs)->r##nm) -#endif /* __x86_64__ */ - -#endif /* ! STAPCONF_X86_UNIREGS */ -static void _stp_populate_i386_register_table(void) -{ - /* - * The order here is the same as in i386 struct pt_regs. - * It's a different order from x86_64 pt_regs; but that doesn't - * matter -- even when compiling for x86_64 -- because the - * offsets are determined by offsetof(), not the calling order. - */ - ADD_EREG(bx); - ADD_EREG(cx); - ADD_EREG(dx); - ADD_EREG(si); - ADD_EREG(di); - ADD_EREG(bp); - ADD_EREG(ax); #ifdef __i386__ - ADD_XREG(ds); - ADD_XREG(es); - ADD_XREG(fs); - /* gs not saved */ -#endif #ifdef STAPCONF_X86_UNIREGS - ADD2NAMES("orig_ax", "orig_eax", orig_ax); +#define EREG(nm, regs) ((regs)->nm) #else -#ifdef __i386__ - ADD2NAMES("orig_ax", "orig_eax", orig_eax); -#else /* __x86_64__ */ - ADD2NAMES("orig_ax", "orig_eax", orig_rax); +#define EREG(nm, regs) ((regs)->e##nm) #endif -#endif /* STAPCONF_X86_UNIREGS */ - ADD_EREG(ip); - ADD_XREG(cs); - ADD_FLAGS_REG(); - ADD_EREG(sp); - ADD_XREG(ss); -} -/* - * For x86_64, this gets a copy of the saved 64-bit register (e.g., regs->rax). - * After a store to %eax, %rax holds the ZERO-extended %eax. - */ -static long -_stp_get_reg32_by_name(const char *name, struct pt_regs *regs) +static long _stp_get_sp(struct pt_regs *regs) { - size_t offset = 0; - long value; // works for i386 or x86_64 - BUG_ON(!name); - if (!regs) - _stp_error("Register values not available in this context.\n"); -#ifdef __i386__ - if (!user_mode(regs)) { - /* esp and ss aren't saved on trap from kernel mode. */ - if (!strcmp(name,"esp") || !strcmp(name, "sp")) - return (long) &EREG(sp, regs); - if (!strcmp(name,"xss") || !strcmp(name, "ss")) { - /* - * Assume ss register hasn't changed since we took - * the trap. - */ - unsigned short ss; - asm volatile("movw %%ss, %0" : : "m" (ss)); - return ss; - } - } -#endif - if (!_stp_find_register(name, &i386_register_table, NULL, &offset)) - _stp_error("Unknown register name: %s\n", name); - (void) memcpy(&value, ((char*)regs) + offset, sizeof(value)); - return value; + if (!user_mode(regs)) + return (long) &EREG(sp, regs); + return EREG(sp, regs); } -#endif /* __i386__ || __x86_64__ */ - -#ifdef __i386__ -static void _stp_populate_register_table(void) +static int _stp_get_regparm(int regparm, struct pt_regs *regs) { - _stp_populate_i386_register_table(); + if (regparm == 0) { + /* Default */ + if (user_mode(regs)) + return 0; + else + // Kernel is built with -mregparm=3. + return 3; + } else + return (regparm & _STP_REGPARM_MASK); } #endif /* __i386__ */ #ifdef __x86_64__ -// I count 32 different names (not the same 32 as i386), but add a fudge factor. -static struct _stp_register_desc x86_64_registers[32+8]; -static struct _stp_register_table x86_64_register_table = { - .registers = x86_64_registers, - .nr_slots = ARRAY_SIZE(x86_64_registers) -}; - -/* NB: Redefining ADD_PT_REG here. ADD2NAMES and such change accordingly. */ -#undef ADD_PT_REG -#define ADD_PT_REG(name, member) \ - _stp_add_register(name, &x86_64_register_table, \ - sizeof(unsigned long), offsetof(struct pt_regs, member)) - -#define ADD_NREG(nm) ADD_PT_REG(#nm, nm) - #ifdef STAPCONF_X86_UNIREGS -#define ADD_RREG(nm) ADD2NAMES(#nm, "r" #nm, nm) +#define EREG(nm, regs) ((regs)->nm) +#define RREG(nm, regs) ((regs)->nm) #else -#define ADD_RREG(nm) ADD2NAMES(#nm, "r" #nm, r##nm) +#define EREG(nm, regs) ((regs)->r##nm) +#define RREG(nm, regs) ((regs)->r##nm) #endif -static void _stp_populate_register_table(void) +static long _stp_get_sp(struct pt_regs *regs) { - /* Same order as in struct pt_regs */ - ADD_NREG(r15); - ADD_NREG(r14); - ADD_NREG(r13); - ADD_NREG(r12); - ADD_RREG(bp); - ADD_RREG(bx); - ADD_NREG(r11); - ADD_NREG(r10); - ADD_NREG(r9); - ADD_NREG(r8); - ADD_RREG(ax); - ADD_RREG(cx); - ADD_RREG(dx); - ADD_RREG(si); - ADD_RREG(di); -#ifdef STAPCONF_X86_UNIREGS - ADD2NAMES("orig_ax", "orig_rax", orig_ax); -#else - ADD2NAMES("orig_ax", "orig_rax", orig_rax); -#endif - ADD_RREG(ip); - ADD_NREG(cs); - ADD_FLAGS_REG(); - ADD_RREG(sp); - ADD_NREG(ss); - - _stp_populate_i386_register_table(); + return RREG(sp, regs); } static int _stp_probing_32bit_app(struct pt_regs *regs) @@ -704,54 +447,26 @@ static int _stp_probing_32bit_app(struct pt_regs *regs) } /* Ensure that the upper 32 bits of val are a sign-extension of the lower 32. */ -static long _stp_sign_extend32(long val) +static int64_t __stp_sign_extend32(int64_t val) { int32_t *val_ptr32 = (int32_t*) &val; return *val_ptr32; } -/* - * Get the value of the 64-bit register with the specified name. "rax", - * "ax", and "eax" all get you regs->[r]ax. Sets *reg32=1 if the name - * designates a 32-bit register (e.g., "eax"), 0 otherwise. - */ -static unsigned long -_stp_get_reg64_by_name(const char *name, struct pt_regs *regs, int *reg32) +static int _stp_get_regparm(int regparm, struct pt_regs *regs) { - size_t offset = 0; - unsigned long value; - BUG_ON(!name); - if (!regs) { - _stp_error("Register values not available in this context.\n"); - return 0; - } - if (_stp_find_register(name, &x86_64_register_table, NULL, &offset)) { - if (reg32) - *reg32 = 0; - (void) memcpy(&value, ((char*)regs) + offset, sizeof(value)); - return value; - } - if (reg32) - *reg32 = 1; - return _stp_get_reg32_by_name(name, regs); + if (regparm == 0) { + /* Default */ + if (_stp_probing_32bit_app(regs)) + return 0; + else + return 6; + } else + return (regparm & _STP_REGPARM_MASK); } -#endif /* __x86_64__ */ - -/* Function arguments */ - -#define _STP_REGPARM 0x8000 -#define _STP_REGPARM_MASK ((_STP_REGPARM) - 1) +#endif /* __x86_64__ */ #if defined(__i386__) || defined(__x86_64__) -static long _stp_get_sp(struct pt_regs *regs) -{ -#ifdef __i386__ - if (!user_mode(regs)) - return (long) &EREG(sp, regs); -#endif - return EREG(sp, regs); -} - /* * Use this for i386 kernel and apps, and for 32-bit apps running on x86_64. * Does arch-specific work for fetching function arg #argnum (1 = first arg). @@ -792,21 +507,6 @@ static int _stp_get_arg32_by_number(int n, int nr_regargs, } #endif /* __i386__ || __x86_64__ */ -#ifdef __i386__ -static int _stp_get_regparm(int regparm, struct pt_regs *regs) -{ - if (regparm == 0) { - /* Default */ - if (user_mode(regs)) - return 0; - else - // Kernel is built with -mregparm=3. - return 3; - } else - return (regparm & _STP_REGPARM_MASK); -} -#endif - #ifdef __x86_64__ /* See _stp_get_arg32_by_number(). */ static int _stp_get_arg64_by_number(int n, int nr_regargs, @@ -835,18 +535,6 @@ static int _stp_get_arg64_by_number(int n, int nr_regargs, return 0; } } - -static int _stp_get_regparm(int regparm, struct pt_regs *regs) -{ - if (regparm == 0) { - /* Default */ - if (_stp_probing_32bit_app(regs)) - return 0; - else - return 6; - } else - return (regparm & _STP_REGPARM_MASK); -} #endif /* __x86_64__ */ /** @} */ |