This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfc] Add syscall restart handling for MIPS GNU/Linux


This patch fixes the interrupt.exp and mi*-syn-frame.exp tests for
native MIPS GNU/Linux.  Just like i386 (and most other Linux
platforms), MIPS has a ptrace-accessible "register" which stores the
error code for restartable syscalls.  If a signal handler is run then
this becomes an EINTR return value; if no signal handler is run, then
the syscall is restarted by backing up $pc two instructions.  This
causes obvious trouble if GDB tries to call a function at some other
location during a restartable syscall.  We have to clear the register
before changing $pc.

This patch touches a fair number of files but is quite simple.  It has
two parts:

  - If the target can describe registers, then the MIPS GNU/Linux
  OSABI reserves a register number for $restart, and maps the
  supplied restart register to that address if it is found.

  - Native MIPS GNU/Linux debugging supplies an XML description
  including this register.  It is stored in the ptrace slot that
  would otherwise be used for $0.  The kernel has done this for years.

Target parts tested mips64-linux all three ABIs; native parts tested
o32.  I'll commit this after the MIPS register description support,
which it requires.

-- 
Daniel Jacobowitz
CodeSourcery

2007-05-18  Daniel Jacobowitz  <dan@codesourcery.com>

	* config/mips/linux.mh (TDEP_XML): New.
	* features/mips-linux.xml, features/mips64-linux.xml: New files.
	* mips-linux-nat.c (mips_linux_register_addr): Handle
	MIPS_RESTART_REGNUM.
	(mips64_linux_register_addr): Likewise.
	(super_xfer_partial, mips_linux_xfer_partial): New.
	(_initialize_mips_linux_nat): Add them to the target_ops.
	* mips-linux-tdep.c (mips_supply_gregset): Handle MIPS_RESTART_REGNUM.
	(mips_fill_gregset, mips64_supply_gregset, mips64_fill_gregset)
	(mips_linux_o32_sigframe_init)
	(mips_linux_n32n64_sigframe_init): Likewise.
	(mips_linux_write_pc, mips_linux_restart_reg_p): New.
	(mips_linux_init_abi): Use mips_linux_write_pc.  Check for the
	"org.gnu.gdb.mips.linux" feature.
	* mips-linux-tdep.h (MIPS_RESTART_REGNUM): New constant.
	(mips_linux_restart_reg_p): New prototype.
	* mips-tdep.c (mips_gdbarch_init): Pass tdesc_data to the OS/ABI
	initialization routine.
	* Makefile.in (mips-linux-tdep.o, mips-linux-nat.o): Update.

	* gdb.texinfo (MIPS Features): Document org.gnu.gdb.mips.linux.

---
 Makefile.in               |    6 +-
 config/mips/linux.mh      |    9 ++++
 doc/gdb.texinfo           |    4 +
 features/mips-linux.xml   |   18 ++++++++
 features/mips64-linux.xml |   18 ++++++++
 mips-linux-nat.c          |   41 ++++++++++++++++++++
 mips-linux-tdep.c         |   93 ++++++++++++++++++++++++++++++++++++++--------
 mips-linux-tdep.h         |   10 ++++
 mips-tdep.c               |    1 
 9 files changed, 182 insertions(+), 18 deletions(-)

Index: gdb/config/mips/linux.mh
===================================================================
--- gdb.orig/config/mips/linux.mh	2007-05-21 09:00:01.000000000 -0400
+++ gdb/config/mips/linux.mh	2007-05-21 09:43:16.000000000 -0400
@@ -5,3 +5,12 @@ NATDEPFILES= inf-ptrace.o fork-child.o m
 	linux-nat.o linux-fork.o
 
 LOADLIBES = -ldl -rdynamic
+
+TDEP_XML = $(srcdir)/features/mips-cpu.xml \
+	$(srcdir)/features/mips-cp0.xml \
+	$(srcdir)/features/mips-fpu.xml \
+	$(srcdir)/features/mips-linux.xml \
+	$(srcdir)/features/mips64-cpu.xml \
+	$(srcdir)/features/mips64-cp0.xml \
+	$(srcdir)/features/mips64-fpu.xml \
+	$(srcdir)/features/mips64-linux.xml
Index: gdb/features/mips-linux.xml
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb/features/mips-linux.xml	2007-05-21 09:43:16.000000000 -0400
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>mips</architecture>
+  <xi:include href="mips-cpu.xml"/>
+  <xi:include href="mips-cp0.xml"/>
+  <xi:include href="mips-fpu.xml"/>
+
+  <feature name="org.gnu.gdb.mips.linux">
+    <reg name="restart" bitsize="32" group="system"/>
+  </feature>
+</target>
Index: gdb/features/mips64-linux.xml
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb/features/mips64-linux.xml	2007-05-21 09:43:16.000000000 -0400
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>mips</architecture>
+  <xi:include href="mips64-cpu.xml"/>
+  <xi:include href="mips64-cp0.xml"/>
+  <xi:include href="mips64-fpu.xml"/>
+
+  <feature name="org.gnu.gdb.mips.linux">
+    <reg name="restart" bitsize="64" group="system"/>
+  </feature>
+</target>
Index: gdb/mips-linux-nat.c
===================================================================
--- gdb.orig/mips-linux-nat.c	2007-05-21 09:00:01.000000000 -0400
+++ gdb/mips-linux-nat.c	2007-05-21 09:43:16.000000000 -0400
@@ -27,10 +27,13 @@
 #include "regcache.h"
 #include "linux-nat.h"
 #include "mips-linux-tdep.h"
+#include "target-descriptions.h"
+#include "xml-support.h"
 
 #include "gdb_proc_service.h"
 #include "gregset.h"
 
+#include <sgidefs.h>
 #include <sys/ptrace.h>
 
 #ifndef PTRACE_GET_THREAD_AREA
@@ -81,6 +84,8 @@ mips_linux_register_addr (struct gdbarch
     regaddr = FPC_CSR;
   else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     regaddr = store? (CORE_ADDR) -1 : FPC_EIR;
+  else if (mips_linux_restart_reg_p (gdbarch) && regno == MIPS_RESTART_REGNUM)
+    regaddr = 0;
   else
     regaddr = (CORE_ADDR) -1;
 
@@ -114,6 +119,8 @@ mips64_linux_register_addr (struct gdbar
     regaddr = MIPS64_FPC_CSR;
   else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     regaddr = store? (CORE_ADDR) -1 : MIPS64_FPC_EIR;
+  else if (mips_linux_restart_reg_p (gdbarch) && regno == MIPS_RESTART_REGNUM)
+    regaddr = 0;
   else
     regaddr = (CORE_ADDR) -1;
 
@@ -335,6 +342,36 @@ mips_linux_register_u_offset (struct gdb
     return mips_linux_register_addr (gdbarch, regno, store_p);
 }
 
+static LONGEST (*super_xfer_partial) (struct target_ops *, enum target_object,
+				      const char *, gdb_byte *, const gdb_byte *,
+				      ULONGEST, LONGEST);
+
+static LONGEST
+mips_linux_xfer_partial (struct target_ops *ops,
+			 enum target_object object,
+			 const char *annex,
+			 gdb_byte *readbuf, const gdb_byte *writebuf,
+			 ULONGEST offset, LONGEST len)
+{
+  if (object == TARGET_OBJECT_AVAILABLE_FEATURES)
+    {
+      if (annex != NULL && strcmp (annex, "target.xml") == 0)
+	{
+	  /* Report that target registers are a size we know for sure
+	     that we can get from ptrace.  */
+	  if (_MIPS_SIM == _ABIO32)
+	    annex = "mips-linux.xml";
+	  else
+	    annex = "mips64-linux.xml";
+	}
+
+      return xml_builtin_xfer_partial (annex, readbuf, writebuf, offset, len);
+    }
+
+  return super_xfer_partial (ops, object, annex, readbuf, writebuf,
+			     offset, len);
+}
+
 void _initialize_mips_linux_nat (void);
 
 void
@@ -348,5 +385,9 @@ _initialize_mips_linux_nat (void)
   t->to_fetch_registers = mips64_linux_fetch_registers;
   t->to_store_registers = mips64_linux_store_registers;
 
+  /* Override the default to_xfer_partial.  */
+  super_xfer_partial = t->to_xfer_partial;
+  t->to_xfer_partial = mips_linux_xfer_partial;
+
   linux_nat_add_target (t);
 }
Index: gdb/mips-linux-tdep.c
===================================================================
--- gdb.orig/mips-linux-tdep.c	2007-05-21 09:00:02.000000000 -0400
+++ gdb/mips-linux-tdep.c	2007-05-21 09:43:16.000000000 -0400
@@ -37,6 +37,7 @@
 #include "solib-svr4.h"
 #include "solist.h"
 #include "symtab.h"
+#include "target-descriptions.h"
 #include "mips-linux-tdep.h"
 
 static struct target_so_ops mips_svr4_so_ops;
@@ -93,9 +94,12 @@ mips_supply_gregset (struct regcache *re
 
   memset (zerobuf, 0, MAX_REGISTER_SIZE);
 
-  for (regi = EF_REG0; regi <= EF_REG31; regi++)
+  for (regi = EF_REG0 + 1; regi <= EF_REG31; regi++)
     supply_32bit_reg (regcache, regi - EF_REG0, regp + regi);
 
+  if (mips_linux_restart_reg_p (current_gdbarch))
+    supply_32bit_reg (regcache, MIPS_RESTART_REGNUM, regp + EF_REG0);
+
   supply_32bit_reg (regcache, mips_regnum (current_gdbarch)->lo,
 		    regp + EF_LO);
   supply_32bit_reg (regcache, mips_regnum (current_gdbarch)->hi,
@@ -110,9 +114,10 @@ mips_supply_gregset (struct regcache *re
 		    regp + EF_CP0_CAUSE);
 
   /* Fill inaccessible registers with zero.  */
+  regcache_raw_supply (regcache, MIPS_ZERO_REGNUM, zerobuf);
   regcache_raw_supply (regcache, MIPS_UNUSED_REGNUM, zerobuf);
   for (regi = MIPS_FIRST_EMBED_REGNUM;
-       regi < MIPS_LAST_EMBED_REGNUM;
+       regi <= MIPS_LAST_EMBED_REGNUM;
        regi++)
     regcache_raw_supply (regcache, regi, zerobuf);
 }
@@ -130,7 +135,7 @@ mips_fill_gregset (const struct regcache
   if (regno == -1)
     {
       memset (regp, 0, sizeof (mips_elf_gregset_t));
-      for (regi = 0; regi < 32; regi++)
+      for (regi = 1; regi < 32; regi++)
 	mips_fill_gregset (regcache, gregsetp, regi);
       mips_fill_gregset (regcache, gregsetp,
 			 mips_regnum (current_gdbarch)->lo);
@@ -143,10 +148,11 @@ mips_fill_gregset (const struct regcache
       mips_fill_gregset (regcache, gregsetp, MIPS_PS_REGNUM);
       mips_fill_gregset (regcache, gregsetp,
 			 mips_regnum (current_gdbarch)->cause);
+      mips_fill_gregset (regcache, gregsetp, MIPS_RESTART_REGNUM);
       return;
    }
 
-  if (regno < 32)
+  if (regno > 0 && regno < 32)
     {
       dst = regp + regno + EF_REG0;
       regcache_raw_collect (regcache, regno, dst);
@@ -165,6 +171,9 @@ mips_fill_gregset (const struct regcache
     regaddr = EF_CP0_STATUS;
   else if (regno == mips_regnum (current_gdbarch)->cause)
     regaddr = EF_CP0_CAUSE;
+  else if (mips_linux_restart_reg_p (current_gdbarch)
+	   && regno == MIPS_RESTART_REGNUM)
+    regaddr = EF_REG0;
   else
     regaddr = -1;
 
@@ -288,10 +297,14 @@ mips64_supply_gregset (struct regcache *
 
   memset (zerobuf, 0, MAX_REGISTER_SIZE);
 
-  for (regi = MIPS64_EF_REG0; regi <= MIPS64_EF_REG31; regi++)
+  for (regi = MIPS64_EF_REG0 + 1; regi <= MIPS64_EF_REG31; regi++)
     supply_64bit_reg (regcache, regi - MIPS64_EF_REG0,
 		      (const gdb_byte *)(regp + regi));
 
+  if (mips_linux_restart_reg_p (current_gdbarch))
+    supply_64bit_reg (regcache, MIPS_RESTART_REGNUM,
+		      (const gdb_byte *)(regp + MIPS64_EF_REG0));
+
   supply_64bit_reg (regcache, mips_regnum (current_gdbarch)->lo,
 		    (const gdb_byte *) (regp + MIPS64_EF_LO));
   supply_64bit_reg (regcache, mips_regnum (current_gdbarch)->hi,
@@ -307,9 +320,10 @@ mips64_supply_gregset (struct regcache *
 		    (const gdb_byte *) (regp + MIPS64_EF_CP0_CAUSE));
 
   /* Fill inaccessible registers with zero.  */
+  regcache_raw_supply (regcache, MIPS_ZERO_REGNUM, zerobuf);
   regcache_raw_supply (regcache, MIPS_UNUSED_REGNUM, zerobuf);
   for (regi = MIPS_FIRST_EMBED_REGNUM;
-       regi < MIPS_LAST_EMBED_REGNUM;
+       regi <= MIPS_LAST_EMBED_REGNUM;
        regi++)
     regcache_raw_supply (regcache, regi, zerobuf);
 }
@@ -327,7 +341,7 @@ mips64_fill_gregset (const struct regcac
   if (regno == -1)
     {
       memset (regp, 0, sizeof (mips64_elf_gregset_t));
-      for (regi = 0; regi < 32; regi++)
+      for (regi = 1; regi < 32; regi++)
         mips64_fill_gregset (regcache, gregsetp, regi);
       mips64_fill_gregset (regcache, gregsetp,
 			   mips_regnum (current_gdbarch)->lo);
@@ -340,10 +354,11 @@ mips64_fill_gregset (const struct regcac
       mips64_fill_gregset (regcache, gregsetp, MIPS_PS_REGNUM);
       mips64_fill_gregset (regcache, gregsetp,
 			   mips_regnum (current_gdbarch)->cause);
+      mips64_fill_gregset (regcache, gregsetp, MIPS_RESTART_REGNUM);
       return;
    }
 
-  if (regno < 32)
+  if (regno > 0 && regno < 32)
     regaddr = regno + MIPS64_EF_REG0;
   else if (regno == mips_regnum (current_gdbarch)->lo)
     regaddr = MIPS64_EF_LO;
@@ -357,6 +372,9 @@ mips64_fill_gregset (const struct regcac
     regaddr = MIPS64_EF_CP0_STATUS;
   else if (regno == mips_regnum (current_gdbarch)->cause)
     regaddr = MIPS64_EF_CP0_CAUSE;
+  else if (mips_linux_restart_reg_p (current_gdbarch)
+	   && regno == MIPS_RESTART_REGNUM)
+    regaddr = MIPS64_EF_REG0;
   else
     regaddr = -1;
 
@@ -830,10 +848,9 @@ mips_linux_o32_sigframe_init (const stru
   else
     regs_base = sigcontext_base;
 
-#if 0
-  trad_frame_set_reg_addr (this_cache, ORIG_ZERO_REGNUM + NUM_REGS,
-			   regs_base + SIGCONTEXT_REGS);
-#endif
+  if (mips_linux_restart_reg_p (current_gdbarch))
+    trad_frame_set_reg_addr (this_cache, MIPS_RESTART_REGNUM + NUM_REGS,
+			     regs_base + SIGCONTEXT_REGS);
 
   for (ireg = 1; ireg < 32; ireg++)
     trad_frame_set_reg_addr (this_cache,
@@ -968,10 +985,9 @@ mips_linux_n32n64_sigframe_init (const s
   else
     sigcontext_base += N64_SIGFRAME_SIGCONTEXT_OFFSET;
 
-#if 0
-  trad_frame_set_reg_addr (this_cache, ORIG_ZERO_REGNUM + NUM_REGS,
-			   sigcontext_base + N64_SIGCONTEXT_REGS);
-#endif
+  if (mips_linux_restart_reg_p (current_gdbarch))
+    trad_frame_set_reg_addr (this_cache, MIPS_RESTART_REGNUM + NUM_REGS,
+			     sigcontext_base + N64_SIGCONTEXT_REGS);
 
   for (ireg = 1; ireg < 32; ireg++)
     trad_frame_set_reg_addr (this_cache,
@@ -1005,6 +1021,30 @@ mips_linux_n32n64_sigframe_init (const s
 				     func));
 }
 
+static void
+mips_linux_write_pc (CORE_ADDR pc, ptid_t ptid)
+{
+  write_register_pid (PC_REGNUM, pc, ptid);
+
+  /* Clear the syscall restart flag.  */
+  if (mips_linux_restart_reg_p (current_gdbarch))
+    write_register_pid (MIPS_RESTART_REGNUM, 0, ptid);
+}
+
+/* Return 1 if MIPS_RESTART_REGNUM is usable.  */
+
+int
+mips_linux_restart_reg_p (struct gdbarch *gdbarch)
+{
+  /* If we do not have a target description with registers, then
+     MIPS_RESTART_REGNUM will not be included in the register set.  */
+  if (!tdesc_has_registers (gdbarch_target_desc (gdbarch)))
+    return 0;
+
+  /* If we do, then MIPS_RESTART_REGNUM is safe to check; it will
+     either be GPR-sized or missing.  */
+  return register_size (gdbarch, MIPS_RESTART_REGNUM) > 0;
+}
 
 /* Initialize one of the GNU/Linux OS ABIs.  */
 
@@ -1014,6 +1054,7 @@ mips_linux_init_abi (struct gdbarch_info
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   enum mips_abi abi = mips_abi (gdbarch);
+  struct tdesc_arch_data *tdesc_data = (void *) info.tdep_info;
 
   switch (abi)
     {
@@ -1074,6 +1115,26 @@ mips_linux_init_abi (struct gdbarch_info
 	= mips_linux_in_dynsym_resolve_code;
     }
   set_solib_ops (gdbarch, &mips_svr4_so_ops);
+
+  set_gdbarch_write_pc (gdbarch, mips_linux_write_pc);
+
+  if (tdesc_data)
+    {
+      const struct tdesc_feature *feature;
+
+      /* If we have target-described registers, then we can safely
+	 reserve a number for MIPS_RESTART_REGNUM (whether it is
+	 described or not).  */
+      gdb_assert (gdbarch_num_regs (gdbarch) <= MIPS_RESTART_REGNUM);
+      set_gdbarch_num_regs (gdbarch, MIPS_RESTART_REGNUM + 1);
+
+      /* If it's present, then assign it to the reserved number.  */
+      feature = tdesc_find_feature (info.target_desc,
+				    "org.gnu.gdb.mips.linux");
+      if (feature != NULL)
+	tdesc_numbered_register (feature, tdesc_data, MIPS_RESTART_REGNUM,
+				 "restart");
+    }
 }
 
 void
Index: gdb/mips-linux-tdep.h
===================================================================
--- gdb.orig/mips-linux-tdep.h	2007-05-21 09:00:02.000000000 -0400
+++ gdb/mips-linux-tdep.h	2007-05-21 09:43:16.000000000 -0400
@@ -92,3 +92,13 @@ void mips64_supply_gregset (struct regca
 void mips64_fill_gregset (const struct regcache *, mips64_elf_gregset_t *, int);
 void mips64_supply_fpregset (struct regcache *, const mips64_elf_fpregset_t *);
 void mips64_fill_fpregset (const struct regcache *, mips64_elf_fpregset_t *, int);
+
+enum {
+  /* The Linux kernel stores an error code from any interrupted
+     syscall in a "register" (in $0's save slot).  */
+  MIPS_RESTART_REGNUM = MIPS_LAST_EMBED_REGNUM + 1
+};
+
+/* Return 1 if MIPS_RESTART_REGNUM is usable.  */
+
+int mips_linux_restart_reg_p (struct gdbarch *gdbarch);
Index: gdb/mips-tdep.c
===================================================================
--- gdb.orig/mips-tdep.c	2007-05-21 09:43:14.000000000 -0400
+++ gdb/mips-tdep.c	2007-05-21 09:43:16.000000000 -0400
@@ -5372,6 +5372,7 @@ mips_gdbarch_init (struct gdbarch_info i
   mips_register_g_packet_guesses (gdbarch);
 
   /* Hook in OS ABI-specific overrides, if they have been registered.  */
+  info.tdep_info = (void *) tdesc_data;
   gdbarch_init_osabi (info, gdbarch);
 
   /* Unwind the frame.  */
Index: gdb/Makefile.in
===================================================================
--- gdb.orig/Makefile.in	2007-05-21 09:43:14.000000000 -0400
+++ gdb/Makefile.in	2007-05-21 09:43:16.000000000 -0400
@@ -2337,12 +2337,14 @@ mips64obsd-tdep.o: mips64obsd-tdep.c $(d
 mips-irix-tdep.o: mips-irix-tdep.c $(defs_h) $(osabi_h) $(elf_bfd_h)
 mips-linux-nat.o: mips-linux-nat.c $(defs_h) $(mips_tdep_h) $(target_h) \
 	$(regcache_h) $(linux_nat_h) $(gdb_proc_service_h) $(gregset_h) \
-	$(mips_linux_tdep_h) $(inferior_h)
+	$(mips_linux_tdep_h) $(inferior_h) $(target_descriptions_h) \
+	$(xml_support_h)
 mips-linux-tdep.o: mips-linux-tdep.c $(defs_h) $(gdbcore_h) $(target_h) \
 	$(solib_svr4_h) $(osabi_h) $(mips_tdep_h) $(gdb_string_h) \
 	$(gdb_assert_h) $(frame_h) $(regcache_h) $(trad_frame_h) \
 	$(tramp_frame_h) $(gdbtypes_h) $(solib_h) $(symtab_h) \
-	$(mips_linux_tdep_h) $(solist_h) $(solib_svr4_h)
+	$(mips_linux_tdep_h) $(solist_h) $(solib_svr4_h) \
+	$(target_descriptions_h)
 mipsnbsd-nat.o: mipsnbsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \
 	$(target_h) $(mips_tdep_h) $(mipsnbsd_tdep_h) $(inf_ptrace_h)
 mipsnbsd-tdep.o: mipsnbsd-tdep.c $(defs_h) $(gdbcore_h) $(regcache_h) \
Index: gdb/doc/gdb.texinfo
===================================================================
--- gdb.orig/doc/gdb.texinfo	2007-05-21 09:44:34.000000000 -0400
+++ gdb/doc/gdb.texinfo	2007-05-21 09:46:44.000000000 -0400
@@ -25688,6 +25688,10 @@ it may be optional in a future version o
 contain registers @samp{f0} through @samp{f31}, @samp{fcsr}, and
 @samp{fir}.  They may be 32-bit or 64-bit depending on the target.
 
+The @samp{org.gnu.gdb.mips.linux} feature is optional.  It should
+contain a single register, @samp{restart}, which is used by the
+Linux kernel to control restartable syscalls.
+
 @include gpl.texi
 
 @raisesections


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]