This is the mail archive of the gdb-patches@sources.redhat.com 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]

Re: [RFA] Enhance GDB to break inside DSO init code


Double-ping?

Thanks,
-- 
Joel

On Fri, Jul 23, 2004 at 01:24:47PM -0700, Joel Brobecker wrote:
> (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

> 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);
>  }


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