* Copyright (c) 1980, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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
static char sccsid
[] = "@(#)ptrace.c 8.1 (Berkeley) 6/6/93";
* routines for tracing the execution of a process
* The system call "ptrace" does all the work, these
* routines just try to interface easily to it.
# define U_AR0 (14*sizeof(int))
* This magic macro enables us to look at the process' registers
* in its user structure. Very gross.
#if defined(vax) || defined(tahoe)
# define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) ))
# define regloc(reg) (ar0val + ( sizeof(int) * (reg) ))
#define WMASK (~(sizeof(WORD) - 1))
#define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
#define ischild(pid) ((pid) == 0)
#define traceme() ptrace(0, 0, 0, 0)
#define setrep(n) (1 << ((n)-1))
#define istraced(p) (p->sigset&setrep(p->signo))
* ptrace options (specified in first argument)
#define UREAD 3 /* read from process's user structure */
#define UWRITE 6 /* write to process's user structure */
#define IREAD 1 /* read from process's instruction space */
#define IWRITE 4 /* write to process's instruction space */
#define DREAD 2 /* read from process's data space */
#define DWRITE 5 /* write to process's data space */
#define CONT 7 /* continue stopped process */
#define SSTEP 9 /* continue for approximately one instruction */
#define PKILL 8 /* terminate the process */
* Start up a new process by forking and exec-ing the
* given argument list, returning when the process is loaded
* and ready to execute. The PROCESS information (pointed to
* by the first argument) is appropriately filled.
* If the given PROCESS structure is associated with an already running
* process, we terminate it.
pstart(p
, cmd
, argv
, infile
, outfile
)
if (p
->pid
!= 0) { /* child already running? */
ptrace(PKILL
, p
->pid
, 0, 0); /* ... kill it! */
psigtrace(p
, SIGTRAP
, TRUE
);
if ((p
->pid
= fork()) == -1) {
if ((in
= fopen(infile
, "r")) == NIL
) {
printf("can't read %s\n", infile
);
if ((out
= fopen(outfile
, "w")) == NIL
) {
printf("can't write %s\n", outfile
);
panic("can't exec %s", argv
[0]);
* Continue a stopped process. The argument points to a PROCESS structure.
* Before the process is restarted it's user area is modified according to
* the values in the structure. When this routine finishes,
* the structure has the new values from the process's user area.
* Pcont terminates when the process stops with a signal pending that
* is being traced (via psigtrace), or when the process terminates.
error("program not active");
if (ptrace(CONT
, p
->pid
, p
->pc
, p
->signo
) < 0) {
panic("can't continue process");
} while (p
->status
== STOPPED
&& !istraced(p
));
* single step as best ptrace can
ptrace(SSTEP
, p
->pid
, p
->pc
, p
->signo
);
* Return from execution when the given signal is pending.
p
->sigset
|= setrep(sig
);
p
->sigset
&= ~setrep(sig
);
* Don't catch any signals.
* Particularly useful when letting a process finish uninhibited (i.e. px).
* turn off attention to signals not being caught
LOCAL
void *onintr
, *onquit
;
onintr
= signal(SIGINT
, SIG_IGN
);
onquit
= signal(SIGQUIT
, SIG_IGN
);
* turn back on attention to signals
(void) signal(SIGINT
, onintr
);
(void) signal(SIGQUIT
, onquit
);
* get PROCESS information from process's user area
R0
, R1
, R2
, R3
, R4
, R5
, R6
, R7
, R8
, R9
, R10
, R11
,
R0
, R1
, R2
, R3
, R4
, R5
, R6
, R7
, R8
, R9
, R10
, R11
, R12
,
R0
, R1
, R2
, R3
, R4
, R5
, R6
, R7
, AR0
, AR1
, AR2
, AR3
, AR4
, AR5
,
p
->signo
= (status
&0177);
p
->exitval
= ((status
>> 8)&0377);
if (p
->signo
== STOPPED
) {
#if !defined(vax) && !defined(tahoe)
ar0val
= ptrace(UREAD
, p
->pid
, U_AR0
, 0);
for (i
= 0; i
< NREG
; i
++) {
p
->reg
[i
] = ptrace(UREAD
, p
->pid
, regloc(rloc
[i
]), 0);
#if defined(vax) || defined(tahoe)
p
->fp
= p
->ofp
= ptrace(UREAD
, p
->pid
, regloc(FP
), 0);
p
->sp
= p
->osp
= ptrace(UREAD
, p
->pid
, regloc(SP
), 0);
p
->pc
= p
->opc
= ptrace(UREAD
, p
->pid
, regloc(PC
), 0);
p
->ap
= p
->oap
= ptrace(UREAD
, p
->pid
, regloc(AP
), 0);
p
->fp
= p
->ofp
= ptrace(UREAD
, p
->pid
, regloc(AR6
), 0);
p
->sp
= p
->osp
= ptrace(UREAD
, p
->pid
, regloc(SP
), 0);
p
->pc
= p
->opc
= ptrace(UREAD
, p
->pid
, regloc(PC
), 0);
* set process's user area information from given PROCESS structure
for (i
= 0; i
< NREG
; i
++) {
if ((r
= p
->reg
[i
]) != p
->oreg
[i
]) {
ptrace(UWRITE
, p
->pid
, regloc(rloc
[i
]), r
);
if ((r
= p
->fp
) != p
->ofp
) {
ptrace(UWRITE
, p
->pid
, regloc(FP
), r
);
if ((r
= p
->ap
) != p
->oap
) {
ptrace(UWRITE
, p
->pid
, regloc(AP
), r
);
if ((r
= p
->fp
) != p
->ofp
) {
ptrace(UWRITE
, p
->pid
, regloc(AR6
), r
);
if ((r
= p
->sp
) != p
->osp
) {
ptrace(UWRITE
, p
->pid
, regloc(SP
), r
);
if ((r
= p
->pc
) != p
->opc
) {
ptrace(UWRITE
, p
->pid
, regloc(PC
), r
);
* Structure for reading and writing by words, but dealing with bytes.
BYTE pbyte
[sizeof(WORD
)];
* Read (write) from (to) the process' address space.
* We must deal with ptrace's inability to look anywhere other
* than at a word boundary.
pio(p
, op
, seg
, buff
, addr
, nbytes
)
register ADDRESS newaddr
;
if (p
->status
!= STOPPED
) {
error("program is not active");
wordaddr
= (newaddr
&WMASK
);
if (wordaddr
!= newaddr
) {
w
.pword
= fetch(p
, seg
, wordaddr
);
for (i
= newaddr
- wordaddr
; i
<sizeof(WORD
) && nbytes
>0; i
++) {
store(p
, seg
, wordaddr
, w
.pword
);
newaddr
= wordaddr
+ sizeof(WORD
);
byteoff
= (nbytes
&(~WMASK
));
w
.pword
= fetch(p
, seg
, newaddr
);
for (k
= 0; k
< sizeof(WORD
); k
++) {
for (k
= 0; k
< sizeof(WORD
); k
++) {
store(p
, seg
, newaddr
, w
.pword
);
w
.pword
= fetch(p
, seg
, newaddr
);
for (i
= 0; i
< byteoff
; i
++) {
store(p
, seg
, newaddr
, w
.pword
);
* Get a word from a process at the given address.
* The address is assumed to be on a word boundary.
* We use a simple cache scheme to avoid redundant references to
* the instruction space (which is assumed to be pure). In the
* case of px, the "instruction" space lies between ENDOFF and
* It is necessary to use a write-through scheme so that
* breakpoints right next to each other don't interfere.
LOCAL WORD
fetch(p
, seg
, addr
)
panic("tried to fetch from px i-space");
if (addr
>= ENDOFF
&& addr
< ENDOFF
+ objsize
) {
wp
= &p
->word
[cachehash(addr
)];
if (addr
== 0 || wp
->addr
!= addr
) {
w
= ptrace(DREAD
, p
->pid
, addr
, 0);
w
= ptrace(DREAD
, p
->pid
, addr
, 0);
panic("fetch: bad seg %d", seg
);
* Put a word into the process' address space at the given address.
* The address is assumed to be on a word boundary.
LOCAL
store(p
, seg
, addr
, data
)
wp
= &p
->word
[cachehash(addr
)];
ptrace(IWRITE
, p
->pid
, addr
, data
);
if (addr
>= ENDOFF
&& addr
< ENDOFF
+ objsize
) {
wp
= &p
->word
[cachehash(addr
)];
ptrace(DWRITE
, p
->pid
, addr
, data
);
panic("store: bad seg %d", seg
);
* Initialize the instruction cache for a process.
* This is particularly necessary after the program has been remade.
for (i
= 0; i
< CSIZE
; i
++) {
process
->word
[i
].addr
= 0;
* Swap file numbers so as to redirect standard input and output.
LOCAL
fswap(oldfd
, newfd
)
if (((status
== (SIGILL
<< 8) | STOPPED
) ||
(status
== (SIGTRAP
<< 8) | STOPPED
))) {
panic("px would not return to interpreter");
} while(INTFP
&& p
->fp
!= INTFP
);