home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Crawly Crypt Collection 1
/
crawlyvol1.bin
/
program
/
compiler
/
szadb21b
/
source
/
src
/
stepping.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-11-13
|
17KB
|
621 lines
/* Copyright (c) 1990 by Sozobon, Limited. Authors: Anthony Howe,
* Michal Jaegermann
*
* Permission is granted to anyone to use this software for any purpose
* on any computer system, and to redistribute it freely, with the
* following restrictions:
* 1) No charge may be made other than reasonable charges for reproduction.
* 2) Modified versions must be clearly marked as such.
* 3) The authors are not responsible for any harmful consequences
* of using this software, even if they result from defects in it.
*
* Source code in this file consist of additons and modifications
* to an original szadb code by Johann Ruegg and Don Dugger and is
* an integral part of expanded version of szadb debugger.
*
* stepping.c 68000 Trace & Break Point Functions
* 23 Mar 90 ACH
* 1 Apr 90 MJ
* added temporary breakpoints
* separated next and jump accross branch
* added new instructions to skip
* added counts on breakpoints
* changed way in which return address is found
* set hooks for executing instructions on
* breakpoints
* 13 Nov 90 MJ
* modified code which searches for a function
* return address in order to marry an original
* method from prstack and ACH heuristic approach
*/
#include <stddef.h>
#include <setjmp.h>
#include "adb.h"
#define IN_STEPPING
#include "lang.h"
extern long bcount;
extern int lastc;
extern int lb_cur, lb_fill;
extern jmp_buf cont_buf;
#define BPT 0x4afc /* illegal op */
#define BP_VALID 1
#define BP_ACTIVE 2
/*** not used
#define JSR 0047200
#define JMP 0047300
** #define EA_MASK 0177700 ***//* mask out effective address */
typedef struct {
short *addr;
short instr;
union {
char *tmp_cmd[3];
struct {
long count;
long o_cnt;
char *cmdline;
} user;
} request;
int flag;
} break_point;
/* various storage for stepping commands */
static char bpstat[3] = {SILENT, SILENT, ON};
static char stp_req[3][LINESZ + 2];
static char findcmds[] = "=\"returns \";<d0=Xrr;<d0=p\n";
int print_regs;
/*
* The first position on the following list is used only for temporary
* breakpoints and is not available to a user.
*/
/*
* Initialize explicitely T_BPT - other are set to 0 by default.
* This union initialization is NOT portable, but Sozobon C accepts it.
*/
static break_point bpt_list[MAXBPTS + 1] = {
{(short *) 0, (short) 0, stp_req[0], stp_req[1], findcmds, 0}
};
#define T_BPT bpt_list[0]
extern char **bpt_cmds;
static break_point *
free_bpt (addr)
short *addr;
/*
* Find a free slot on a list of user breakpoints
*/
{
break_point *bp = &bpt_list[1];
break_point *last = &bpt_list[MAXBPTS + 1];
for (; bp < last; bp++)
if ((bp->flag & BP_VALID) == 0 || bp->addr == addr)
return (bp);
return (NULL);
} /* free_bpt */
static break_point *
find_bpt (addr)
short *addr;
/*
* Find if a user breakpoint is set at a given address
*/
{
break_point *bp = &bpt_list[1];
break_point *last = &bpt_list[MAXBPTS + 1];
for (; bp < last; bp++)
if (bp->flag & BP_VALID && bp->addr == addr)
return (bp);
return (NULL);
} /* find_bpt */
static char *
find_bcmd (bp, kind)
break_point *bp;
int kind;
/*
* Return an address of a buffer with commands which should be
* executed for a given breakpoint or NULL
*/
{
char *cmds;
int stat;
if (CM_CONT == kind) {
if (NULL != (cmds = bp->request.user.cmdline)) {
if (ON != *cmds++)
cmds = NULL;
}
}
else { /* temporary breakpoint set by a stepping
* command */
cmds = NULL;
if (SILENT != (stat = bpstat[--kind])) {
for (;;) {
if (ON == stat || (0 == kind && SILENT != stat)) {
cmds = T_BPT.request.tmp_cmd[kind];
break;
}
if (0 == kind)
break;
stat = bpstat[--kind];
}
}
}
return (cmds);
}
static void
exec_break (bp, kind)
break_point *bp;
int kind;
/*
* Execute commands associated with a given breakpoint
*/
{
extern registers regs[];
extern long dot;
if (CM_CONT == kind) {
if (NULL != bp) {
if (bp->request.user.count) {
--(bp->request.user.count);
longjmp (cont_buf, 1);
}
else {
bp->request.user.count = bp->request.user.o_cnt;
dot = (long) bp->addr;
src_line (find_bcmd (bp, kind));
prtf (BREAK_AT, bp->addr);
}
}
}
else { /* CM_STEP, CM_NEXT, CM_FINISH */
dot = *regs[PC].value;
src_line (find_bcmd ((break_point *) NULL, kind));
}
if (print_regs)
prregs ();
prbpt (dot = *regs[PC].value);
putchr ('\n');
return;
}
static void
check_break (kind)
int kind;
/*
* Kind indicates which stepping command caused break.
* Not used for traps of other kind.
*/
{
extern int lasttrap;
extern registers regs[];
extern char *tnm[];
void exec_break ();
switch (lasttrap) {
case 2:
case 3: /* bus & address errors */
prbuserr ();
break;
case 9:
exec_break (&T_BPT, kind);
break; /* trace */
case 10:
prt (PROCESS_EXIT);
seeerr ();
exit (0);
case 4: /* illegal instruction -- breakpoint */
/*
* Give priority to temporary breakpoints set by stepping commands.
* One may reprocess if first condition fires but we are going to
* miss breakpoint anyway if walked through by :n(ext) which reverted
* to :s(tep), so let be consistent.
*/
if ((T_BPT.flag & BP_VALID) &&
(T_BPT.addr == (short *) (*regs[PC].value))) {
exec_break (&T_BPT, kind);
}
else {
exec_break (find_bpt ((short *) (*regs[PC].value)), kind);
} /* if */
break;
default:
prtf ("trap: %s\n", tnm[lasttrap]);
} /* switch */
} /* check_break */
static void
bpts_on ()
/*
* Turn on all the break points, user and temporary,
* before full speed execution. First go through user
* breakpoints. If T_BPT is on the same location as
* a user breakpoint, then the second breakpoint turned
* on will get BPT (invalid) instruction stored in it.
* Turn off in a reverse order!
*/
{
int i = MAXBPTS;
break_point *bp = &bpt_list[1];
extern long getn ();
for (;;) {
if (bp->flag & BP_VALID) {
bp->flag |= BP_ACTIVE;
bp->instr = (short) getn (bp->addr, 2);
putn ((long) BPT, bp->addr, 2);
} /* if */
if (bp == &T_BPT)
break;
if (--i)
bp++;
else
bp = &T_BPT;
} /* for */
} /* bpts_on */
static void
bpts_off ()
/*
* Turn off all the break points after full speed execution.
* This restores the instructions so that they can be viewed normally.
* Temporary breakpoint has to be turned off before user breakpoints.
* Ordering is critical - otherwise you may got BPT (invalid)
* instructions sprinkled over your code
*/
{
break_point *bp = &bpt_list[0];
break_point *last = &bpt_list[MAXBPTS + 1];
do {
if (bp->flag & BP_ACTIVE) {
bp->flag &= ~BP_ACTIVE;
putn ((long) bp->instr, bp->addr, 2);
} /* if */
bp++;
} while (bp < last);
} /* bpts_off */
int
MakeReq (kind)
short kind;
/*
* Handle request line for stepping commands.
* Return 0 if there was no requests on a command line and non-zero otherwise.
*/
{
int idx = kind - 1;
int status;
extern int getrequs ();
if (BLANK == (status = getrequs (stp_req[idx]))) {
return 0;
}
if (REVERT == status) {
if (CM_FINISH != kind) {
status = ON;
}
else {
T_BPT.request.tmp_cmd[idx] = findcmds;
bpstat[idx] = ON;
status = IGNORE;
}
}
if (DOWN == status && 0 == idx) /* no way down */
status = ON;
if (ON == status || NEW == status) {
if (stp_req[idx][0] > ' ') { /* if there is something here */
bpstat[idx] = ON;
if (CM_FINISH == kind) {
T_BPT.request.tmp_cmd[idx] = stp_req[idx];
}
}
}
else if (IGNORE != status) {
bpstat[idx] = status;
}
return 1;
} /* MakeReq */
void
PrBptList ()
/*
* Print list of user breakpoints
*/
{
break_point *bp = &bpt_list[1];
break_point *last = &bpt_list[MAXBPTS + 1];
char *cmdline;
for (; bp < last; bp++) {
if (bp->flag & BP_VALID) {
prbpt (bp->addr);
if (bp->request.user.o_cnt) { /* print count only if > 1 */
align (46);
prtn (bp->request.user.o_cnt + 1L, 6);
}
putchr ('\n');
if (NULL != (cmdline = bp->request.user.cmdline)) {
if (ON == *cmdline++)
prtf (" %s", cmdline);
}
}
}
putchr ('\n');
if (NULL != (cmdline = find_bcmd ((break_point *) NULL, CM_FINISH)))
prtf (":f %s", cmdline);
if (NULL != (cmdline = find_bcmd ((break_point *) NULL, CM_NEXT)))
prtf (":n %s", cmdline);
if (NULL != (cmdline = find_bcmd ((break_point *) NULL, CM_STEP)))
prtf (":s %s", cmdline);
return;
} /* PrBptList */
bool
SetBpt (addr)
short *addr;
{
break_point *bp;
char c;
char *cmds;
int status;
extern int getrequs ();
if (bp = free_bpt (addr)) {
bp->addr = addr;
bp->request.user.count = bp->request.user.o_cnt = (bcount - 1L);
bp->flag |= BP_VALID;
if (NULL != bpt_cmds) {
cmds = bpt_cmds[(bp - bpt_list) - 1];
status = getrequs (&cmds[1]);
if (NEW == status) {
bp->request.user.cmdline = cmds;
*cmds = ON;
}
else if (0 != (cmds = bp->request.user.cmdline)) {
if (SILENT == status || BLANK == status)
*cmds = SILENT;
else if (IGNORE != status)
*cmds = ON;
}
}
status = TRUE;
}
else {
prt (TOO_MANY_BPT);
status = FALSE;
} /* if */
return (status);
} /* SetBpt */
bool
ClrBpt (addr)
short *addr;
{
break_point *bp;
if (bp = find_bpt (addr)) {
bp->flag = 0;
bp->request.user.cmdline = NULL;
return (TRUE);
}
else {
/* no breakpoint found to remove */
prt (NO_BPT);
return (FALSE);
} /* if */
} /* ClrBpt */
void
SingleStep (addr, kind)
short *addr;
int kind;
{
extern registers regs[];
*regs[PC].value = (long) addr;
ptrace (SSTEP, 0, 0, 0);
check_break (kind);
} /* SingleStep */
void
FullStep (addr, target, kind)
short *addr;
short *target;
int kind;
/*
* Execute full speed until breakpoint hit. Kind indicates a command
* which caused an execution of FullStep.
* For kind == CM_CONT breakpoints are taken from a table of user
* breakpoints, so target does not matter. For other kinds a temporary
* breakpoint is set and later removed at a location target. It is
* a responsibility of a caller to pass some reasonable argument.
*/
{
extern registers regs[];
if (CM_CONT != kind) { /* set a temporary breakpoint */
T_BPT.addr = target;
T_BPT.flag |= BP_VALID;
}
*regs[PC].value = (long) addr;
/* Make sure we're not sitting on a break point */
if (find_bpt (addr)) {
ptrace (SSTEP, 0, 0, 0);
addr = (short *) *regs[PC].value;
} /* if */
if (find_bpt (addr) == NULL) {
bpts_on ();
winswtch ();
ptrace (CONTINUE, 0, 0, 0);
winswtch ();
bpts_off ();
} /* if */
check_break (target == (short *) *regs[PC].value ? kind : CM_CONT);
if (CM_CONT != kind) { /* remove temporary breakpoint */
T_BPT.flag = 0;
}
} /* FullStep */
next_step next_list[] = {
{0060400, 0177777, 2}, /* bsr.l */
{0060400, 0177400, 1}, /* bsr.s */
{0047100, 0177760, 1}, /* trap #x */
{0041766, 0177777, 1}, /* trapv */
{0047271, 0177777, 3}, /* jsr abs.l */
{0047220, 0177770, 1}, /* jsr (a) */
{0047240, 0177740, 2}, /* jsr d(a) d(a,x) abs.w
* d(pc) d(pc,x) */
{0, 0, 0}
}; /* next_list */
next_step jump_list[] = {
{0060000, 0170377, 2}, /* bcc, bra long */
{0060000, 0170000, 1}, /* bcc, bra short */
{0050310, 0170370, 2}, /* dbcc */
{0047371, 0177777, 3}, /* jmp abs.l */
{0047320, 0177770, 1}, /* jmp (a) */
{0047340, 0177740, 2}, /* jmp d(a) d(a,x) abs.w
* d(pc) d(pc,x) */
{0, 0, 0}
}; /* jump_list */
void
NextStep (addr, skip_list)
short *addr;
next_step skip_list[];
/*
* If skip_list == next_list step to next instruction on the same call level.
* If skip_list == jump_list execute until pc reaches the next instruction
* in a text of a program.
* If skip_list none of the above all bets are off!!!
* After 'jump' adb() automatically restores default request to 'next'.
*/
{
next_step *ns;
short op_word = *addr;
extern putx (), prt ();
for (;;) {
for (ns = &skip_list[0]; 0 != ns->code; ns++) {
if ((op_word & ns->mask) == ns->code) {
/* Set temporary breakpoint at addr + ns->len. */
FullStep (addr, addr + ns->len, CM_NEXT);
return;
} /* if */
} /* for ns */
if (skip_list == next_list) {
SingleStep (addr, CM_NEXT);
return;
}
skip_list = next_list;
} /* for (;;) */
} /* NextStep */
void
FuncStep (addr)
/*
* Execute until we are out of the current function
*/
short *addr;
{
short *target;
short *Unwind();
extern registers regs[];
int at_sp = 1; /* this needed really by prstack() */
if (target = Unwind (&at_sp, addr, *(regs[SP].value), *(regs[FP].value))) {
FullStep (addr, target, CM_FINISH);
}
else {
/* no return address found */
prtf (NO_RETURN);
}
return;
} /* FuncStep */
short *
Unwind (where, addr, sp, fp)
int *where;
short *addr;
long sp;
long fp;
/*
* Search for a spot to return from a function which contains 'addr'.
* If a value at 'where' is 0 look only at frame pointer, otherwise
* try to get value examining stack pointer. In any case value at
* where will be reset if we had to look at frame pointer.
* Check if computed address falls into the text segment. If yes
* it is returned, otherwise the following heuristic is used:
* We assume that (1) all function calls lie between tbase and tbase + tlen
* (2) sp points somewhere below hitpa
* (3) sp grows down toward the text segment.
* We search from 'addr', by a word step, until hitpa. We try to find
* the first entry which looks like an address which lies within the
* text segment. We decree that to be the return address of the current
* function. If everything fails then we return NULL.
*
* Part of a code for finding a return address stolen from an original
* prstack(). Nasty type puns also inherited from there.
*/
{
extern base_page *bpage;
extern long getn (), atbranch ();
extern int atrts (), atlink ();
short *target;
short *tbase = (short *) bpage->p_tbase;
short *tend = tbase + bpage->p_tlen;
short *hitpa, *stp;
long brpc;
if (*where && (atlink (addr) || atrts (addr) ||
((brpc = atbranch (addr)) && atlink (brpc)))) {
target = (short *) getn (sp, 4);
}
else {
*where = 0;
target = (short *) getn (fp + 4, 4);
}
if (tbase <= target && target < tend)
return (target);
/* target doesn't look good - try harder */
hitpa = (short *) bpage->p_hitpa;
for (stp = (short *) sp; stp < hitpa; stp++) {
target = * (short **) stp;
if (tbase <= target && target < tend)
return (target);
}
/* no return address in sight */
return (NULL);
} /* Unwind */