* GDB and LD_PRELOAD library-call interception @ 2011-03-31 8:25 Kevin Pouget 2011-03-31 8:47 ` Jan Kratochvil 0 siblings, 1 reply; 9+ messages in thread From: Kevin Pouget @ 2011-03-31 8:25 UTC (permalink / raw) To: gdb Hello, I'm playing with LD_PRELOAD to intercept some libC calls, and the behavior I observe under seems a bit strange: my shared library: > > #define __USE_GNU > #include <dlfcn.h> > > static void my_init (void) __attribute__ ((constructor)); > static void my_init (void) > { > printf("Hello world\n"); > } > > void * malloc(size_t size) > { > > void * ret; > > if(!malloc_func) { > printf("define malloc") ; > malloc_func = (void *(*)()) dlsym(RTLD_NEXT, "malloc"); > } > ret = malloc_func(size); > printf("malloc(%ld) = %p\n", size, ret); > return(ret); > } a standard execution: > > $ LD_PRELOAD=./libjit.so ./sleeper > Hello world > define malloc > malloc(64) = 0x1158010 and a GDB execution: > > $ gdb-cvs ./sleeper > GNU gdb (GDB) 7.2.50.20110321-cvs > # (same with GNU gdb (GDB) Fedora (7.2-46.fc14)) > (gdb) set environment LD_PRELOAD=./libjit.so > (gdb) start > Temporary breakpoint 1 at 0x400508: file sleeper.c, line 5. > Starting program: /home/kevin/travail/arm/perso/root/sample/debugger/sleeper > Hello world > define malloc > malloc(5) = 0x8e6010 > ... (repeated thousands of times, with different sizes) ... > Hello world > > Temporary breakpoint 1, main () at sleeper.c:8 > 8 malloc(64) ; > (gdb) next > define malloc malloc > malloc(64) = 0x601010 > 9 return 0; it looks like if the library is loaded twice, without any control on the first load ('my_init' pending breakpoint is only resolved before the second execution) Is it a bug or a feature? (or did I do something wrong ?) Thanks, Kevin ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 8:25 GDB and LD_PRELOAD library-call interception Kevin Pouget @ 2011-03-31 8:47 ` Jan Kratochvil 2011-03-31 9:47 ` Kevin Pouget 0 siblings, 1 reply; 9+ messages in thread From: Jan Kratochvil @ 2011-03-31 8:47 UTC (permalink / raw) To: Kevin Pouget; +Cc: gdb On Thu, 31 Mar 2011 10:25:17 +0200, Kevin Pouget wrote: > it looks like if the library is loaded twice, without any control on > the first load ('my_init' pending breakpoint is only resolved before > the second execution) See `set exec-wrapper', GDB runs a normal shell to process the startup, GDB traps its first exec() call. Therefore LD_PRELOAD applies already on the shell. Regards, Jan ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 8:47 ` Jan Kratochvil @ 2011-03-31 9:47 ` Kevin Pouget 2011-03-31 12:16 ` Jan Kratochvil 2011-03-31 15:06 ` Tom Tromey 0 siblings, 2 replies; 9+ messages in thread From: Kevin Pouget @ 2011-03-31 9:47 UTC (permalink / raw) To: Jan Kratochvil; +Cc: gdb oh yes, thanks, that's an interesting command ! I was wondering if there could be a way to hack this exec-wrapper to launch for instance the executable in an xterm window? personally, I don't like debugging GDB with GDB from a single terminal, so I need to do a pid-attach; "set exec-wrapper xterm" would be very conveniant for any console-based interactive program, I guess but it it possible to follow easily (two) forks? let me know if you think that the idea is interesting / feasible Cordially, Kevin On Thu, Mar 31, 2011 at 4:47 AM, Jan Kratochvil <jan.kratochvil@redhat.com> wrote: > On Thu, 31 Mar 2011 10:25:17 +0200, Kevin Pouget wrote: >> it looks like if the library is loaded twice, without any control on >> the first load ('my_init' pending breakpoint is only resolved before >> the second execution) > > See `set exec-wrapper', GDB runs a normal shell to process the startup, GDB > traps its first exec() call. Therefore LD_PRELOAD applies already on the > shell. > > > Regards, > Jan > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 9:47 ` Kevin Pouget @ 2011-03-31 12:16 ` Jan Kratochvil 2011-03-31 15:07 ` Tom Tromey 2011-04-03 16:54 ` Xavier de Gaye 2011-03-31 15:06 ` Tom Tromey 1 sibling, 2 replies; 9+ messages in thread From: Jan Kratochvil @ 2011-03-31 12:16 UTC (permalink / raw) To: Kevin Pouget; +Cc: gdb On Thu, 31 Mar 2011 11:46:56 +0200, Kevin Pouget wrote: > I was wondering if there could be a way to hack this exec-wrapper to > launch for instance the executable in an xterm window? It is not such straightforward, GDB expects the PID it has spawned will be debugged while with xterm the process being debugged is its child. I guess you can write a small C helper which will: * create new pty * change its fds 0/1/2 to the slave of this pty * fork xterm -e own-helper-part pty-unique-id In own-helper-part interconnect the pty master part and its fds 0/1/2. Another way is to make fork-child.c more flexible/configurable. > but it it possible to follow easily (two) forks? Yes, `set detach-on-fork off', so-called multi-inferior mode, it has some issues on native GNU/Linux system. Regards, Jan ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 12:16 ` Jan Kratochvil @ 2011-03-31 15:07 ` Tom Tromey 2011-04-03 16:54 ` Xavier de Gaye 1 sibling, 0 replies; 9+ messages in thread From: Tom Tromey @ 2011-03-31 15:07 UTC (permalink / raw) To: Jan Kratochvil; +Cc: Kevin Pouget, gdb Kevin> but it it possible to follow easily (two) forks? Jan> Yes, `set detach-on-fork off', so-called multi-inferior mode, it has some Jan> issues on native GNU/Linux system. If you do try this, and run into bugs, please file them in bugzilla. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 12:16 ` Jan Kratochvil 2011-03-31 15:07 ` Tom Tromey @ 2011-04-03 16:54 ` Xavier de Gaye [not found] ` <BANLkTi=j6+-85R4B+N1Nd5kb9bkEZfsT9A@mail.gmail.com> 1 sibling, 1 reply; 9+ messages in thread From: Xavier de Gaye @ 2011-04-03 16:54 UTC (permalink / raw) To: Jan Kratochvil; +Cc: Kevin Pouget, gdb [-- Attachment #1: Type: text/plain, Size: 1465 bytes --] On Thu, Mar 31, 2011 at 2:16 PM, Jan Kratochvil wrote: > > It is not such straightforward, GDB expects the PID it has spawned will be > debugged while with xterm the process being debugged is its child. > > I guess you can write a small C helper which will: > * create new pty > * change its fds 0/1/2 to the slave of this pty > * fork xterm -e own-helper-part pty-unique-id > In own-helper-part interconnect the pty master part and its fds 0/1/2. > The attached files 'xterm_wrapper.py' and 'interconnect_pty.py' are a raw implementation written in python of the above scheme. In gdb do: set exec-wrapper python xterm_wrapper.py The problem with the implementation is that the debuggee cannot set the slave pty as its controlling terminal without forking (set SET_CONTROLLING_TERMINAL to True in 'xterm_wrapper.py' in order to do that, but then exec-wrapper cannot be used). So that it is not possible to interrupt the debuggee with a 'C-c' character. On the other hand the 'interconnect_pty.py' helper used by 'xterm_wrapper.py' can also be used by itself in stand-alone. When run without arguments, it creates a pty and prints the slave pty name so that it can be used in gdb with: set inferior-tty /dev/pts/nn It can also be made to spawn gdb in a new xterm and set correctly the '-tty' gdb option with tne new pty name (kind of the reverse of the initial scheme): python interconnect_pty.py --exec gdb --args '/path/to/debuggee' Xavier [-- Attachment #2: interconnect_pty.py --] [-- Type: text/x-python, Size: 3670 bytes --] #! /usr/bin/env python import os import sys import signal import errno import copy import pty import optparse import termios import asyncore class FileDispatcher(asyncore.file_dispatcher): def __init__(self, in_fd, out_fd, quit_char=None): asyncore.file_dispatcher.__init__(self, in_fd) self.out_fd = out_fd self.quit_char = quit_char def writable(self): return False def handle_read(self): # called from the select loop whenever data is available try: data = self.recv(1024) # terminate the select loop if data == self.quit_char: raise asyncore.ExitNow( '\n%s terminated.' % os.path.basename(sys.argv[0])) os.write(self.out_fd, data) except OSError, err: if err[0] != errno.EAGAIN: raise asyncore.ExitNow(err) def parse_options(argv): formatter = optparse.IndentedHelpFormatter(max_help_position=30) parser = optparse.OptionParser( usage='usage: python %prog [options]', formatter=formatter) parser.add_option('-e', '--exec', type='string', dest='pgm', help='program to spawn in a new xterm terminal ' 'that will use the slave pseudo terminal') parser.add_option('-t', '--tty', type='string', default='-tty', metavar='TTY_OPT', help='pass to PGM the slave pseudo terminal name using' ' the TTY_OPT option (default \'%default\')') parser.add_option('-a', '--args', type='string', help='PGM arguments (must be quoted)') parser.add_option('-m', '--master_fd', type='int', metavar='FD', help='master pseudo terminal file descriptor to use') (options, args) = parser.parse_args(args=argv) return options def spawn_xterm(options, ptyname, slave_fd, master_fd): argv = ['xterm', '-e', options.pgm, options.tty, ptyname] if options.args is not None: # FIXME quotes in args are not handled argv.extend(options.args.split()) pid = os.fork() if pid == 0: os.close(slave_fd) os.close(master_fd) os.setsid() os.execvp(argv[0], argv) master_fd = None def sigint_handler(signum, frame): # write a C-c character if master_fd is not None: os.write(master_fd, chr(3)) def main(): global master_fd options = parse_options(sys.argv[1:]) master_fd = options.master_fd if not isinstance(master_fd, int): master_fd, slave_fd = pty.openpty() ptyname = os.ttyname(slave_fd) print "Slave pseudo terminal to use: '%s'" % ptyname if options.pgm is not None: spawn_xterm(options, ptyname, slave_fd, master_fd) print 'Type C-a to exit.\n' signal.signal(signal.SIGINT, sigint_handler) # no echo, no canonical processing attr = termios.tcgetattr(0) init_attr = copy.deepcopy(attr) attr[3] = attr[3] & ~termios.ECHO attr[3] = attr[3] & ~termios.ICANON termios.tcsetattr(0, termios.TCSADRAIN, attr) # interconnect stdin to master_fd, and master_fd to stdout # with an asyncore select loop # C-a is the Quit character master_slave = FileDispatcher(0, master_fd, chr(1)) slave_master = FileDispatcher(master_fd, 1) err = '' try: while asyncore.socket_map: asyncore.poll() except asyncore.ExitNow, err: pass os.close(slave_fd) os.close(master_fd) termios.tcsetattr(0, termios.TCSADRAIN, init_attr) if err: print err if __name__ == '__main__': main() [-- Attachment #3: xterm_wrapper.py --] [-- Type: text/x-python, Size: 1072 bytes --] #! /usr/bin/env python import sys import os import pty import termios import fcntl def fork_xterm(master_fd, slave_fd): pid = os.fork() if pid == 0: os.close(slave_fd) helper = os.path.join( os.path.dirname(sys.argv[0]), 'interconnect_pty.py') argv = ['xterm', '-e', 'python', helper, '--master_fd', str(master_fd)] os.execvp(argv[0], argv) os.close(master_fd) return pid def exec_pgm(slave_fd, argv): os.dup2(slave_fd, 0) os.dup2(slave_fd, 1) os.dup2(slave_fd, 2) os.close(slave_fd) os.execvp(argv[0], argv) SET_CONTROLLING_TERMINAL = False def main(): master_fd, slave_fd = pty.openpty() pid = fork_xterm(master_fd, slave_fd) argv = sys.argv[1:] if SET_CONTROLLING_TERMINAL: argv[0] = os.path.abspath(argv[0]) pid = os.fork() if pid == 0: os.setsid() fcntl.ioctl(slave_fd, termios.TIOCSCTTY) exec_pgm(slave_fd, argv) else: exec_pgm(slave_fd, argv) if __name__ == '__main__': main() ^ permalink raw reply [flat|nested] 9+ messages in thread
[parent not found: <BANLkTi=j6+-85R4B+N1Nd5kb9bkEZfsT9A@mail.gmail.com>]
* Re: GDB and LD_PRELOAD library-call interception [not found] ` <BANLkTi=j6+-85R4B+N1Nd5kb9bkEZfsT9A@mail.gmail.com> @ 2011-04-04 13:35 ` Kevin Pouget 0 siblings, 0 replies; 9+ messages in thread From: Kevin Pouget @ 2011-04-04 13:35 UTC (permalink / raw) To: gdb [-- Attachment #1: Type: text/plain, Size: 2437 bytes --] Here is a *prototype* patch of what I discussed earlier, which allows to follow the N-th fork of a wrapper. (my) `xterm' first forks /usr/libexec/utempter/utempter before starting the program to debug, so it's skipped, then GDB follows the forked child instead of the parent (I still need to figure out how not to create a new inferior in this case). C-c in the xterm is not intercepted by GDB, I don't know if that's a bug or a feature; however C-c in GDB terminal kills the xterm instead of stopping the debuggee ... I guess it's a matter of terminal ownership, the signal is just not sent to the right process (that's the opposite of Xavier's problem :) let me know what you think about it, I'll fix the bugs if it is seem interesting for the community Kevin On Sun, Apr 3, 2011 at 12:54 PM, Xavier de Gaye <xdegaye@gmail.com> wrote: > > On Thu, Mar 31, 2011 at 2:16 PM, Jan Kratochvil wrote: > > > > It is not such straightforward, GDB expects the PID it has spawned will be > > debugged while with xterm the process being debugged is its child. > > > > I guess you can write a small C helper which will: > > * create new pty > > * change its fds 0/1/2 to the slave of this pty > > * fork xterm -e own-helper-part pty-unique-id > > In own-helper-part interconnect the pty master part and its fds 0/1/2. > > > > > The attached files 'xterm_wrapper.py' and 'interconnect_pty.py' are a > raw implementation written in python of the above scheme. In gdb do: > > set exec-wrapper python xterm_wrapper.py > > The problem with the implementation is that the debuggee cannot set > the slave pty as its controlling terminal without forking (set > SET_CONTROLLING_TERMINAL to True in 'xterm_wrapper.py' in order to do > that, but then exec-wrapper cannot be used). So that it is not > possible to interrupt the debuggee with a 'C-c' character. > > On the other hand the 'interconnect_pty.py' helper used by > 'xterm_wrapper.py' can also be used by itself in stand-alone. When run > without arguments, it creates a pty and prints the slave pty name so > that it can be used in gdb with: > > set inferior-tty /dev/pts/nn > > It can also be made to spawn gdb in a new xterm and set correctly the > '-tty' gdb option with tne new pty name (kind of the reverse of the > initial scheme): > > python interconnect_pty.py --exec gdb --args '/path/to/debuggee' > > Xavier [-- Attachment #2: gdb-xterm.diff --] [-- Type: application/octet-stream, Size: 2747 bytes --] diff --git a/gdb/fork-child.c b/gdb/fork-child.c index bb173e7..9971c83 100644 --- a/gdb/fork-child.c +++ b/gdb/fork-child.c @@ -34,7 +34,7 @@ #include "command.h" /* for dont_repeat () */ #include "gdbcmd.h" #include "solib.h" - +#include "linux-nat.h" #include <signal.h> /* This just gets used as a default if we can't find SHELL. */ @@ -421,11 +421,20 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env, return pid; } +/* Follow the NFORK-th child. */ +#define NFORK 2 +static int +I_want_follow_fork_child() { + static int fork = 0 ; + return (++fork % (NFORK)) == 0 ; +} + /* Accept NTRAPS traps from the inferior. */ void startup_inferior (int ntraps) { + int enabled = 0 ; int pending_execs = ntraps; int terminal_initted = 0; ptid_t resume_ptid; @@ -451,16 +460,37 @@ startup_inferior (int ntraps) memset (&ws, 0, sizeof (ws)); event_ptid = target_wait (resume_ptid, &ws, 0); + if (!enabled) { + linux_enable_event_reporting (inferior_ptid); + enabled = 1 ; + } + if (ws.kind == TARGET_WAITKIND_IGNORE) /* The inferior didn't really stop, keep waiting. */ continue; switch (ws.kind) { + case TARGET_WAITKIND_FORKED: + case TARGET_WAITKIND_VFORKED: { + int follow_the_child = I_want_follow_fork_child() ; + int old_detach_fork = detach_fork ; + + /* Force GDB to detach the forks. */ + detach_fork = 1 ; + inferior_thread ()->pending_follow = ws ; + + if (target_follow_fork(follow_the_child) != 0) { + warning("couldn't follow fork child") ; + } else if (follow_the_child) { + /* Resume the new child PID. */ + resume_ptid = inferior_ptid ; + } + detach_fork = old_detach_fork ; + break ; + } case TARGET_WAITKIND_SPURIOUS: case TARGET_WAITKIND_LOADED: - case TARGET_WAITKIND_FORKED: - case TARGET_WAITKIND_VFORKED: case TARGET_WAITKIND_SYSCALL_ENTRY: case TARGET_WAITKIND_SYSCALL_RETURN: /* Ignore gracefully during startup of the inferior. */ diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c index 7f654af..3eddea4 100644 --- a/gdb/linux-fork.c +++ b/gdb/linux-fork.c @@ -628,6 +628,11 @@ checkpoint_command (char *args, int from_tty) pid_t retpid; struct cleanup *old_chain; + /* Ensure that the inferior is not multithreaded. */ + update_thread_list () ; + if (thread_count () > 1) + error(_("checkpoint: can't checkpoint multiple threads.")) ; + /* Make the inferior fork, record its (and gdb's) state. */ if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL) ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 9:47 ` Kevin Pouget 2011-03-31 12:16 ` Jan Kratochvil @ 2011-03-31 15:06 ` Tom Tromey 2011-03-31 15:55 ` Kevin Pouget 1 sibling, 1 reply; 9+ messages in thread From: Tom Tromey @ 2011-03-31 15:06 UTC (permalink / raw) To: Kevin Pouget; +Cc: Jan Kratochvil, gdb >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes: Kevin> I was wondering if there could be a way to hack this exec-wrapper to Kevin> launch for instance the executable in an xterm window? Thiago sent a patch a while back for this. Look for "[RFC] patch to make GDB open a new terminal window for the inferior", on May 19 2009. I never tried it, but it seems like a decent idea. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: GDB and LD_PRELOAD library-call interception 2011-03-31 15:06 ` Tom Tromey @ 2011-03-31 15:55 ` Kevin Pouget 0 siblings, 0 replies; 9+ messages in thread From: Kevin Pouget @ 2011-03-31 15:55 UTC (permalink / raw) To: Tom Tromey; +Cc: Jan Kratochvil, gdb here is a link the the post: http://sourceware.org/ml/gdb-patches/2009-05/msg00409.html I was rather thinking about following the fork()s and exec()s (don't really know much about tty and pts), so that instead of executing > $shell -c $wrapper $bin where GDB has to skip 2 or 3 'exec', we could do something like > $shell -c xterm -e $bin where GDB would skip 2 exec, and attach to the child forked by 'xterm' (or equivalent). in fact, it could look like an 'set fork-wrapper', because the debuggee is not 'exec'ed, but forked I'll give it a try, and come back on the thread if it seems to work (one way or ther other) Kevin On Thu, Mar 31, 2011 at 5:06 PM, Tom Tromey <tromey@redhat.com> wrote: > > >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes: > > Kevin> I was wondering if there could be a way to hack this exec-wrapper to > Kevin> launch for instance the executable in an xterm window? > > Thiago sent a patch a while back for this. Look for "[RFC] patch to > make GDB open a new terminal window for the inferior", on May 19 2009. > > I never tried it, but it seems like a decent idea. > > Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2011-04-04 13:35 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-31 8:25 GDB and LD_PRELOAD library-call interception Kevin Pouget
2011-03-31 8:47 ` Jan Kratochvil
2011-03-31 9:47 ` Kevin Pouget
2011-03-31 12:16 ` Jan Kratochvil
2011-03-31 15:07 ` Tom Tromey
2011-04-03 16:54 ` Xavier de Gaye
[not found] ` <BANLkTi=j6+-85R4B+N1Nd5kb9bkEZfsT9A@mail.gmail.com>
2011-04-04 13:35 ` Kevin Pouget
2011-03-31 15:06 ` Tom Tromey
2011-03-31 15:55 ` Kevin Pouget
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox