1521fa314SDavid van Moolenbroek /*
2521fa314SDavid van Moolenbroek * This file, and only this file, should contain all the ugliness needed to
3521fa314SDavid van Moolenbroek * obtain values from the kernel. It has to be recompiled every time the
4521fa314SDavid van Moolenbroek * layout of the kernel "struct proc" and/or "struct priv" structures changes.
5521fa314SDavid van Moolenbroek * In addition, this file contains the platform-dependent code related to
6521fa314SDavid van Moolenbroek * interpreting the registers exposed by the kernel.
7521fa314SDavid van Moolenbroek *
8521fa314SDavid van Moolenbroek * As a quick note, some functions return TRUE/FALSE, and some return 0/-1.
9521fa314SDavid van Moolenbroek * The former convention is used for functions that return a boolean value;
10521fa314SDavid van Moolenbroek * the latter is used for functions that set errno in all cases of failure,
11521fa314SDavid van Moolenbroek * and where the caller may conceivably use errno as a result.
12521fa314SDavid van Moolenbroek *
13521fa314SDavid van Moolenbroek * On a related note, relevant here and elsewhere: we define _MINIX_SYSTEM but
14521fa314SDavid van Moolenbroek * not _SYSTEM, which means that we should not get negative error numbers.
15521fa314SDavid van Moolenbroek */
16521fa314SDavid van Moolenbroek
17521fa314SDavid van Moolenbroek #include "inc.h"
18521fa314SDavid van Moolenbroek
19521fa314SDavid van Moolenbroek #include <machine/archtypes.h>
20521fa314SDavid van Moolenbroek #include <minix/timers.h>
21521fa314SDavid van Moolenbroek #include "kernel/proc.h"
22521fa314SDavid van Moolenbroek #include "kernel/priv.h"
23521fa314SDavid van Moolenbroek #if defined(__i386__)
24521fa314SDavid van Moolenbroek #include "kernel/arch/i386/include/archconst.h" /* for the KTS_ constants */
25521fa314SDavid van Moolenbroek #endif
26594df55eSDavid van Moolenbroek #include <lib.h>
27521fa314SDavid van Moolenbroek
28521fa314SDavid van Moolenbroek /*
29521fa314SDavid van Moolenbroek * Working area. By obtaining values from the kernel into these local process
30521fa314SDavid van Moolenbroek * structures, and then returning them, we gain a little robustness against
31521fa314SDavid van Moolenbroek * changes in data types of the fields we need.
32521fa314SDavid van Moolenbroek */
33521fa314SDavid van Moolenbroek static struct proc kernel_proc;
34521fa314SDavid van Moolenbroek static struct priv kernel_priv;
35521fa314SDavid van Moolenbroek
36521fa314SDavid van Moolenbroek /*
37521fa314SDavid van Moolenbroek * Check whether our notion of the kernel process structure layout matches that
38521fa314SDavid van Moolenbroek * of the kernel, by comparing magic values. This can be done only once we
39521fa314SDavid van Moolenbroek * have attached to a process. Return TRUE if everything seems alright; FALSE
40521fa314SDavid van Moolenbroek * otherwise.
41521fa314SDavid van Moolenbroek */
42521fa314SDavid van Moolenbroek int
kernel_check(pid_t pid)43521fa314SDavid van Moolenbroek kernel_check(pid_t pid)
44521fa314SDavid van Moolenbroek {
45521fa314SDavid van Moolenbroek
46521fa314SDavid van Moolenbroek if (mem_get_user(pid, offsetof(struct proc, p_magic),
47521fa314SDavid van Moolenbroek &kernel_proc.p_magic, sizeof(kernel_proc.p_magic)) < 0)
48521fa314SDavid van Moolenbroek return FALSE;
49521fa314SDavid van Moolenbroek
50521fa314SDavid van Moolenbroek return (kernel_proc.p_magic == PMAGIC);
51521fa314SDavid van Moolenbroek }
52521fa314SDavid van Moolenbroek
53521fa314SDavid van Moolenbroek /*
54521fa314SDavid van Moolenbroek * Obtain the kernel name for the given (stopped) process. Return 0 on
55521fa314SDavid van Moolenbroek * success, with the (possibly truncated) name stored in the 'name' buffer
56521fa314SDavid van Moolenbroek * which is of 'size' bytes; the name will be null-terminated. Note that the
57521fa314SDavid van Moolenbroek * name may contain any suffixes as set by the kernel. Return -1 on failure,
58521fa314SDavid van Moolenbroek * with errno set as appropriate.
59521fa314SDavid van Moolenbroek */
60521fa314SDavid van Moolenbroek int
kernel_get_name(pid_t pid,char * name,size_t size)61521fa314SDavid van Moolenbroek kernel_get_name(pid_t pid, char * name, size_t size)
62521fa314SDavid van Moolenbroek {
63521fa314SDavid van Moolenbroek
64521fa314SDavid van Moolenbroek if (mem_get_user(pid, offsetof(struct proc, p_name),
65521fa314SDavid van Moolenbroek kernel_proc.p_name, sizeof(kernel_proc.p_name)) < 0)
66521fa314SDavid van Moolenbroek return -1;
67521fa314SDavid van Moolenbroek
68521fa314SDavid van Moolenbroek strlcpy(name, kernel_proc.p_name, size);
69521fa314SDavid van Moolenbroek return 0;
70521fa314SDavid van Moolenbroek }
71521fa314SDavid van Moolenbroek
72521fa314SDavid van Moolenbroek /*
73521fa314SDavid van Moolenbroek * Check whether the given process, which we have just attached to, is a system
74521fa314SDavid van Moolenbroek * service. PM does not prevent us from attaching to most system services,
75521fa314SDavid van Moolenbroek * even though this utility only supports tracing user programs. Unlike a few
76521fa314SDavid van Moolenbroek * other routines in this file, this function can not use ProcFS to obtain its
77521fa314SDavid van Moolenbroek * result, because the given process may actually be VFS or ProcFS itself!
78521fa314SDavid van Moolenbroek * Return TRUE if the given process is a system service; FALSE if not.
79521fa314SDavid van Moolenbroek */
80521fa314SDavid van Moolenbroek int
kernel_is_service(pid_t pid)81521fa314SDavid van Moolenbroek kernel_is_service(pid_t pid)
82521fa314SDavid van Moolenbroek {
83521fa314SDavid van Moolenbroek size_t align, off;
84521fa314SDavid van Moolenbroek
85521fa314SDavid van Moolenbroek /*
86521fa314SDavid van Moolenbroek * For T_GETUSER, the priv structure follows the proc structure, but
87521fa314SDavid van Moolenbroek * possibly with padding in between so as to align the priv structure
88521fa314SDavid van Moolenbroek * to long boundary.
89521fa314SDavid van Moolenbroek */
90521fa314SDavid van Moolenbroek align = sizeof(long) - 1;
91521fa314SDavid van Moolenbroek off = (sizeof(struct proc) + align) & ~align;
92521fa314SDavid van Moolenbroek
93521fa314SDavid van Moolenbroek if (mem_get_user(pid, off + offsetof(struct priv, s_id),
94521fa314SDavid van Moolenbroek &kernel_priv.s_id, sizeof(kernel_priv.s_id)) < 0)
95521fa314SDavid van Moolenbroek return FALSE; /* process may have disappeared, so no danger */
96521fa314SDavid van Moolenbroek
97521fa314SDavid van Moolenbroek return (kernel_priv.s_id != USER_PRIV_ID);
98521fa314SDavid van Moolenbroek }
99521fa314SDavid van Moolenbroek
100521fa314SDavid van Moolenbroek /*
101521fa314SDavid van Moolenbroek * For the given process, which must be stopped on entering a system call,
102521fa314SDavid van Moolenbroek * retrieve the three register values describing the system call. Return 0 on
103521fa314SDavid van Moolenbroek * success, or -1 on failure with errno set as appropriate.
104521fa314SDavid van Moolenbroek */
105521fa314SDavid van Moolenbroek int
kernel_get_syscall(pid_t pid,reg_t reg[3])106521fa314SDavid van Moolenbroek kernel_get_syscall(pid_t pid, reg_t reg[3])
107521fa314SDavid van Moolenbroek {
108521fa314SDavid van Moolenbroek
109521fa314SDavid van Moolenbroek assert(sizeof(kernel_proc.p_defer) == sizeof(reg_t) * 3);
110521fa314SDavid van Moolenbroek
111521fa314SDavid van Moolenbroek if (mem_get_user(pid, offsetof(struct proc, p_defer),
112521fa314SDavid van Moolenbroek &kernel_proc.p_defer, sizeof(kernel_proc.p_defer)) < 0)
113521fa314SDavid van Moolenbroek return -1;
114521fa314SDavid van Moolenbroek
115521fa314SDavid van Moolenbroek reg[0] = kernel_proc.p_defer.r1;
116521fa314SDavid van Moolenbroek reg[1] = kernel_proc.p_defer.r2;
117521fa314SDavid van Moolenbroek reg[2] = kernel_proc.p_defer.r3;
118521fa314SDavid van Moolenbroek return 0;
119521fa314SDavid van Moolenbroek }
120521fa314SDavid van Moolenbroek
121521fa314SDavid van Moolenbroek /*
122521fa314SDavid van Moolenbroek * Retrieve the value of the primary return register for the given process,
123521fa314SDavid van Moolenbroek * which must be stopped on leaving a system call. This register contains the
124521fa314SDavid van Moolenbroek * IPC-level result of the system call. Return 0 on success, or -1 on failure
125521fa314SDavid van Moolenbroek * with errno set as appropriate.
126521fa314SDavid van Moolenbroek */
127521fa314SDavid van Moolenbroek int
kernel_get_retreg(pid_t pid,reg_t * retreg)128521fa314SDavid van Moolenbroek kernel_get_retreg(pid_t pid, reg_t * retreg)
129521fa314SDavid van Moolenbroek {
130521fa314SDavid van Moolenbroek size_t off;
131521fa314SDavid van Moolenbroek
132521fa314SDavid van Moolenbroek /*
133521fa314SDavid van Moolenbroek * Historically p_reg had to be the first field in the proc structure,
134521fa314SDavid van Moolenbroek * but since this is no longer a hard requirement, getting its actual
135521fa314SDavid van Moolenbroek * offset into the proc structure certainly doesn't hurt.
136521fa314SDavid van Moolenbroek */
137521fa314SDavid van Moolenbroek off = offsetof(struct proc, p_reg);
138521fa314SDavid van Moolenbroek
139521fa314SDavid van Moolenbroek if (mem_get_user(pid, off + offsetof(struct stackframe_s, retreg),
140521fa314SDavid van Moolenbroek &kernel_proc.p_reg.retreg, sizeof(kernel_proc.p_reg.retreg)) < 0)
141521fa314SDavid van Moolenbroek return -1;
142521fa314SDavid van Moolenbroek
143521fa314SDavid van Moolenbroek *retreg = kernel_proc.p_reg.retreg;
144521fa314SDavid van Moolenbroek return 0;
145521fa314SDavid van Moolenbroek }
146521fa314SDavid van Moolenbroek
147521fa314SDavid van Moolenbroek /*
148521fa314SDavid van Moolenbroek * Return the stack top for user processes. This is needed for execve(), since
149521fa314SDavid van Moolenbroek * the supplied frame contains pointers prepared for the new location of the
150521fa314SDavid van Moolenbroek * frame, which is at the stack top of the process after the execve().
151521fa314SDavid van Moolenbroek */
152521fa314SDavid van Moolenbroek vir_bytes
kernel_get_stacktop(void)153521fa314SDavid van Moolenbroek kernel_get_stacktop(void)
154521fa314SDavid van Moolenbroek {
155521fa314SDavid van Moolenbroek
15620054ae9SDavid van Moolenbroek return minix_get_user_sp();
157521fa314SDavid van Moolenbroek }
158521fa314SDavid van Moolenbroek
159521fa314SDavid van Moolenbroek /*
160521fa314SDavid van Moolenbroek * For the given stopped process, get its program counter (pc), stack pointer
161521fa314SDavid van Moolenbroek * (sp), and optionally its frame pointer (fp). The given fp pointer may be
162521fa314SDavid van Moolenbroek * NULL, in which case the frame pointer is not obtained. The given pc and sp
163521fa314SDavid van Moolenbroek * pointers must not be NULL, and this is intentional: obtaining fp may require
164521fa314SDavid van Moolenbroek * obtaining sp first. Return 0 on success, or -1 on failure with errno set
165521fa314SDavid van Moolenbroek * as appropriate. This functionality is not essential for tracing processes,
166521fa314SDavid van Moolenbroek * and may not be supported on all platforms, in part or full. In particular,
167521fa314SDavid van Moolenbroek * on some platforms, a zero (= invalid) frame pointer may be returned on
168521fa314SDavid van Moolenbroek * success, indicating that obtaining frame pointers is not supported.
169521fa314SDavid van Moolenbroek */
170521fa314SDavid van Moolenbroek int
kernel_get_context(pid_t pid,reg_t * pc,reg_t * sp,reg_t * fp)171521fa314SDavid van Moolenbroek kernel_get_context(pid_t pid, reg_t * pc, reg_t * sp, reg_t * fp)
172521fa314SDavid van Moolenbroek {
173521fa314SDavid van Moolenbroek size_t off;
174521fa314SDavid van Moolenbroek
175521fa314SDavid van Moolenbroek off = offsetof(struct proc, p_reg); /* as above */
176521fa314SDavid van Moolenbroek
177521fa314SDavid van Moolenbroek if (mem_get_user(pid, off + offsetof(struct stackframe_s, pc),
178521fa314SDavid van Moolenbroek &kernel_proc.p_reg.pc, sizeof(kernel_proc.p_reg.pc)) < 0)
179521fa314SDavid van Moolenbroek return -1;
180521fa314SDavid van Moolenbroek if (mem_get_user(pid, off + offsetof(struct stackframe_s, sp),
181521fa314SDavid van Moolenbroek &kernel_proc.p_reg.sp, sizeof(kernel_proc.p_reg.sp)) < 0)
182521fa314SDavid van Moolenbroek return -1;
183521fa314SDavid van Moolenbroek
184521fa314SDavid van Moolenbroek *pc = kernel_proc.p_reg.pc;
185521fa314SDavid van Moolenbroek *sp = kernel_proc.p_reg.sp;
186521fa314SDavid van Moolenbroek
187521fa314SDavid van Moolenbroek if (fp == NULL)
188521fa314SDavid van Moolenbroek return 0;
189521fa314SDavid van Moolenbroek
190521fa314SDavid van Moolenbroek #if defined(__i386__)
191521fa314SDavid van Moolenbroek if (mem_get_user(pid, offsetof(struct proc, p_seg) +
192521fa314SDavid van Moolenbroek offsetof(struct segframe, p_kern_trap_style),
193521fa314SDavid van Moolenbroek &kernel_proc.p_seg.p_kern_trap_style,
194521fa314SDavid van Moolenbroek sizeof(kernel_proc.p_seg.p_kern_trap_style)) < 0)
195521fa314SDavid van Moolenbroek return -1;
196521fa314SDavid van Moolenbroek
197521fa314SDavid van Moolenbroek /* This is taken from the kernel i386 exception code. */
198521fa314SDavid van Moolenbroek switch (kernel_proc.p_seg.p_kern_trap_style) {
199521fa314SDavid van Moolenbroek case KTS_SYSENTER:
200521fa314SDavid van Moolenbroek case KTS_SYSCALL:
201521fa314SDavid van Moolenbroek if (mem_get_data(pid, *sp + 16, fp, sizeof(fp)) < 0)
202521fa314SDavid van Moolenbroek return -1;
203521fa314SDavid van Moolenbroek break;
204521fa314SDavid van Moolenbroek
205521fa314SDavid van Moolenbroek default:
206521fa314SDavid van Moolenbroek if (mem_get_user(pid, off + offsetof(struct stackframe_s, fp),
207521fa314SDavid van Moolenbroek &kernel_proc.p_reg.fp, sizeof(kernel_proc.p_reg.fp)) < 0)
208521fa314SDavid van Moolenbroek return -1;
209521fa314SDavid van Moolenbroek
210521fa314SDavid van Moolenbroek *fp = kernel_proc.p_reg.fp;
211521fa314SDavid van Moolenbroek }
212521fa314SDavid van Moolenbroek #else
213521fa314SDavid van Moolenbroek *fp = 0; /* not supported; this is not a failure (*pc is valid) */
214521fa314SDavid van Moolenbroek #endif
215521fa314SDavid van Moolenbroek return 0;
216521fa314SDavid van Moolenbroek }
217521fa314SDavid van Moolenbroek
218521fa314SDavid van Moolenbroek /*
219521fa314SDavid van Moolenbroek * Given a frame pointer, obtain the next program counter and frame pointer.
220521fa314SDavid van Moolenbroek * Return 0 if successful, or -1 on failure with errno set appropriately. The
221521fa314SDavid van Moolenbroek * functionality is not essential for tracing processes, and may not be
222521fa314SDavid van Moolenbroek * supported on all platforms. Thus, on some platforms, this function may
223521fa314SDavid van Moolenbroek * always fail.
224521fa314SDavid van Moolenbroek */
225521fa314SDavid van Moolenbroek static int
kernel_get_nextframe(pid_t pid,reg_t fp,reg_t * next_pc,reg_t * next_fp)226521fa314SDavid van Moolenbroek kernel_get_nextframe(pid_t pid, reg_t fp, reg_t * next_pc, reg_t * next_fp)
227521fa314SDavid van Moolenbroek {
228521fa314SDavid van Moolenbroek #if defined(__i386__)
229521fa314SDavid van Moolenbroek void *p[2];
230521fa314SDavid van Moolenbroek
231521fa314SDavid van Moolenbroek if (mem_get_data(pid, (vir_bytes)fp, &p, sizeof(p)) < 0)
232521fa314SDavid van Moolenbroek return -1;
233521fa314SDavid van Moolenbroek
234521fa314SDavid van Moolenbroek *next_pc = (reg_t)p[1];
235521fa314SDavid van Moolenbroek *next_fp = (reg_t)p[0];
236521fa314SDavid van Moolenbroek return 0;
237521fa314SDavid van Moolenbroek #else
238521fa314SDavid van Moolenbroek /* Not supported (yet). */
239521fa314SDavid van Moolenbroek errno = ENOSYS;
240521fa314SDavid van Moolenbroek return -1;
241521fa314SDavid van Moolenbroek #endif
242521fa314SDavid van Moolenbroek }
243521fa314SDavid van Moolenbroek
244521fa314SDavid van Moolenbroek /*
245521fa314SDavid van Moolenbroek * Print a stack trace for the given process, which is known to be stopped on
246521fa314SDavid van Moolenbroek * entering a system call. This function does not really belong here, but
247521fa314SDavid van Moolenbroek * without a doubt it is going to have to be fully rewritten to support
248521fa314SDavid van Moolenbroek * anything other than i386.
249521fa314SDavid van Moolenbroek *
250521fa314SDavid van Moolenbroek * Getting symbol names is currently an absolute nightmare. Not just because
251521fa314SDavid van Moolenbroek * of shared libraries, but also since ProcFS does not offer a /proc/NNN/exe,
252521fa314SDavid van Moolenbroek * so that we cannot reliably determine the binary being executed: not for
253521fa314SDavid van Moolenbroek * processes being attached to, and not for exec calls using a relative path.
254521fa314SDavid van Moolenbroek */
255521fa314SDavid van Moolenbroek void
kernel_put_stacktrace(struct trace_proc * procp)256*b58e161cSDavid van Moolenbroek kernel_put_stacktrace(struct trace_proc * procp)
257521fa314SDavid van Moolenbroek {
258521fa314SDavid van Moolenbroek unsigned int count, max;
259521fa314SDavid van Moolenbroek reg_t pc, sp, fp, low, high;
260521fa314SDavid van Moolenbroek
261*b58e161cSDavid van Moolenbroek if (kernel_get_context(procp->pid, &pc, &sp, &fp) < 0)
262521fa314SDavid van Moolenbroek return;
263521fa314SDavid van Moolenbroek
264521fa314SDavid van Moolenbroek /*
265521fa314SDavid van Moolenbroek * A low default limit such as 6 looks much prettier, but is simply not
266521fa314SDavid van Moolenbroek * useful enough for moderately-sized programs in practice. Right now,
267521fa314SDavid van Moolenbroek * 15 is about two lines on a 80-column terminal.
268521fa314SDavid van Moolenbroek */
269521fa314SDavid van Moolenbroek if (verbose == 0) max = 15;
270521fa314SDavid van Moolenbroek else if (verbose == 1) max = 31;
271521fa314SDavid van Moolenbroek else max = UINT_MAX;
272521fa314SDavid van Moolenbroek
273521fa314SDavid van Moolenbroek /*
274521fa314SDavid van Moolenbroek * We keep formatting to an absolute minimum, to facilitate passing
275521fa314SDavid van Moolenbroek * the lines straight into tools such as addr2line.
276521fa314SDavid van Moolenbroek */
277521fa314SDavid van Moolenbroek put_newline();
278*b58e161cSDavid van Moolenbroek put_fmt(procp, " 0x%x", pc);
279521fa314SDavid van Moolenbroek
280521fa314SDavid van Moolenbroek low = high = fp;
281521fa314SDavid van Moolenbroek
282521fa314SDavid van Moolenbroek for (count = 1; count < max && fp != 0; count++) {
283*b58e161cSDavid van Moolenbroek if (kernel_get_nextframe(procp->pid, fp, &pc, &fp) < 0)
284521fa314SDavid van Moolenbroek break;
285521fa314SDavid van Moolenbroek
286*b58e161cSDavid van Moolenbroek put_fmt(procp, " 0x%x", pc);
287521fa314SDavid van Moolenbroek
288521fa314SDavid van Moolenbroek /*
289521fa314SDavid van Moolenbroek * Stop if we see a frame pointer that falls within the range
290521fa314SDavid van Moolenbroek * of the frame pointers we have seen so far. This also
291521fa314SDavid van Moolenbroek * prevents getting stuck in a loop on the same frame pointer.
292521fa314SDavid van Moolenbroek */
293521fa314SDavid van Moolenbroek if (fp >= low && fp <= high)
294521fa314SDavid van Moolenbroek break;
295521fa314SDavid van Moolenbroek if (low > fp)
296521fa314SDavid van Moolenbroek low = fp;
297521fa314SDavid van Moolenbroek if (high < fp)
298521fa314SDavid van Moolenbroek high = fp;
299521fa314SDavid van Moolenbroek }
300521fa314SDavid van Moolenbroek
301521fa314SDavid van Moolenbroek if (fp != 0)
302*b58e161cSDavid van Moolenbroek put_text(procp, " ..");
303521fa314SDavid van Moolenbroek put_newline();
304521fa314SDavid van Moolenbroek }
305