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]

Patches for signal handling in Linux i386



Hi cygnus gurus,

Here is a patch to handle correctly signal handler trampoline in Linux i386
(step/next commands) and to be able to get correct symbol addresses when
debugging inside shared libraries (definition of SOFUN_ADDRESS_MAYBE_MISSING).
If you have any question, don't hesitate to ask me (although I may spend some
days in vacation during the next 3 weeks). I tested the patches under
Linux 2.0.32 for i386 and compiled them with gcc 2.7.2.3.

Best regards, and thanks for leaving your latest gdb snapshots available
-Eric
P.S. Is there a way to be informed by Email of the availability of the latest
	snapshot on your ftp server ?
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ Eric PAIRE
Email : e.paire@gr.opengroup.org  | THE Open GROUP - Research Institute
Phone : +33 (0) 476 63 48 71 	  | 2, avenue de Vignate
Fax   : +33 (0) 476 51 05 32	  | F-38610 Gieres      FRANCE

------ ChangeLog here  ------ ChangeLog here  ------ ChangeLog here  ------
Fri Apr 24 16:31:48 1998  Eric Paire  <e.paire@gr.opengroup.org>

	Signal handler trampoline support for Linux i386:
	* i386-tdep.c (i386_linux_sigtramp_offset): New function returning
	the PC offset from the beginning of the signal handler trampoline.
	(i386_linux_sigtramp_saved_pc): New function returning the PC value
	from a signal handler sigcontext.
	(child_resume): Redefined here (hide the one in inftarg.c) to set
	the trap flag of the signal handler sigcontext when single step mode
	is required at the last instruction of the signal handler trampoline.
	* infrun.c (START_SIGHANDLER): define default if not yet defined.
	(wait_for_inferior): use new macro START_SIGHANDLER to validate
	whether this is the first instruction of a new signal handler (the old
	validation did not detect a new handler signal to occur during the
	signal handler trampoline execution).
	* config/i386/tm-linux.h (SIGCONTEXT_PC_OFFSET): set to correct value.
	(SIGCONTEXT_EFLAGS_OFFSET, SIGCONTEXT_SIZE, SIGCONTEXT_ADDR,
	SIGTRAMP_START, SIGTRAMP_END, START_SIGHANDLER, CHILD_RESUME,
	SYSCALL_TRAP, SYSCALL_TRAP_SIZE): define.
	(IN_SIGTRAMP, SAVED_PC_AFTER_CALL, FRAME_SAVED_PC): override the 
	definitions from config/i386/tm-i386.h.
	
	* infrun.c (wait_for_inferior): under subroutine call, don't put the
	frame address in the step_resume_breakpoint created when the PC is in
	the signal handler trampoline, and reinitialize the stepping range
	when the return PC is out of it (handle jump to different function).

	* config/i386/tm-linux.h (SOFUN_ADDRESS_MAYBE_MISSING): defined in
	order to get the correct symbol addresses from shared libraries.

----- diffs here  ----- diffs here  ----- diffs here ----- diffs here  -----
diff -u -r gdb-4.16.98/gdb/i386-tdep.c~ gdb-4.16.98/gdb/i386-tdep.c
--- gdb-4.16.98/gdb/i386-tdep.c~	Fri Apr 24 15:43:21 1998
+++ gdb-4.16.98/gdb/i386-tdep.c	Fri Apr 24 19:11:36 1998
@@ -712,6 +712,126 @@
   return 0;			/* not a trampoline */
 }
 
+/* Under Linux, signal handler invocations can be identified by the
+   designated code sequence that is used to return from a signal
+   handler.  In particular, the return address of a signal handler
+   points to the following sequence:
+
+	0x58		popl %eax
+	0xb877000000	movl $0x77,%eax
+	0xcd80		int  $0x80
+
+   Each instruction has a unique encoding, so we simply attempt to
+   match the instruction the pc is pointing to with any of the above
+   instructions.  If there is a hit, we know the offset to the start
+   of the designated sequence and can then check whether we really are
+   executing in a designated sequence.  If not, -1 is returned,
+   otherwise the offset from the start of the desingated sequence is
+   returned.
+
+   There is a slight chance of false hits: code could jump into the
+   middle of the designated sequence, in which case there is no
+   guarantee that we are in the middle of a sigreturn syscall.  Don't
+   think this will be a problem in praxis, though.
+*/
+int
+i386_linux_sigtramp_offset (pc)
+     CORE_ADDR pc;
+{
+  unsigned char code[8];
+  unsigned char sigtramp[] = { 0x58, 0xb8, 0x77, 0x00, 0x00, 0x00, 0xcd, 0x80 };
+  int off, i;
+
+  if (read_memory_nobpt(pc, (char *) code, 1) != 0)
+    return -1;
+
+  switch (code[0])
+  {
+    case 0x58:	off = 0; break;	/* popl %eax */
+    case 0xb8:	off = 1; break;	/* movl $0x77,%eax */
+    case 0xcd:	off = 6; break;	/* int  $0x80 */
+    default:	return -1;
+  }
+  pc -= off;
+
+  for (i = 0; i < sizeof (code); i++)
+    if (read_memory_nobpt(pc + i, (char *) &code[i], 1) != 0)
+      return -1;
+
+  return memcmp (sigtramp, code, sizeof (code)) == 0 ? off : -1;
+}
+
+/* Get saved user PC for sigtramp from sigcontext for Linux style sigtramp.  */
+
+CORE_ADDR
+i386_linux_sigtramp_saved_pc (frame)
+     struct frame_info *frame;
+{
+  char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
+  int ptrbytes = TARGET_PTR_BIT / TARGET_CHAR_BIT;
+
+  /* Don't cause a memory_error when accessing sigcontext in case the stack
+     layout has changed or the stack is corrupt.  */
+  target_read_memory (SIGCONTEXT_ADDR (frame) + SIGCONTEXT_PC_OFFSET,
+		      buf, ptrbytes);
+  return extract_unsigned_integer (buf, ptrbytes);
+}
+
+#ifdef CHILD_RESUME
+
+#include <sys/ptrace.h>
+
+void
+child_resume(pid, step, signal)
+     int pid;
+     int step;
+     enum target_signal signal;
+{
+  int request;
+  unsigned char code;
+  CORE_ADDR pc;
+  int i;
+
+  errno = 0;
+
+  if (pid == -1)
+    /* Resume all threads.  */
+    /* I think this only gets used in the non-threaded case, where "resume
+	all threads" and "resume inferior_pid" are the same.  */
+    pid = inferior_pid;
+
+  if (!step)
+    request = PT_CONTINUE;
+  else
+    {
+      request = PT_STEP;
+      pc = read_pc_pid (pid);
+      for (i = 0; i < SYSCALL_TRAP_SIZE; i++)
+	if (read_memory_nobpt(pc + i, (char *) &code, 1) != 0
+	    || code != ((SYSCALL_TRAP >> ((SYSCALL_TRAP_SIZE - 1 - i) * 8))
+			& 0xFF))
+	  break;
+
+      if (i >= SYSCALL_TRAP_SIZE && IN_SIGTRAMP (pc, (char *)NULL))
+	{
+	  /* Put TF in the eflags from the frame set up by the signal handler */
+	  unsigned long eflags;
+	  CORE_ADDR addr = read_sp () + SIGCONTEXT_EFLAGS_OFFSET;
+	  if (target_read_memory (addr, (char *) &eflags, 4) == 0)
+	    {
+	      eflags |= 0x100; /* Trap Flag */
+	      write_memory (addr, (char *) &eflags, 4);
+	    }
+	}
+    }
+  call_ptrace (request, pid, (PTRACE_ARG3_TYPE) 0,
+	       target_signal_to_host (signal));
+
+  if (errno)
+    perror_with_name ("ptrace");
+}
+#endif
+
 
 void
 _initialize_i386_tdep ()
diff -u -r gdb-4.16.98/gdb/infrun.c~ gdb-4.16.98/gdb/infrun.c
--- gdb-4.16.98/gdb/infrun.c~	Fri Apr 24 15:43:21 1998
+++ gdb-4.16.98/gdb/infrun.c	Fri Apr 24 18:48:25 1998
@@ -59,6 +59,15 @@
 #define GET_LONGJMP_TARGET(PC_ADDR) 0
 #endif
 
+/* Determine whether a pc is pointing to the first instruction of a signal
+   handler.  This can be difficult to compute on some systems (like Linux)
+   where the sigreturn trampoline is only used on return and not on call.  */
+
+#ifndef START_SIGHANDLER
+#define START_SIGHANDLER(pc, func_start, func_name) \
+  ((pc) == (func_start) - FUNCTION_START_OFFSET \
+   && IN_SIGTRAMP((pc), (func_name)))
+#endif
 
 /* Some machines have trampoline code that sits between function callers
    and the actual functions themselves.  If this machine doesn't have
@@ -1239,9 +1248,7 @@
       update_step_sp = 1;
 
       /* Did we just take a signal?  */
-      if (IN_SIGTRAMP (stop_pc, stop_func_name)
-	  && !IN_SIGTRAMP (prev_pc, prev_func_name)
-	  && read_sp () INNER_THAN step_sp)
+      if (START_SIGHANDLER (stop_pc, stop_func_start, stop_func_name))
 	{
 	  /* We've just taken a signal; go until we are back to
 	     the point where we took it and one more.  */
@@ -1432,8 +1439,19 @@
 	    step_resume_breakpoint =
 	      set_momentary_breakpoint (sr_sal, get_current_frame (),
 					bp_step_resume);
-	    if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
+	    if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc)
+		&& !IN_SIGTRAMP (stop_pc, NULL))
 	      step_resume_breakpoint->frame = step_frame_address;
+
+	    if (sr_sal.pc < step_range_start
+		|| sr_sal.pc >= step_range_end)
+	      {
+		/* If the return function is out_of stepping range, then
+		   update stepping range to match the return address. This
+		   code deals with jumps to functions that did not return
+		   to the current function.  */
+		step_range_start = step_range_end = sr_sal.pc;
+	      }
 	    if (breakpoints_inserted)
 	      insert_breakpoints ();
 	  }
@@ -1583,9 +1601,7 @@
 
     check_sigtramp2:
       if (trap_expected
-	  && IN_SIGTRAMP (stop_pc, stop_func_name)
-	  && !IN_SIGTRAMP (prev_pc, prev_func_name)
-	  && read_sp () INNER_THAN step_sp)
+	  && START_SIGHANDLER (stop_pc, stop_func_start, stop_func_name))
 	{
 	  /* What has happened here is that we have just stepped the inferior
 	     with a signal (because it is a signal which shouldn't make
diff -u -r gdb-4.16.98/gdb/config/i386/tm-linux.h~ gdb-4.16.98/gdb/config/i386/tm-linux.h
--- gdb-4.16.98/gdb/config/i386/tm-linux.h~	Wed Jan 14 05:28:07 1998
+++ gdb-4.16.98/gdb/config/i386/tm-linux.h	Fri Apr 24 19:00:29 1998
@@ -1,5 +1,5 @@
 /* Definitions to target GDB to Linux on 386.
-   Copyright 1992, 1993 Free Software Foundation, Inc.
+   Copyright 1992, 1993, 1998 Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -25,8 +25,62 @@
 
 #include "i386/tm-i386.h"
 
-/* Offset to saved PC in sigcontext, from <linux/signal.h>.  */
-#define SIGCONTEXT_PC_OFFSET 38
+/* Offset to saved PC and EFLAGS in sigcontext, from <linux/signal.h>.  */
+#define SIGCONTEXT_PC_OFFSET (14 * 4)
+#define SIGCONTEXT_EFLAGS_OFFSET (16 * 4)
+
+/* Size of sigcontext, from <linux/signal.h>.  */
+#define SIGCONTEXT_SIZE (22 * 4)
+
+/* Address of sigcontext given the sigtramp frame */
+
+#define SIGCONTEXT_ADDR(frame) (SIGTRAMP_START((frame)->pc) - SIGCONTEXT_SIZE)
+
+/* Are we currently handling a signal ?  */
+
+extern int i386_linux_sigtramp_offset PARAMS ((CORE_ADDR));
+#undef IN_SIGTRAMP
+#define IN_SIGTRAMP(pc, name)   (i386_linux_sigtramp_offset (pc) >= 0)
+
+/* Get start and end address of sigtramp handler.  */
+
+#define SIGTRAMP_START(pc)      ((pc) - i386_linux_sigtramp_offset (pc))
+#define SIGTRAMP_END(pc)        (SIGTRAMP_START(pc) + 8)
+
+/* Determine whether a pc is the first instruction of a signal handler.  */
+
+#define START_SIGHANDLER(pc, func_start, func_name) \
+  ((pc) == (func_start) && i386_linux_sigtramp_offset (read_sp()) == 0)
+
+/* Need to redefine child_resume for the step/next action in signal handlers */
+
+#define	CHILD_RESUME
+
+/* If PC contains this instruction, then we know that next instruction
+   will be a system call.  */
+
+#define SYSCALL_TRAP 0xcd80	/* int $0x80 */
+#define SYSCALL_TRAP_SIZE 2	/* SYSCALL_TRAP instruction size */
+
+/* Immediately after a function call, return the saved pc.  Can't always go
+   through the frames for this because on some machines the new frame is not
+   set up until the new function executes some instructions.  */
+
+#undef SAVED_PC_AFTER_CALL
+#define SAVED_PC_AFTER_CALL(_frame) \
+  (read_memory_integer (read_register (SP_REGNUM) \
+			+ ((_frame)->signal_handler_caller \
+			   ? SIGCONTEXT_PC_OFFSET + 4 : 0), 4)) 
+
+/* Saved PC.  Get it from sigcontext if within sigtramp.  */
+
+extern CORE_ADDR i386_linux_sigtramp_saved_pc PARAMS ((struct frame_info *));
+
+#undef FRAME_SAVED_PC
+#define FRAME_SAVED_PC(_frame) \
+  (((_frame)->signal_handler_caller \
+    ? i386_linux_sigtramp_saved_pc (_frame) \
+    : read_memory_integer ((_frame)->frame + 4, 4)))
 
 /* We need this file for the SOLIB_TRAMPOLINE stuff. */
 
@@ -35,5 +89,8 @@
 /* The following works around a problem with /usr/include/sys/procfs.h  */
 #define sys_quotactl 1
 
+/* The compiler/loader puts out 0 instead of the address in N_SO symbols,
+   and in N_FUN symbols too.  */
+#define SOFUN_ADDRESS_MAYBE_MISSING
 
 #endif  /* #ifndef TM_LINUX_H */