Skip to content

Commit b1cf540

Browse files
Masami Hiramatsufweisbec
authored andcommitted
x86: Add pt_regs register and stack access APIs
Add following APIs for accessing registers and stack entries from pt_regs. These APIs are required by kprobes-based event tracer on ftrace. Some other debugging tools might be able to use it too. - regs_query_register_offset(const char *name) Query the offset of "name" register. - regs_query_register_name(unsigned int offset) Query the name of register by its offset. - regs_get_register(struct pt_regs *regs, unsigned int offset) Get the value of a register by its offset. - regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) Check the address is in the kernel stack. - regs_get_kernel_stack_nth(struct pt_regs *reg, unsigned int nth) Get Nth entry of the kernel stack. (N >= 0) - regs_get_argument_nth(struct pt_regs *reg, unsigned int nth) Get Nth argument at function call. (N >= 0) Signed-off-by: Masami Hiramatsu <[email protected]> Cc: [email protected] Cc: Ananth N Mavinakayanahalli <[email protected]> Cc: Avi Kivity <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Frank Ch. Eigler <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jason Baron <[email protected]> Cc: Jim Keniston <[email protected]> Cc: K.Prasad <[email protected]> Cc: Lai Jiangshan <[email protected]> Cc: Li Zefan <[email protected]> Cc: Przemysław Pawełczyk <[email protected]> Cc: Roland McGrath <[email protected]> Cc: Sam Ravnborg <[email protected]> Cc: Srikar Dronamraju <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Tom Zanussi <[email protected]> Cc: Vegard Nossum <[email protected]> LKML-Reference: <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]>
1 parent 89ae465 commit b1cf540

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

arch/x86/include/asm/ptrace.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#ifdef __KERNEL__
99
#include <asm/segment.h>
10+
#include <asm/page_types.h>
1011
#endif
1112

1213
#ifndef __ASSEMBLY__
@@ -216,6 +217,67 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs)
216217
return regs->sp;
217218
}
218219

220+
/* Query offset/name of register from its name/offset */
221+
extern int regs_query_register_offset(const char *name);
222+
extern const char *regs_query_register_name(unsigned int offset);
223+
#define MAX_REG_OFFSET (offsetof(struct pt_regs, ss))
224+
225+
/**
226+
* regs_get_register() - get register value from its offset
227+
* @regs: pt_regs from which register value is gotten.
228+
* @offset: offset number of the register.
229+
*
230+
* regs_get_register returns the value of a register whose offset from @regs
231+
* is @offset. The @offset is the offset of the register in struct pt_regs.
232+
* If @offset is bigger than MAX_REG_OFFSET, this returns 0.
233+
*/
234+
static inline unsigned long regs_get_register(struct pt_regs *regs,
235+
unsigned int offset)
236+
{
237+
if (unlikely(offset > MAX_REG_OFFSET))
238+
return 0;
239+
return *(unsigned long *)((unsigned long)regs + offset);
240+
}
241+
242+
/**
243+
* regs_within_kernel_stack() - check the address in the stack
244+
* @regs: pt_regs which contains kernel stack pointer.
245+
* @addr: address which is checked.
246+
*
247+
* regs_within_kenel_stack() checks @addr is within the kernel stack page(s).
248+
* If @addr is within the kernel stack, it returns true. If not, returns false.
249+
*/
250+
static inline int regs_within_kernel_stack(struct pt_regs *regs,
251+
unsigned long addr)
252+
{
253+
return ((addr & ~(THREAD_SIZE - 1)) ==
254+
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
255+
}
256+
257+
/**
258+
* regs_get_kernel_stack_nth() - get Nth entry of the stack
259+
* @regs: pt_regs which contains kernel stack pointer.
260+
* @n: stack entry number.
261+
*
262+
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
263+
* is specifined by @regs. If the @n th entry is NOT in the kernel stack,
264+
* this returns 0.
265+
*/
266+
static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
267+
unsigned int n)
268+
{
269+
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
270+
addr += n;
271+
if (regs_within_kernel_stack(regs, (unsigned long)addr))
272+
return *addr;
273+
else
274+
return 0;
275+
}
276+
277+
/* Get Nth argument at function call */
278+
extern unsigned long regs_get_argument_nth(struct pt_regs *regs,
279+
unsigned int n);
280+
219281
/*
220282
* These are defined as per linux/ptrace.h, which see.
221283
*/

arch/x86/kernel/ptrace.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,118 @@ enum x86_regset {
4949
REGSET_IOPERM32,
5050
};
5151

52+
struct pt_regs_offset {
53+
const char *name;
54+
int offset;
55+
};
56+
57+
#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
58+
#define REG_OFFSET_END {.name = NULL, .offset = 0}
59+
60+
static const struct pt_regs_offset regoffset_table[] = {
61+
#ifdef CONFIG_X86_64
62+
REG_OFFSET_NAME(r15),
63+
REG_OFFSET_NAME(r14),
64+
REG_OFFSET_NAME(r13),
65+
REG_OFFSET_NAME(r12),
66+
REG_OFFSET_NAME(r11),
67+
REG_OFFSET_NAME(r10),
68+
REG_OFFSET_NAME(r9),
69+
REG_OFFSET_NAME(r8),
70+
#endif
71+
REG_OFFSET_NAME(bx),
72+
REG_OFFSET_NAME(cx),
73+
REG_OFFSET_NAME(dx),
74+
REG_OFFSET_NAME(si),
75+
REG_OFFSET_NAME(di),
76+
REG_OFFSET_NAME(bp),
77+
REG_OFFSET_NAME(ax),
78+
#ifdef CONFIG_X86_32
79+
REG_OFFSET_NAME(ds),
80+
REG_OFFSET_NAME(es),
81+
REG_OFFSET_NAME(fs),
82+
REG_OFFSET_NAME(gs),
83+
#endif
84+
REG_OFFSET_NAME(orig_ax),
85+
REG_OFFSET_NAME(ip),
86+
REG_OFFSET_NAME(cs),
87+
REG_OFFSET_NAME(flags),
88+
REG_OFFSET_NAME(sp),
89+
REG_OFFSET_NAME(ss),
90+
REG_OFFSET_END,
91+
};
92+
93+
/**
94+
* regs_query_register_offset() - query register offset from its name
95+
* @name: the name of a register
96+
*
97+
* regs_query_register_offset() returns the offset of a register in struct
98+
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
99+
*/
100+
int regs_query_register_offset(const char *name)
101+
{
102+
const struct pt_regs_offset *roff;
103+
for (roff = regoffset_table; roff->name != NULL; roff++)
104+
if (!strcmp(roff->name, name))
105+
return roff->offset;
106+
return -EINVAL;
107+
}
108+
109+
/**
110+
* regs_query_register_name() - query register name from its offset
111+
* @offset: the offset of a register in struct pt_regs.
112+
*
113+
* regs_query_register_name() returns the name of a register from its
114+
* offset in struct pt_regs. If the @offset is invalid, this returns NULL;
115+
*/
116+
const char *regs_query_register_name(unsigned int offset)
117+
{
118+
const struct pt_regs_offset *roff;
119+
for (roff = regoffset_table; roff->name != NULL; roff++)
120+
if (roff->offset == offset)
121+
return roff->name;
122+
return NULL;
123+
}
124+
125+
static const int arg_offs_table[] = {
126+
#ifdef CONFIG_X86_32
127+
[0] = offsetof(struct pt_regs, ax),
128+
[1] = offsetof(struct pt_regs, dx),
129+
[2] = offsetof(struct pt_regs, cx)
130+
#else /* CONFIG_X86_64 */
131+
[0] = offsetof(struct pt_regs, di),
132+
[1] = offsetof(struct pt_regs, si),
133+
[2] = offsetof(struct pt_regs, dx),
134+
[3] = offsetof(struct pt_regs, cx),
135+
[4] = offsetof(struct pt_regs, r8),
136+
[5] = offsetof(struct pt_regs, r9)
137+
#endif
138+
};
139+
140+
/**
141+
* regs_get_argument_nth() - get Nth argument at function call
142+
* @regs: pt_regs which contains registers at function entry.
143+
* @n: argument number.
144+
*
145+
* regs_get_argument_nth() returns @n th argument of a function call.
146+
* Since usually the kernel stack will be changed right after function entry,
147+
* you must use this at function entry. If the @n th entry is NOT in the
148+
* kernel stack or pt_regs, this returns 0.
149+
*/
150+
unsigned long regs_get_argument_nth(struct pt_regs *regs, unsigned int n)
151+
{
152+
if (n < ARRAY_SIZE(arg_offs_table))
153+
return *((unsigned long *)regs + arg_offs_table[n]);
154+
else {
155+
/*
156+
* The typical case: arg n is on the stack.
157+
* (Note: stack[0] = return address, so skip it)
158+
*/
159+
n -= ARRAY_SIZE(arg_offs_table);
160+
return regs_get_kernel_stack_nth(regs, 1 + n);
161+
}
162+
}
163+
52164
/*
53165
* does not yet catch signals sent when the child dies.
54166
* in exit.c or in signal.c.

0 commit comments

Comments
 (0)