This is the mail archive of the gdb-patches@sourceware.cygnus.com mailing list for the GDB project. See the GDB home page for more information.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
This patch adds support for debugging functions where the frame pointer has been eliminated and the stack pointer is unchanging. ChangeLog: Mon Nov 2 23:39:37 EST 1998 John Wehle (john@feith.com) * i386-tdep.c (codestream_fill): Use read_memory_nobpt. (i386_analyze_prologue, i386_frame_chain, i386_init_extra_frame_info): New functions. (i386_get_frame_setup, i386_frame_find_saved_regs, i386_skip_prologue, i386_pop_frame): Support prologues which don't setup a frame pointer. * tm-i386.h (EXTRA_FRAME_INFO, INIT_EXTRA_FRAME_INFO, INIT_FRAME_PC): Define. (FRAME_CHAIN): Call i386_frame_chain. (FRAME_FIND_SAVED_REGS, FRAME_SAVED_PC): Use fsr to locate the saved registers. (FRAMELESS_FUNCTION_INVOCATION): Don't define. (i386_init_extra_frame_info, i386_frame_chain): Add prototypes. -- John Wehle ------------------8<------------------------8<------------------------ *** gdb/i386-tdep.c.ORIGINAL Sat Apr 11 01:39:37 1998 --- gdb/i386-tdep.c Mon Nov 2 23:23:59 1998 *************** Foundation, Inc., 59 Temple Place - Suit *** 28,37 **** #include "symtab.h" #include "gdbcmd.h" - static long i386_get_frame_setup PARAMS ((CORE_ADDR)); - - static void i386_follow_jump PARAMS ((void)); - static void codestream_read PARAMS ((unsigned char *, int)); static void codestream_seek PARAMS ((CORE_ADDR)); --- 28,33 ---- *************** static unsigned char *** 72,82 **** codestream_fill (peek_flag) int peek_flag; { codestream_addr = codestream_next_addr; codestream_next_addr += CODESTREAM_BUFSIZ; codestream_off = 0; codestream_cnt = CODESTREAM_BUFSIZ; ! read_memory (codestream_addr, (char *) codestream_buf, CODESTREAM_BUFSIZ); if (peek_flag) return (codestream_peek()); --- 68,83 ---- codestream_fill (peek_flag) int peek_flag; { + int status; + codestream_addr = codestream_next_addr; codestream_next_addr += CODESTREAM_BUFSIZ; codestream_off = 0; codestream_cnt = CODESTREAM_BUFSIZ; ! status = read_memory_nobpt (codestream_addr, ! (char *) codestream_buf, CODESTREAM_BUFSIZ); ! if (status != 0) ! memory_error (status, codestream_addr); if (peek_flag) return (codestream_peek()); *************** i386_follow_jump () *** 161,185 **** } /* ! * find & return amound a local space allocated, and advance codestream to ! * first register push (if any) * ! * if entry sequence doesn't make sense, return -1, and leave ! * codestream pointer random */ ! static long ! i386_get_frame_setup (pc) ! CORE_ADDR pc; { unsigned char op; - codestream_seek (pc); - - i386_follow_jump (); - op = codestream_get (); - if (op == 0x58) /* popl %eax */ { /* --- 162,186 ---- } /* ! * Determine the amount of local space allocated, and advance codestream to ! * first register push (if any). * ! * If the entry sequence doesn't make sense, then assume it is a frameless ! * function with no local space. */ ! #define MY_FRAME_IN_SP 0x1 ! #define MY_FRAME_IN_FP 0x2 ! #define NO_MORE_FRAMES 0x4 ! #define INCOMPLETE_FRAME 0x8 ! ! static void ! i386_get_frame_setup (fi) ! struct frame_info *fi; { unsigned char op; op = codestream_get (); if (op == 0x58) /* popl %eax */ { /* *************** i386_get_frame_setup (pc) *** 214,275 **** if (op == 0x55) /* pushl %ebp */ { /* check for movl %esp, %ebp - can be written two ways */ ! switch (codestream_get ()) ! { ! case 0x8b: ! if (codestream_get () != 0xec) ! return (-1); ! break; ! case 0x89: ! if (codestream_get () != 0xe5) ! return (-1); ! break; ! default: ! return (-1); ! } ! /* check for stack adjustment ! * ! * subl $XXX, %esp ! * ! * note: you can't subtract a 16 bit immediate ! * from a 32 bit reg, so we don't have to worry ! * about a data16 prefix ! */ ! op = codestream_peek (); ! if (op == 0x83) ! { ! /* subl with 8 bit immed */ ! codestream_get (); ! if (codestream_get () != 0xec) ! /* Some instruction starting with 0x83 other than subl. */ ! { ! codestream_seek (codestream_tell () - 2); ! return 0; ! } ! /* subl with signed byte immediate ! * (though it wouldn't make sense to be negative) ! */ ! return (codestream_get()); ! } ! else if (op == 0x81) ! { ! char buf[4]; ! /* Maybe it is subl with 32 bit immedediate. */ ! codestream_get(); ! if (codestream_get () != 0xec) ! /* Some instruction starting with 0x81 other than subl. */ ! { ! codestream_seek (codestream_tell () - 2); ! return 0; ! } ! /* It is subl with 32 bit immediate. */ ! codestream_read ((unsigned char *)buf, 4); ! return extract_signed_integer (buf, 4); ! } ! else ! { ! return (0); } } else if (op == 0xc8) { --- 215,232 ---- if (op == 0x55) /* pushl %ebp */ { /* check for movl %esp, %ebp - can be written two ways */ ! unsigned char buf[2]; ! static unsigned char proto1[2] = { 0x8b,0xec }; ! static unsigned char proto2[2] = { 0x89,0xe5 }; ! codestream_read (buf, sizeof(buf)); ! if (memcmp (buf, proto1, 2) != 0 ! && memcmp (buf, proto2, 2) != 0) ! { ! /* frameless function with no local space */ ! codestream_seek (codestream_tell () - 3); ! return; } + fi->status = MY_FRAME_IN_FP; } else if (op == 0xc8) { *************** i386_get_frame_setup (pc) *** 277,285 **** /* enter instruction: arg is 16 bit unsigned immed */ codestream_read ((unsigned char *)buf, 2); codestream_get (); /* flush final byte of enter instruction */ ! return extract_unsigned_integer (buf, 2); } ! return (-1); } /* Return number of args passed to a frame. --- 234,287 ---- /* enter instruction: arg is 16 bit unsigned immed */ codestream_read ((unsigned char *)buf, 2); codestream_get (); /* flush final byte of enter instruction */ ! fi->status = MY_FRAME_IN_FP; ! fi->stack_size = extract_unsigned_integer (buf, 2); ! return; } ! else ! codestream_seek (codestream_tell () - 1); ! ! /* check for stack adjustment ! * ! * subl $XXX, %esp ! * ! * note: you can't subtract a 16 bit immediate ! * from a 32 bit reg, so we don't have to worry ! * about a data16 prefix ! */ ! op = codestream_peek (); ! if (op == 0x83) ! { ! /* subl with 8 bit immed */ ! codestream_get (); ! if (codestream_get () != 0xec) ! /* Some instruction starting with 0x83 other than subl. */ ! { ! codestream_seek (codestream_tell () - 2); ! return; ! } ! /* subl with signed byte immediate ! * (though it wouldn't make sense to be negative) ! */ ! fi->stack_size = codestream_get(); ! } ! else if (op == 0x81) ! { ! char buf[4]; ! /* Maybe it is subl with 32 bit immedediate. */ ! codestream_get(); ! if (codestream_get () != 0xec) ! /* Some instruction starting with 0x81 other than subl. */ ! { ! codestream_seek (codestream_tell () - 2); ! return; ! } ! /* It is subl with 32 bit immediate. */ ! codestream_read ((unsigned char *)buf, 4); ! fi->stack_size = extract_signed_integer (buf, 4); ! } ! ! return; } /* Return number of args passed to a frame. *************** i386_frame_num_args (fi) *** 359,372 **** } /* ! * parse the first few instructions of the function to see ! * what registers were stored. * * We handle these cases: * * The startup sequence can be at the start of the function, * or the function can start with a branch to startup code at the end. * * %ebp can be set up with either the 'enter' instruction, or * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful, * but was once used in the sys5 compiler) --- 361,447 ---- } /* ! * Determine where the registers were stored. ! */ ! ! static void ! i386_frame_find_saved_regs (fi) ! struct frame_info *fi; ! { ! unsigned char op; ! CORE_ADDR adr; ! int i; ! int offset; ! ! /* ! * First determine the offset of each register that has ! * been placed on the stack. ! */ ! ! offset = 4 + fi->stack_size; ! ! for (i = 0; i < 8; i++) ! { ! op = codestream_peek (); ! if (op < 0x50 || op > 0x57) ! break; ! codestream_get (); ! #ifdef I386_REGNO_TO_SYMMETRY ! /* Dynix uses different internal numbering. Ick. */ ! fi->fsr.regs[I386_REGNO_TO_SYMMETRY(op - 0x50)] = offset; ! #else ! fi->fsr.regs[op - 0x50] = offset; ! #endif ! offset += 4; ! } ! ! /* ! * Then determine where some important registers are located ! * and fix fi->frame if it's bogus. ! */ ! ! if (fi->status == MY_FRAME_IN_FP) ! { ! fi->fsr.regs[FP_REGNUM] = fi->frame; ! fi->fsr.regs[PC_REGNUM] = fi->frame + 4; ! } ! else if (fi->status == MY_FRAME_IN_SP) ! { ! /* Fix fi->frame if it's bogus at this point. */ ! if (fi->next == NULL) ! fi->frame = read_sp() + (offset - 4); ! fi->fsr.regs[PC_REGNUM] = fi->frame; ! } ! ! /* ! * Finally determine the actual location of each register ! * that has been placed on the stack. ! */ ! ! for (i = 0; i < 8; i++) ! { ! #ifdef I386_REGNO_TO_SYMMETRY ! /* Dynix uses different internal numbering. Ick. */ ! if (fi->fsr.regs[I386_REGNO_TO_SYMMETRY(i)]) ! fi->fsr.regs[I386_REGNO_TO_SYMMETRY(i)] = ! fi->frame - fi->fsr.regs[I386_REGNO_TO_SYMMETRY(i)]; ! #else ! if (fi->fsr.regs[i]) ! fi->fsr.regs[i] = fi->frame - fi->fsr.regs[i]; ! #endif ! } ! } ! ! /* ! * Parse the prologue. * * We handle these cases: * * The startup sequence can be at the start of the function, * or the function can start with a branch to startup code at the end. * + * The function can be frameless. + * * %ebp can be set up with either the 'enter' instruction, or * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful, * but was once used in the sys5 compiler) *************** i386_frame_num_args (fi) *** 374,448 **** * Local space is allocated just below the saved %ebp by either the * 'enter' instruction, or by 'subl $<size>, %esp'. 'enter' has * a 16 bit unsigned argument for space to allocate, and the ! * 'addl' instruction could have either a signed byte, or * 32 bit immediate. * * Next, the registers used by this function are pushed. In * the sys5 compiler they will always be in the order: %edi, %esi, %ebx * (and sometimes a harmless bug causes it to also save but not restore %eax); ! * however, the code below is willing to see the pushes in any order, * and will handle up to 8 of them. * * If the setup sequence is at the end of the function, then the * next instruction will be a branch back to the start. */ ! void ! i386_frame_find_saved_regs (fip, fsrp) ! struct frame_info *fip; ! struct frame_saved_regs *fsrp; { ! long locals = -1; unsigned char op; ! CORE_ADDR dummy_bottom; ! CORE_ADDR adr; ! CORE_ADDR pc; ! int i; ! ! memset (fsrp, 0, sizeof *fsrp); ! ! /* if frame is the end of a dummy, compute where the ! * beginning would be ! */ ! dummy_bottom = fip->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH; ! ! /* check if the PC is in the stack, in a dummy frame */ ! if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) { ! /* all regs were saved by push_call_dummy () */ ! adr = fip->frame; ! for (i = 0; i < NUM_REGS; i++) { ! adr -= REGISTER_RAW_SIZE (i); ! fsrp->regs[i] = adr; } - return; } ! pc = get_pc_function_start (fip->pc); ! if (pc != 0) ! locals = i386_get_frame_setup (pc); ! ! if (locals >= 0) { ! adr = fip->frame - 4 - locals; ! for (i = 0; i < 8; i++) ! { ! op = codestream_get (); ! if (op < 0x50 || op > 0x57) ! break; #ifdef I386_REGNO_TO_SYMMETRY ! /* Dynix uses different internal numbering. Ick. */ ! fsrp->regs[I386_REGNO_TO_SYMMETRY(op - 0x50)] = adr; #else ! fsrp->regs[op - 0x50] = adr; #endif ! adr -= 4; ! } } - - fsrp->regs[PC_REGNUM] = fip->frame + 4; - fsrp->regs[FP_REGNUM] = fip->frame; } /* return pc of first real instruction */ --- 449,604 ---- * Local space is allocated just below the saved %ebp by either the * 'enter' instruction, or by 'subl $<size>, %esp'. 'enter' has * a 16 bit unsigned argument for space to allocate, and the ! * 'subl' instruction could have either a signed byte, or * 32 bit immediate. * * Next, the registers used by this function are pushed. In * the sys5 compiler they will always be in the order: %edi, %esi, %ebx * (and sometimes a harmless bug causes it to also save but not restore %eax); ! * however, the code is willing to see the pushes in any order, * and will handle up to 8 of them. * * If the setup sequence is at the end of the function, then the * next instruction will be a branch back to the start. */ ! static void ! i386_analyze_prologue (fi, skip_prologue) ! struct frame_info *fi; ! int skip_prologue; { ! CORE_ADDR func_addr, func_end; ! char *name; unsigned char op; ! int status; ! ! /* Find the start of this function. */ ! status = find_pc_partial_function (fi->pc, &name, &func_addr, &func_end); ! ! /* Do nothing if we couldn't find the start of this function. */ ! if (status == 0) ! return; ! ! /* If we're in start, then give up. */ ! if (strcmp (name, "start") == 0) { ! fi->status = NO_MORE_FRAMES; ! return; ! } ! ! /* At the start of a function our frame is in the stack pointer. */ ! fi->status = MY_FRAME_IN_SP; ! fi->stack_size = 0; ! ! /* If we're physically on the first insn of a prologue, then our frame ! hasn't been allocated yet. And if we're physically on an "return" ! instruction, then our frame has already been deallocated. */ ! if (!skip_prologue) ! { ! codestream_seek (fi->pc); ! op = codestream_peek(); ! if (fi->pc == func_addr ! || op == 0xc2 || op == 0xc3 || op == 0xca || op == 0xcb) { ! if (fi->next == NULL) ! { ! fi->frame = read_sp(); ! fi->fsr.regs[PC_REGNUM] = fi->frame; ! fi->status = INCOMPLETE_FRAME; ! } ! return; } } + + /* Start scanning on the first instruction of this function. */ + codestream_seek (func_addr); + + i386_follow_jump (); + + i386_get_frame_setup (fi); ! i386_frame_find_saved_regs (fi); ! } ! ! /* Function: frame_chain ! Figure out and return the caller's frame pointer given current ! frame_info struct. ! ! We don't handle dummy frames yet but we would probably just return the ! stack pointer that was in use at the time the function call was made? */ ! ! CORE_ADDR ! i386_frame_chain (fi) ! struct frame_info *fi; ! { ! struct frame_info dummy_frame; ! ! /* Walk through the prologue to determine the stack size, ! location of saved registers, end of the prologue, etc. */ ! if (fi->status == 0) ! i386_analyze_prologue (fi, 0); ! ! /* Quit now if i386_analyze_prologue set NO_MORE_FRAMES. */ ! if (fi->status & NO_MORE_FRAMES) ! return 0; ! ! /* Now that we've analyzed our prologue, determine the frame ! pointer for our caller. ! ! If our caller has a frame pointer, then we need to ! find the entry value of %ebp to our function. ! ! If fsr.regs[FP_REGNUM] is nonzero, then it's at the memory ! location pointed to by fsr.regs[FP_REGNUM]. ! ! Else it's still in %ebp. ! ! If our caller does not have a frame pointer, then his ! frame base is fi->frame + <pc save space> + ! <caller's register save space> + <caller's stack size> ! assuming that the caller's stack pointer was unchanging. ! ! The easiest way to get that info is to analyze our caller's frame. ! So we set up a dummy frame and call i386_init_extra_frame_info to ! find stuff for us. BTW, the actual value of dummy_frame.frame ! doesn't matter so long as it isn't zero. */ ! ! dummy_frame.next = fi; ! dummy_frame.frame = fi->frame; ! i386_init_extra_frame_info (&dummy_frame); ! ! if (dummy_frame.status & MY_FRAME_IN_FP) ! { ! /* Our caller has a frame pointer. So find the frame in %ebp or ! in the stack. */ ! if (fi->fsr.regs[FP_REGNUM]) ! return read_memory_integer (fi->fsr.regs[FP_REGNUM], REGISTER_SIZE); ! else ! return read_register (FP_REGNUM); ! } ! else { ! int offset; ! int i; ! ! offset = 4 + dummy_frame.stack_size; ! ! for (i = 0; i < 8; i++) #ifdef I386_REGNO_TO_SYMMETRY ! /* Dynix uses different internal numbering. Ick. */ ! if (dummy_frame.fsr.regs[I386_REGNO_TO_SYMMETRY(i)]) ! offset += 4; #else ! if (dummy_frame.fsr.regs[i]) ! offset += 4; #endif ! ! /* Our caller does not have a frame pointer. Assume his stack ! pointer was constant which means that his frame starts at ! the base of our frame (fi->frame) + pc save space + ! <his register save space> + <his size>. */ ! return fi->frame + offset; } } /* return pc of first real instruction */ *************** i386_skip_prologue (pc) *** 457,479 **** 0x5b, /* popl %ebx */ }; CORE_ADDR pos; ! if (i386_get_frame_setup (pc) < 0) ! return (pc); ! ! /* found valid frame setup - codestream now points to ! * start of push instructions for saving registers ! */ ! ! /* skip over register saves */ ! for (i = 0; i < 8; i++) ! { ! op = codestream_peek (); ! /* break if not pushl inst */ ! if (op < 0x50 || op > 0x57) ! break; ! codestream_get (); ! } /* The native cc on SVR4 in -K PIC mode inserts the following code to get the address of the global offset table (GOT) into register %ebx. --- 613,624 ---- 0x5b, /* popl %ebx */ }; CORE_ADDR pos; + struct frame_info fi; ! fi.frame = NULL; ! fi.pc = pc; ! ! i386_analyze_prologue (&fi, 1); /* The native cc on SVR4 in -K PIC mode inserts the following code to get the address of the global offset table (GOT) into register %ebx. *************** i386_skip_prologue (pc) *** 525,531 **** i386_follow_jump (); ! return (codestream_tell ()); } void --- 670,676 ---- i386_follow_jump (); ! return codestream_tell (); } void *************** void *** 550,561 **** i386_pop_frame () { struct frame_info *frame = get_current_frame (); - CORE_ADDR fp; int regnum; struct frame_saved_regs fsr; char regbuf[MAX_REGISTER_RAW_SIZE]; - fp = FRAME_FP (frame); get_frame_saved_regs (frame, &fsr); for (regnum = 0; regnum < NUM_REGS; regnum++) { --- 695,704 ---- *************** i386_pop_frame () *** 568,576 **** REGISTER_RAW_SIZE (regnum)); } } ! write_register (FP_REGNUM, read_memory_integer (fp, 4)); ! write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); ! write_register (SP_REGNUM, fp + 8); flush_cached_frames (); } --- 711,718 ---- REGISTER_RAW_SIZE (regnum)); } } ! write_register (PC_REGNUM, read_memory_integer (fsr.regs[PC_REGNUM], 4)); ! write_register (SP_REGNUM, fsr.regs[PC_REGNUM] + 4); flush_cached_frames (); } *************** skip_trampoline_code (pc, name) *** 712,717 **** --- 854,887 ---- return 0; /* not a trampoline */ } + /* Function: init_extra_frame_info + Setup the frame's frame pointer, pc, and frame addresses for saved + registers. Most of the work is done in i386_analyze_prologue(). + + Note that when we are called for the last frame (currently active frame), + that fi->pc and fi->frame will already be setup. However, fi->frame will + be valid only if this routine uses FP. For previous frames, fi->frame will + always be correct. i386_analyze_prologue will fix fi->frame if + it's not valid. + + We can be called with the PC in the call dummy under two circumstances. + First, during normal backtracing, second, while figuring out the frame + pointer just prior to calling the target function (see run_stack_dummy). */ + + void + i386_init_extra_frame_info (fi) + struct frame_info *fi; + { + + if (fi->next) + fi->pc = FRAME_SAVED_PC (fi->next); + + memset (fi->fsr.regs, '\000', sizeof fi->fsr.regs); + fi->status = 0; + fi->stack_size = 0; + + i386_analyze_prologue (fi, 0); + } void _initialize_i386_tdep () *** gdb/config/i386/tm-i386.h.ORIGINAL Thu Jan 4 02:23:24 1996 --- gdb/config/i386/tm-i386.h Mon Nov 2 23:11:50 1998 *************** extern void i386_extract_return_value PA *** 188,193 **** --- 188,200 ---- #define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF)) + #define EXTRA_FRAME_INFO struct frame_saved_regs fsr; int status; int stack_size; + + #define INIT_EXTRA_FRAME_INFO(fromleaf, fi) i386_init_extra_frame_info (fi) + #define INIT_FRAME_PC /* Not necessary */ + + extern void i386_init_extra_frame_info PARAMS ((struct frame_info *)); + /* The following redefines make backtracing through sigtramp work. They manufacture a fake sigtramp frame and obtain the saved pc in sigtramp from the sigcontext structure which is pushed by the kernel on the *************** extern void i386_extract_return_value PA *** 202,228 **** ((thisframe)->signal_handler_caller \ ? (thisframe)->frame \ : (!inside_entry_file ((thisframe)->pc) \ ! ? read_memory_integer ((thisframe)->frame, 4) \ : 0)) ! /* A macro that tells us whether the function invocation represented ! by FI does not have a frame on the stack associated with it. If it ! does not, FRAMELESS is set to 1, else 0. */ ! ! #define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \ ! do { \ ! if ((FI)->signal_handler_caller) \ ! (FRAMELESS) = 0; \ ! else \ ! (FRAMELESS) = frameless_look_for_prologue(FI); \ ! } while (0) /* Saved Pc. Get it from sigcontext if within sigtramp. */ #define FRAME_SAVED_PC(FRAME) \ (((FRAME)->signal_handler_caller \ ? sigtramp_saved_pc (FRAME) \ ! : read_memory_integer ((FRAME)->frame + 4, 4)) \ ) extern CORE_ADDR sigtramp_saved_pc PARAMS ((struct frame_info *)); --- 209,225 ---- ((thisframe)->signal_handler_caller \ ? (thisframe)->frame \ : (!inside_entry_file ((thisframe)->pc) \ ! ? i386_frame_chain (thisframe) \ : 0)) ! extern CORE_ADDR i386_frame_chain PARAMS ((struct frame_info *)); /* Saved Pc. Get it from sigcontext if within sigtramp. */ #define FRAME_SAVED_PC(FRAME) \ (((FRAME)->signal_handler_caller \ ? sigtramp_saved_pc (FRAME) \ ! : read_memory_integer ((FRAME)->fsr.regs[PC_REGNUM], 4)) \ ) extern CORE_ADDR sigtramp_saved_pc PARAMS ((struct frame_info *)); *************** extern int i386_frame_num_args PARAMS (( *** 248,259 **** ways in the stack frame. sp is even more special: the address we return for it IS the sp for the next frame. */ ! #define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ ! { i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); } ! ! extern void i386_frame_find_saved_regs PARAMS ((struct frame_info *, ! struct frame_saved_regs *)); ! /* Things needed for making the inferior call functions. */ --- 245,251 ---- ways in the stack frame. sp is even more special: the address we return for it IS the sp for the next frame. */ ! #define FRAME_FIND_SAVED_REGS(fi, regaddr) regaddr = fi->fsr /* Things needed for making the inferior call functions. */ ------------------------------------------------------------------------- | Feith Systems | Voice: 1-215-646-8000 | Email: john@feith.com | | John Wehle | Fax: 1-215-540-5495 | | -------------------------------------------------------------------------