public inbox for gdb@sourceware.org
 help / color / mirror / Atom feed
* 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  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 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 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

* 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

* 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

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;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).