This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: [RFA] Enhance GDB to break inside DSO init code
- From: Joel Brobecker <brobecker at gnat dot com>
- To: gdb-patches at sources dot redhat dot com
- Date: Thu, 19 Aug 2004 09:55:30 +0200
- Subject: Re: [RFA] Enhance GDB to break inside DSO init code
- References: <20040723202447.GM20596@gnat.com>
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);
> }