Changeset View
Changeset View
Standalone View
Standalone View
usr.bin/truss/amd64-fbsd32.c
Show All 23 Lines | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#ifndef lint | #include <sys/cdefs.h> | ||||
static const char rcsid[] = | __FBSDID("$FreeBSD$"); | ||||
"$FreeBSD$"; | |||||
#endif /* not lint */ | |||||
/* | /* FreeBSD/i386-specific system call handling. */ | ||||
* FreeBSD/i386-specific system call handling. This is probably the most | |||||
* complex part of the entire truss program, although I've got lots of | |||||
* it handled relatively cleanly now. The system call names are generated | |||||
* automatically, thanks to /usr/src/sys/kern/syscalls.master. The | |||||
* names used for the various structures are confusing, I sadly admit. | |||||
*/ | |||||
#include <sys/types.h> | |||||
#include <sys/ptrace.h> | #include <sys/ptrace.h> | ||||
#include <sys/syscall.h> | #include <sys/syscall.h> | ||||
#include <machine/reg.h> | #include <machine/reg.h> | ||||
#include <machine/psl.h> | #include <machine/psl.h> | ||||
#include <errno.h> | |||||
#include <fcntl.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | |||||
#include <time.h> | |||||
#include <unistd.h> | |||||
#include "truss.h" | #include "truss.h" | ||||
#include "syscall.h" | |||||
#include "extern.h" | |||||
#include "freebsd32_syscalls.h" | #include "freebsd32_syscalls.h" | ||||
static int nsyscalls = nitems(freebsd32_syscallnames); | static int | ||||
amd64_fbsd32_fetch_args(struct trussinfo *trussinfo) | |||||
/* | |||||
* This is what this particular file uses to keep track of a system call. | |||||
* It is probably not quite sufficient -- I can probably use the same | |||||
* structure for the various syscall personalities, and I also probably | |||||
* need to nest system calls (for signal handlers). | |||||
* | |||||
* 'struct syscall' describes the system call; it may be NULL, however, | |||||
* if we don't know about this particular system call yet. | |||||
*/ | |||||
struct freebsd32_syscall { | |||||
struct syscall *sc; | |||||
const char *name; | |||||
int number; | |||||
unsigned long *args; | |||||
unsigned int *args32; | |||||
int nargs; /* number of arguments -- *not* number of words! */ | |||||
char **s_args; /* the printable arguments */ | |||||
}; | |||||
static struct freebsd32_syscall * | |||||
alloc_fsc(void) | |||||
{ | { | ||||
return (malloc(sizeof(struct freebsd32_syscall))); | |||||
} | |||||
/* Clear up and free parts of the fsc structure. */ | |||||
static void | |||||
free_fsc(struct freebsd32_syscall *fsc) | |||||
{ | |||||
int i; | |||||
free(fsc->args); | |||||
free(fsc->args32); | |||||
if (fsc->s_args) { | |||||
for (i = 0; i < fsc->nargs; i++) | |||||
free(fsc->s_args[i]); | |||||
free(fsc->s_args); | |||||
} | |||||
free(fsc); | |||||
} | |||||
/* | |||||
* Called when a process has entered a system call. nargs is the | |||||
* number of words, not number of arguments (a necessary distinction | |||||
* in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c | |||||
* is ever changed these functions need to keep up. | |||||
*/ | |||||
void | |||||
amd64_fbsd32_syscall_entry(struct trussinfo *trussinfo, int nargs) | |||||
{ | |||||
struct ptrace_io_desc iorequest; | struct ptrace_io_desc iorequest; | ||||
struct reg regs; | struct reg regs; | ||||
struct freebsd32_syscall *fsc; | struct current_syscall *cs; | ||||
struct syscall *sc; | unsigned int *args32; | ||||
lwpid_t tid; | |||||
unsigned long parm_offset; | unsigned long parm_offset; | ||||
int i, syscall_num; | lwpid_t tid; | ||||
int i; | |||||
tid = trussinfo->curthread->tid; | tid = trussinfo->curthread->tid; | ||||
cs = &trussinfo->curthread->cs; | |||||
if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { | if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { | ||||
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); | fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); | ||||
return; | return (-1); | ||||
} | } | ||||
parm_offset = regs.r_rsp + sizeof(int); | parm_offset = regs.r_rsp + sizeof(int); | ||||
/* | /* | ||||
* FreeBSD has two special kinds of system call redirctions -- | * FreeBSD has two special kinds of system call redirections -- | ||||
* SYS_syscall, and SYS___syscall. The former is the old syscall() | * SYS_syscall, and SYS___syscall. The former is the old syscall() | ||||
* routine, basically; the latter is for quad-aligned arguments. | * routine, basically; the latter is for quad-aligned arguments. | ||||
* | |||||
* The system call argument count and code from ptrace() already | |||||
* account for these, but we need to skip over the first argument. | |||||
*/ | */ | ||||
syscall_num = regs.r_rax; | switch (regs.r_rax) { | ||||
switch (syscall_num) { | |||||
case SYS_syscall: | case SYS_syscall: | ||||
syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); | |||||
parm_offset += sizeof(int); | parm_offset += sizeof(int); | ||||
break; | break; | ||||
case SYS___syscall: | case SYS___syscall: | ||||
syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); | |||||
parm_offset += sizeof(quad_t); | parm_offset += sizeof(quad_t); | ||||
break; | break; | ||||
} | } | ||||
fsc = alloc_fsc(); | args32 = calloc(1 + cs->nargs, sizeof(unsigned int)); | ||||
if (fsc == NULL) | |||||
return; | |||||
fsc->number = syscall_num; | |||||
fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? | |||||
NULL : freebsd32_syscallnames[syscall_num]; | |||||
if (!fsc->name) { | |||||
fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", | |||||
syscall_num); | |||||
} | |||||
if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && | |||||
(strcmp(fsc->name, "fork") == 0 || | |||||
strcmp(fsc->name, "pdfork") == 0 || | |||||
strcmp(fsc->name, "rfork") == 0 || | |||||
strcmp(fsc->name, "vfork") == 0)) | |||||
trussinfo->curthread->in_fork = 1; | |||||
if (nargs == 0) | |||||
return; | |||||
fsc->args32 = malloc((1 + nargs) * sizeof(unsigned int)); | |||||
iorequest.piod_op = PIOD_READ_D; | iorequest.piod_op = PIOD_READ_D; | ||||
iorequest.piod_offs = (void *)parm_offset; | iorequest.piod_offs = (void *)parm_offset; | ||||
iorequest.piod_addr = fsc->args32; | iorequest.piod_addr = args32; | ||||
iorequest.piod_len = (1 + nargs) * sizeof(unsigned int); | iorequest.piod_len = (1 + cs->nargs) * sizeof(unsigned int); | ||||
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); | ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); | ||||
if (iorequest.piod_len == 0) | if (iorequest.piod_len == 0) { | ||||
return; | free(args32); | ||||
return (-1); | |||||
fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); | |||||
for (i = 0; i < nargs + 1; i++) | |||||
fsc->args[i] = fsc->args32[i]; | |||||
sc = NULL; | |||||
if (fsc->name) | |||||
sc = get_syscall(fsc->name); | |||||
if (sc) | |||||
fsc->nargs = sc->nargs; | |||||
else { | |||||
#if DEBUG | |||||
fprintf(trussinfo->outfile, "unknown syscall %s -- setting " | |||||
"args to %d\n", fsc->name, nargs); | |||||
#endif | |||||
fsc->nargs = nargs; | |||||
} | } | ||||
fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); | for (i = 0; i < cs->nargs + 1; i++) | ||||
fsc->sc = sc; | cs->args[i] = args32[i]; | ||||
free(args32); | |||||
/* | return (0); | ||||
* At this point, we set up the system call arguments. | |||||
* We ignore any OUT ones, however -- those are arguments that | |||||
* are set by the system call, and so are probably meaningless | |||||
* now. This doesn't currently support arguments that are | |||||
* passed in *and* out, however. | |||||
*/ | |||||
if (fsc->name) { | |||||
#if DEBUG | |||||
fprintf(stderr, "syscall %s(", fsc->name); | |||||
#endif | |||||
for (i = 0; i < fsc->nargs; i++) { | |||||
#if DEBUG | |||||
fprintf(stderr, "0x%x%s", sc ? | |||||
fsc->args[sc->args[i].offset] : fsc->args[i], | |||||
i < (fsc->nargs - 1) ? "," : ""); | |||||
#endif | |||||
if (sc && !(sc->args[i].type & OUT)) { | |||||
fsc->s_args[i] = print_arg(&sc->args[i], | |||||
fsc->args, 0, trussinfo); | |||||
} | } | ||||
} | |||||
#if DEBUG | |||||
fprintf(stderr, ")\n"); | |||||
#endif | |||||
} | |||||
#if DEBUG | static int | ||||
fprintf(trussinfo->outfile, "\n"); | amd64_fbsd32_fetch_retval(struct trussinfo *trussinfo, long *retval, | ||||
#endif | int *errorp) | ||||
trussinfo->curthread->fsc = fsc; | |||||
} | |||||
/* | |||||
* And when the system call is done, we handle it here. | |||||
* Currently, no attempt is made to ensure that the system calls | |||||
* match -- this needs to be fixed (and is, in fact, why S_SCX includes | |||||
* the system call number instead of, say, an error status). | |||||
*/ | |||||
long | |||||
amd64_fbsd32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) | |||||
{ | { | ||||
struct reg regs; | struct reg regs; | ||||
struct freebsd32_syscall *fsc; | |||||
struct syscall *sc; | |||||
lwpid_t tid; | lwpid_t tid; | ||||
long retval; | |||||
int errorp, i; | |||||
if (trussinfo->curthread->fsc == NULL) | |||||
return (-1); | |||||
tid = trussinfo->curthread->tid; | tid = trussinfo->curthread->tid; | ||||
if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { | if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { | ||||
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); | fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
retval = regs.r_rax; | retval[0] = regs.r_rax & 0xffffffff; | ||||
errorp = !!(regs.r_rflags & PSL_C); | retval[1] = regs.r_rdx & 0xffffffff; | ||||
*errorp = !!(regs.r_rflags & PSL_C); | |||||
/* | return (0); | ||||
* This code, while simpler than the initial versions I used, could | |||||
* stand some significant cleaning. | |||||
*/ | |||||
fsc = trussinfo->curthread->fsc; | |||||
sc = fsc->sc; | |||||
if (!sc) { | |||||
for (i = 0; i < fsc->nargs; i++) | |||||
asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); | |||||
} else { | |||||
/* | |||||
* Here, we only look for arguments that have OUT masked in -- | |||||
* otherwise, they were handled in the syscall_entry function. | |||||
*/ | |||||
for (i = 0; i < sc->nargs; i++) { | |||||
char *temp; | |||||
if (sc->args[i].type & OUT) { | |||||
/* | |||||
* If an error occurred, then don't bother | |||||
* getting the data; it may not be valid. | |||||
*/ | |||||
if (errorp) { | |||||
asprintf(&temp, "0x%lx", | |||||
fsc->args[sc->args[i].offset]); | |||||
} else { | |||||
temp = print_arg(&sc->args[i], | |||||
fsc->args, retval, trussinfo); | |||||
} | } | ||||
kib: Shouldn't this assignment explicitely zero upper word of the retval[0,1] ? It probably… | |||||
Not Done Inline ActionsDone. I almost did this the first time. Note that the amd64/linux32 argument fetching does not mask off the upper 32-bits of the registers still, but the kernel doesn't do this either. (It probably should be doing that for correctness in both places though.) jhb: Done. I almost did this the first time. Note that the amd64/linux32 argument fetching does… | |||||
kibUnsubmitted Not Done Inline ActionsRegisters coming from the 32bit mode, must have upper word zeroed, according to SDM. But indeed, we do not enforce the kernel code to treat the upper half as zero. kib: Registers coming from the 32bit mode, must have upper word zeroed, according to SDM. But… | |||||
fsc->s_args[i] = temp; | |||||
} | |||||
} | |||||
} | |||||
if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 || | static struct procabi amd64_fbsd32 = { | ||||
strcmp(fsc->name, "exit") == 0)) | "FreeBSD ELF32", | ||||
trussinfo->curthread->in_syscall = 1; | freebsd32_syscallnames, | ||||
nitems(freebsd32_syscallnames), | |||||
amd64_fbsd32_fetch_args, | |||||
amd64_fbsd32_fetch_retval | |||||
}; | |||||
/* | PROCABI(amd64_fbsd32); | ||||
* It would probably be a good idea to merge the error handling, | |||||
* but that complicates things considerably. | |||||
*/ | |||||
print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, | static struct procabi amd64_fbsd32_aout = { | ||||
retval, fsc->sc); | "FreeBSD a.out", | ||||
free_fsc(fsc); | freebsd32_syscallnames, | ||||
nitems(freebsd32_syscallnames), | |||||
amd64_fbsd32_fetch_args, | |||||
amd64_fbsd32_fetch_retval | |||||
}; | |||||
return (retval); | PROCABI(amd64_fbsd32_aout); | ||||
} |
Shouldn't this assignment explicitely zero upper word of the retval[0,1] ? It probably typically comes zero from kernel, but I would not bet that this is guaranteed.