This is the mail archive of the
gdb@sourceware.org
mailing list for the GDB project.
Re: GDB and LD_PRELOAD library-call interception
- From: Xavier de Gaye <xdegaye at gmail dot com>
- To: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- Cc: Kevin Pouget <kevin dot pouget at gmail dot com>, gdb at sourceware dot org
- Date: Sun, 3 Apr 2011 18:54:36 +0200
- Subject: Re: GDB and LD_PRELOAD library-call interception
- References: <AANLkTi=tESNe+Uj574gAQpiGXCX0ptM3ANsBWUOu+z+J@mail.gmail.com> <20110331084729.GA29683@host1.jankratochvil.net> <AANLkTimPAd3v2x=G5PVkzB0Jk_V9i2Zw-=vGMJPLTxWT@mail.gmail.com> <20110331121617.GA32739@host1.jankratochvil.net>
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
#! /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()
#! /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()