From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29991 invoked by alias); 6 Aug 2004 18:31:46 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 29970 invoked from network); 6 Aug 2004 18:31:42 -0000 Received: from unknown (HELO takamaka.act-europe.fr) (142.179.108.108) by sourceware.org with SMTP; 6 Aug 2004 18:31:42 -0000 Received: by takamaka.act-europe.fr (Postfix, from userid 507) id 723AC47D91; Fri, 6 Aug 2004 11:31:42 -0700 (PDT) Date: Fri, 06 Aug 2004 18:31:00 -0000 From: Joel Brobecker To: gdb-patches@sources.redhat.com Subject: Re: [RFA] Enhance GDB to break inside DSO init code Message-ID: <20040806183142.GT1192@gnat.com> References: <20040723202447.GM20596@gnat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20040723202447.GM20596@gnat.com> User-Agent: Mutt/1.4i X-SW-Source: 2004-08/txt/msg00162.txt.bz2 Ping? 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 > > * procfs.c (dbx_link_bpt_addr): New static global variable. > (dbx_link_shadow_contents): New static global variable. > (procfs_wait, case ): Handle syssgi events. > (procfs_wait, case ): 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); > } -- Joel