home *** CD-ROM | disk | FTP | other *** search
- From: aj@zyx.UUCP (Arndt Jonasson)
- Newsgroups: comp.sources.misc
- Subject: v02i094: trapfile - a file-opening logger
- Message-ID: <2458@zyx.UUCP>
- Date: 10 Apr 88 00:10:55 GMT
- Approved: allbery@ncoast.UUCP
-
- comp.sources.misc: Volume 2, Issue 94
- Submitted-By: "Arndt Jonasson" <aj@zyx.UUCP>
- Archive-Name: trapfile
-
- [The "options" package was posted to comp.unix.wizards a week or so ago,
- there may be more documentation for it there.
-
- WARNING: This program assumes BSD system calls and is otherwise machine and
- OS dependent -- suffice it to say that it uses ptrace (if you don't know what
- that means, you probably don't want to touch this program) to trap system
- calls. ++bsa]
-
- # This is a shell archive. Remove anything before this line,
- # then unpack it by saving it in a file and typing "sh file".
- #
- # Wrapped by arndt at lynx on Sun Apr 10 02:07:41 1988
- #
- # This archive contains:
- # README Makefile.BSD Makefile.hpux options.c
- # options.h trapfile.1 trapfile.c
- #
-
- LANG=""; export LANG
-
- echo x - README
- cat >README <<'@EOF'
- This packet contains the source for the 'trapfile' program, which runs
- a program as a subprocess while reporting to the user any calls the
- subprocess makes to the system calls 'open' and 'close'. To parse its
- command line arguments, 'trapfile' uses my 'options' library, which
- can be used by itself and is portable (whereas 'trapfile' is not).
-
- trapfile.c source for trapfile
- trapfile.1 manual page for trapfile (use nroff -man)
- options.h include file for options parsing
- options.c source code for options parser
- Makefile.BSD makefile for 4.2BSD on Vax
- Makefile.hpux makefile for HP-UX s300 and s800
-
- No manual page for 'options' yet, sorry. Look at how 'trapfile' uses
- it.
-
- Copy the appropriate makefile to Makefile and just type 'make'. If
- your system is not either HP-UX or 4.2BSD, you are on your own. Try
- anyway, and if you succeed, I would be happy if you mailed your
- changes to me. 4.3BSD just might work directly, and there should be
- no very difficult changes for Sun-3.
-
- me = Arndt Jonasson, aj@zyx.SE (uucp: ...!uunet!mcvax!enea!zyx!aj)
- @EOF
-
- chmod 664 README
-
- echo x - Makefile.BSD
- cat >Makefile.BSD <<'@EOF'
- trapfile: options.a trapfile.o
- cc $(CFLAGS) -o trapfile trapfile.o options.a -ltermcap
-
- trapfile.o: options.h trapfile.c
- cc $(CFLAGS) -c -DBSD trapfile.c
-
- options.o: options.h options.c
- cc $(CFLAGS) -c -Dstrchr=index options.c
-
- options.a: options.o
- ar r options.a options.o
- ranlib options.a
- @EOF
-
- chmod 664 Makefile.BSD
-
- echo x - Makefile.hpux
- cat >Makefile.hpux <<'@EOF'
- trapfile: trapfile.o options.a
- cc $(CFLAGS) -o trapfile trapfile.o options.a -lcurses
-
- trapfile.o: trapfile.c options.h
- cc $(CFLAGS) -c trapfile.c
-
- options.o: options.h options.c
- cc $(CFLAGS) -c options.c
-
- options.a: options.o
- ar r options.a options.o
- @EOF
-
- chmod 664 Makefile.hpux
-
- echo x - options.c
- cat >options.c <<'@EOF'
- /*
- Prerelease 0.2 of the command line option parser, 28 Mar 88.
- options.c
-
- Copyright Arndt Jonasson, 1988. Please send enhancements and corrections
- back to the author.
-
- Author: Arndt Jonasson, Zyx Sweden
- aj@zyx.SE (...<backbone>!mcvax!enea!zyx!aj)
-
- No manual page yet.
- */
-
- #include "options.h"
-
- extern void exit (); /* '#define void' if your compiler doesn't */
- /* have it.*/
-
- char *O_programname = NULL;
-
- static char *usage_string = NULL;
- static int option_error;
-
- static fatal (msg)
- char *msg;
- {
- fprintf (stderr, "option parsing failure: %s\n", msg);
- exit (1);
- }
-
- static char *basename (str)
- char *str;
- {
- char *p1, *p2;
- extern char *strchr ();
-
- p2 = str;
- while ((p1 = strchr (p2, '/')) != NULL)
- {
- if (p1[1] == '\0')
- {
- p1[0] = '\0';
- return p2;
- }
- p2 = p1 + 1;
- }
- return p2;
- }
-
- static char *match (prefix, str)
- char *prefix, *str;
- {
- int n = strlen (prefix);
-
- if (strncmp (prefix, str, n) == 0)
- return str + n;
- else
- return NULL;
- }
-
- O_usage ()
- {
- if (usage_string != NULL)
- {
- if (O_programname == NULL)
- fprintf (stderr, "valid options: %s\n", usage_string);
- else
- fprintf (stderr, "usage: %s %s\n", O_programname, usage_string);
- }
- else
- {
- if (O_programname != NULL)
- fprintf (stderr, "%s: ", O_programname);
- fprintf (stderr, "invalid usage\n");
- }
-
- exit (1);
- }
-
- int O_atoi (str)
- char *str;
- {
- char c;
- int base = 10;
- int res = 0;
- int negative = 1;
-
- /*
- No check for overflow.
- */
-
- c = str[0];
-
- if (*str == '-')
- {
- negative = -1;
- str++;
- }
-
- if (*str == '0')
- {
- if (*++str == 'x')
- {
- str++;
- base = 16;
- }
- else
- base = 8;
- }
-
- if (*str == '\0' && (negative != -1 || base != 8)) /* kludgie */
- {
- option_error = 1;
- return 0;
- }
-
- for (;;)
- {
- switch (c = *str++)
- {
- case '8':
- case '9':
- if (base == 8)
- {
- option_error = 1;
- return 0;
- }
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- res = base * res + c - '0';
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- if (base == 16)
- res = base * res + c - 'a' + 10;
- else
- {
- option_error = 1;
- return 0;
- }
- break;
- case '\0':
- return (negative * res);
- /* NOTREACHED */
- break;
- default:
- option_error = 1;
- return 0;
- }
- }
- }
-
- double O_atof (s)
- char *s;
- {
- /*
- No error-checking currently.
- */
- extern double atof ();
-
- return atof (s);
- }
-
- char *O_strid (s)
- char *s;
- {
- return s;
- }
-
- char O_chrid (s)
- char *s;
- {
- return s[0];
- }
-
- static get_it (p, arg, c)
- Option *p;
- char *arg;
- char c;
- {
- option_error = 0;
-
- switch ((p->flags & 037))
- {
- case O_INT:
- *p->ip = (*p->ipf) (arg);
- if (option_error)
- {
- fprintf (stderr,
- "Invalid integer '%s' given to the -%c option\n", arg, c);
- O_usage ();
- }
- break;
- case O_STR:
- *p->cp = (*p->cpf) (arg);
- break;
- case O_DBL:
- *p->dp = (*p->dpf) (arg);
- if (option_error)
- {
- fprintf (stderr,
- "Invalid floating-point number '%s' given to the -%c option\n", arg, c);
- O_usage ();
- }
- break;
- case O_CHR:
- *p->tp = (*p->tpf) (arg);
- break;
- case O_MUL:
- (*p->table_p)[p->data++] = arg;
- break;
- default:
- fatal ("invalid option type");
- }
- }
-
- int O_parse_options (desc, argc, argv)
- Option *desc;
- int argc;
- char **argv;
- {
- static char *empty_table[] = {NULL};
- int first_char, multiple_error, multiple_add, hyphen_is_arg;
- int min, max, i, remaining;
- Option *p, *default_p = NULL;
- char *cp, *dir, *arg;
- char c;
-
- multiple_error = 1;
- multiple_add = 0;
- hyphen_is_arg = 1;
- min = 0;
- max = -1;
-
- for (p = desc; p->flags != O_END; p++)
- {
- p->data = 0;
-
- if (p->flags == O_DIR)
- {
- dir = p->directive;
- if (cp = match ("usage: ", dir))
- usage_string = cp;
- else if (cp = match ("multiple: ", dir))
- {
- multiple_error = (strcmp ("error", cp) == 0);
- multiple_add = (strcmp ("add", cp) == 0);
- }
- else if (cp = match ("remaining: ", dir))
- {
- int n;
- char dummy;
-
- n = sscanf (cp, "%d%c%d", &min, &dummy, &max);
- if (n == 1)
- max = min;
- else if (n == 2)
- max = -1;
- }
- else
- fatal ("unknown option directive");
- }
- else if (p->flags == O_MUL)
- *p->table_p = (char **) malloc (argc * sizeof (char *));
- else if (p->flags == O_INT && p->c == '\0')
- {
- p->c = -1;
- default_p = p;
- }
- else if (p->flags == O_FLG && p->c == '\0')
- hyphen_is_arg = 0;
-
- if (p->flags == O_FLG)
- *p->ip = 0;
- }
-
- O_programname = basename (argv[0]);
-
- for (i = 1; i < argc; i++)
- {
- arg = argv[i];
- if (arg[0] != '-' || (arg[1] == '\0' && hyphen_is_arg))
- break;
-
- first_char = 1;
-
- while ((c = *++arg) != '\0' || first_char)
- {
- for (p = desc; p->flags != O_END; p++)
- if (p->c == c)
- {
- if (p->flags & 040)
- {
- fprintf (stderr, "The -%c option was given twice\n", c);
- O_usage ();
- }
-
- if (multiple_error && p->flags != O_MUL)
- p->flags |= 040;
-
- if ((p->flags & 037) == O_FLG)
- {
-
- if (multiple_add)
- *(p->ip) += 1;
- else
- *(p->ip) = 1;
- if (c == '\0')
- goto next_argument;
- else
- goto next_character;
- }
-
- arg += 1;
- if (arg[0] == '\0')
- {
- if (++i == argc)
- {
- fprintf (stderr,
- "The -%c option requires an argument\n", c);
- O_usage ();
- }
- arg = argv[i];
- }
- get_it (p, arg, c);
- goto next_argument;
- }
- if ((p = default_p) != NULL && first_char)
- {
- *p->ip = (*p->ipf) (arg);
- if (!option_error)
- goto next_argument;
- }
- fprintf (stderr, "There is no -%c option\n", c);
- O_usage ();
-
- /* NOTREACHED*/
-
- next_character:
- first_char = 0;
- }
- next_argument: ;
- }
-
- for (p = desc; p->flags != O_END; p++)
- if ((p->flags & 037) == O_MUL)
- {
- if (p->data == 0)
- (*p->table_p = empty_table);
- else
- {
- (*p->table_p)[p->data++] = NULL;
- *p->table_p = (char **) realloc (*p->table_p,
- p->data * sizeof (char *));
- }
- }
-
- remaining = argc - i;
- if (remaining < min || max != -1 && remaining > max)
- {
- if (max == -1)
- fprintf (stderr, "At least %d non-option argument%s required\n",
- min, min == 1 ? " is" : "s are");
- else if (max == 0)
- fprintf (stderr, "No non-option arguments are allowed\n");
- else if (min == max)
- fprintf (stderr, "Exactly %d non-option argument%s required\n",
- min, min == 1 ? " is" : "s are");
- else
- fprintf (stderr, "%d to %d non-option arguments are required\n",
- min, max);
- O_usage ();
- }
- return i;
- }
- @EOF
-
- chmod 664 options.c
-
- echo x - options.h
- cat >options.h <<'@EOF'
- /*
- Prerelease 0.2 of the command line option parser, 28 Mar 88.
- options.h
-
- Copyright Arndt Jonasson, 1988. Please send enhancements and corrections
- back to the author.
-
- Author: Arndt Jonasson, Zyx Sweden
- aj@zyx.SE (...<backbone>!mcvax!enea!zyx!aj)
-
- No manual page yet.
- */
-
- #include <stdio.h>
-
- typedef struct
- {
- char c;
- int flags;
- char **cp;
- char *(*cpf) ();
- int *ip;
- int (*ipf) ();
- double *dp;
- double (*dpf) ();
- char *tp;
- char (*tpf) ();
- char ***table_p;
- char *directive;
- int data;
- } Option;
-
- extern int O_atoi ();
- extern double O_atof ();
- extern char O_chrid ();
-
- extern char *O_strid ();
- extern char *O_programname;
-
- #define O_FLG 0
- #define O_INT 1
- #define O_STR 2
- #define O_DBL 3
- #define O_END 4
- #define O_DIR 5
- #define O_CHR 6
- #define O_MUL 7
-
-
- #define O_flg(c, ip) {c, O_FLG, 0, 0, &ip, 0, 0, 0, 0, 0, 0, 0}
- #define O_int(c, ip) {c, O_INT, 0, 0, &ip, O_atoi, 0, 0, 0, 0, 0, 0}
- #define O_str(c, cp) {c, O_STR, &cp, O_strid, 0, 0, 0, 0, 0, 0, 0, 0}
- #define O_dbl(c, dp) {c, O_DBL, 0, 0, 0, 0, &dp, O_atof, 0, 0, 0, 0}
- #define O_end {-1, O_END, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
- #define O_directive(str) {-1, O_DIR, 0, 0, 0, 0, 0, 0, 0, 0, 0, str}
- #define O_chr(c, cp) {c, O_CHR, 0, 0, 0, 0, 0, 0, &cp, O_chrid, 0, 0}
- #define O_mul(c, cpp) {c, O_MUL, 0, 0, 0, 0, 0, 0, 0, 0, &cpp, 0}
-
- #define O_Empty '\0'
-
- #define usage() O_usage ()
- @EOF
-
- chmod 664 options.h
-
- echo x - trapfile.1
- cat >trapfile.1 <<'@EOF'
- .TH TRAPFILE 1 local ""
- .SH NAME
- trapfile - trap all file opening calls in a program
- .SH SYNOPSIS
- .B trapfile
- [options] command [args ...]
- .SH DESCRIPTION
- .I trapfile
- runs an application program in an inferior fork, trapping all calls
- the application makes to the system calls
- .I open
- and
- .IR close ,
- and reporting them on standard error. The report consists of the name
- of the system call and its arguments. If the call was successful, the return
- value is shown (except for
- .IR close,
- which always returns 0 when successful), otherwise an error message,
- as defined by
- .IR perror (3).
- In order to let subprocesses of the application run correctly,
- .I trapfile
- must also trap calls to
- .I fork
- (and
- .IR vfork ,
- if it exists). Thus, these system calls will be reported too.
- .PP
- On some systems, for complicated reasons, it doesn't work to single-step a
- call to
- .I fork
- (or
- .IR vfork ).
- Instead
- .I trapfile
- lets the subprocess run free for a short while. When this happens, only
- the call itself will be reported, not the result (whether successful
- or not).
- .PP
- The following options exist:
- .TP 11
- .BR -l
- Don't run the program; only print out all system calls in it to standard
- output, and the
- locations they would be in if the program were run.
- .TP
- .BR -s
- Report signals sent to the application as well as system calls.
- .TP
- .BI -w " time"
- When the application running under
- .I trapfile
- spawns a child of its own,
- .I trapfile
- can no longer write breakpoints into the address space of the application,
- since the system will have made the core image shared and thus non-writable
- (unless the application is of the old non-shared type, see
- .IR ld (1).
- In this situation,
- .I trapfile
- reports why it is waiting, and by default sleeps until the child of
- the application has done an exec or exited, at which time the application's
- address space will be writable again. If the
- .B \-w
- option is used,
- .I time
- indicates how many seconds
- .I trapfile
- is to wait before continuing to run. In this case, no more system call
- reports will be
- issued, since the application is now running free. Signal reporting, if enabled
- with the
- .B \-s
- option, will still be done.
- .TP
- .B -i
- Turn off interrupts for
- .IR trapfile .
- Normally, interrupt, hangup and quit signals kill both
- .I trapfile
- and its subprocess.
- .TP
- .B -d
- When
- .I trapfile
- looks for system calls in the program file, it normally only searches
- the code area (text segment) for system calls. This is sufficient for
- the majority of all programs. With the
- .B \-d
- option, the entire file is searched.
- .TP
- .B -r
- Display the reports given by
- .I trapfile
- in reverse video on the terminal, if possible.
- .TP
- .BI -T " term"
- Use the terminal definition for
- .IR term ,
- instead of the one given by the environment variable TERM. This is only
- useful together with the
- .B \-r
- option.
- .TP
- .BI -o " logfile"
- Outputs the reports to the file
- .I logfile
- instead of standard error.
- .SH WARNING
- Since
- .I trapfile
- makes very heavy use of the SIGTRAP signal, the application is not likely
- to run correctly if it uses that signal for any purpose.
- .SH ENVIRONMENT VARIABLES
- .TP 11
- TERM
- The name of the terminal database entry. Used for obtaining the escape
- sequences for reverse video when using the
- .B \-r
- option, unless overridden by the
- .B \-T
- option.
- .TP
- TMPDIR
- If
- .I trapfile
- doesn't have write access to the application program file,
- it has to make a temporary copy; otherwise it won't be able to set any
- breakpoints in the program image. The temporary copy is by default put in the
- directory
- .BR /tmp ,
- unless overridden by the TMPDIR environment variable.
- Setting TMPDIR only works in systems that have the
- .B tempnam(3)
- library routine.
- .SH FILES
- .TP 11
- /tmp/trapfXXXXXX
- Temporary writable copy of the application.
- .SH SYSTEM DEPENDENCIES
- .I trapfile
- currently works for HP-UX s300 (MC68020), HP-UX s800 (HP Precision
- Architecture) and 4.2BSD (Vax-11/750). Due to the small granularity in
- the Vax (system calls instructions are only two bytes and may start at
- any address), the possibility of finding what looks like a system
- call but isn't, and thus trapping the wrong location, is larger on a Vax.
-
- Of these three systems, only the HP-UX s300 can single-step calls to
- .IR fork .
- .SH SEE ALSO
- adb(1), ptrace(2), perror(3), tempnam(3).
- .SH BUGS
- .I trapfile
- doesn't look at the PATH environment variable when opening the application
- file. Thus, the full pathname of the application must be given.
- .PP
- In a program containing several instances of one of the trapped system
- calls (\fIopen\fR,
- .IR close ,
- .I fork
- or
- .IR vfork ),
- only the first occurrence will be trapped; the others are ignored.
- .PP
- If the application receives a signal during a
- .I fork
- call on a system where single-stepping a
- .I fork
- can't be done, the newly created child process may not run correctly.
- .SH DIAGNOSTICS
- "couldn't remove temporary file XXXXXX"
- .IP
- The temporary copy of the application program couldn't be removed.
- See the discussion of TMPDIR.
- .PP
- "child killed by signal NNN"
- .br
- "child killed by signal NNN (core dumped)"
- .IP
- The application was killed by a signal, and possibly a core dump was
- produced.
- .PP
- "TERM not set; can't use reverse video"
- .br
- "terminal type XXX not known; can't use reverse video"
- .br
- "terminal type XXX doesn't support reverse video"
- .IP
- The
- .B \-r
- option was requested, but reverse video can't be used, for
- the named reason. The program will ignore the
- .B \-r
- option.
- .PP
- "the application is setuid and may not run correctly"
- .IP
- Since traced programs do not honour the setuid/setgid bit, a
- program that needs a special user identity to run correctly may
- fail when run under
- .IR trapfile .
- .PP
- "couldn't exec XXX"
- .IP
- The application exists, but couldn't be started.
- .PP
- "couldn't open XXX for writing, using stderr"
- .IP
- The logfile given with the
- .B \-o
- option couldn't be opened.
- .PP
- "warning, multiple occurrences of trap NN"
- .IP
- Only the first of several occurrences of a system call will be
- trapped; the rest will be ignored. This can cause file openings
- not to be reported, or worse, a child process of the application
- to terminate prematurely.
- .PP
- "Waiting for child's child to exit or exec"
- .br
- "No more system calls will be made"
- .IP
- See the discussion of the
- .B \-w
- option.
- .PP
- These messages are followed by a standard error printout if relevant (as
- printed by
- .IR perror (3).
- .PP
- Messages containing the word
- .I fatal
- indicate some internal error in
- .IR trapfile ,
- caused either by a bug, or by external circumstances preventing
- .I trapfile
- from continuing.
- .SH AUTHOR
- Arndt Jonasson
- @EOF
-
- chmod 664 trapfile.1
-
- echo x - trapfile.c
- cat >trapfile.c <<'@EOF'
- /*
- Author: Arndt Jonasson, Zyx Sweden AB
- Mail address: aj@zyx.SE (uucp: <backbone>!mcvax!enea!zyx!aj)
- Date: March 20, 1988
- Version 1.0
-
- This program arose from a question in the news group comp.unix.questions
- (or was it comp.unix.wizards?) - someone wanted to know whether the
- functionality of TOPS-20's "Set Trap File-Openings" command existed in
- Unix. The answer is "well, yes, in a way" and this program is the result.
-
- It is very dependent on the 'ptrace(2)' system call and knows a few
- system- and processor-dependent things about what a system call looks
- like.
-
- Assuming that 'ptrace(2)' exists and that the registers can be accessed,
- it shouldn't be too hard to port.
- */
-
- /*
- Bad style:
-
- We use goto.
-
- We deliberately use a preprocessor definition with no () around it,
- but I won't fix that - it's too convenient. Just watch out for it.
-
- We assume sizeof(int) = 4.
- */
-
- /*
- All the different preprocessor symbols are quite intertangled; e.g.
- BSD currently means not only 4.2BSD Unix, but the Vax processor.
- hp9000s300 implies the MC68020 processor. It's too soon to untangle
- this until this program has been ported to more systems.
-
- Currently used preprocessor symbols:
-
- hpux HP-UX system.
- hp9000s300, hp9000s800 Imply hpux.
- BSD 4.2BSD.
-
- BSD currently implies Vax processor. hp9000s800 implies HP PA, and
- hp9000s300 implies MC68020.
-
- BSD and hpux are mutually exclusive. So are hp9000s300 and hp9000s800.
- */
-
- /*
- What does a system call look like?
-
- General assumptions: system calls have a uniform calling sequence on
- any given machine, with the arguments in known places. When a system
- call returns, either the result is in a certain register, or an
- error value defined by <errno.h> is in that same register. There
- exists an instruction that will generate a SIGTRAP for the process
- executing it.
-
- ==================
- HP-UX s300, 68020:
- ==================
-
- MOVQ &0x5,%d0 0x7005
- or
- MOV.W &0xA5,%d0 0x303c
- 0xA5
- or
- MOV.L &0xA5,%d0 0x203c
- 0x0
- 0xA5
- followed by
- TRAP &0 0x4e40
-
- Top of stack is return pc, then come the arguments.
- If the call failed, 0x10000 in the status flag word is set, and errno is
- in d0. If the call was successful, the result is typically in d0.
-
- The trap is transformed into a 'trap &1' (0x4e41).
-
- =================
- HP-UX s800, HPPA:
- =================
-
- ldil -40000000,r1
- ble 4(sr7,r1) 0xe420e008
- ldo 0xA5(r0),r22 0x341600e0
-
- Arguments are passed in arg0, arg1, etc.
- If the call failed, r22 is 1 with errno in ret0. Otherwise r22 is 0,
- and the return value is in ret0. The 'ble' jumps into execute-only
- kernel space, and there the real trap instruction lies. Can't put
- a breakpoint there, of course.
-
- The ble is supplanted with a 'break 4,8' (0x10004).
-
- ============
- 4.2BSD, Vax:
- ============
-
- chmk $4 0x04bc
- OR
- chmk $42 0x8fbc
- 0x42
-
- Arguments are passed on the stack, but the system call is typically
- called via the 'calls' instruction that pushes five additional
- words on the stack before the return pc. We have to adjust the sp
- accordingly. Return value in R0.
-
- The chmk is replaced by a 'bpt' (0x803).
- */
-
- #include "options.h" /* My option parser. */
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <signal.h>
-
- #include <machine/reg.h>
- #ifdef hpux
- # define PS 16 /* These two are not defined in reg.h */
- # define PC 16 + 2 /* WARNING: BIG KLUDGE (but how convenient) */
- #endif
-
- #ifdef hpux
- # include <sys/ptrace.h>
- #else
- # define PT_SETTRC 0
- # define PT_RIUSER 1
- # define PT_RDUSER 2
- # define PT_RUAREA 3
- # define PT_WIUSER 4
- # define PT_WDUSER 5
- # define PT_WUAREA 6
- # define PT_CONTIN 7
- # define PT_EXIT 8
- # define PT_SINGLE 9
- #endif
-
- /*
- System call numbers. Some systems define them in an include file, some
- don't. The numbers are the same on all the systems where this program
- currently runs.
- */
-
- #ifndef hpux
- # define SYS_FORK 0x2
- # define SYS_OPEN 0x5
- # define SYS_CLOSE 0x6
- #endif
-
- #ifndef hp9000s300
- # define SYS_VFORK 0x42
- #endif
-
- #include <sys/param.h>
- #include <sys/dir.h>
-
- #ifdef hp9000s800
- # include <filehdr.h>
- # include <aouthdr.h>
- # include <spacehdr.h>
- # include <scnhdr.h>
- #else
- # ifdef BSD
- # include <a.out.h>
- # endif
- # include <sys/user.h>
- #endif
-
- /*
- Unix system incompatibility.
- */
-
- #ifndef BSD
- # include <string.h>
- #else
- # include <strings.h>
- # define strchr index
- #endif
-
- #include <errno.h>
- extern char *sys_errlist[]; /* And pray tell me, why aren't */
- extern int sys_nerr; /* these defined in <errno.h>? */
- extern void perror ();
- #ifdef BSD
- extern int errno; /* sigh ... */
- #endif
-
- extern int tgetent (); /* For obtaining information from the */
- extern char *tgetstr (); /* terminfo or termcap database. */
-
- extern void _exit (), exit ();
- extern unsigned long sleep ();
- extern unsigned short geteuid (), getegid ();
- extern char *getenv ();
-
- char *signame[] =
- {
- "NAL 0",
- "HUP", "INT", "QUIT", "ILL", "TRAP",
- "IOT", "EMT", "FPE", "KILL", "BUS",
- "SEGV", "SYS", "PIPE", "ALRM", "TERM",
- #ifdef BSD
- "URG", "STOP", "TSTP", "CONT", "CHLD",
- "TTIN", "TTOU", "IO", "XCPU", "XFSZ",
- "VTALRM", "PROF"
- #else
- "USR1", "USR2", "CLD", "PWR", "VTALRM",
- "PROF", "IO", "WINDOW", "STOP", "TSTP",
- "CONT", "TTIN", "TTOU", "URG"
- #endif
- };
-
- /*
- Approximate - it's only important that the number be large enough.
- */
-
- #ifdef BSD
- # define SYS_n 153
- #else
- # define SYS_n 239
- #endif
-
- typedef struct
- {
- int nr; /* The entry's index in the 'calls' array. */
- enum {Absent, Present, Bpt} type; /* Status of this system call. */
- int old_word; /* Old contents before breakpoint was set. */
- int new_word; /* Breakpoint instruction. */
- int address; /* Address in subprocess's address space. */
- }
- system_call;
- system_call calls[SYS_n]; /* Array of all system calls in the */
- /* application, indexed by trap number. */
-
- #define Check(d, msg) if (!(d)) fatal (msg, 1)
-
- #define with_reverse_video(x) do { \
- fputs (rev_on, log); \
- x \
- fputs (rev_off, log); \
- } while (0)
-
- char *tempfile = NULL; /* If application executable isn't writable, */
- /* this is the name of the temporary file. */
- char *term = NULL; /* Terminal type, set by the -T option. */
- FILE *log; /* Name of logfile, set by the -o option. */
- char *rev_on, *rev_off; /* Control sequences to turn on/off reverse */
- /* video on the terminal. Set to "" when no */
- /* such capability is available or desired. */
- int report_signals = 0; /* If non-zero, report signals. Set by the */
- /* -s option. */
- int sigm = 0; /* Mask of pending signals. */
-
-
- /*
- Return to our superior, with the given exit status. The log file is closed
- and the temporary copy removed, if one was made.
- */
-
- cleanup_and_exit (status)
- int status;
- {
- int s;
-
- fclose (log);
-
- if (tempfile != NULL)
- {
- s = unlink (tempfile);
- if (s == -1 && errno != ENOENT)
- fprintf (stderr, "%s: couldn't remove temporary file %s\n",
- O_programname, tempfile);
- }
- exit (status);
- }
-
- /*
- Report a fatal error on stderr. If 'perr' is set, 'errno' contains useful
- information, so print it using 'perror'. Then exit with status 1.
- */
-
- fatal (msg, perr)
- char *msg;
- int perr;
- {
- fprintf (stderr, "%s: fatal - %s", O_programname, msg);
- if (perr)
- {
- fprintf (stderr, " - ");
- perror ("");
- }
- else
- fprintf (stderr, "\n");
- cleanup_and_exit (1);
- }
-
- /*
- From a pathname, extract the last component.
- */
-
- char *basename (str)
- char *str;
- {
- char *p1, *p2;
-
- p2 = str;
- while ((p1 = strchr (p2, '/')) != NULL)
- p2 = p1 + 1;
- return p2;
- }
-
- /*
- If 'sig' is non-zero, it is a pending signal for the subprocess
- 'pid'. Report it to the user if desired. Then continue the subprocess,
- giving it the signal.
- */
-
- report_signal_and_continue (pid, sig)
- int pid, sig;
- {
- int s;
-
- if (report_signals && sig > 0)
- with_reverse_video
- ({
- fprintf (log, "SIG%s\n", signame[sig]);
- });
-
- s = ptrace (PT_CONTIN, pid, 1, sig);
- Check (s != -1, "couldn't continue subprocess after signal");
- }
-
- /*
- Let the subprocess 'pid' run until it either exits or encounters the signal
- 'sig'. The value -1 for 'sig' means return on all signals. The return
- value is the signal that the subprocess received, unless it was a SIGTRAP,
- in which case the return value is 0.
- */
-
- int do_wait (pid, sig)
- int pid, sig;
- {
- int s, status, lo, hi;
-
- /*
- o If 'wait' returns some other pid than the one we expect, try again.
- When using pipes in sh, such things can happen.
- o If the child exited normally, so do we, with the same exit status.
- */
-
- for (;;)
- {
- s = wait (&status);
-
- if (s == -1)
- {
- if (errno == EINTR)
- continue;
- else
- fatal ("wait failed", 1);
- }
-
- if (s != pid)
- continue;
-
- lo = (status) & 0377;
- hi = ((status) >> 8) & 0377;
-
- if (lo == 0177)
- {
- if (hi == SIGTRAP)
- return 0;
-
- if (sig == -1)
- {
- sigm |= (1 << hi);
- return hi;
- }
-
- report_signal_and_continue (pid, hi);
- continue;
- }
-
- if (lo == 0)
- cleanup_and_exit (hi);
-
- if (hi == 0)
- {
- fprintf (stderr, "%s: child killed by signal %d%s\n",
- O_programname,
- lo & 0177,
- (lo & 0200) ? "(core dumped)" : "");
- cleanup_and_exit (lo);
- }
- }
- }
-
- /*
- Read a 16-bit chunk from the file 'f'. Depending on the processor, the
- first byte will be either the low or the high byte.
- */
-
- unsigned int get_chunk (f)
- FILE *f;
- {
- static int first = 1;
- static int saved;
-
- if (!first)
- {
- first = 1;
- #ifdef BSD
- return (saved >> 16) & 0xffff;
- #else
- return (saved & 0xffff);
- #endif
- }
- else
- {
- first = 0;
- saved = getw (f);
- #ifdef BSD
- return (saved & 0xffff);
- #else
- return (saved >> 16) & 0xffff;
- #endif
- }
- }
-
- /*
- Given an address in the application's code area, the system call at that
- address is returned, or NULL if it wasn't found.
- */
-
- system_call *find_system_call (adr)
- int adr;
- {
- int i;
-
- for (i = 0; i < SYS_n; i++)
- if (calls[i].address == adr)
- return &calls[i];
-
- return NULL;
- }
-
- /*
- Obtain the sequences for turning reverse video on and off. If that fails,
- they are set to the empty string.
- */
-
- setup_standout_strings (on, off)
- char **on, **off;
- {
- int s;
- char *r_on, *r_off;
- static char termbuf[1024], databuf[1024];
- char *scratch = databuf;
-
- *on = *off = "";
-
- if (term == NULL)
- term = getenv ("TERM");
- if (term == NULL)
- {
- fprintf (stderr, "%s: TERM not set; can't use reverse video\n",
- O_programname);
- return;
- }
- s = tgetent (termbuf, term);
- if (s != 1)
- {
- fprintf (stderr,
- "%s: terminal type \"%s\" not known; can't use reverse video\n",
- O_programname, term);
- return;
- }
- r_on = tgetstr ("so", &scratch);
- r_off = tgetstr ("se", &scratch);
- if (r_on == 0 || r_off == 0)
- {
- fprintf (stderr,
- "%s: terminal type \"%s\" doesn't support reverse video\n",
- O_programname, term);
- return;
- }
- *on = r_on;
- *off = r_off;
- }
-
- /*
- If the application is setuid or setgid to something other than the current
- user's id, give a warning.
- */
-
- warn_if_setuid (file)
- char *file;
- {
- int s;
- struct stat b;
-
- s = stat (file, &b);
- Check (s == 0, "couldn't stat program file");
- if (((b.st_mode & S_ISUID) && geteuid () != b.st_uid)
- ||
- ((b.st_mode & S_ISGID) && getegid () != b.st_gid))
- {
- fprintf (stderr,
- "%s: the application is set%cid and may not work correctly\n",
- O_programname, (b.st_mode & S_ISUID) ? 'u' : 'g');
- }
- }
-
- /*
- Like 'perror', but to an arbitrary file stream and without the newline.
- */
-
- print_error (f, err)
- FILE *f;
- int err;
- {
- if (err < 0 || err > sys_nerr)
- fprintf (f, " Error %d", err);
- else
- fprintf (f, " %s", sys_errlist[err]);
- }
-
- /*
- Report the result of a system call. 'print_p' tells whether to print the
- result if the call succeeded (useless in the case of 'close', for instance).
- 'flags' is some value that says whether the system call succeeded or not.
- Mostly it is the processor's status register. What bits in it are relevant
- is processor-specific.
- */
-
- print_result (f, val, flags, print_p)
- FILE *f;
- int val, flags, print_p;
- {
- #ifdef hp9000s800
- if (flags != 0)
- #endif
- #ifdef BSD
- if (flags & 1)
- #endif
- #ifdef hp9000s300
- if (flags & 0x10000)
- #endif
- print_error (f, val);
- else if (print_p)
- fprintf (f, " = %d", val);
-
- fprintf (f, "\n");
- }
-
- /*
- The nice 'tempnam' function doesn't exist in 4.2BSD, so we use what
- we have, i.e. 'mktemp'.
- */
-
- char *tempfile_name ()
- {
- char *s;
-
- #ifndef BSD
- extern char *tempnam ();
- s = tempnam ("/tmp", "trapf");
- #else
- extern char *mktemp ();
- s = mktemp ("/tmp/trapfXXXXXX");
- #endif
- return s;
- }
-
- /*
- Start the application in an inferior fork (the fork will already have been
- done). 'f' is the logfile - if not stderr, the application shouldn't see
- it.
- */
-
- do_exec (f, file, argv)
- FILE *f;
- char *file;
- char **argv;
- {
- if (f != stderr)
- close (fileno (f));
-
- (void) signal (SIGINT, SIG_DFL);
- (void) signal (SIGQUIT, SIG_DFL);
- argv[0] = basename (argv[0]);
- (void) execv (file, argv);
- fatal ("couldn't exec program", 1);
- }
-
- /*
- Obtain the base of the register area in the u area of the application.
- Apparently, this changes when the application is running, so it has
- to be done each time the application has been continued or single-stepped.
-
- In HP-UX s300, the register area lies somewhat after the user area proper,
- and is accessed in the same way. All we need to know is the address of that
- area, and the address of the start of the user area, so we can give a
- relative address to 'ptrace'. That relative address is given by
- <machine/reg.h>, but not for pc and ps. ps is immediately after SP, and
- since it is only two bytes, pc can't be defined as an index into the
- u.u_ar0 array.
-
- In 4.2BSD Vax, the above remains valid, except that pc and pc are indeed
- defined in reg.h.
-
- In HP-UX s800, the register area lies somewhere totally different, and is
- not accessed through the user area. There is a special request P_RUREGS
- for reading the registers.
- */
-
- #ifndef hp9000s800
- int get_reg_base (pid)
- {
- int base, minus;
- struct user u;
-
- #define u_offset(a) ((char *) a - (char *) &u)
-
- base = ptrace (PT_RUAREA, pid, u_offset (&u.u_ar0), 0);
- Check (base != -1, "couldn't get user area register block pointer");
- minus = ptrace (PT_RUAREA, pid, u_offset (&u.u_ap), 0) - u_offset (u.u_arg);
- Check (minus != -1, "couldn't get user area u_ap");
-
- return base - minus;
- }
- #endif
-
- /*
- Read a structure from a file into memory, complain if it failed.
- */
-
- #define slurp(f, x) if (fread (&(x), sizeof (x), 1, f) != 1) \
- fatal ("slurp", 1)
-
- /*
- From the file stream 'f', open to an executable file, obtain the offset
- of the code in the file, the offset of the code in the address space when
- the program is run, and the size of the code area.
- */
-
- get_offsets (f, file, code, size)
- int *file, *code, *size;
- FILE *f;
- {
- #ifdef hp9000s800
-
- /*
- In s800, the code always starts at 0x800 in the process's address space.
- The start of code in the file can be obtained by examination of the
- header.
- */
-
- struct header hdr;
- struct som_exec_auxhdr auxhdr;
- int n, i;
-
- slurp (f, hdr);
-
- fseek (f, hdr.aux_header_location, 0);
- slurp (f, auxhdr);
- if (auxhdr.som_auxhdr.type != HPUX_AUX_ID)
- fatal ("not right header type", 0);
-
- *file = auxhdr.exec_tfile;
- *code = 0x800;
- *size = auxhdr.exec_tsize;
- #else
-
- /*
- In s300 and BSD, the code always starts at 0 in the process's address space.
- Depending on the kind of executable, the start of the code in the
- file is different.
- */
-
- struct exec hdr;
-
- #ifdef hpux
- # define N_TXTOFF TEXT_OFFSET
- #endif
-
- slurp (f, hdr);
-
- if (N_BADMAG (hdr))
- fatal ("unknown magic number", 0);
-
- *file = N_TXTOFF (hdr);
- *code = 0;
- *size = hdr.a_text;
- #endif
- }
-
- /*
- Return index of lowest set bit in *maskp. Return 0 if *maskp is 0. Lowest
- bit in *maskp will never be set, since that would mean signal 0. If that
- signal occurs, something is seriously wrong.
- */
-
- int pick_first_signal (maskp)
- unsigned int *maskp;
- {
- unsigned int m = *maskp;
- int i = 0;
-
- if (m == 0)
- return 0;
-
- for (;;)
- {
- i += 1;
- m >>= 1;
- if (m & 1)
- {
- *maskp ^= (1 << i);
- return i;
- }
- }
- }
-
- main (argc, argv)
- int argc;
- char **argv;
- {
- #ifdef hp9000s800
- struct save_state state;
- # define s_offset(a) ((char *) &state.a - (char *) &state)
- #endif
-
- int sig;
- int address;
- char *string_arg;
- char **command;
- system_call *p;
- #ifndef hp9000s800
- int reg_base;
- int sp;
- #endif
- int trapno;
- int pid, i;
- FILE *f;
- int *ip;
- int bytes;
- unsigned int w1, w2;
- int val, arg1, arg2, arg3, res;
- int s;
- int pc, flags;
- int c_offset, f_offset, c_size;
- char *file;
- int n;
-
- static int ignore_signals = 0, reverse_video = 0, look_only = 0;
- static int search_data = 0;
- static char *logname = NULL;
- static int wait_for_child = -1;
-
- static Option desc[] = {
- O_flg ('d', search_data),
- O_flg ('i', ignore_signals),
- O_flg ('s', report_signals),
- O_flg ('r', reverse_video),
- O_flg ('l', look_only),
- O_str ('o', logname),
- O_str ('T', term),
- O_int ('w', wait_for_child),
- O_directive ("remaining: 1-infinity"),
- O_directive
- ("usage: [-isrld] [-T term] [-o logfile] [-w time] command [args ...]"),
- O_end,
- };
-
- n = O_parse_options (desc, argc, argv);
-
- command = argv + n;
-
- file = command[0];
-
- /*
- Set up the log file.
- */
-
- if (logname != NULL)
- {
- log = fopen (logname, "w");
- if (log == NULL)
- {
- fprintf (stderr,
- "%s: couldn't open %s for writing, using stderr\n",
- O_programname, logname);
- log = stderr;
- }
- }
- else
- log = stderr;
-
- /*
- Set up reverse video.
- */
-
- if (reverse_video)
- setup_standout_strings (&rev_on, &rev_off);
-
- /*
- If we are going to run the application, it has to be writable, otherwise
- no breakpoint can be set. So make a temporary copy if the application
- isn't writable.
- */
-
- if (!look_only)
- {
- warn_if_setuid (file);
-
- s = open (file, O_WRONLY, 0); /* Try to open for writing. */
- if (s == -1) /* Couldn't, so make a writable copy. */
- {
- char buf[1024];
-
- tempfile = tempfile_name ();
- sprintf (buf, "cp %s %s && chmod u+w %s", file, tempfile, tempfile);
- s = system (buf);
- if (s != 0)
- fatal ("couldn't make a temporary copy", 0);
- file = tempfile;
- }
- else
- (void) close (s);
- }
-
- f = fopen (file, "r");
- Check (f != 0, "couldn't open the program file");
-
- /*
- Get pointers to where the code starts in the file, and in core.
- Set the file pointer so that we start reading the first code
- instruction from the file.
- */
-
- get_offsets (f, &f_offset, &c_offset, &c_size);
- s = fseek (f, (long) f_offset, 0);
- Check (s != -1, "couldn't fseek the program file");
-
- /*
- Initialize the array of system calls.
- */
-
- for (i = 0; i < SYS_n; i++)
- {
- calls[i].type = Absent;
- calls[i].nr = i;
- }
-
- /*
- Read through the executable file, with the core address 'bytes' starting
- at the address where the code will be loaded when run.
- */
-
- w1 = 0;
- bytes = c_offset;
-
- for (;;)
- {
- if (!search_data && bytes > c_offset + c_size)
- break;
-
- bytes += 2;
-
- w2 = get_chunk (f);
- if (feof (f))
- break;
-
- trapno = -1;
-
- #ifdef hp9000s300
- if (((w1 & ~0xff) == 0x7000 || (w1 & ~0xff) == 0x0)
- &&
- w2 == 0x4e40)
- {
- trapno = (w1 & 0xff);
- address = bytes - 2;
- }
- #endif
- #ifdef hp9000s800
- if (w1 == 0xe420 && w2 == 0xe008)
- {
- bytes += 2;
- w1 = get_chunk (f);
- bytes += 2;
- w2 = get_chunk (f);
-
- if (w1 == 0x3416)
- {
- trapno = (w2 >> 1);
- address = bytes - 8;
- }
- }
- #endif
- #ifdef BSD
- if ((w2 & 0xff) == 0xbc)
- {
- unsigned int saved_w1 = w1;
-
- trapno = (w2 >> 8) & 0xff;
- address = bytes - 2;
- if (trapno == 0x8f)
- {
- bytes += 2;
- w2 = get_chunk (f);
- trapno = w2;
- }
- else if (trapno == 0 || trapno > 0x3f)
- trapno = -1;
-
- bytes += 2;
- w2 = get_chunk (f);
- if (trapno != -1
- && (w2 & 0xff) != 0x1f /* all except exit, getppid and geteuid */
- && (w2 & 0xff) != 0x1e /* vfork and wait3 */
- && (saved_w1 != 0)) /* all except brk, sbrk, ptrace */
- trapno = -2;
- }
- /* Instructions may start on odd addresses */
- /* on a Vax. */
- if (trapno == -1)
- {
- unsigned int saved_w1 = w1;
-
- if ((w1 & 0xff00) == 0xbc00)
- {
- trapno = (w2 & 0xff);
- address = bytes - 3;
- if (trapno == 0x8f)
- {
- w1 = w2;
- bytes += 2;
- w2 = get_chunk (f);
- trapno = ((w2 & 0xff) << 8) + ((w1 >> 8) & 0xff);
- }
- else if (trapno == 0 || trapno > 0x3f)
- trapno = -1;
-
- if ((saved_w1 & 0xff) != 0
- && (w2 & 0xff00) != 0x1e00
- && (w2 & 0xff00) != 0x1f00)
- trapno = -1;
- }
- }
- #endif
- if (trapno > 0 && trapno < SYS_n)
- {
- if (calls[trapno].type == Present && !look_only
- && (i == SYS_OPEN
- || i == SYS_CLOSE
- || i == SYS_FORK
- || i == SYS_VFORK))
- {
- fprintf (stderr, "%s: warning, multiple occurrences of trap %#x\n",
- O_programname, trapno);
- }
- calls[trapno].address = address;
- calls[trapno].type = Present;
- if (look_only)
- printf ("system call %#x at %#x\n", trapno, address);
- }
- w1 = w2;
- }
- fclose (f);
-
- if (look_only) /* If only listing system calls, we are */
- exit (0); /* done now. */
-
- fflush (stdout); /* Cause child's stdio buffers to start */
- fflush (stdout); /* out clean, in case the child wants to */
- fflush (stderr); /* report an error. */
-
- if (ignore_signals) /* Ignore signals, if the user told us to. */
- {
- (void) signal (SIGHUP, SIG_IGN);
- (void) signal (SIGINT, SIG_IGN);
- (void) signal (SIGQUIT, SIG_IGN);
- }
-
- pid = fork (); /* vfork won't do. Bug in hpux. */
- Check (pid != -1, "couldn't fork");
-
- if (pid == 0)
- {
- (void) ptrace (PT_SETTRC, 0, 0, 0); /* Shouldn't fail */
- do_exec (log, file, command);
- /* NOTREACHED */
- }
-
- (void) do_wait (pid, SIGTRAP);
- /* Wait for the 'exec' to happen, */
- for (i = 0; i < SYS_n; i++) /* then start putting in our breakpoints. */
- {
- if (i != SYS_OPEN
- && i != SYS_CLOSE
- && i != SYS_FORK
- && i != SYS_VFORK)
- continue;
-
- if (calls[i].type == Present)
- {
- s = ptrace (PT_RIUSER, pid, calls[i].address, 0);
- Check (s != -1, "couldn't read trap word");
- calls[i].old_word = s;
-
- #ifdef BSD
- calls[i].new_word = (s & 0xffff0000) | 0x803;
- #endif
- #ifdef hp9000s300
- calls[i].new_word = 0x4e410000 + (s & 0xffff);
- #endif
- #ifdef hp9000s800
- calls[i].new_word = 0x10004;
- #endif
-
- calls[i].type = Bpt;
-
- s = ptrace (PT_WIUSER, pid, calls[i].address, calls[i].new_word);
- Check (s != -1, "couldn't write new trap word");
- }
- }
-
- /*
- The rest of the 'main' function is within this loop. The basic flow
- of control is:
-
- 1) Subprocess is in the stopped state.
- 2) Continue it, passing along any pending signal that occurred during the
- last loop.
- 3) Wait until it hits another breakpoint (which will cause a SIGTRAP signal
- and stop the subprocess).
- 4) Find what system call is at the location of the breakpoint.
- 5) If none (spurious signal), just go back to step 1.
- 6) Find the arguments to the system call.
- 7) Put back the real system call.
- 8) Single-step the system call.
- 9) Get the result of the system call (including error status).
- 10) Report the whole system call.
- 11) Put back the breakpoint and come back to step 1.
- */
-
- for (;;)
- {
- /* 1 */
- report_signal_and_continue (pid, pick_first_signal (&sigm));
- /* 2 */
- (void) do_wait (pid, SIGTRAP);
-
- /* 3 */
- #ifdef hp9000s800
- pc = ptrace (PT_RUREGS, pid, s_offset (ss_pcoq_head), 0);
- pc &= ~3; /* remove privilege level */
- #else
- reg_base = get_reg_base (pid);
- pc = ptrace (PT_RUAREA, pid, reg_base + 4*PC, 0);
- # ifdef hp9000s300
- pc -= 2;
- # endif
- #endif
-
- /* 4 */
- p = find_system_call (pc);
-
- /* 5 */
- if (p == 0)
- continue; /* Spurious SIGTRAP, just ignore it. */
-
- /* 6 */
- #ifdef hp9000s800
- arg1 = ptrace (PT_RUREGS, pid, s_offset (ss_arg0), 0);
- arg2 = ptrace (PT_RUREGS, pid, s_offset (ss_arg1), 0);
- arg3 = ptrace (PT_RUREGS, pid, s_offset (ss_arg2), 0);
- #else
- reg_base = get_reg_base (pid); /* unnecessary, really, since it was */
- /* done above. */
- sp = ptrace (PT_RUAREA, pid, reg_base + 4*SP, 0);
- # ifdef BSD
- sp += 4*5; /* adjustment for junk on stack */
- # endif
- arg1 = ptrace (PT_RDUSER, pid, sp+4, 0);
- arg2 = ptrace (PT_RDUSER, pid, sp+8, 0);
- arg3 = ptrace (PT_RDUSER, pid, sp+12, 0);
- #endif
-
- if (p->nr == SYS_OPEN)
- {
- static char buf[1024];
- int adjusted_arg1 = arg1 & ~3;
- int offset = (arg1 & 3);
- #ifdef BSD
- int mask = (0x01010101 >> (32 - 8 * offset));
- #else
- int mask = (0x01010101 >> (8 * offset)) ^ 0x01010101;
- #endif
-
- ip = (int *) buf;
- for (i = 0; i < 100; i++)
- {
- val = ptrace (PT_RDUSER, pid, adjusted_arg1, 0);
- Check (val != -1, "couldn't read string from subprocess");
-
- /*
- Not all systems allow address arguments to 'ptrace' to be odd, so we
- do this to get things right. The Vax does allow it, but it's easier to
- special-case 'mask' than the whole loop.
- */
-
- if (i == 0)
- val |= mask;
- *ip++ = val;
- if (((val & 0xff000000) == 0)
- | ((val & 0x00ff0000) == 0)
- | ((val & 0x0000ff00) == 0)
- | ((val & 0x000000ff) == 0))
- break;
- adjusted_arg1 += 4;
- }
- *ip = 0;
- string_arg = buf + offset;
- }
-
- /*
- Put back the true system call in order to run it.
-
- If the call is a kind of fork, we have to remove all the breakpoints;
- otherwise the child process would die on SIGTRAP as soon as it tried
- to use one of the system calls that we have put breakpoints in.
- */
-
- /* 7 */
- if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
- {
- for (i = 0; i < SYS_n; i++)
- if (calls[i].type == Bpt)
- {
- s = ptrace (PT_WIUSER,
- pid, calls[i].address, calls[i].old_word);
- Check (s != -1, "couldn't write back old trap words");
- }
- }
- else
- {
- s = ptrace (PT_WIUSER, pid, p->address, p->old_word);
- Check (s != -1, "couldn't write back old trap word");
- }
-
- /*
- Now run the system call by single-stepping it.
-
- Some systems lose when the 'fork' or 'vfork' system call is
- single-stepped over; the child immediately gets a SIGTRAP. The only
- solution to this problem is to continue the process instead of
- single-stepping it, then stopping it as soon as possible with a
- 'kill' call. After this, we jump down to the label 'put_back',
- and put back all our breakpoints. We may miss some system calls that
- we should have reported in this way, but that can't be helped.
- */
-
- /* 8 */
- #if defined(hp9000s800) | defined(BSD)
-
- /*
- To avoid anomalies (such as reporting the fork call after the child
- has printed things on our terminal), we do the reporting here already.
- Unfortunately, we can't report the pid of the child process.
- */
-
- if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
- {
- with_reverse_video
- ({
- fprintf (log, "%sfork()\n", p->nr == SYS_FORK ? "" : "v");
- });
-
- /*
- There is a slight chance here that a signal is stopping the process just
- before the system call is done. In that case, the result won't show up
- correctly, and in the case of fork, the child process loses. The only
- way to detect that is to look at the pc. We don't do that yet.
- */
-
- s = ptrace (PT_CONTIN, pid, pc, 0);
- kill (pid, SIGTRAP);
- sig = do_wait (pid, -1);
- goto put_back;
- }
- #endif
-
- /*
- On some systems, the correct data don't show up in the registers until
- we have single-stepped more than once.
- */
-
- #ifdef hp9000s800
- # define EXTRA_STEPS 2 /* Once for the instruction after the jump */
- /* (yes, this is a RISC), once for the */
- /* actual trap somewhere in the protected */
- /* kernel code. */
- #endif
- #ifdef hp9000s300
- # define EXTRA_STEPS 1 /* Scheduling anomaly, perhaps? */
- #endif
- #ifdef BSD
- # define EXTRA_STEPS 0 /* No problem. */
- #endif
-
- for (i = 0; i < 1 + EXTRA_STEPS;)
- {
- s = ptrace (PT_SINGLE, pid, pc, 0);
- Check (s != -1, "couldn't singlestep 1");
- pc = 1;
- sig = do_wait (pid, -1);
- if (sig == 0)
- i += 1;
- }
-
- /*
- Pick up the return value of the system call from the right register, and
- the flag word that says whether the call succeeded. This flag word will
- be decoded by the function 'print_result'.
- */
-
- /* 9 */
- #ifdef hp9000s800
- flags = ptrace (PT_RUREGS, pid, s_offset (ss_gr22), 0);
- res = ptrace (PT_RUREGS, pid, s_offset (ss_ret0), 0);
- #else
- reg_base = get_reg_base (pid);
- flags = ptrace (PT_RUAREA, pid, reg_base + 4*PS, 0);
- res = ptrace (PT_RUAREA, pid, reg_base + 4*R0, 0);
- #endif
-
- /*
- Now the system call is complete; report it along with its arguments,
- and whether it succeeded or failed.
- */
-
- /* 10 */
- with_reverse_video
- ({
- if (p->nr == SYS_OPEN)
- {
- if ((arg2 & O_CREAT) != 0)
- fprintf (log, "open(\"%s\", %#x, %#o)",
- string_arg, arg2, arg3);
- else
- fprintf (log, "open(\"%s\", %#x)", string_arg, arg2);
- print_result (log, res, flags, 1);
- }
- else if (p->nr == SYS_CLOSE)
- {
- fprintf (log, "close(%d)", arg1);
- print_result (log, res, flags, 0);
- }
- else if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
- {
- fprintf (log, "%sfork()", p->nr == SYS_FORK ? "" : "v");
- print_result (log, res, flags, 1);
- }
- });
-
- put_back:
-
- /*
- If we previously removed all our breakpoints before single-stepping a
- fork or vfork, now put them back again.
-
- If the child hasn't had time to exec or exit, there are now two processes
- sharing the same code area, and the system consequently prohibits us from
- writing into it. Wait either until we can write, or until the user-supplied
- timeout expires.
-
- If not fork or vfork, just put back the one system call that was called.
- */
-
- /* 11 */
- if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
- {
- int waiting = 0;
-
- retry:
- for (i = 0; i < SYS_n; i++)
- if (calls[i].type == Bpt)
- {
- s = ptrace (PT_WIUSER,
- pid, calls[i].address, calls[i].new_word);
- if (s == -1)
- {
- if (wait_for_child == waiting)
- {
- with_reverse_video
- ({
- fprintf (log,
- "No more system call reports will be made\n");
- });
- break;
- }
-
- if (waiting == 0)
- with_reverse_video
- ({
- fprintf (log,
- "Waiting for child's child to exit or exec\n");
- });
-
- sleep (1);
- waiting += 1;
- goto retry;
- }
- }
- }
- else
- {
- s = ptrace (PT_WIUSER, pid, p->address, p->new_word);
- Check (s != -1, "couldn't write new trap word");
- }
- }
- }
- @EOF
-
- chmod 664 trapfile.c
-
- exit 0
- --
- Arndt Jonasson, ZYX Sweden AB, Styrmansgatan 6, 114 54 Stockholm, Sweden
- email address: aj@zyx.SE or <backbone>!mcvax!enea!zyx!aj
-