diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-08-13 16:34:44 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-08-26 18:35:57 -0400 |
commit | b1cf540f0e5278ecfe8532557e547d833ed269d7 (patch) | |
tree | 32223f353bef43b6a5cd7d0f938c2500ad816f61 /arch | |
parent | 89ae465b0ee470f7d3f8a1c61353445c3acbbe2a (diff) |
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 <mhiramat@redhat.com>
Cc: linux-arch@vger.kernel.org
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Avi Kivity <avi@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Przemysław Pawełczyk <przemyslaw@pawelczyk.it>
Cc: Roland McGrath <roland@redhat.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tom Zanussi <tzanussi@gmail.com>
Cc: Vegard Nossum <vegard.nossum@gmail.com>
LKML-Reference: <20090813203444.31965.26374.stgit@localhost.localdomain>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/ptrace.h | 62 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 112 |
2 files changed, 174 insertions, 0 deletions
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index 0f0d908349aa..a3d49dd7d26e 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #ifdef __KERNEL__ | 8 | #ifdef __KERNEL__ |
9 | #include <asm/segment.h> | 9 | #include <asm/segment.h> |
10 | #include <asm/page_types.h> | ||
10 | #endif | 11 | #endif |
11 | 12 | ||
12 | #ifndef __ASSEMBLY__ | 13 | #ifndef __ASSEMBLY__ |
@@ -216,6 +217,67 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs) | |||
216 | return regs->sp; | 217 | return regs->sp; |
217 | } | 218 | } |
218 | 219 | ||
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 | |||
219 | /* | 281 | /* |
220 | * These are defined as per linux/ptrace.h, which see. | 282 | * These are defined as per linux/ptrace.h, which see. |
221 | */ | 283 | */ |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 8d7d5c9c1be3..a33a17d5d5c8 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -49,6 +49,118 @@ enum x86_regset { | |||
49 | REGSET_IOPERM32, | 49 | REGSET_IOPERM32, |
50 | }; | 50 | }; |
51 | 51 | ||
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 | |||
52 | /* | 164 | /* |
53 | * does not yet catch signals sent when the child dies. | 165 | * does not yet catch signals sent when the child dies. |
54 | * in exit.c or in signal.c. | 166 | * in exit.c or in signal.c. |