Changeset View
Standalone View
usr.bin/truss/syscalls.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | ||||||||||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | |||||||||||
#include <sys/types.h> | #include <sys/types.h> | |||||||||||
#define _WANT_FREEBSD11_KEVENT | #define _WANT_FREEBSD11_KEVENT | |||||||||||
#include <sys/event.h> | #include <sys/event.h> | |||||||||||
#include <sys/ioccom.h> | #include <sys/ioccom.h> | |||||||||||
#include <sys/mman.h> | #include <sys/mman.h> | |||||||||||
#include <sys/mount.h> | #include <sys/mount.h> | |||||||||||
#include <sys/poll.h> | #include <sys/poll.h> | |||||||||||
#include <sys/procfs.h> | ||||||||||||
#include <sys/ptrace.h> | #include <sys/ptrace.h> | |||||||||||
#include <sys/resource.h> | #include <sys/resource.h> | |||||||||||
#include <sys/sched.h> | #include <sys/sched.h> | |||||||||||
#include <sys/socket.h> | #include <sys/socket.h> | |||||||||||
#define _WANT_FREEBSD11_STAT | #define _WANT_FREEBSD11_STAT | |||||||||||
#include <sys/stat.h> | #include <sys/stat.h> | |||||||||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | |||||||||||
#include <sys/time.h> | #include <sys/time.h> | |||||||||||
▲ Show 20 Lines • Show All 798 Lines • ▼ Show 20 Lines | ||||||||||||
static const char * | static const char * | |||||||||||
lookup(struct xlat *xlat, int val, int base) | lookup(struct xlat *xlat, int val, int base) | |||||||||||
{ | { | |||||||||||
static char tmp[16]; | static char tmp[16]; | |||||||||||
for (; xlat->str != NULL; xlat++) | for (; xlat->str != NULL; xlat++) | |||||||||||
if (xlat->val == val) | if (xlat->val == val) | |||||||||||
return (xlat->str); | return (xlat->str); | |||||||||||
switch (base) { | switch (base) { | |||||||||||
case 8: | case 8: | |||||||||||
sprintf(tmp, "0%o", val); | sprintf(tmp, "0%o", val); | |||||||||||
break; | break; | |||||||||||
case 16: | case 16: | |||||||||||
sprintf(tmp, "0x%x", val); | sprintf(tmp, "0x%x", val); | |||||||||||
break; | break; | |||||||||||
case 10: | case 10: | |||||||||||
sprintf(tmp, "%u", val); | sprintf(tmp, "%u", val); | |||||||||||
break; | break; | |||||||||||
default: | default: | |||||||||||
errx(1,"Unknown lookup base"); | errx(1, "Unknown lookup base"); | |||||||||||
jhb: Maybe fix this while here? | ||||||||||||
break; | ||||||||||||
} | } | |||||||||||
return (tmp); | return (tmp); | |||||||||||
} | } | |||||||||||
static const char * | static const char * | |||||||||||
xlookup(struct xlat *xlat, int val) | xlookup(struct xlat *xlat, int val) | |||||||||||
{ | { | |||||||||||
▲ Show 20 Lines • Show All 225 Lines • ▼ Show 20 Lines | #endif | |||||||||||
add_syscall(t->proc->abi, number, sc); | add_syscall(t->proc->abi, number, sc); | |||||||||||
return (sc); | return (sc); | |||||||||||
} | } | |||||||||||
/* | /* | |||||||||||
* Copy a fixed amount of bytes from the process. | * Copy a fixed amount of bytes from the process. | |||||||||||
*/ | */ | |||||||||||
static int | static int | |||||||||||
get_struct(pid_t pid, uintptr_t offset, void *buf, int len) | get_struct(pid_t pid, psaddr_t offset, void *buf, size_t len) | |||||||||||
Done Inline ActionsSo kvaddr_t is in theory for kernel virtual addresses, not necessarily userland. However, there is a psaddr_t in <sys/procfs.h> that you could use as that is "process addresses" and is the type used in things like the libthread_db interface for user addresses. psaddr_t is uint64_t. jhb: So `kvaddr_t` is in theory for kernel virtual addresses, not necessarily userland. However… | ||||||||||||
{ | { | |||||||||||
struct ptrace_io_desc iorequest; | struct ptrace_io_desc iorequest; | |||||||||||
iorequest.piod_op = PIOD_READ_D; | iorequest.piod_op = PIOD_READ_D; | |||||||||||
iorequest.piod_offs = (void *)offset; | iorequest.piod_offs = (void *)(uintptr_t)offset; | |||||||||||
iorequest.piod_addr = buf; | iorequest.piod_addr = buf; | |||||||||||
iorequest.piod_len = len; | iorequest.piod_len = len; | |||||||||||
if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) | if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) | |||||||||||
return (-1); | return (-1); | |||||||||||
return (0); | return (0); | |||||||||||
} | } | |||||||||||
#define MAXSIZE 4096 | #define MAXSIZE 4096 | |||||||||||
/* | /* | |||||||||||
* Copy a string from the process. Note that it is | * Copy a string from the process. Note that it is | |||||||||||
* expected to be a C string, but if max is set, it will | * expected to be a C string, but if max is set, it will | |||||||||||
* only get that much. | * only get that much. | |||||||||||
*/ | */ | |||||||||||
static char * | static char * | |||||||||||
get_string(pid_t pid, uintptr_t addr, int max) | get_string(pid_t pid, psaddr_t addr, int max) | |||||||||||
{ | { | |||||||||||
struct ptrace_io_desc iorequest; | struct ptrace_io_desc iorequest; | |||||||||||
char *buf, *nbuf; | char *buf, *nbuf; | |||||||||||
size_t offset, size, totalsize; | size_t offset, size, totalsize; | |||||||||||
offset = 0; | offset = 0; | |||||||||||
if (max) | if (max) | |||||||||||
size = max + 1; | size = max + 1; | |||||||||||
else { | else { | |||||||||||
/* Read up to the end of the current page. */ | /* Read up to the end of the current page. */ | |||||||||||
size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE); | size = PAGE_SIZE - (addr % PAGE_SIZE); | |||||||||||
if (size > MAXSIZE) | if (size > MAXSIZE) | |||||||||||
size = MAXSIZE; | size = MAXSIZE; | |||||||||||
} | } | |||||||||||
totalsize = size; | totalsize = size; | |||||||||||
buf = malloc(totalsize); | buf = malloc(totalsize); | |||||||||||
if (buf == NULL) | if (buf == NULL) | |||||||||||
return (NULL); | return (NULL); | |||||||||||
for (;;) { | for (;;) { | |||||||||||
iorequest.piod_op = PIOD_READ_D; | iorequest.piod_op = PIOD_READ_D; | |||||||||||
iorequest.piod_offs = (void *)(addr + offset); | iorequest.piod_offs = (void *)((uintptr_t)addr + offset); | |||||||||||
iorequest.piod_addr = buf + offset; | iorequest.piod_addr = buf + offset; | |||||||||||
iorequest.piod_len = size; | iorequest.piod_len = size; | |||||||||||
if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { | if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { | |||||||||||
free(buf); | free(buf); | |||||||||||
return (NULL); | return (NULL); | |||||||||||
} | } | |||||||||||
if (memchr(buf + offset, '\0', size) != NULL) | if (memchr(buf + offset, '\0', size) != NULL) | |||||||||||
return (buf); | return (buf); | |||||||||||
▲ Show 20 Lines • Show All 544 Lines • ▼ Show 20 Lines | print_sysctl(FILE *fp, int *oid, size_t len) | |||||||||||
i = sizeof(name); | i = sizeof(name); | |||||||||||
if (sysctl(qoid, len + 2, name, &i, 0, 0) == -1) | if (sysctl(qoid, len + 2, name, &i, 0, 0) == -1) | |||||||||||
print_sysctl_oid(fp, oid, len); | print_sysctl_oid(fp, oid, len); | |||||||||||
else | else | |||||||||||
fprintf(fp, "%s", name); | fprintf(fp, "%s", name); | |||||||||||
} | } | |||||||||||
/* | /* | |||||||||||
* Convert a 32-bit user-space pointer to psaddr_t. Currently, this | ||||||||||||
Not Done Inline ActionsI don't know what the right choice is for sign-extending vs not. I suspect it's actually wrong to sign-extend on just about every architecture. The question is what effective virtual address is used by the CPU when running in 32-bit mode? Perhaps for MIPS we have to sign extend, but it's probably wrong for every other platform. I would probably be tempted to invert this to sign extend only on MIPS and zero-extend everywhere else. Hmm, indeed, truss is broken today for i386 due to the sign extending: 70294: vfork() = 70295 (0x11297) 70295: <new process> 70295: freebsd32_execve("/sbin/echo",[ "echo", "I'm in the child " ],[ "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(nu ll)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(nu ll)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(nu ll)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)", "(null)" ]) ERR#2 'No such file or directory' With sign extension disabled: 70371: <new process> 70370: vfork() = 70371 (0x112e3) 70371: freebsd32_execve("/sbin/echo",[ "echo", "I'm in the child " ],[ "ALTERNATE_EDITOR=vi", "BLOCKSIZE=K", "COLORFGBG=0;15", "COLORTERM=truecolor", "CSCOPE_EDITOR=emacsclient", "CVS_RSH=ssh", "DISPLAY=:0", ... jhb: I don't know what the right choice is for sign-extending vs not. I suspect it's actually wrong… | ||||||||||||
Done Inline ActionsSounds good I'll change it to be MIPS only. arichardson: Sounds good I'll change it to be MIPS only. | ||||||||||||
* sign-extends on MIPS and zero-extends on all other architectures. | ||||||||||||
*/ | ||||||||||||
static psaddr_t | ||||||||||||
user_ptr32_to_psaddr(int32_t user_pointer) | ||||||||||||
{ | ||||||||||||
#if defined(__mips__) | ||||||||||||
return ((psaddr_t)(intptr_t)user_pointer); | ||||||||||||
#else | ||||||||||||
return ((psaddr_t)(uintptr_t)user_pointer); | ||||||||||||
Not Done Inline Actions
jhb: | ||||||||||||
#endif | ||||||||||||
} | ||||||||||||
/* | ||||||||||||
* Converts a syscall argument into a string. Said string is | * Converts a syscall argument into a string. Said string is | |||||||||||
* allocated via malloc(), so needs to be free()'d. sc is | * allocated via malloc(), so needs to be free()'d. sc is | |||||||||||
* a pointer to the syscall description (see above); args is | * a pointer to the syscall description (see above); args is | |||||||||||
* an array of all of the system call arguments. | * an array of all of the system call arguments. | |||||||||||
*/ | */ | |||||||||||
char * | char * | |||||||||||
print_arg(struct syscall_arg *sc, unsigned long *args, register_t *retval, | print_arg(struct syscall_arg *sc, unsigned long *args, register_t *retval, | |||||||||||
struct trussinfo *trussinfo) | struct trussinfo *trussinfo) | |||||||||||
Show All 34 Lines | print_arg(struct syscall_arg *sc, unsigned long *args, register_t *retval, | |||||||||||
case Long: | case Long: | |||||||||||
fprintf(fp, "%ld", args[sc->offset]); | fprintf(fp, "%ld", args[sc->offset]); | |||||||||||
break; | break; | |||||||||||
case Sizet: | case Sizet: | |||||||||||
fprintf(fp, "%zu", (size_t)args[sc->offset]); | fprintf(fp, "%zu", (size_t)args[sc->offset]); | |||||||||||
break; | break; | |||||||||||
case ShmName: | case ShmName: | |||||||||||
/* Handle special SHM_ANON value. */ | /* Handle special SHM_ANON value. */ | |||||||||||
if ((char *)args[sc->offset] == SHM_ANON) { | if ((char *)(uintptr_t)args[sc->offset] == SHM_ANON) { | |||||||||||
fprintf(fp, "SHM_ANON"); | fprintf(fp, "SHM_ANON"); | |||||||||||
break; | break; | |||||||||||
} | } | |||||||||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | |||||||||||
case Name: { | case Name: { | |||||||||||
/* NULL-terminated string. */ | /* NULL-terminated string. */ | |||||||||||
char *tmp2; | char *tmp2; | |||||||||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | case BinString: { | |||||||||||
} else { | } else { | |||||||||||
print_pointer(fp, args[sc->offset]); | print_pointer(fp, args[sc->offset]); | |||||||||||
} | } | |||||||||||
break; | break; | |||||||||||
} | } | |||||||||||
case ExecArgs: | case ExecArgs: | |||||||||||
case ExecEnv: | case ExecEnv: | |||||||||||
case StringArray: { | case StringArray: { | |||||||||||
uintptr_t addr; | psaddr_t addr; | |||||||||||
union { | union { | |||||||||||
int32_t strarray32[PAGE_SIZE / sizeof(int32_t)]; | int32_t strarray32[PAGE_SIZE / sizeof(int32_t)]; | |||||||||||
int64_t strarray64[PAGE_SIZE / sizeof(int64_t)]; | int64_t strarray64[PAGE_SIZE / sizeof(int64_t)]; | |||||||||||
char buf[PAGE_SIZE]; | char buf[PAGE_SIZE]; | |||||||||||
} u; | } u; | |||||||||||
char *string; | char *string; | |||||||||||
size_t len; | size_t len; | |||||||||||
u_int first, i; | u_int first, i; | |||||||||||
Show All 13 Lines | case StringArray: { | |||||||||||
} | } | |||||||||||
/* | /* | |||||||||||
* Read a page of pointers at a time. Punt if the top-level | * Read a page of pointers at a time. Punt if the top-level | |||||||||||
* pointer is not aligned. Note that the first read is of | * pointer is not aligned. Note that the first read is of | |||||||||||
* a partial page. | * a partial page. | |||||||||||
*/ | */ | |||||||||||
addr = args[sc->offset]; | addr = args[sc->offset]; | |||||||||||
if (addr % pointer_size != 0) { | if (!__is_aligned(addr, pointer_size)) { | |||||||||||
print_pointer(fp, args[sc->offset]); | print_pointer(fp, args[sc->offset]); | |||||||||||
break; | break; | |||||||||||
} | } | |||||||||||
len = PAGE_SIZE - (addr & PAGE_MASK); | len = PAGE_SIZE - (addr & PAGE_MASK); | |||||||||||
if (get_struct(pid, addr, u.buf, len) == -1) { | if (get_struct(pid, addr, u.buf, len) == -1) { | |||||||||||
print_pointer(fp, args[sc->offset]); | print_pointer(fp, args[sc->offset]); | |||||||||||
break; | break; | |||||||||||
} | } | |||||||||||
assert(len > 0); | assert(len > 0); | |||||||||||
fputc('[', fp); | fputc('[', fp); | |||||||||||
first = 1; | first = 1; | |||||||||||
i = 0; | i = 0; | |||||||||||
for (;;) { | for (;;) { | |||||||||||
uintptr_t straddr; | psaddr_t straddr; | |||||||||||
if (pointer_size == 4) { | if (pointer_size == 4) { | |||||||||||
if (u.strarray32[i] == 0) | straddr = user_ptr32_to_psaddr(u.strarray32[i]); | |||||||||||
Done Inline ActionsI kind of think intptr_t is better here than int64_t? For CheriBSD I'd guess this would need the signed variant of ptraddr_t (hah!, another reason for sptraddr_t). Also, I'm not quite sure if sign-extending is always correct (it is for MIPS maybe, but not i386). jhb: I kind of think intptr_t is better here than int64_t? For CheriBSD I'd guess this would need… | ||||||||||||
Done Inline ActionsThe latest version uses (u)intptr_t. I've changed it to zero-extend for i386 and sign-extend otherwise. arichardson: The latest version uses (u)intptr_t. I've changed it to zero-extend for i386 and sign-extend… | ||||||||||||
break; | ||||||||||||
/* sign-extend 32-bit pointers */ | ||||||||||||
straddr = (intptr_t)u.strarray32[i]; | ||||||||||||
} else if (pointer_size == 8) { | } else if (pointer_size == 8) { | |||||||||||
if (u.strarray64[i] == 0) | straddr = (psaddr_t)u.strarray64[i]; | |||||||||||
break; | ||||||||||||
straddr = (intptr_t)u.strarray64[i]; | ||||||||||||
} else { | } else { | |||||||||||
errx(1, "Unsupported pointer size: %zu", | errx(1, "Unsupported pointer size: %zu", | |||||||||||
pointer_size); | pointer_size); | |||||||||||
} | } | |||||||||||
Done Inline Actions
jhb: | ||||||||||||
/* Stop once we read the first NULL pointer. */ | ||||||||||||
if (straddr == 0) | ||||||||||||
break; | ||||||||||||
string = get_string(pid, straddr, 0); | string = get_string(pid, straddr, 0); | |||||||||||
fprintf(fp, "%s \"%s\"", first ? "" : ",", string); | fprintf(fp, "%s \"%s\"", first ? "" : ",", string); | |||||||||||
free(string); | free(string); | |||||||||||
first = 0; | first = 0; | |||||||||||
i++; | i++; | |||||||||||
if (i == len / pointer_size) { | if (i == len / pointer_size) { | |||||||||||
addr += len; | addr += len; | |||||||||||
▲ Show 20 Lines • Show All 1,156 Lines • Show Last 20 Lines |
Maybe fix this while here?