global _reg_offsets, _stp_regs_registered, _sp_offset, _ss_offset function test_x86_gs:long() %{ /* pure */ #ifdef STAPCONF_X86_GS THIS->__retvalue = 1; #else THIS->__retvalue = 0; #endif %} function _stp_register_regs() { /* Same order as pt_regs */ _reg_offsets["ebx"] = 0 _reg_offsets["bx"] = 0 _reg_offsets["ecx"] = 4 _reg_offsets["cx"] = 4 _reg_offsets["edx"] = 8 _reg_offsets["dx"] = 8 _reg_offsets["esi"] = 12 _reg_offsets["si"] = 12 _reg_offsets["edi"] = 16 _reg_offsets["di"] = 16 _reg_offsets["ebp"] = 20 _reg_offsets["bp"] = 20 _reg_offsets["eax"] = 24 _reg_offsets["ax"] = 24 _reg_offsets["xds"] = 28 _reg_offsets["ds"] = 28 _reg_offsets["xes"] = 32 _reg_offsets["es"] = 32 _reg_offsets["xfs"] = 36 _reg_offsets["fs"] = 36 gs_incr = 0 if (test_x86_gs()) { gs_incr = 4 _reg_offsets["xgs"] = 40 _reg_offsets["gs"] = 40 } _reg_offsets["orig_eax"] = 40 + gs_incr _reg_offsets["orig_ax"] = 40 + gs_incr _reg_offsets["eip"] = 44 + gs_incr _reg_offsets["ip"] = 44 + gs_incr _reg_offsets["xcs"] = 48 + gs_incr _reg_offsets["cs"] = 48 + gs_incr _reg_offsets["eflags"] = 52 + gs_incr _reg_offsets["flags"] = 52 + gs_incr _reg_offsets["esp"] = 56 + gs_incr _reg_offsets["sp"] = 56 + gs_incr _reg_offsets["xss"] = 60 + gs_incr _reg_offsets["ss"] = 60 + gs_incr _sp_offset = 56 + gs_incr _ss_offset = 60 + gs_incr _stp_regs_registered = 1 } function _stp_get_register_by_offset:long (offset:long) %{ /* pure */ long value; if (!CONTEXT->regs) { CONTEXT->last_error = "No registers available in this context"; return; } if (THIS->offset < 0 || THIS->offset > sizeof(struct pt_regs) - sizeof(long)) { snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), "Bad register offset: %lld", THIS->offset); CONTEXT->last_error = CONTEXT->error_buffer; return; } memcpy(&value, ((char *)CONTEXT->regs) + THIS->offset, sizeof(value)); THIS->__retvalue = value; %} function _stp_probing_kernel:long () %{ /* pure */ THIS->__retvalue = !user_mode(CONTEXT->regs); %} /* * esp and ss aren't saved on a breakpoint in kernel mode, so * the pre-trap stack pointer is ®s->sp. */ function _stp_kernel_sp:long (sp_offset:long) %{ /* pure */ THIS->__retvalue = ((long) CONTEXT->regs) + THIS->sp_offset; %} /* Assume ss register hasn't changed since we took the trap. */ function _stp_kernel_ss:long () %{ /* pure */ unsigned short ss; asm volatile("movw %%ss, %0" : : "m" (ss)); THIS->__retvalue = ss; %} /* Return the named register value as a signed value. */ function register:long (name:string) { if (!registers_valid()) { error("cannot access CPU registers in this context") return 0 } if (!_stp_regs_registered) _stp_register_regs() offset = _reg_offsets[name] if (offset == 0 && !(name in _reg_offsets)) { error("Unknown register: " . name) return 0 } if (_stp_probing_kernel()) { if (offset == _sp_offset) return _stp_kernel_sp(_sp_offset) else if (offset == _ss_offset) return _stp_kernel_ss() } return _stp_get_register_by_offset(offset) } /* * Return the named register value as an unsigned value. Specifically, * don't sign-extend the register value when promoting it to 64 bits. */ function u_register:long (name:string) { return register(name) & 0xffffffff; } /* Return the value of function arg #argnum (1=first arg) as a signed value. */ function _stp_arg:long (argnum:long) %{ /* pure */ long val; int n, nr_regargs, result; THIS->__retvalue = 0; if (!CONTEXT->regs) { snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), "cannot access function args in this context"); CONTEXT->last_error = CONTEXT->error_buffer; return; } if (THIS->argnum < 1) goto bad_argnum; n = (int) THIS->argnum; nr_regargs = _stp_get_regparm(CONTEXT->regparm, CONTEXT->regs); result = _stp_get_arg32_by_number(n, nr_regargs, CONTEXT->regs, &val); switch (result) { case 0: /* Arg is in register. */ THIS->__retvalue = (int64_t) val; break; case 1: /* Arg is on kernel stack. */ THIS->__retvalue = kread((long *) val); break; case 2: { /* Arg is on user stack. */ const char __user *vaddr = (const char __user*) val; if (_stp_copy_from_user((char*)&val, vaddr, sizeof(val)) != 0) { /* Stack page not resident. */ _stp_warn("cannot access arg(%d) " "at user stack address %p\n", n, vaddr); THIS->__retvalue = 0; } else THIS->__retvalue = (int64_t) val; break; } default: goto bad_argnum; } return; bad_argnum: snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), "cannot access arg(%lld)", THIS->argnum); CONTEXT->last_error = CONTEXT->error_buffer; return; if (0) { deref_fault: /* branched to from kread() */ snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), "kernel fault at %#lx accessing arg(%lld)", val, THIS->argnum); CONTEXT->last_error = CONTEXT->error_buffer; } %} /* Return the value of function arg #argnum as a signed int. */ function int_arg:long (argnum:long) { return _stp_arg(argnum) } /* Return the value of function arg #argnum as an unsigned int. */ function uint_arg:long (argnum:long) { return _stp_arg(argnum) & 0xffffffff; } function long_arg:long (argnum:long) { return int_arg(argnum) } function ulong_arg:long (argnum:long) { return uint_arg(argnum) } function longlong_arg:long (argnum:long) { /* * TODO: If argnum == nr_regarg, gcc puts the whole 64-bit arg * on the stack. */ lowbits = uint_arg(argnum) highbits = uint_arg(argnum+1) return ((highbits << 32) | lowbits) } function ulonglong_arg:long (argnum:long) { return longlong_arg(argnum) } function pointer_arg:long (argnum:long) { return ulong_arg(argnum) } function s32_arg:long (argnum:long) { return int_arg(argnum) } function u32_arg:long (argnum:long) { return uint_arg(argnum) } function s64_arg:long (argnum:long) { return longlong_arg(argnum) } function u64_arg:long (argnum:long) { return ulonglong_arg(argnum) } function asmlinkage() %{ CONTEXT->regparm = _STP_REGPARM | 0; %} function fastcall() %{ CONTEXT->regparm = _STP_REGPARM | 3; %} function regparm(n:long) %{ if (THIS->n < 0 || THIS->n > 3) { snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), "For i386, regparm value must be in the range 0-3."); CONTEXT->last_error = CONTEXT->error_buffer; } else CONTEXT->regparm = _STP_REGPARM | (int) THIS->n; %}