Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFA] Enhance GDB to break inside DSO init code
@ 2004-07-23 20:24 Joel Brobecker
  2004-07-24  7:56 ` Eli Zaretskii
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Joel Brobecker @ 2004-07-23 20:24 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 3409 bytes --]

(My many thanks to David Anderson of SGI for explain in great details
how the runtime loader works, and how to get this working in GDB)

We are running on mips-irix native.

Consider a small program using a DSO (shared library) which has
been compiled with the following switch: -Wl,-init,myinit. This
means that the function myinit() is called when the shared library
is loaded, and before the program is executed.

Now, consider the following sequence of GDB commands:

        % gdb hello_sal
        (gdb) start
        (gdb) b myinit
        (gdb) run

We need to start the program first, in order for GDB to read the DSO
symbols. The expected behavior after the run is for GDB to stop in
myinit. But a limitation in the current startup implementation for
mips-irix is such that we end up inserting the breakpoint in myinit
*after* the DSO -init code is executed, hence too late. As a consequence,
The program runs to completion, vis:

        The program being debugged has been started already.
        Start it from the beginning? (y or n) y
        
        Starting program: /[...]/hello_sal 
        [...]
        Program exited normally.
        Current language:  auto; currently asm

For more details, you can refer to:

        http://sources.redhat.com/ml/gdb-patches/2004-07/msg00030.html

The trick, as explained in the message above, is to stop on syssgi()
exit notifications until we can detect that rld has been mapped in
memory by finding the __dbx_link symbol. Then insert a breakpoint
at its address and run until we reach this breakpoint. We then know
that all DSOs have been mapped at this point, and therefore compute
the list of DSOs and load their symbols. At which point we can finally
insert our shared-library breakpoints and let the program run as usual.
With the attached patch, we now stop at the breakpoint, as expected:

        (gdb) run
        The program being debugged has been started already.
        Start it from the beginning? (y or n) y
        
        Starting program: /[...]/hello_sal 
        
        Breakpoint 2, myinit () at /[...]/pack1.adb:29
        29          i := write (1, msg'address, msg'length);

2004-07-23  Joel Brobecker  <brobecker@gnat.com>

        * procfs.c (dbx_link_bpt_addr): New static global variable.
        (dbx_link_shadow_contents): New static global variable.
        (procfs_wait, case <PR_SYSEXIT>): Handle syssgi events.
        (procfs_wait, case <FLTBPT>): Remove the __dbx_link brekapoint
        if we just hit it.
        (procfs_init_inferior): Enable syssgi() syscall trace if appropriate.
        Reset dbx_link_bpt_addr as the address of __dbx_link() may change
        from run to run.
        (procfs_create_inferior): Remove syssgi syscall-exit notifications
        after the inferior has been forked.
        (remove_dbx_link_breakpoint): New function.
        (dbx_link_addr): New function.
        (insert_dbx_link_bpt_in_file): New function.
        (insert_dbx_link_bpt_in_region): New function.
        (insert_dbx_link_breakpoint): New function.
        (proc_trace_syscalls_1): New function, extracted from
        proc_trace_syscalls.
        (proc_trace_syscalls): Replace extract code by call to
        proc_trace_syscalls_1.
        * solib-irix.c (disable_break): Remove stop_pc assertion, as it
        is no longer valid.

Tested on mips-irix, no regression. OK to commit?

Thanks,
-- 
Joel

[-- Attachment #2: irix-rld.diff --]
[-- Type: text/plain, Size: 12238 bytes --]

Index: procfs.c
===================================================================
RCS file: /cvs/src/src/gdb/procfs.c,v
retrieving revision 1.54
diff -u -p -r1.54 procfs.c
--- procfs.c	25 May 2004 14:58:30 -0000	1.54
+++ procfs.c	23 Jul 2004 18:01:46 -0000
@@ -3382,6 +3382,17 @@ proc_iterate_over_threads (procinfo *pi,
 static ptid_t do_attach (ptid_t ptid);
 static void do_detach (int signo);
 static int register_gdb_signals (procinfo *, gdb_sigset_t *);
+static void proc_trace_syscalls_1 (procinfo *pi, int syscallnum,
+                                   int entry_or_exit, int mode, int from_tty);
+static int insert_dbx_link_breakpoint (procinfo *pi);
+static void remove_dbx_link_breakpoint (void);
+
+/* On mips-irix, we need to insert a breakpoint at __dbx_link during
+   the startup phase.  The following two variables are used to record
+   the address of the breakpoint, and the code that was replaced by
+   a breakpoint.  */
+static int dbx_link_bpt_addr = 0;
+static char dbx_link_shadow_contents[BREAKPOINT_MAX];
 
 /*
  * Function: procfs_debug_inferior
@@ -4065,6 +4076,22 @@ wait_again:
 		       address. */
 		    wstat = (SIGTRAP << 8) | 0177;
 		  }
+#ifdef SYS_syssgi
+                else if (what == SYS_syssgi)
+                  {
+                    /* see if we can break on dbx_link().  If yes, then
+                       we no longer need the SYS_syssgi notifications.  */
+                    if (insert_dbx_link_breakpoint (pi))
+                      proc_trace_syscalls_1 (pi, SYS_syssgi, PR_SYSEXIT,
+                                             FLAG_RESET, 0);
+
+                    /* This is an internal event and should be transparent
+                       to wfi, so resume the execution and wait again.  See
+                       comment in procfs_init_inferior() for more details.  */
+                    target_resume (ptid, 0, TARGET_SIGNAL_0);
+                    goto wait_again;
+                  }
+#endif
 		else if (syscall_is_lwp_create (pi, what))
 		  {
 		    /*
@@ -4191,6 +4218,13 @@ wait_again:
 #if (FLTTRACE != FLTBPT)	/* avoid "duplicate case" error */
 		case FLTTRACE:
 #endif
+                  /* If we hit our __dbx_link() internal breakpoint,
+                     then remove it.  See comments in procfs_init_inferior()
+                     for more details.  */
+                  if (dbx_link_bpt_addr != 0
+                      && dbx_link_bpt_addr == read_pc ())
+                    remove_dbx_link_breakpoint ();
+
 		  wstat = (SIGTRAP << 8) | 0177;
 		  break;
 		case FLTSTACK:
@@ -4842,6 +4876,32 @@ procfs_init_inferior (int pid)
   /* Typically two, one trap to exec the shell, one to exec the
      program being debugged.  Defined by "inferior.h".  */
   startup_inferior (START_INFERIOR_TRAPS_EXPECTED);
+
+#ifdef SYS_syssgi
+  /* On mips-irix, we need to stop the inferior early enough during
+     the startup phase in order to be able to load the shared library
+     symbols and insert the breakpoints that are located in these shared
+     libraries.  Stopping at the program entry point is not good enough
+     because the -init code is executed before the execution reaches
+     that point.
+
+     So what we need to do is to insert a breakpoint in the runtime
+     loader (rld), more precisely in __dbx_link().  This procedure is
+     called by rld once all shared libraries have been mapped, but before
+     the -init code is executed. Unfortuantely, this is not straightforward,
+     as rld is not part of the executable we are running, and thus we need
+     the inferior to run until rld itself has been mapped in memory.
+     
+     For this, we trace all syssgi() syscall exit events.  Each time
+     we detect such an event, we iterate over each text memory maps,
+     get its associated fd, and scan the symbol table for __dbx_link().
+     When found, we know that rld has been mapped, and that we can insert
+     the breakpoint at the symbol address.  Once the dbx_link() breakpoint
+     has been inserted, the syssgi() notifications are no longer necessary,
+     so they should be canceled.  */
+  proc_trace_syscalls_1 (pi, SYS_syssgi, PR_SYSEXIT, FLAG_SET, 0);
+  dbx_link_bpt_addr = 0;
+#endif
 }
 
 /*
@@ -5048,6 +5108,16 @@ procfs_create_inferior (char *exec_file,
   fork_inferior (exec_file, allargs, env, procfs_set_exec_trap,
 		 procfs_init_inferior, NULL, shell_file);
 
+#ifdef SYS_syssgi
+  /* Make sure to cancel the syssgi() syscall-exit notifications.  
+     They should normally have been removed by now, but they may still
+     be activated if the inferior doesn't use shared libraries, or if
+     we didn't locate __dbx_link, or if we never stopped in __dbx_link.
+     See procfs_init_inferior() for more details.  */
+  proc_trace_syscalls_1 (find_procinfo_or_die (PIDGET (inferior_ptid), 0),
+                         SYS_syssgi, PR_SYSEXIT, FLAG_RESET, 0);
+#endif
+  
   /* We are at the first instruction we care about.  */
   /* Pedal to the metal... */
 
@@ -5520,6 +5590,131 @@ proc_find_memory_regions (int (*func) (C
 				find_memory_regions_callback);
 }
 
+/* Remove the breakpoint that we inserted in __dbx_link().
+   Does nothing if the breakpoint hasn't been inserted or has already
+   been removed.  */
+
+static void
+remove_dbx_link_breakpoint (void)
+{
+  if (dbx_link_bpt_addr == 0)
+    return;
+
+  if (memory_remove_breakpoint (dbx_link_bpt_addr,
+                                dbx_link_shadow_contents) != 0)
+    warning ("Unable to remove __dbx_link breakpoint.");
+
+  dbx_link_bpt_addr = 0;
+}
+
+/* Return the address of the __dbx_link() function in the file
+   refernced by ABFD by scanning its symbol table.  Return 0 if
+   the symbol was not found.  */
+
+static CORE_ADDR
+dbx_link_addr (bfd *abfd)
+{
+  long storage_needed;
+  asymbol **symbol_table;
+  long number_of_symbols;
+  long i;
+
+  storage_needed = bfd_get_symtab_upper_bound (abfd);
+  if (storage_needed <= 0)
+    return 0;
+
+  symbol_table = (asymbol **) xmalloc (storage_needed);
+  make_cleanup (xfree, symbol_table);
+
+  number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+
+  for (i = 0; i < number_of_symbols; i++)
+    {
+      asymbol *sym = symbol_table[i];
+
+      if ((sym->flags & BSF_GLOBAL)
+          && sym->name != NULL && strcmp (sym->name, "__dbx_link") == 0)
+        return (sym->value + sym->section->vma);
+    }
+
+  /* Symbol not found, return NULL.  */
+  return 0;
+}
+
+/* Search the symbol table of the file referenced by FD for a symbol
+   named __dbx_link(). If found, then insert a breakpoint at this location,
+   and return nonzero.  Return zero otherwise.  */
+
+static int
+insert_dbx_link_bpt_in_file (int fd, CORE_ADDR ignored)
+{
+  bfd *abfd;
+  long storage_needed;
+  CORE_ADDR sym_addr;
+
+  abfd = bfd_fdopenr ("unamed", 0, fd);
+  if (abfd == NULL)
+    {
+      warning ("Failed to create a bfd: %s.\n", bfd_errmsg (bfd_get_error ()));
+      return 0;
+    }
+
+  if (!bfd_check_format (abfd, bfd_object))
+    {
+      /* Not the correct format, so we can not possibly find the dbx_link
+         symbol in it.  */
+      bfd_close (abfd);
+      return 0;
+    }
+
+  sym_addr = dbx_link_addr (abfd);
+  if (sym_addr != 0)
+    {
+      /* Insert the breakpoint.  */
+      dbx_link_bpt_addr = sym_addr;
+      if (target_insert_breakpoint (sym_addr, dbx_link_shadow_contents) != 0)
+        {
+          warning ("Failed to insert dbx_link breakpoint.");
+          bfd_close (abfd);
+          return 0;
+        }
+      bfd_close (abfd);
+      return 1;
+    }
+
+  bfd_close (abfd);
+  return 0;
+} 
+
+/* If the given memory region MAP contains a symbol named __dbx_link,
+   insert a breakpoint at this location and return nonzero.  Return
+   zero otherwise.  */
+
+static int
+insert_dbx_link_bpt_in_region (struct prmap *map,
+                               int (*child_func) (),
+                               void *data)
+{     
+  procinfo *pi = (procinfo *) data;
+        
+  /* We know the symbol we're looking for is in a text region, so
+     only look for it if the region is a text one.  */
+  if (map->pr_mflags & MA_EXEC)
+    return solib_mappings_callback (map, insert_dbx_link_bpt_in_file, pi);
+ 
+  return 0;
+}           
+
+/* Search all memory regions for a symbol named __dbx_link.  If found,
+   insert a breakpoint at its location, and return nonzero.  Return zero
+   otherwise.  */
+
+static int
+insert_dbx_link_breakpoint (procinfo *pi)
+{
+  return iterate_over_mappings (pi, NULL, pi, insert_dbx_link_bpt_in_region);
+}
+
 /*
  * Function: mappingflags
  *
@@ -5708,12 +5903,50 @@ info_proc_cmd (char *args, int from_tty)
   do_cleanups (old_chain);
 }
 
+/* Modify the status of the system call identified by SYSCALLNUM in
+   the set of syscalls that are currently traced/debugged.
+
+   If ENTRY_OR_EXIT is set to PR_SYSENTRY, then the entry syscalls set
+   will be updated. Otherwise, the exit syscalls set will be updated.
+
+   If MODE is FLAG_SET, then traces will be enabled. Otherwise, they
+   will be disabled.  */
+
+static void
+proc_trace_syscalls_1 (procinfo *pi, int syscallnum, int entry_or_exit,
+                      int mode, int from_tty)
+{
+  sysset_t *sysset;
+  
+  if (entry_or_exit == PR_SYSENTRY)
+    sysset = proc_get_traced_sysentry (pi, NULL);
+  else
+    sysset = proc_get_traced_sysexit (pi, NULL);
+
+  if (sysset == NULL)
+    proc_error (pi, "proc-trace, get_traced_sysset", __LINE__);
+
+  if (mode == FLAG_SET)
+    gdb_praddsysset (sysset, syscallnum);
+  else
+    gdb_prdelsysset (sysset, syscallnum);
+
+  if (entry_or_exit == PR_SYSENTRY)
+    {
+      if (!proc_set_traced_sysentry (pi, sysset))
+        proc_error (pi, "proc-trace, set_traced_sysentry", __LINE__);
+    }
+  else
+    {
+      if (!proc_set_traced_sysexit (pi, sysset))
+        proc_error (pi, "proc-trace, set_traced_sysexit", __LINE__);
+    }
+}
+
 static void
 proc_trace_syscalls (char *args, int from_tty, int entry_or_exit, int mode)
 {
   procinfo *pi;
-  sysset_t *sysset;
-  int       syscallnum = 0;
 
   if (PIDGET (inferior_ptid) <= 0)
     error ("you must be debugging a process to use this command.");
@@ -5724,30 +5957,9 @@ proc_trace_syscalls (char *args, int fro
   pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
   if (isdigit (args[0]))
     {
-      syscallnum = atoi (args);
-      if (entry_or_exit == PR_SYSENTRY)
-	sysset = proc_get_traced_sysentry (pi, NULL);
-      else
-	sysset = proc_get_traced_sysexit (pi, NULL);
-
-      if (sysset == NULL)
-	proc_error (pi, "proc-trace, get_traced_sysset", __LINE__);
-
-      if (mode == FLAG_SET)
-	gdb_praddsysset (sysset, syscallnum);
-      else
-	gdb_prdelsysset (sysset, syscallnum);
+      const int syscallnum = atoi (args);
 
-      if (entry_or_exit == PR_SYSENTRY)
-	{
-	  if (!proc_set_traced_sysentry (pi, sysset))
-	    proc_error (pi, "proc-trace, set_traced_sysentry", __LINE__);
-	}
-      else
-	{
-	  if (!proc_set_traced_sysexit (pi, sysset))
-	    proc_error (pi, "proc-trace, set_traced_sysexit", __LINE__);
-	}
+      proc_trace_syscalls_1 (pi, syscallnum, entry_or_exit, mode, from_tty);
     }
 }
 
Index: solib-irix.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-irix.c,v
retrieving revision 1.5
diff -u -p -r1.5 solib-irix.c
--- solib-irix.c	14 Feb 2004 15:46:33 -0000	1.5
+++ solib-irix.c	23 Jul 2004 18:01:46 -0000
@@ -324,15 +324,11 @@ disable_break (void)
       status = 0;
     }
 
-  /* For the SVR4 version, we always know the breakpoint address.  For the
-     SunOS version we don't know it until the above code is executed.
-     Grumble if we are stopped anywhere besides the breakpoint address. */
-
-  if (stop_pc != breakpoint_addr)
-    {
-      warning
-	("stopped at unknown breakpoint while handling shared libraries");
-    }
+  /* Note that it is possible that we have stopped at a location that
+     is different from the location where we inserted our breakpoint.
+     On mips-irix, we can actually land in __dbx_init(), so we should
+     not check the PC against our breakpoint address here.  See procfs.c
+     for more details.  */
 
   return (status);
 }

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2004-08-27 13:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-07-23 20:24 [RFA] Enhance GDB to break inside DSO init code Joel Brobecker
2004-07-24  7:56 ` Eli Zaretskii
2004-07-24 15:01   ` Joel Brobecker
2004-07-24 17:16     ` Eli Zaretskii
2004-08-06 18:31 ` Joel Brobecker
2004-08-19  7:55 ` Joel Brobecker
2004-08-24 23:45 ` Kevin Buettner
2004-08-27 13:39   ` Joel Brobecker

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox