This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH] Fix the register() and u_register() tapset functions for x86
- From: "Yichun Zhang (agentzh)" <yichun at openresty dot com>
- To: systemtap at sourceware dot org
- Cc: "Yichun Zhang (agentzh)" <yichun at openresty dot com>
- Date: Sun, 16 Sep 2018 00:41:24 -0700
- Subject: [PATCH] Fix the register() and u_register() tapset functions for x86
The register() and u_register() functions incorrectly read 16-bit
x86 registers as 32-bit on both i386 and x86_64.
Fixed the sign-extension of register values on i386.
Also added support for 8-bit x86 reigsters like "al" and "bh" on both
i386 and x86_64.
---
runtime/regs.c | 18 ++-
tapset/i386/registers.stp | 120 +++++++++++++++----
tapset/x86_64/registers.stp | 103 +++++++++++++---
testsuite/systemtap.base/at_register_x86.exp | 155 +++++++++++++++++++++++++
testsuite/systemtap.base/at_register_x86_1.c | 9 ++
testsuite/systemtap.base/at_register_x86_1.stp | 15 +++
testsuite/systemtap.base/at_register_x86_2.c | 9 ++
testsuite/systemtap.base/at_register_x86_2.stp | 15 +++
testsuite/systemtap.base/at_register_x86_3.c | 9 ++
testsuite/systemtap.base/at_register_x86_3.stp | 14 +++
testsuite/systemtap.base/at_register_x86_4.c | 9 ++
testsuite/systemtap.base/at_register_x86_4.stp | 14 +++
12 files changed, 447 insertions(+), 43 deletions(-)
create mode 100644 testsuite/systemtap.base/at_register_x86.exp
create mode 100644 testsuite/systemtap.base/at_register_x86_1.c
create mode 100644 testsuite/systemtap.base/at_register_x86_1.stp
create mode 100644 testsuite/systemtap.base/at_register_x86_2.c
create mode 100644 testsuite/systemtap.base/at_register_x86_2.stp
create mode 100644 testsuite/systemtap.base/at_register_x86_3.c
create mode 100644 testsuite/systemtap.base/at_register_x86_3.stp
create mode 100644 testsuite/systemtap.base/at_register_x86_4.c
create mode 100644 testsuite/systemtap.base/at_register_x86_4.stp
diff --git a/runtime/regs.c b/runtime/regs.c
index ade3ad9fc..0d4536f54 100644
--- a/runtime/regs.c
+++ b/runtime/regs.c
@@ -59,7 +59,7 @@ static long _stp_get_sp(struct pt_regs *regs)
#endif /* __x86_64__ */
-#if defined(__x86_64__) || defined(__ia64__)
+#if defined(__x86_64__) || defined(__ia64__) || defined(__i386__)
/* Ensure that the upper 32 bits of val are a sign-extension of the lower 32. */
static int64_t __stp_sign_extend32(int64_t val)
@@ -68,7 +68,21 @@ static int64_t __stp_sign_extend32(int64_t val)
return *val_ptr32;
}
-#endif /* __x86_64__ || __ia64__ */
+/* Ensure that the upper 48 bits of val are a sign-extension of the lower 16. */
+static int64_t __stp_sign_extend16(int64_t val)
+{
+ int16_t *val_ptr16 = (int16_t*) &val;
+ return *val_ptr16;
+}
+
+/* Ensure that the upper 56 bits of val are a sign-extension of the lower 8. */
+static int64_t __stp_sign_extend8(int64_t val)
+{
+ int8_t *val_ptr8 = (int8_t*) &val;
+ return *val_ptr8;
+}
+
+#endif /* __x86_64__ || __ia64__ || __i386__ */
#if defined(__i386__) || defined(__x86_64__)
/*
diff --git a/tapset/i386/registers.stp b/tapset/i386/registers.stp
index af792b40a..a47b457a3 100644
--- a/tapset/i386/registers.stp
+++ b/tapset/i386/registers.stp
@@ -5,7 +5,8 @@
#endif
%}
-@__private30 global _reg_offsets[34], _sp_offset, _ss_offset
+@__private30 global _reg_offsets[17], _r16_offsets[17], _r8_offsets[8],
+ _sp_offset, _ss_offset
function test_x86_fs:long() %{ /* pure */
#if defined(STAPCONF_X86_XFS) || defined (STAPCONF_X86_FS)
@@ -27,27 +28,39 @@ probe init {
/* Same order as pt_regs */
_offset = 0
- _reg_offsets["ebx"] = _reg_offsets["bx"] = _offset
- _reg_offsets["ecx"] = _reg_offsets["cx"] = (_offset += 4)
- _reg_offsets["edx"] = _reg_offsets["dx"] = (_offset += 4)
- _reg_offsets["esi"] = _reg_offsets["si"] = (_offset += 4)
- _reg_offsets["edi"] = _reg_offsets["di"] = (_offset += 4)
- _reg_offsets["ebp"] = _reg_offsets["bp"] = (_offset += 4)
- _reg_offsets["eax"] = _reg_offsets["ax"] = (_offset += 4)
- _reg_offsets["xds"] = _reg_offsets["ds"] = (_offset += 4)
- _reg_offsets["xes"] = _reg_offsets["es"] = (_offset += 4)
+ _reg_offsets["ebx"] = _r16_offsets["bx"] = _r8_offsets["bl"] = _offset
+ _r8_offsets["bh"] = _offset + 1
+
+ _reg_offsets["ecx"] = _r16_offsets["cx"] = _r8_offsets["cl"]
+ = (_offset += 4)
+ _r8_offsets["ch"] = _offset + 1
+
+ _reg_offsets["edx"] = _r16_offsets["dx"] = _r8_offsets["dl"]
+ = (_offset += 4)
+ _r8_offsets["dh"] = _offset + 1
+
+ _reg_offsets["esi"] = _r16_offsets["si"] = (_offset += 4)
+ _reg_offsets["edi"] = _r16_offsets["di"] = (_offset += 4)
+ _reg_offsets["ebp"] = _r16_offsets["bp"] = (_offset += 4)
+
+ _reg_offsets["eax"] = _r16_offsets["ax"] = _r8_offsets["al"]
+ = (_offset += 4)
+ _r8_offsets["ah"] = _offset + 1
+
+ _reg_offsets["xds"] = _r16_offsets["ds"] = (_offset += 4)
+ _reg_offsets["xes"] = _r16_offsets["es"] = (_offset += 4)
if (test_x86_fs()) {
- _reg_offsets["xfs"] = _reg_offsets["fs"] = (_offset += 4)
+ _reg_offsets["xfs"] = _r16_offsets["fs"] = (_offset += 4)
}
if (test_x86_gs()) {
- _reg_offsets["xgs"] = _reg_offsets["gs"] = (_offset += 4)
+ _reg_offsets["xgs"] = _r16_offsets["gs"] = (_offset += 4)
}
- _reg_offsets["orig_eax"] = _reg_offsets["orig_ax"] = (_offset += 4)
- _reg_offsets["eip"] = _reg_offsets["ip"] = (_offset += 4)
- _reg_offsets["xcs"] = _reg_offsets["cs"] = (_offset += 4)
- _reg_offsets["eflags"] = _reg_offsets["flags"] = (_offset += 4)
- _reg_offsets["esp"] = _reg_offsets["sp"] = _sp_offset = (_offset += 4)
- _reg_offsets["xss"] = _reg_offsets["ss"] = _ss_offset = (_offset += 4)
+ _reg_offsets["orig_eax"] = _r16_offsets["orig_ax"] = (_offset += 4)
+ _reg_offsets["eip"] = _r16_offsets["ip"] = (_offset += 4)
+ _reg_offsets["xcs"] = _r16_offsets["cs"] = (_offset += 4)
+ _reg_offsets["eflags"] = _r16_offsets["flags"] = (_offset += 4)
+ _reg_offsets["esp"] = _r16_offsets["sp"] = _sp_offset = (_offset += 4)
+ _reg_offsets["xss"] = _r16_offsets["ss"] = _ss_offset = (_offset += 4)
}
function _stp_get_register_by_offset:long (offset:long) %{ /* pure */
@@ -104,18 +117,79 @@ function _stp_kernel_ss:long () %{ /* pure */
STAP_RETVALUE = ss;
%}
-/* Return the named register value as a signed value. */
-function register:long (name:string) {
+/*
+ * _stp_sign_extend32() is callable from a script function.
+ * __stp_sign_extend32() (in regs.c) is callable from a C function.
+ */
+function _stp_sign_extend32:long (value:long) %{ /* pure */
+ STAP_RETVALUE = __stp_sign_extend32(STAP_ARG_value);
+%}
+
+/*
+ * _stp_sign_extend16() is callable from a script function.
+ * __stp_sign_extend16() (in regs.c) is callable from a C function.
+ */
+function _stp_sign_extend16:long (value:long) %{ /* pure */
+ STAP_RETVALUE = __stp_sign_extend16(STAP_ARG_value);
+%}
+
+/*
+ * _stp_sign_extend8() is callable from a script function.
+ * __stp_sign_extend8() (in regs.c) is callable from a C function.
+ */
+function _stp_sign_extend8:long (value:long) %{ /* pure */
+ STAP_RETVALUE = __stp_sign_extend8(STAP_ARG_value);
+%}
+
+function _stp_register:long (name:string, sign_extend:long) {
+ reg16 = 0
+ reg8 = 0
assert(registers_valid(), "cannot access CPU registers in this context")
offset = _reg_offsets[name]
- assert(offset != 0 || (name in _reg_offsets), "Unknown register: " . name)
+ if (offset == 0 && !(name in _reg_offsets)) {
+ offset = _r16_offsets[name]
+ if (offset == 0 && !(name in _r16_offsets)) {
+ offset = _r8_offsets[name]
+ if (offset == 0 && !(name in _r8_offsets)) {
+ assert(0, "Unknown register: " . name)
+ } else {
+ reg8 = 1;
+ }
+ } else {
+ reg16 = 1;
+ }
+ }
+
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)
+
+ value = _stp_get_register_by_offset(offset)
+ if (reg16) {
+ if (sign_extend)
+ value = _stp_sign_extend16(value)
+ else
+ value &= 0xffff
+ } else if (reg8) {
+ if (sign_extend)
+ value = _stp_sign_extend8(value)
+ else
+ value &= 0xff
+ } else {
+ if (sign_extend)
+ value = _stp_sign_extend32(value)
+ else
+ value &= 0xffffffff
+ }
+ return value
+}
+
+/* Return the named register value as a signed value. */
+function register:long (name:string) {
+ return _stp_register (name, 1)
}
/*
@@ -123,7 +197,7 @@ function register:long (name:string) {
* don't sign-extend the register value when promoting it to 64 bits.
*/
function u_register:long (name:string) {
- return register(name) & 0xffffffff;
+ return _stp_register(name, 0)
}
/* Return the value of function arg #argnum (1=first arg) as a signed value. */
diff --git a/tapset/x86_64/registers.stp b/tapset/x86_64/registers.stp
index 61701d038..54dbe2204 100644
--- a/tapset/x86_64/registers.stp
+++ b/tapset/x86_64/registers.stp
@@ -5,7 +5,8 @@
#endif
%}
-@__private30 global _reg_offsets[34], _r32_offsets[10]
+@__private30 global _reg_offsets[21], _r32_offsets[10], _r16_offsets[13],
+ _r8_offsets[8]
probe init {
/* Same order as pt_regs */
@@ -13,23 +14,23 @@ probe init {
_reg_offsets["r14"] = 8
_reg_offsets["r13"] = 16
_reg_offsets["r12"] = 24
- _reg_offsets["rbp"] = 32 _reg_offsets["bp"] = 32
- _reg_offsets["rbx"] = 40 _reg_offsets["bx"] = 40
+ _reg_offsets["rbp"] = 32
+ _reg_offsets["rbx"] = 40
_reg_offsets["r11"] = 48
_reg_offsets["r10"] = 56
_reg_offsets["r9"] = 64
_reg_offsets["r8"] = 72
- _reg_offsets["rax"] = 80 _reg_offsets["ax"] = 80
- _reg_offsets["rcx"] = 88 _reg_offsets["cx"] = 88
- _reg_offsets["rdx"] = 96 _reg_offsets["dx"] = 96
- _reg_offsets["rsi"] = 104 _reg_offsets["si"] = 104
- _reg_offsets["rdi"] = 112 _reg_offsets["di"] = 112
- _reg_offsets["orig_rax"] = 120 _reg_offsets["orig_ax"] = 120
- _reg_offsets["rip"] = 128 _reg_offsets["ip"] = 128
- _reg_offsets["xcs"] = 136 _reg_offsets["cs"] = 136
- _reg_offsets["eflags"] = 144 _reg_offsets["flags"] = 144
- _reg_offsets["rsp"] = 152 _reg_offsets["sp"] = 152
- _reg_offsets["xss"] = 160 _reg_offsets["ss"] = 160
+ _reg_offsets["rax"] = 80
+ _reg_offsets["rcx"] = 88
+ _reg_offsets["rdx"] = 96
+ _reg_offsets["rsi"] = 104
+ _reg_offsets["rdi"] = 112
+ _reg_offsets["orig_rax"] = 120
+ _reg_offsets["rip"] = 128
+ _reg_offsets["xcs"] = 136
+ _reg_offsets["eflags"] = 144
+ _reg_offsets["rsp"] = 152
+ _reg_offsets["xss"] = 160
_r32_offsets["ebp"] = 32
_r32_offsets["ebx"] = 40
@@ -41,6 +42,29 @@ probe init {
_r32_offsets["orig_eax"] = 120
_r32_offsets["eip"] = 128
_r32_offsets["esp"] = 152
+
+ _r16_offsets["bp"] = 32
+ _r16_offsets["bx"] = 40
+ _r16_offsets["ax"] = 80
+ _r16_offsets["cx"] = 88
+ _r16_offsets["dx"] = 96
+ _r16_offsets["si"] = 104
+ _r16_offsets["di"] = 112
+ _r16_offsets["orig_ax"] = 120
+ _r16_offsets["ip"] = 128
+ _r16_offsets["cs"] = 136
+ _r16_offsets["flags"] = 144
+ _r16_offsets["sp"] = 152
+ _r16_offsets["ss"] = 160
+
+ _r8_offsets["al"] = 80
+ _r8_offsets["ah"] = 81
+ _r8_offsets["bl"] = 40
+ _r8_offsets["bh"] = 41
+ _r8_offsets["cl"] = 88
+ _r8_offsets["ch"] = 89
+ _r8_offsets["dl"] = 96
+ _r8_offsets["dh"] = 97
}
function _stp_get_register_by_offset:long (offset:long) %{ /* pure */
@@ -53,7 +77,7 @@ function _stp_get_register_by_offset:long (offset:long) %{ /* pure */
}
if (STAP_ARG_offset < 0 || STAP_ARG_offset > sizeof(struct pt_regs) - sizeof(long)) {
snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
- "Bad register offset: %lld", STAP_ARG_offset);
+ "Bad register offset: %lld", (long long) STAP_ARG_offset);
CONTEXT->last_error = CONTEXT->error_buffer;
return;
}
@@ -69,14 +93,46 @@ function _stp_sign_extend32:long (value:long) %{ /* pure */
STAP_RETVALUE = __stp_sign_extend32(STAP_ARG_value);
%}
+/*
+ * _stp_sign_extend16() is callable from a script function.
+ * __stp_sign_extend16() (in regs.c) is callable from a C function.
+ */
+function _stp_sign_extend16:long (value:long) %{ /* pure */
+ STAP_RETVALUE = __stp_sign_extend16(STAP_ARG_value);
+%}
+
+/*
+ * _stp_sign_extend8() is callable from a script function.
+ * __stp_sign_extend8() (in regs.c) is callable from a C function.
+ */
+function _stp_sign_extend8:long (value:long) %{ /* pure */
+ STAP_RETVALUE = __stp_sign_extend8(STAP_ARG_value);
+%}
+
function _stp_register:long (name:string, sign_extend:long) {
reg32 = 0
- assert(registers_valid(), "cannot access CPU registers in this context")
+ reg16 = 0
+ reg8 = 0
+ assert(registers_valid(), "cannot access CPU registers in this context")
offset = _reg_offsets[name]
if (offset == 0 && !(name in _reg_offsets)) {
offset = _r32_offsets[name]
- assert(offset != 0 || (name in _r32_offsets), "Unknown register: " . name)
- reg32 = 1
+ if (offset == 0 && !(name in _r32_offsets)) {
+ offset = _r16_offsets[name]
+ if (offset == 0 && !(name in _r16_offsets)) {
+ offset = _r8_offsets[name]
+ if (offset == 0 && !(name in _r8_offsets)) {
+ assert(0, "Unknown register: " . name)
+ } else {
+ reg8 = 1;
+ }
+ } else {
+ reg16 = 1;
+ }
+
+ } else {
+ reg32 = 1
+ }
}
value = _stp_get_register_by_offset(offset)
if (reg32) {
@@ -84,6 +140,17 @@ function _stp_register:long (name:string, sign_extend:long) {
value = _stp_sign_extend32(value)
else
value &= 0xffffffff
+ } else if (reg16) {
+ if (sign_extend)
+ value = _stp_sign_extend16(value)
+ else
+ value &= 0xffff
+ } else {
+ assert (reg8);
+ if (sign_extend)
+ value = _stp_sign_extend8(value)
+ else
+ value &= 0xff
}
return value
}
diff --git a/testsuite/systemtap.base/at_register_x86.exp b/testsuite/systemtap.base/at_register_x86.exp
new file mode 100644
index 000000000..475ba42a4
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86.exp
@@ -0,0 +1,155 @@
+set test "at_register_x86"
+set testpath "$srcdir/$subdir"
+
+if {! [installtest_p]} { untested $test; return }
+if {! [uretprobes_p]} { untested $test; return }
+set arch [exec uname -i]
+if {$arch ne "i386" && $arch ne "x86_64"} { untested $test; return }
+
+# --- TEST 1 ---
+
+set subtest1 "TEST 1: 8-bit and 16-bit registers for eax"
+
+set res [target_compile ${testpath}/${test}_1.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-O0"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest1: unable to compile ${test}_1.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest1 ($runtime)"
+ set cmd "stap --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_1.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "eax = 0xffffffffbeefdead
+u eax = 0xbeefdead
+
+ax = 0xffffffffffffdead
+u ax = 0xdead
+
+al = 0xffffffffffffffad
+u al = 0xad
+
+ah = 0xffffffffffffffde
+u ah = 0xde
+"
+ is "${test_name}: stdout" $out $exp_out
+ is "${test_name}: exit code" $exit_code 0
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+ }
+}
+
+# --- TEST 2 ---
+
+set subtest2 "TEST 2: 8-bit and 16-bit registers for edx"
+
+set res [target_compile ${testpath}/${test}_2.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-O0"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest2: unable to compile ${test}_2.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest2 ($runtime)"
+ set cmd "stap --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_2.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "edx = 0xffffffffbeefdead
+u edx = 0xbeefdead
+
+dx = 0xffffffffffffdead
+u dx = 0xdead
+
+dl = 0xffffffffffffffad
+u dl = 0xad
+
+dh = 0xffffffffffffffde
+u dh = 0xde
+"
+ is "${test_name}: stdout" $out $exp_out
+ is "${test_name}: exit code" $exit_code 0
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+ }
+}
+
+# --- TEST 3 ---
+
+set subtest3 "TEST 3: 8-bit and 16-bit registers for ecx"
+
+set res [target_compile ${testpath}/${test}_3.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-O0"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest3: unable to compile ${test}_3.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest3 ($runtime)"
+ set cmd "stap --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_3.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ecx = 0xffffffffbeefdead
+u ecx = 0xbeefdead
+
+cx = 0xffffffffffffdead
+u cx = 0xdead
+
+cl = 0xffffffffffffffad
+u cl = 0xad
+
+ch = 0xffffffffffffffde
+u ch = 0xde
+"
+ is "${test_name}: stdout" $out $exp_out
+ is "${test_name}: exit code" $exit_code 0
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+ }
+}
+
+# --- TEST 4 ---
+
+set subtest4 "TEST 4: 8-bit and 16-bit registers for eax"
+
+set res [target_compile ${testpath}/${test}_4.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-O0"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest4: unable to compile ${test}_4.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest4 ($runtime)"
+ set cmd "stap --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_4.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ebx = 0xffffffffbeefdead
+u ebx = 0xbeefdead
+
+bx = 0xffffffffffffdead
+u bx = 0xdead
+
+bl = 0xffffffffffffffad
+u bl = 0xad
+
+bh = 0xffffffffffffffde
+u bh = 0xde
+"
+ is "${test_name}: stdout" $out $exp_out
+ is "${test_name}: exit code" $exit_code 0
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+ }
+}
diff --git a/testsuite/systemtap.base/at_register_x86_1.c b/testsuite/systemtap.base/at_register_x86_1.c
new file mode 100644
index 000000000..2b9e5de8b
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_1.c
@@ -0,0 +1,9 @@
+void foo(void) {
+ asm("movl $0xbeefdead, %eax");
+ return;
+}
+
+int main(void) {
+ foo();
+ return 0;
+}
diff --git a/testsuite/systemtap.base/at_register_x86_1.stp b/testsuite/systemtap.base/at_register_x86_1.stp
new file mode 100644
index 000000000..5cadeceab
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_1.stp
@@ -0,0 +1,15 @@
+probe process.function("foo").return {
+ printf("eax = %#x\n", register("eax"));
+ printf("u eax = %#x\n\n", u_register("eax"));
+
+ printf("ax = %#x\n", register("ax"));
+ printf("u ax = %#x\n\n", u_register("ax"));
+
+ printf("al = %#x\n", register("al"));
+ printf("u al = %#x\n\n", u_register("al"));
+
+ printf("ah = %#x\n", register("ah"));
+ printf("u ah = %#x\n", u_register("ah"));
+
+ exit()
+}
diff --git a/testsuite/systemtap.base/at_register_x86_2.c b/testsuite/systemtap.base/at_register_x86_2.c
new file mode 100644
index 000000000..e057e4add
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_2.c
@@ -0,0 +1,9 @@
+void foo(void) {
+ asm("movl $0xbeefdead, %edx");
+ return;
+}
+
+int main(void) {
+ foo();
+ return 0;
+}
diff --git a/testsuite/systemtap.base/at_register_x86_2.stp b/testsuite/systemtap.base/at_register_x86_2.stp
new file mode 100644
index 000000000..356b47938
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_2.stp
@@ -0,0 +1,15 @@
+probe process.function("foo").return {
+ printf("edx = %#x\n", register("edx"));
+ printf("u edx = %#x\n\n", u_register("edx"));
+
+ printf("dx = %#x\n", register("dx"));
+ printf("u dx = %#x\n\n", u_register("dx"));
+
+ printf("dl = %#x\n", register("dl"));
+ printf("u dl = %#x\n\n", u_register("dl"));
+
+ printf("dh = %#x\n", register("dh"));
+ printf("u dh = %#x\n", u_register("dh"));
+
+ exit()
+}
diff --git a/testsuite/systemtap.base/at_register_x86_3.c b/testsuite/systemtap.base/at_register_x86_3.c
new file mode 100644
index 000000000..568784165
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_3.c
@@ -0,0 +1,9 @@
+void foo(void) {
+ asm("movl $0xbeefdead, %ecx");
+ return;
+}
+
+int main(void) {
+ foo();
+ return 0;
+}
diff --git a/testsuite/systemtap.base/at_register_x86_3.stp b/testsuite/systemtap.base/at_register_x86_3.stp
new file mode 100644
index 000000000..eed08fe1b
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_3.stp
@@ -0,0 +1,14 @@
+probe process.function("foo").return {
+ printf("ecx = %#x\n", register("ecx"));
+ printf("u ecx = %#x\n\n", u_register("ecx"));
+
+ printf("cx = %#x\n", register("cx"));
+ printf("u cx = %#x\n\n", u_register("cx"));
+
+ printf("cl = %#x\n", register("cl"));
+ printf("u cl = %#x\n\n", u_register("cl"));
+
+ printf("ch = %#x\n", register("ch"));
+ printf("u ch = %#x\n", u_register("ch"));
+ exit()
+}
diff --git a/testsuite/systemtap.base/at_register_x86_4.c b/testsuite/systemtap.base/at_register_x86_4.c
new file mode 100644
index 000000000..6ccc6f8bb
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_4.c
@@ -0,0 +1,9 @@
+void foo(void) {
+ asm("movl $0xbeefdead, %ebx");
+ return;
+}
+
+int main(void) {
+ foo();
+ return 0;
+}
diff --git a/testsuite/systemtap.base/at_register_x86_4.stp b/testsuite/systemtap.base/at_register_x86_4.stp
new file mode 100644
index 000000000..791ca0656
--- /dev/null
+++ b/testsuite/systemtap.base/at_register_x86_4.stp
@@ -0,0 +1,14 @@
+probe process.function("foo").return {
+ printf("ebx = %#x\n", register("ebx"));
+ printf("u ebx = %#x\n\n", u_register("ebx"));
+
+ printf("bx = %#x\n", register("bx"));
+ printf("u bx = %#x\n\n", u_register("bx"));
+
+ printf("bl = %#x\n", register("bl"));
+ printf("u bl = %#x\n\n", u_register("bl"));
+
+ printf("bh = %#x\n", register("bh"));
+ printf("u bh = %#x\n", u_register("bh"));
+ exit()
+}
--
2.11.0.295.gd7dffce