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 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 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 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 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 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 153521fa314SDavid van Moolenbroek kernel_get_stacktop(void) 154521fa314SDavid van Moolenbroek { 155521fa314SDavid van Moolenbroek 156*20054ae9SDavid 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 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 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 256521fa314SDavid van Moolenbroek kernel_put_stacktrace(struct trace_proc * proc) 257521fa314SDavid van Moolenbroek { 258521fa314SDavid van Moolenbroek unsigned int count, max; 259521fa314SDavid van Moolenbroek reg_t pc, sp, fp, low, high; 260521fa314SDavid van Moolenbroek 261521fa314SDavid van Moolenbroek if (kernel_get_context(proc->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(); 278521fa314SDavid van Moolenbroek put_fmt(proc, " 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++) { 283521fa314SDavid van Moolenbroek if (kernel_get_nextframe(proc->pid, fp, &pc, &fp) < 0) 284521fa314SDavid van Moolenbroek break; 285521fa314SDavid van Moolenbroek 286521fa314SDavid van Moolenbroek put_fmt(proc, " 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) 302521fa314SDavid van Moolenbroek put_text(proc, " .."); 303521fa314SDavid van Moolenbroek put_newline(); 304521fa314SDavid van Moolenbroek } 305