home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / compiler / szadb21b / source / src / stepping.c < prev    next >
C/C++ Source or Header  |  1990-11-13  |  17KB  |  621 lines

  1. /* Copyright (c) 1990 by Sozobon, Limited.  Authors: Anthony Howe,
  2.  *                             Michal Jaegermann
  3.  *
  4.  * Permission is granted to anyone to use this software for any purpose
  5.  * on any computer system, and to redistribute it freely, with the
  6.  * following restrictions:
  7.  * 1) No charge may be made other than reasonable charges for reproduction.
  8.  * 2) Modified versions must be clearly marked as such.
  9.  * 3) The authors are not responsible for any harmful consequences
  10.  *    of using this software, even if they result from defects in it.
  11.  *
  12.  * Source code in this file consist of additons and modifications
  13.  * to an original szadb code by Johann Ruegg and Don Dugger and is
  14.  * an integral part of expanded version of szadb debugger.
  15.  *
  16.  *    stepping.c        68000 Trace & Break Point Functions
  17.  *        23 Mar 90    ACH
  18.  *          1 Apr 90       MJ
  19.  *                added temporary breakpoints
  20.  *                    separated next and jump accross branch
  21.  *                     added new instructions to skip
  22.  *                    added counts on breakpoints
  23.  *                    changed way in which return address is found
  24.  *                    set hooks for executing instructions on
  25.  *                    breakpoints
  26.  *              13 Nov 90       MJ
  27.  *                              modified code which searches for a function
  28.  *                              return address in order to marry an original
  29.  *                              method from prstack and ACH heuristic approach
  30.  */
  31.  
  32.  
  33. #include <stddef.h>
  34. #include <setjmp.h>
  35. #include "adb.h"
  36. #define IN_STEPPING
  37. #include "lang.h"
  38.  
  39. extern long     bcount;
  40. extern int      lastc;
  41. extern int      lb_cur, lb_fill;
  42. extern jmp_buf  cont_buf;
  43.  
  44. #define BPT        0x4afc    /* illegal op */
  45. #define BP_VALID    1
  46. #define BP_ACTIVE    2
  47.  
  48. /*** not used
  49. #define JSR        0047200
  50. #define JMP        0047300
  51.   ** #define EA_MASK        0177700     ***//* mask out effective  address */
  52.  
  53. typedef struct {
  54.     short          *addr;
  55.     short           instr;
  56.     union {
  57.     char           *tmp_cmd[3];
  58.     struct {
  59.         long            count;
  60.         long            o_cnt;
  61.         char           *cmdline;
  62.     }               user;
  63.     }               request;
  64.     int             flag;
  65. }               break_point;
  66.  
  67. /*  various storage for stepping commands */
  68.  
  69. static char     bpstat[3] = {SILENT, SILENT, ON};
  70. static char     stp_req[3][LINESZ + 2];
  71. static char     findcmds[] = "=\"returns \";<d0=Xrr;<d0=p\n";
  72. int             print_regs;
  73.  
  74. /*
  75.  * The first position on the following list is used only for temporary
  76.  * breakpoints and is not available to a user.
  77.  */
  78. /*
  79.  * Initialize explicitely T_BPT - other are set to 0 by default.
  80.  * This union initialization is NOT portable, but Sozobon C accepts it.
  81.  */
  82. static break_point bpt_list[MAXBPTS + 1] = {
  83.            {(short *) 0, (short) 0, stp_req[0], stp_req[1], findcmds, 0}
  84. };
  85. #define T_BPT        bpt_list[0]
  86.  
  87. extern char   **bpt_cmds;
  88.  
  89.  
  90. static break_point *
  91. free_bpt (addr)
  92.     short          *addr;
  93. /*
  94.  * Find a free slot on a list of user breakpoints
  95.  */
  96. {
  97.     break_point    *bp = &bpt_list[1];
  98.     break_point    *last = &bpt_list[MAXBPTS + 1];
  99.  
  100.     for (; bp < last; bp++)
  101.     if ((bp->flag & BP_VALID) == 0 || bp->addr == addr)
  102.         return (bp);
  103.  
  104.     return (NULL);
  105. }                /* free_bpt */
  106.  
  107.  
  108. static break_point *
  109. find_bpt (addr)
  110.     short          *addr;
  111. /*
  112.  * Find if a user breakpoint is set at a given address
  113.  */
  114. {
  115.     break_point    *bp = &bpt_list[1];
  116.     break_point    *last = &bpt_list[MAXBPTS + 1];
  117.  
  118.     for (; bp < last; bp++)
  119.     if (bp->flag & BP_VALID && bp->addr == addr)
  120.         return (bp);
  121.  
  122.     return (NULL);
  123. }                /* find_bpt */
  124.  
  125. static char    *
  126. find_bcmd (bp, kind)
  127.     break_point    *bp;
  128.     int             kind;
  129. /*
  130.  * Return an address of a buffer with commands which should be
  131.  * executed for a given breakpoint or NULL
  132.  */
  133. {
  134.     char           *cmds;
  135.     int             stat;
  136.  
  137.     if (CM_CONT == kind) {
  138.     if (NULL != (cmds = bp->request.user.cmdline)) {
  139.         if (ON != *cmds++)
  140.         cmds = NULL;
  141.     }
  142.     }
  143.     else {            /* temporary breakpoint set by a stepping
  144.                  * command */
  145.     cmds = NULL;
  146.     if (SILENT != (stat = bpstat[--kind])) {
  147.         for (;;) {
  148.         if (ON == stat || (0 == kind && SILENT != stat)) {
  149.             cmds = T_BPT.request.tmp_cmd[kind];
  150.             break;
  151.         }
  152.         if (0 == kind)
  153.             break;
  154.         stat = bpstat[--kind];
  155.         }
  156.     }
  157.     }
  158.     return (cmds);
  159. }
  160.  
  161. static void
  162. exec_break (bp, kind)
  163.     break_point    *bp;
  164.     int             kind;
  165. /*
  166.  * Execute commands associated with a given breakpoint
  167.  */
  168. {
  169.     extern registers regs[];
  170.     extern long     dot;
  171.  
  172.     if (CM_CONT == kind) {
  173.     if (NULL != bp) {
  174.         if (bp->request.user.count) {
  175.         --(bp->request.user.count);
  176.         longjmp (cont_buf, 1);
  177.         }
  178.         else {
  179.         bp->request.user.count = bp->request.user.o_cnt;
  180.         dot = (long) bp->addr;
  181.         src_line (find_bcmd (bp, kind));
  182.         prtf (BREAK_AT, bp->addr);
  183.         }
  184.     }
  185.     }
  186.     else {            /* CM_STEP, CM_NEXT, CM_FINISH */
  187.     dot = *regs[PC].value;
  188.     src_line (find_bcmd ((break_point *) NULL, kind));
  189.     }
  190.  
  191.     if (print_regs)
  192.     prregs ();
  193.     prbpt (dot = *regs[PC].value);
  194.     putchr ('\n');
  195.     return;
  196. }
  197.  
  198. static void
  199. check_break (kind)
  200.     int             kind;
  201. /*
  202.  * Kind indicates which stepping command caused break.
  203.  * Not used for traps of other kind.
  204.  */
  205. {
  206.     extern int      lasttrap;
  207.     extern registers regs[];
  208.     extern char    *tnm[];
  209.     void            exec_break ();
  210.  
  211.     switch (lasttrap) {
  212.     case 2:
  213.     case 3:            /* bus & address errors */
  214.     prbuserr ();
  215.     break;
  216.     case 9:
  217.     exec_break (&T_BPT, kind);
  218.     break;            /* trace */
  219.     case 10:
  220.     prt (PROCESS_EXIT);
  221.     seeerr ();
  222.     exit (0);
  223.     case 4:            /* illegal instruction -- breakpoint */
  224.  
  225.     /*
  226.      * Give priority to temporary breakpoints set by stepping commands.
  227.      * One may reprocess if first condition fires but we are going to
  228.      * miss breakpoint anyway if walked through by :n(ext) which reverted
  229.      * to :s(tep), so let be consistent.
  230.      */
  231.     if ((T_BPT.flag & BP_VALID) &&
  232.         (T_BPT.addr == (short *) (*regs[PC].value))) {
  233.         exec_break (&T_BPT, kind);
  234.     }
  235.     else {
  236.         exec_break (find_bpt ((short *) (*regs[PC].value)), kind);
  237.     }            /* if */
  238.     break;
  239.     default:
  240.     prtf ("trap: %s\n", tnm[lasttrap]);
  241.     }                /* switch */
  242. }                /* check_break */
  243.  
  244.  
  245. static void
  246. bpts_on ()
  247. /*
  248.  *    Turn on all the break points, user and temporary,
  249.  *    before full speed execution. First go through user
  250.  *      breakpoints.  If T_BPT is on the same location as
  251.  *      a user breakpoint, then the second breakpoint turned
  252.  *      on will get BPT (invalid) instruction stored in it.
  253.  *      Turn off in a reverse order!
  254.  */
  255. {
  256.     int             i = MAXBPTS;
  257.     break_point    *bp = &bpt_list[1];
  258.     extern long     getn ();
  259.  
  260.  
  261.     for (;;) {
  262.     if (bp->flag & BP_VALID) {
  263.         bp->flag |= BP_ACTIVE;
  264.         bp->instr = (short) getn (bp->addr, 2);
  265.         putn ((long) BPT, bp->addr, 2);
  266.     }            /* if */
  267.     if (bp == &T_BPT)
  268.         break;
  269.     if (--i)
  270.         bp++;
  271.     else
  272.         bp = &T_BPT;
  273.     }                /* for */
  274. }                /* bpts_on */
  275.  
  276.  
  277. static void
  278. bpts_off ()
  279. /*
  280.  *    Turn off all the break points after full speed execution.
  281.  *    This restores the instructions so that they can be viewed normally.
  282.  *      Temporary breakpoint has to be turned off before user breakpoints.
  283.  *      Ordering is critical - otherwise you may got BPT (invalid)
  284.  *      instructions sprinkled over your code
  285.  */
  286. {
  287.     break_point    *bp = &bpt_list[0];
  288.     break_point    *last = &bpt_list[MAXBPTS + 1];
  289.  
  290.     do {
  291.     if (bp->flag & BP_ACTIVE) {
  292.         bp->flag &= ~BP_ACTIVE;
  293.         putn ((long) bp->instr, bp->addr, 2);
  294.     }            /* if */
  295.     bp++;
  296.     } while (bp < last);
  297. }                /* bpts_off */
  298.  
  299.  
  300. int
  301. MakeReq (kind)
  302.     short           kind;
  303. /*
  304.  * Handle request line for stepping commands.
  305.  * Return 0 if there was no requests on a command line and non-zero otherwise.
  306.  */
  307. {
  308.     int             idx = kind - 1;
  309.     int             status;
  310.     extern int      getrequs ();
  311.  
  312.     if (BLANK == (status = getrequs (stp_req[idx]))) {
  313.     return 0;
  314.     }
  315.  
  316.     if (REVERT == status) {
  317.     if (CM_FINISH != kind) {
  318.         status = ON;
  319.     }
  320.     else {
  321.         T_BPT.request.tmp_cmd[idx] = findcmds;
  322.         bpstat[idx] = ON;
  323.         status = IGNORE;
  324.     }
  325.     }
  326.     if (DOWN == status && 0 == idx)    /* no way down */
  327.     status = ON;
  328.  
  329.     if (ON == status || NEW == status) {
  330.     if (stp_req[idx][0] > ' ') {    /* if there is something here */
  331.         bpstat[idx] = ON;
  332.         if (CM_FINISH == kind) {
  333.         T_BPT.request.tmp_cmd[idx] = stp_req[idx];
  334.         }
  335.     }
  336.     }
  337.     else if (IGNORE != status) {
  338.     bpstat[idx] = status;
  339.     }
  340.  
  341.     return 1;
  342. }                /* MakeReq */
  343.  
  344. void
  345. PrBptList ()
  346. /*
  347.  *   Print list of user breakpoints
  348.  */
  349. {
  350.     break_point    *bp = &bpt_list[1];
  351.     break_point    *last = &bpt_list[MAXBPTS + 1];
  352.     char           *cmdline;
  353.  
  354.     for (; bp < last; bp++) {
  355.     if (bp->flag & BP_VALID) {
  356.         prbpt (bp->addr);
  357.         if (bp->request.user.o_cnt) {    /* print count only if > 1 */
  358.         align (46);
  359.         prtn (bp->request.user.o_cnt + 1L, 6);
  360.         }
  361.         putchr ('\n');
  362.         if (NULL != (cmdline = bp->request.user.cmdline)) {
  363.         if (ON == *cmdline++)
  364.             prtf ("  %s", cmdline);
  365.         }
  366.     }
  367.     }
  368.     putchr ('\n');
  369.     if (NULL != (cmdline = find_bcmd ((break_point *) NULL, CM_FINISH)))
  370.     prtf (":f %s", cmdline);
  371.     if (NULL != (cmdline = find_bcmd ((break_point *) NULL, CM_NEXT)))
  372.     prtf (":n %s", cmdline);
  373.     if (NULL != (cmdline = find_bcmd ((break_point *) NULL, CM_STEP)))
  374.     prtf (":s %s", cmdline);
  375.     return;
  376. }                /* PrBptList */
  377.  
  378.  
  379. bool
  380. SetBpt (addr)
  381.     short          *addr;
  382. {
  383.     break_point    *bp;
  384.     char            c;
  385.     char           *cmds;
  386.     int             status;
  387.     extern int      getrequs ();
  388.  
  389.     if (bp = free_bpt (addr)) {
  390.     bp->addr = addr;
  391.     bp->request.user.count = bp->request.user.o_cnt = (bcount - 1L);
  392.     bp->flag |= BP_VALID;
  393.     if (NULL != bpt_cmds) {
  394.         cmds = bpt_cmds[(bp - bpt_list) - 1];
  395.         status = getrequs (&cmds[1]);
  396.         if (NEW == status) {
  397.         bp->request.user.cmdline = cmds;
  398.         *cmds = ON;
  399.         }
  400.         else if (0 != (cmds = bp->request.user.cmdline)) {
  401.         if (SILENT == status || BLANK == status)
  402.             *cmds = SILENT;
  403.         else if (IGNORE != status)
  404.             *cmds = ON;
  405.         }
  406.     }
  407.     status = TRUE;
  408.     }
  409.     else {
  410.     prt (TOO_MANY_BPT);
  411.     status = FALSE;
  412.     }                /* if */
  413.  
  414.     return (status);
  415. }                /* SetBpt */
  416.  
  417.  
  418. bool
  419. ClrBpt (addr)
  420.     short          *addr;
  421. {
  422.     break_point    *bp;
  423.     if (bp = find_bpt (addr)) {
  424.     bp->flag = 0;
  425.     bp->request.user.cmdline = NULL;
  426.     return (TRUE);
  427.     }
  428.     else {
  429.     /* no breakpoint found to remove */
  430.     prt (NO_BPT);
  431.     return (FALSE);
  432.     }                /* if */
  433. }                /* ClrBpt */
  434.  
  435.  
  436. void
  437. SingleStep (addr, kind)
  438.     short          *addr;
  439.     int             kind;
  440. {
  441.     extern registers regs[];
  442.     *regs[PC].value = (long) addr;
  443.     ptrace (SSTEP, 0, 0, 0);
  444.     check_break (kind);
  445. }                /* SingleStep */
  446.  
  447.  
  448. void
  449. FullStep (addr, target, kind)
  450.     short          *addr;
  451.     short          *target;
  452.     int             kind;
  453. /*
  454.  * Execute full speed until breakpoint hit. Kind indicates a command
  455.  * which caused an execution of FullStep.
  456.  * For kind == CM_CONT breakpoints are taken from a table of user
  457.  * breakpoints, so target does not matter.  For other kinds a temporary
  458.  * breakpoint is set and later removed at a location target.  It is
  459.  * a responsibility of a caller to pass some reasonable argument.
  460.  */
  461. {
  462.     extern registers regs[];
  463.  
  464.     if (CM_CONT != kind) {    /* set a temporary breakpoint */
  465.     T_BPT.addr = target;
  466.     T_BPT.flag |= BP_VALID;
  467.     }
  468.  
  469.     *regs[PC].value = (long) addr;
  470.     /* Make sure we're not sitting on a break point */
  471.     if (find_bpt (addr)) {
  472.     ptrace (SSTEP, 0, 0, 0);
  473.     addr = (short *) *regs[PC].value;
  474.     }                /* if */
  475.     if (find_bpt (addr) == NULL) {
  476.     bpts_on ();
  477.     winswtch ();
  478.     ptrace (CONTINUE, 0, 0, 0);
  479.     winswtch ();
  480.     bpts_off ();
  481.     }                /* if */
  482.     check_break (target == (short *) *regs[PC].value ? kind : CM_CONT);
  483.     if (CM_CONT != kind) {    /* remove temporary breakpoint */
  484.     T_BPT.flag = 0;
  485.     }
  486. }                /* FullStep */
  487.  
  488. next_step       next_list[] = {
  489.                    {0060400, 0177777, 2},    /* bsr.l */
  490.                    {0060400, 0177400, 1},    /* bsr.s */
  491.                    {0047100, 0177760, 1},    /* trap #x */
  492.                    {0041766, 0177777, 1},    /* trapv */
  493.                    {0047271, 0177777, 3},    /* jsr abs.l */
  494.                    {0047220, 0177770, 1},    /* jsr (a) */
  495.                    {0047240, 0177740, 2},    /* jsr d(a) d(a,x) abs.w
  496.                              * d(pc) d(pc,x) */
  497.                    {0, 0, 0}
  498. };                /* next_list */
  499.  
  500. next_step       jump_list[] = {
  501.                    {0060000, 0170377, 2},    /* bcc, bra long */
  502.                    {0060000, 0170000, 1},    /* bcc, bra short */
  503.                    {0050310, 0170370, 2},    /* dbcc */
  504.                    {0047371, 0177777, 3},    /* jmp abs.l */
  505.                    {0047320, 0177770, 1},    /* jmp (a) */
  506.                    {0047340, 0177740, 2},    /* jmp d(a) d(a,x) abs.w
  507.                              * d(pc) d(pc,x) */
  508.                    {0, 0, 0}
  509. };                /* jump_list */
  510.  
  511. void
  512. NextStep (addr, skip_list)
  513.     short          *addr;
  514.     next_step       skip_list[];
  515. /*
  516.  * If skip_list == next_list step to next instruction on the same call level.
  517.  * If skip_list == jump_list execute until pc reaches the next instruction
  518.  * in a text of a program.
  519.  * If skip_list none of the above all bets are off!!!
  520.  * After 'jump' adb() automatically restores default request to 'next'.
  521.  */
  522. {
  523.     next_step      *ns;
  524.     short           op_word = *addr;
  525.     extern          putx (), prt ();
  526.  
  527.     for (;;) {
  528.     for (ns = &skip_list[0]; 0 != ns->code; ns++) {
  529.         if ((op_word & ns->mask) == ns->code) {
  530.         /* Set temporary breakpoint at addr + ns->len. */
  531.         FullStep (addr, addr + ns->len, CM_NEXT);
  532.         return;
  533.         }            /* if */
  534.     }            /* for ns */
  535.     if (skip_list == next_list) {
  536.         SingleStep (addr, CM_NEXT);
  537.         return;
  538.     }
  539.     skip_list = next_list;
  540.     }                /* for (;;) */
  541. }                /* NextStep */
  542.  
  543.  
  544. void
  545. FuncStep (addr)
  546. /*
  547.  *  Execute until we are out of the current function
  548.  */
  549.     short          *addr;
  550. {
  551.     short          *target;
  552.     short          *Unwind();
  553.     extern registers regs[];
  554.     int             at_sp = 1; /* this needed really by prstack() */
  555.  
  556.     if (target = Unwind (&at_sp, addr, *(regs[SP].value), *(regs[FP].value))) {
  557.     FullStep (addr, target, CM_FINISH);
  558.     }
  559.     else {
  560.     /* no return address found */
  561.     prtf (NO_RETURN);
  562.     }
  563.     return;
  564. }                /* FuncStep */
  565.  
  566. short *
  567. Unwind (where, addr, sp, fp)
  568.     int            *where;
  569.     short          *addr;
  570.     long            sp;
  571.     long            fp;
  572. /*
  573.  * Search for a spot to return from a function which contains 'addr'.
  574.  * If a value at 'where' is 0 look only at frame pointer, otherwise
  575.  * try to get value examining stack pointer. In any case value at
  576.  * where will be reset if we had to look at frame pointer.
  577.  * Check if computed address falls into the text segment.  If yes
  578.  * it is returned, otherwise the following heuristic is used: 
  579.  * We assume that (1) all function calls lie between tbase and tbase + tlen
  580.  *                (2) sp points somewhere below hitpa
  581.  *                (3) sp grows down toward the text segment.
  582.  * We search from 'addr', by a word step, until hitpa. We try to find
  583.  * the first entry which looks like an address which lies within the
  584.  * text segment.  We decree that to be the return address of the current
  585.  * function.  If everything fails then we return NULL.
  586.  *
  587.  * Part of a code for finding a return address stolen from an original
  588.  * prstack(). Nasty type puns also inherited from there.
  589.  */
  590. {
  591.     extern base_page *bpage;
  592.     extern long     getn (), atbranch ();
  593.     extern int      atrts (), atlink ();
  594.     short          *target;
  595.     short          *tbase = (short *) bpage->p_tbase;
  596.     short          *tend  = tbase +  bpage->p_tlen;
  597.     short          *hitpa, *stp;
  598.     long            brpc;
  599.  
  600.     if (*where && (atlink (addr) || atrts (addr) ||
  601.     ((brpc = atbranch (addr)) && atlink (brpc)))) {
  602.     target = (short *) getn (sp, 4);
  603.     }
  604.     else {
  605.     *where = 0;
  606.     target = (short *) getn (fp + 4, 4);
  607.     }
  608.  
  609.     if (tbase <= target && target < tend)
  610.     return (target);
  611.     /* target doesn't look good - try harder */
  612.     hitpa = (short *) bpage->p_hitpa;
  613.     for (stp = (short *) sp; stp < hitpa; stp++) {
  614.         target = * (short **) stp;
  615.     if (tbase <= target && target < tend)
  616.         return (target);
  617.     }
  618.     /* no return address in sight */
  619.     return (NULL);
  620. }                /* Unwind */
  621.