Changeset View
Standalone View
usr.bin/truss/syscalls.c
Show First 20 Lines • Show All 953 Lines • ▼ Show 20 Lines | print_mask_arg32(bool (*decoder)(FILE *, uint32_t, uint32_t *), FILE *fp, | |||||||||
uint32_t rem; | uint32_t rem; | |||||||||
if (!decoder(fp, value, &rem)) | if (!decoder(fp, value, &rem)) | |||||||||
fprintf(fp, "0x%x", rem); | fprintf(fp, "0x%x", rem); | |||||||||
else if (rem != 0) | else if (rem != 0) | |||||||||
fprintf(fp, "|0x%x", rem); | fprintf(fp, "|0x%x", rem); | |||||||||
} | } | |||||||||
#ifndef __LP64__ | ||||||||||
/* | /* | |||||||||
* Add argument padding to subsequent system calls after Quad | * Add argument padding to subsequent system calls after Quad | |||||||||
* syscall arguments as needed. This used to be done by hand in the | * syscall arguments as needed. This used to be done by hand in the | |||||||||
* decoded_syscalls table which was ugly and error prone. It is | * decoded_syscalls table which was ugly and error prone. It is | |||||||||
* simpler to do the fixup of offsets at initialization time than when | * simpler to do the fixup of offsets at initialization time than when | |||||||||
* decoding arguments. | * decoding arguments. | |||||||||
*/ | */ | |||||||||
static void | static void | |||||||||
Show All 28 Lines | #ifdef __powerpc__ | |||||||||
} | } | |||||||||
#endif | #endif | |||||||||
offset++; | offset++; | |||||||||
default: | default: | |||||||||
break; | break; | |||||||||
} | } | |||||||||
} | } | |||||||||
} | } | |||||||||
#endif | ||||||||||
static struct syscall * | static struct syscall * | |||||||||
find_syscall(struct procabi *abi, u_int number) | find_syscall(struct procabi *abi, u_int number) | |||||||||
{ | { | |||||||||
struct extra_syscall *es; | struct extra_syscall *es; | |||||||||
if (number < nitems(abi->syscalls)) | if (number < nitems(abi->syscalls)) | |||||||||
return (abi->syscalls[number]); | return (abi->syscalls[number]); | |||||||||
STAILQ_FOREACH(es, &abi->extra_syscalls, entries) { | STAILQ_FOREACH(es, &abi->extra_syscalls, entries) { | |||||||||
if (es->number == number) | if (es->number == number) | |||||||||
return (es->sc); | return (es->sc); | |||||||||
} | } | |||||||||
return (NULL); | return (NULL); | |||||||||
} | } | |||||||||
static void | static void | |||||||||
add_syscall(struct procabi *abi, u_int number, struct syscall *sc) | add_syscall(struct procabi *abi, u_int number, struct syscall *sc) | |||||||||
{ | { | |||||||||
struct extra_syscall *es; | struct extra_syscall *es; | |||||||||
#ifndef __LP64__ | /* TODO: Is quad fixup needed for all 32-bit ABIs or only FreeBSD32? */ | |||||||||
/* FIXME: should be based on sycall ABI not truss ABI */ | if (abi->pointer_size == 4) | |||||||||
quad_fixup(&sc->info); | quad_fixup(&sc->info); | |||||||||
jhb: Yes, it's needed for all 32-bit FreeBSD ABIs. One way to handle this might be to have a… | ||||||||||
Done Inline ActionsI had a quick look at adding the function pointer, since that seems like the cleaner solution. However, that makes the changes more invasive since I need to move lots of stuff around. I think it makes more sense as a follow-up commit especially if all 32-bit ABIs (including Linux32) need it. arichardson: I had a quick look at adding the function pointer, since that seems like the cleaner solution. | ||||||||||
#endif | ||||||||||
if (number < nitems(abi->syscalls)) { | if (number < nitems(abi->syscalls)) { | |||||||||
assert(abi->syscalls[number] == NULL); | assert(abi->syscalls[number] == NULL); | |||||||||
abi->syscalls[number] = sc; | abi->syscalls[number] = sc; | |||||||||
} else { | } else { | |||||||||
es = malloc(sizeof(*es)); | es = malloc(sizeof(*es)); | |||||||||
es->sc = sc; | es->sc = sc; | |||||||||
es->number = number; | es->number = number; | |||||||||
STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries); | STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries); | |||||||||
} | } | |||||||||
STAILQ_INSERT_HEAD(&seen_syscalls, sc, entries); | STAILQ_INSERT_HEAD(&seen_syscalls, sc, entries); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* If/when the list gets big, it might be desirable to do it | * If/when the list gets big, it might be desirable to do it | |||||||||
* as a hash table or binary search. | * as a hash table or binary search. | |||||||||
*/ | */ | |||||||||
struct syscall * | struct syscall * | |||||||||
get_syscall(struct threadinfo *t, u_int number, u_int nargs) | get_syscall(struct threadinfo *t, u_int number, u_int nargs) | |||||||||
{ | { | |||||||||
struct syscall *sc; | struct syscall *sc; | |||||||||
const char *sysdecode_name; | const char *sysdecode_name; | |||||||||
const char *lookup_name; | ||||||||||
char *name; | char *name; | |||||||||
u_int i; | u_int i; | |||||||||
sc = find_syscall(t->proc->abi, number); | sc = find_syscall(t->proc->abi, number); | |||||||||
if (sc != NULL) | if (sc != NULL) | |||||||||
return (sc); | return (sc); | |||||||||
sysdecode_name = sysdecode_syscallname(t->proc->abi->abi, number); | sysdecode_name = sysdecode_syscallname(t->proc->abi->abi, number); | |||||||||
Done Inline ActionsThis seems to be a duplicate line now? jhb: This seems to be a duplicate line now? | ||||||||||
if (sysdecode_name == NULL) | if (sysdecode_name == NULL) | |||||||||
asprintf(&name, "#%d", number); | asprintf(&name, "#%d", number); | |||||||||
else | else | |||||||||
name = strdup(sysdecode_name); | name = strdup(sysdecode_name); | |||||||||
Done Inline Actionsfreebsd64 should stay downstream jrtc27: freebsd64 should stay downstream | ||||||||||
Done Inline Actionsyeah will remove that before commiting. Missed it while cleaning up CHERI stuff. arichardson: yeah will remove that before commiting. Missed it while cleaning up CHERI stuff. | ||||||||||
sc = calloc(1, sizeof(*sc)); | sc = calloc(1, sizeof(*sc)); | |||||||||
sc->display_name = name; | sc->display_name = name; | |||||||||
/* Correctly decode compat syscalls arguments by stripping the prefix */ | ||||||||||
if (strncmp("freebsd32_", name, strlen("freebsd32_")) == 0) { | ||||||||||
Done Inline ActionsRather than hardcoding this here, let's add a new member to abi that is something like 'compat_prefix'. You would define it to "freebsd32_" over in main.c for the FreeBSD 32 compat ABIs. Then this code would be something like: lookup_name = name; if (strncmp(abi->compat_prefix, name, strlen(compat_prefix)) == 0) lookup_name += strlen(compat_prefix); This would let you handle freebsd64_ easily in CheriBSD by just adding it to the FreeBSD64 ABI over in main.c. It would also permit handling Linux32 if someone really wanted to. It also avoids potential false positive matches. jhb: Rather than hardcoding this here, let's add a new member to `abi` that is something like… | ||||||||||
lookup_name = name + strlen("freebsd32_"); | ||||||||||
Done Inline ActionsI still don't like this messing with stailq's and would be fine printing the non-prefixed syscall name (and we still have the ABI floating around if we wanted to reconstruct the prefixed name later). jrtc27: I still don't like this messing with stailq's and would be fine printing the non-prefixed… | ||||||||||
Done Inline ActionsI agree this is rather ugly. Let me have another look to see if I can simplify this while keeping the original names. arichardson: I agree this is rather ugly. Let me have another look to see if I can simplify this while… | ||||||||||
Done Inline Actions
It's too bad there's no 'strstartswith()' or the like. jhb: It's too bad there's no 'strstartswith()' or the like. | ||||||||||
} else { | ||||||||||
lookup_name = name; | ||||||||||
} | ||||||||||
for (i = 0; i < nitems(decoded_syscalls); i++) { | for (i = 0; i < nitems(decoded_syscalls); i++) { | |||||||||
if (strcmp(name, decoded_syscalls[i].name) == 0) { | if (strcmp(lookup_name, decoded_syscalls[i].name) == 0) { | |||||||||
sc->info = decoded_syscalls[i]; | sc->info = decoded_syscalls[i]; | |||||||||
add_syscall(t->proc->abi, number, sc); | add_syscall(t->proc->abi, number, sc); | |||||||||
return (sc); | return (sc); | |||||||||
} | } | |||||||||
} | } | |||||||||
/* It is unknown. Add it into the list. */ | /* It is unknown. Add it into the list. */ | |||||||||
#if DEBUG | #if DEBUG | |||||||||
fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name, | fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name, | |||||||||
nargs); | nargs); | |||||||||
#endif | #endif | |||||||||
sc->unknown = sysdecode_name == NULL; | sc->unknown = sysdecode_name == NULL; | |||||||||
/* | /* | |||||||||
* Note: info.name is either a constant or points to sc->display_name | * Note: info.name is either a constant or points to sc->display_name | |||||||||
* which be free'd toger with sc on exit. | * which be free'd toger with sc on exit. | |||||||||
*/ | */ | |||||||||
sc->info.name = name; | sc->info.name = lookup_name; | |||||||||
sc->info.ret_type = 1; /* Assume 1 return value. */ | sc->info.ret_type = 1; /* Assume 1 return value. */ | |||||||||
sc->info.nargs = nargs; | sc->info.nargs = nargs; | |||||||||
for (i = 0; i < nargs; i++) { | for (i = 0; i < nargs; i++) { | |||||||||
sc->info.args[i].offset = i; | sc->info.args[i].offset = i; | |||||||||
/* Treat all unknown arguments as LongHex. */ | /* Treat all unknown arguments as LongHex. */ | |||||||||
sc->info.args[i].type = LongHex; | sc->info.args[i].type = LongHex; | |||||||||
} | } | |||||||||
add_syscall(t->proc->abi, number, sc); | add_syscall(t->proc->abi, number, sc); | |||||||||
▲ Show 20 Lines • Show All 714 Lines • ▼ Show 20 Lines | case BinString: { | |||||||||
} | } | |||||||||
break; | break; | |||||||||
} | } | |||||||||
case ExecArgs: | case ExecArgs: | |||||||||
case ExecEnv: | case ExecEnv: | |||||||||
case StringArray: { | case StringArray: { | |||||||||
uintptr_t addr; | uintptr_t addr; | |||||||||
union { | union { | |||||||||
char *strarray[0]; | int32_t strarray32[PAGE_SIZE / sizeof(int32_t)]; | |||||||||
int64_t strarray64[PAGE_SIZE / sizeof(int64_t)]; | ||||||||||
char buf[PAGE_SIZE]; | char buf[PAGE_SIZE]; | |||||||||
Not Done Inline Actions
and keep strarray64 (which only makes sense alongside the __capability annotation for strarray) downstream jrtc27: and keep strarray64 (which only makes sense alongside the __capability annotation for strarray)… | ||||||||||
Done Inline ActionsI prefer int64_t here because a) it makes the diff to cheribsd a bit smaller and b) we only ever care about the address so using a pointer could suggest that it's dereferenceable. arichardson: I prefer int64_t here because a) it makes the diff to cheribsd a bit smaller and b) we only… | ||||||||||
Not Done Inline Actionsuintptr_t strarray then? (Which can be uintcap_t for us instead) jrtc27: `uintptr_t strarray` then? (Which can be uintcap_t for us instead) | ||||||||||
Done Inline ActionsThat would work although I think it the code below a bit less readable. if (pointer_size == sizeof(u.strarray[0])) { if (u.strarray[i] == 0) break; straddr = u.strarray[i]; } else if (pointer_size == 4) { if (u.strarray32[i] == 0) break; /* sign-extend 32-bit pointers */ straddr = (intptr_t)u.strarray32[i]; } I think I prefer the explicit size version. arichardson: That would work although I think it the code below a bit less readable.
It would then need to… | ||||||||||
Not Done Inline ActionsWhat's wrong with that? Though I'd make it more like the kernel COMPAT_FREEBSD32 code and add #ifs like: #if __SIZEOF_POINTER__ > 4 if (pointer_size == 4) { if (u.strarray32[i] == 0) break; /* sign-extend 32-bit pointers */ straddr = (intptr_t)u.strarray32[i]; } else #endif { if (u.strarray[i] == 0) break; straddr = u.strarray[i]; } possibly with a pointer_size check and errx in the else branch if you want. jrtc27: What's wrong with that? Though I'd make it more like the kernel COMPAT_FREEBSD32 code and add… | ||||||||||
} u; | } u; | |||||||||
char *string; | char *string; | |||||||||
size_t len; | size_t len; | |||||||||
u_int first, i; | u_int first, i; | |||||||||
size_t pointer_size = | ||||||||||
trussinfo->curthread->proc->abi->pointer_size; | ||||||||||
/* | /* | |||||||||
* Only parse argv[] and environment arrays from exec calls | * Only parse argv[] and environment arrays from exec calls | |||||||||
* if requested. | * if requested. | |||||||||
*/ | */ | |||||||||
if (((sc->type & ARG_MASK) == ExecArgs && | if (((sc->type & ARG_MASK) == ExecArgs && | |||||||||
(trussinfo->flags & EXECVEARGS) == 0) || | (trussinfo->flags & EXECVEARGS) == 0) || | |||||||||
((sc->type & ARG_MASK) == ExecEnv && | ((sc->type & ARG_MASK) == ExecEnv && | |||||||||
(trussinfo->flags & EXECVEENVS) == 0)) { | (trussinfo->flags & EXECVEENVS) == 0)) { | |||||||||
print_pointer(fp, args[sc->offset]); | print_pointer(fp, args[sc->offset]); | |||||||||
break; | break; | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* 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 % sizeof(char *) != 0) { | if (addr % pointer_size != 0) { | |||||||||
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); | ||||||||||
fputc('[', fp); | fputc('[', fp); | |||||||||
first = 1; | first = 1; | |||||||||
i = 0; | i = 0; | |||||||||
while (u.strarray[i] != NULL) { | while (true) { | |||||||||
string = get_string(pid, (uintptr_t)u.strarray[i], 0); | uintptr_t straddr; | |||||||||
if (pointer_size == 4) { | ||||||||||
if (u.strarray32[i] == 0) | ||||||||||
break; | ||||||||||
/* sign-extend 32-bit pointers */ | ||||||||||
straddr = (intptr_t)u.strarray32[i]; | ||||||||||
} else if (pointer_size == 8) { | ||||||||||
if (u.strarray64[i] == 0) | ||||||||||
break; | ||||||||||
straddr = (intptr_t)u.strarray64[i]; | ||||||||||
} else { | ||||||||||
errx(1, "Unsupported pointer size: %zu", | ||||||||||
pointer_size); | ||||||||||
} | ||||||||||
string = get_string(pid, straddr, 0); | ||||||||||
Done Inline Actions
Is what style(9) prefers for infinite loops. jhb: Is what style(9) prefers for infinite loops. | ||||||||||
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 / sizeof(char *)) { | if (i == len / pointer_size) { | |||||||||
addr += len; | addr += len; | |||||||||
len = PAGE_SIZE; | len = PAGE_SIZE; | |||||||||
if (get_struct(pid, addr, u.buf, len) == | if (get_struct(pid, addr, u.buf, len) == -1) { | |||||||||
-1) { | ||||||||||
fprintf(fp, ", <inval>"); | fprintf(fp, ", <inval>"); | |||||||||
break; | break; | |||||||||
} | } | |||||||||
i = 0; | i = 0; | |||||||||
} | } | |||||||||
} | } | |||||||||
fputs(" ]", fp); | fputs(" ]", fp); | |||||||||
break; | break; | |||||||||
▲ Show 20 Lines • Show All 1,146 Lines • Show Last 20 Lines |
Yes, it's needed for all 32-bit FreeBSD ABIs. One way to handle this might be to have a function pointer in 'abi' for "syscallfixup" and use quad_fixup as that for the FreeBSD ABI (on ILP32) and the FreeBSD32 ABIs (on LP64). This would also permitting adding different behavior if needed for Linux32 in the future.