1999-07-29 Jim Blandy Change from Ian Lance Taylor . The i386_linux_sigtramp* functions should be moved to i386-linux-tdep.c, when that file is introduced. * config/i386/tm-linux.h (LINUX_SIGCONTEXT_SIZE): Define. (LINUX_SIGCONTEXT_PC_OFFSET): Define. (LINUX_SIGCONTEXT_SP_OFFSET): Define. (SIGCONTEXT_PC_OFFSET): Don't define. (I386_LINUX_SIGTRAMP): Define. (IN_SIGTRAMP): Define. (i386_linux_sigtramp): Declare. (sigtramp_saved_pc): Define. (i386_linux_sigtramp_saved_pc): Declare. (FRAMELESS_SIGNAL): Define. (FRAME_CHAIN, FRAME_SAVED_PC): Define after #undef. * i386-tdep.c (i386_linux_sigtramp_start): New static function if I386_LINUX_SIGTRAMP. (i386_linux_sigtramp): New function if I386_LINUX_SIGTRAMP. (i386_linux_sigtramp_saved_pc): Likewise. (i386_linux_sigtramp_saved_sp): Likewise. =================================================================== RCS file: /cvs/gdb/gdb/gdb/config/i386/tm-linux.h,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.3 diff -c -r1.1.1.2 -r1.1.1.3 *** gdb-4.18/gdb/config/i386/tm-linux.h 1999/07/07 20:13:20 1.1.1.2 --- gdb-4.18/gdb/config/i386/tm-linux.h 1999/08/02 23:46:27 1.1.1.3 *************** *** 26,39 **** #include "i386/tm-i386.h" ! /* Offset to saved PC in sigcontext, from . */ ! #define SIGCONTEXT_PC_OFFSET 38 /* We need this file for the SOLIB_TRAMPOLINE stuff. */ #include "tm-sysv4.h" /* The following works around a problem with /usr/include/sys/procfs.h */ #define sys_quotactl 1 #endif /* #ifndef TM_LINUX_H */ --- 26,107 ---- #include "i386/tm-i386.h" ! /* Size of sigcontext, from . */ ! #define LINUX_SIGCONTEXT_SIZE (88) + /* Offset to saved PC in sigcontext, from . */ + #define LINUX_SIGCONTEXT_PC_OFFSET (56) + + /* Offset to saved SP in sigcontext, from . */ + #define LINUX_SIGCONTEXT_SP_OFFSET (28) + /* We need this file for the SOLIB_TRAMPOLINE stuff. */ #include "tm-sysv4.h" /* The following works around a problem with /usr/include/sys/procfs.h */ #define sys_quotactl 1 + + /* When the i386 Linux kernel calls a signal handler, the return + address points to a bit of code on the stack. These definitions + are used to identify this bit of code as a signal trampoline in + order to support backtracing through calls to signal handlers. */ + + #define I386_LINUX_SIGTRAMP + #define IN_SIGTRAMP(pc, name) ((name) == NULL && i386_linux_sigtramp (pc)) + + extern int i386_linux_sigtramp PARAMS ((CORE_ADDR)); + + /* We need our own version of sigtramp_saved_pc to get the saved PC in + a sigtramp routine. */ + + #define sigtramp_saved_pc i386_linux_sigtramp_saved_pc + extern CORE_ADDR i386_linux_sigtramp_saved_pc PARAMS ((struct frame_info *)); + + /* Signal trampolines don't have a meaningful frame. As in tm-i386.h, + the frame pointer value we use is actually the frame pointer of the + calling frame--that is, the frame which was in progress when the + signal trampoline was entered. gdb mostly treats this frame + pointer value as a magic cookie. We detect the case of a signal + trampoline by looking at the SIGNAL_HANDLER_CALLER field, which is + set based on IN_SIGTRAMP. + + When a signal trampoline is invoked from a frameless function, we + essentially have two frameless functions in a row. In this case, + we use the same magic cookie for three frames in a row. We detect + this case by seeing whether the next frame has + SIGNAL_HANDLER_CALLER set, and, if it does, checking whether the + current frame is actually frameless. In this case, we need to get + the PC by looking at the SP register value stored in the signal + context. + + This should work in most cases except in horrible situations where + a signal occurs just as we enter a function but before the frame + has been set up. */ + + #define FRAMELESS_SIGNAL(FRAME) \ + ((FRAME)->next != NULL \ + && (FRAME)->next->signal_handler_caller \ + && frameless_look_for_prologue (FRAME)) + + #undef FRAME_CHAIN + #define FRAME_CHAIN(FRAME) \ + ((FRAME)->signal_handler_caller \ + ? (FRAME)->frame \ + : (FRAMELESS_SIGNAL (FRAME) \ + ? (FRAME)->frame \ + : (!inside_entry_file ((FRAME)->pc) \ + ? read_memory_integer ((FRAME)->frame, 4) \ + : 0))) + + #undef FRAME_SAVED_PC + #define FRAME_SAVED_PC(FRAME) \ + ((FRAME)->signal_handler_caller \ + ? sigtramp_saved_pc (FRAME) \ + : (FRAMELESS_SIGNAL (FRAME) \ + ? read_memory_integer (i386_linux_sigtramp_saved_sp ((FRAME)->next), 4) \ + : read_memory_integer ((FRAME)->frame + 4, 4))) + + extern CORE_ADDR i386_linux_sigtramp_saved_sp PARAMS ((struct frame_info *)); #endif /* #ifndef TM_LINUX_H */ Index: i386-tdep.c =================================================================== RCS file: /cvs/gdb/gdb/gdb/i386-tdep.c,v retrieving revision 1.1.1.4 retrieving revision 1.1.1.5 diff -c -r1.1.1.4 -r1.1.1.5 *** gdb-4.18/gdb/i386-tdep.c 1999/07/07 20:06:55 1.1.1.4 --- gdb-4.18/gdb/i386-tdep.c 1999/08/02 23:45:36 1.1.1.5 *************** *** 722,727 **** --- 722,861 ---- } #endif /* I386V4_SIGTRAMP_SAVED_PC */ + #ifdef I386_LINUX_SIGTRAMP + + /* When the i386 Linux kernel calls a signal handler, the return + address points to a bit of code on the stack. This function + returns whether the PC appears to be within this bit of code. + + The instruction sequence is + pop %eax + mov $0x77,%eax + int $0x80 + or 0x58 0xb8 0x77 0x00 0x00 0x00 0xcd 0x80. + + Checking for the code sequence should be somewhat reliable, because + the effect is to call the system call sigreturn. This is unlikely + to occur anywhere other than a signal trampoline. + + It kind of sucks that we have to read memory from the process in + order to identify a signal trampoline, but there doesn't seem to be + any other way. The IN_SIGTRAMP macro in tm-linux.h arranges to + only call us if no function name could be identified, which should + be the case since the code is on the stack. */ + + #define LINUX_SIGTRAMP_INSN0 (0x58) /* pop %eax */ + #define LINUX_SIGTRAMP_OFFSET0 (0) + #define LINUX_SIGTRAMP_INSN1 (0xb8) /* mov $NNNN,%eax */ + #define LINUX_SIGTRAMP_OFFSET1 (1) + #define LINUX_SIGTRAMP_INSN2 (0xcd) /* int */ + #define LINUX_SIGTRAMP_OFFSET2 (6) + + static const unsigned char linux_sigtramp_code[] = + { + LINUX_SIGTRAMP_INSN0, /* pop %eax */ + LINUX_SIGTRAMP_INSN1, 0x77, 0x00, 0x00, 0x00, /* mov $0x77,%eax */ + LINUX_SIGTRAMP_INSN2, 0x80 /* int $0x80 */ + }; + + #define LINUX_SIGTRAMP_LEN (sizeof linux_sigtramp_code) + + /* If PC is in a sigtramp routine, return the address of the start of + the routine. Otherwise, return 0. */ + + static CORE_ADDR + i386_linux_sigtramp_start (pc) + CORE_ADDR pc; + { + unsigned char buf[LINUX_SIGTRAMP_LEN]; + + /* We only recognize a signal trampoline if PC is at the start of + one of the three instructions. We optimize for finding the PC at + the start, as will be the case when the trampoline is not the + first frame on the stack. We assume that in the case where the + PC is not at the start of the instruction sequence, there will be + a few trailing readable bytes on the stack. */ + + if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0) + return 0; + + if (buf[0] != LINUX_SIGTRAMP_INSN0) + { + int adjust; + + switch (buf[0]) + { + case LINUX_SIGTRAMP_INSN1: + adjust = LINUX_SIGTRAMP_OFFSET1; + break; + case LINUX_SIGTRAMP_INSN2: + adjust = LINUX_SIGTRAMP_OFFSET2; + break; + default: + return 0; + } + + pc -= adjust; + + if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0) + return 0; + } + + if (memcmp (buf, linux_sigtramp_code, LINUX_SIGTRAMP_LEN) != 0) + return 0; + + return pc; + } + + /* Return whether PC is in a Linux sigtramp routine. */ + + int + i386_linux_sigtramp (pc) + CORE_ADDR pc; + { + return i386_linux_sigtramp_start (pc) != 0; + } + + /* Assuming FRAME is for a Linux sigtramp routine, return the saved + program counter. The Linux kernel will set up a sigcontext + structure immediately before the sigtramp routine on the stack. */ + + CORE_ADDR + i386_linux_sigtramp_saved_pc (frame) + struct frame_info *frame; + { + CORE_ADDR pc; + + pc = i386_linux_sigtramp_start (frame->pc); + if (pc == 0) + error ("i386_linux_sigtramp_saved_pc called when no sigtramp"); + return read_memory_integer ((pc + - LINUX_SIGCONTEXT_SIZE + + LINUX_SIGCONTEXT_PC_OFFSET), + 4); + } + + /* Assuming FRAME is for a Linux sigtramp routine, return the saved + stack pointer. The Linux kernel will set up a sigcontext structure + immediately before the sigtramp routine on the stack. */ + + CORE_ADDR + i386_linux_sigtramp_saved_sp (frame) + struct frame_info *frame; + { + CORE_ADDR pc; + + pc = i386_linux_sigtramp_start (frame->pc); + if (pc == 0) + error ("i386_linux_sigtramp_saved_sp called when no sigtramp"); + return read_memory_integer ((pc + - LINUX_SIGCONTEXT_SIZE + + LINUX_SIGCONTEXT_SP_OFFSET), + 4); + } + + #endif /* I386_LINUX_SIGTRAMP */ + #ifdef STATIC_TRANSFORM_NAME /* SunPRO encodes the static variables. This is not related to C++ mangling, it is done for C too. */