From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23793 invoked by alias); 8 Feb 2008 18:57:51 -0000 Received: (qmail 23779 invoked by uid 22791); 8 Feb 2008 18:57:49 -0000 X-Spam-Check-By: sourceware.org Received: from NaN.false.org (HELO nan.false.org) (208.75.86.248) by sourceware.org (qpsmtpd/0.31) with ESMTP; Fri, 08 Feb 2008 18:57:31 +0000 Received: from nan.false.org (localhost [127.0.0.1]) by nan.false.org (Postfix) with ESMTP id 06E2498300; Fri, 8 Feb 2008 18:57:29 +0000 (GMT) Received: from caradoc.them.org (22.svnf5.xdsl.nauticom.net [209.195.183.55]) by nan.false.org (Postfix) with ESMTP id A16A79810C; Fri, 8 Feb 2008 18:57:28 +0000 (GMT) Received: from drow by caradoc.them.org with local (Exim 4.68) (envelope-from ) id 1JNYPf-0007vz-69; Fri, 08 Feb 2008 13:57:27 -0500 Date: Fri, 08 Feb 2008 18:57:00 -0000 From: Daniel Jacobowitz To: gdb-patches@sourceware.org Cc: Eli Zaretskii Subject: RFC: Allow a wrapper when starting programs Message-ID: <20080208185727.GA30185@caradoc.them.org> Mail-Followup-To: gdb-patches@sourceware.org, Eli Zaretskii MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.17 (2007-12-11) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-02/txt/msg00155.txt.bz2 This patch allows GDB and gdbserver to use wrapper programs when they launch an inferior process. One place this is useful is the example I used in the manual: incompatible environment variables. If you use "set environment", the changed variable is passed first to $SHELL and from there to the program. Normally that's fine, but you might be setting an LD_LIBRARY_PATH or LD_PRELOAD which will crash the shell. So instead you do this: (gdb) set exec-wrapper env 'LD_PRELOAD=libtest.so' (gdb) run And the variable is automatically passed to the right place. There's other examples, too. For instance you could use "su" as a wrapper to run the program as another user. That will only work if you started as root, since otherwise GDB tracing "su" will remove su's privileges. Does this look OK? -- Daniel Jacobowitz CodeSourcery 2008-02-08 Daniel Jacobowitz * Makefile.in (fork-child.o): Update. * NEWS: Document "set exec-wrapper" and the gdbserver --wrapper argument. Gather all gdbserver features together. * fork-child.c (exec_wrapper): New variable. (fork_inferior): Use it. (startup_inferior): Skip an extra trap if using "set exec-wrapper". (unset_exec_wrapper_command, _initialize_fork_child): New. * gdb.texinfo (Starting): Document "set exec-wrapper". (Server): Document gdbserver --wrapper. * server.c (wrapper_argv): New. (start_inferior): Handle wrapper_argv. If set, expect an extra trap. (gdbserver_usage): Document --wrapper. (main): Parse --wrapper. Index: Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/Makefile.in,v retrieving revision 1.978 diff -u -p -r1.978 Makefile.in --- Makefile.in 30 Jan 2008 07:17:31 -0000 1.978 +++ Makefile.in 8 Feb 2008 18:46:39 -0000 @@ -2109,7 +2109,7 @@ f-lang.o: f-lang.c $(defs_h) $(gdb_strin $(valprint_h) $(value_h) fork-child.o: fork-child.c $(defs_h) $(gdb_string_h) $(frame_h) \ $(inferior_h) $(target_h) $(gdb_wait_h) $(gdb_vfork_h) $(gdbcore_h) \ - $(terminal_h) $(gdbthread_h) $(command_h) $(solib_h) + $(terminal_h) $(gdbthread_h) $(command_h) $(gdbcmd_h) $(solib_h) frame-base.o: frame-base.c $(defs_h) $(frame_base_h) $(frame_h) \ $(gdb_obstack_h) frame.o: frame.c $(defs_h) $(frame_h) $(target_h) $(value_h) $(inferior_h) \ Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.256 diff -u -p -r1.256 NEWS --- NEWS 7 Feb 2008 19:23:10 -0000 1.256 +++ NEWS 8 Feb 2008 18:46:39 -0000 @@ -34,18 +34,6 @@ more than one contiguous range of addres * Target descriptions can now describe registers for PowerPC. -* The GDB remote stub, gdbserver, now supports the AltiVec and SPE -registers on PowerPC targets. - -* The GDB remote stub, gdbserver, now supports thread debugging on GNU/Linux -targets even when the libthread_db library is not available. - -* The GDB remote stub, gdbserver, now supports the new file transfer -commands (remote put, remote get, and remote delete). - -* The GDB remote stub, gdbserver, now supports run and attach in -extended-remote mode. - * hppa*64*-*-hpux11* target broken The debugger is unable to start a program and fails with the following error: "Error trying to get information about dynamic linker". @@ -60,6 +48,25 @@ Decimal Floating Point extension. In ad now has a set of pseudo-registers to inspect decimal float values stored in two consecutive float registers. +* New features in the GDB remote stub, gdbserver + + - AltiVec and SPE registers on PowerPC targets are now accessible. + + - Thread debugging is now supported on GNU/Linux targets even when + the libthread_db library is missing. + + - The new file transfer commands (remote put, remote get, and remote + delete) are supported with gdbserver. + + - In extended-remote mode, "run" and "attach" can be used to debug new + processes. + + - The "--multi" command-line argument lets gdbserver start without + a debuggee in extended-remote mode. + + - The "--wrapper" command-line argument tells gdbserver to use a + wrapper program to launch programs for debugging. + * New commands set print frame-arguments (all|scalars|none) @@ -72,6 +79,11 @@ remote get remote delete Transfer files to and from a remote target, and delete remote files. +set exec-wrapper +show exec-wrapper +unset exec-wrapper + Use a wrapper program to set launch programs for debugging. + * New MI commands -target-file-put Index: fork-child.c =================================================================== RCS file: /cvs/src/src/gdb/fork-child.c,v retrieving revision 1.38 diff -u -p -r1.38 fork-child.c --- fork-child.c 29 Jan 2008 21:11:24 -0000 1.38 +++ fork-child.c 8 Feb 2008 18:46:39 -0000 @@ -31,6 +31,7 @@ #include "terminal.h" #include "gdbthread.h" #include "command.h" /* for dont_repeat () */ +#include "gdbcmd.h" #include "solib.h" #include @@ -40,6 +41,8 @@ extern char **environ; +static char *exec_wrapper; + /* Break up SCRATCH into an argument vector suitable for passing to execvp and store it in ARGV. E.g., on "run a b c d" this routine would get as input the string "a b c d", and as output it would @@ -160,6 +163,9 @@ fork_inferior (char *exec_file_arg, char fact that it may expand when quoted; it is a worst-case number based on every character being '. */ len = 5 + 4 * strlen (exec_file) + 1 + strlen (allargs) + 1 + /*slop */ 12; + if (exec_wrapper) + len += strlen (exec_wrapper) + 1; + shell_command = (char *) alloca (len); shell_command[0] = '\0'; @@ -178,14 +184,22 @@ fork_inferior (char *exec_file_arg, char { /* We're going to call a shell. */ - /* Now add exec_file, quoting as necessary. */ - char *p; int need_to_quote; const int escape_bang = escape_bang_in_quoted_argument (shell_file); strcat (shell_command, "exec "); + /* Add any exec wrapper. That may be a program name with arguments, so + the user must handle quoting. */ + if (exec_wrapper) + { + strcat (shell_command, exec_wrapper); + strcat (shell_command, " "); + } + + /* Now add exec_file, quoting as necessary. */ + /* Quoting in this style is said to work with all shells. But csh on IRIX 4.0.1 can't deal with it. So we only quote it if we need to. */ @@ -399,6 +413,9 @@ startup_inferior (int ntraps) have stopped one instruction after execing the shell. Here we must get it up to actual execution of the real program. */ + if (exec_wrapper) + pending_execs++; + clear_proceed_status (); init_wait_for_inferior (); @@ -446,3 +463,26 @@ startup_inferior (int ntraps) } stop_soon = NO_STOP_QUIETLY; } + +/* Implement the "unset exec-wrapper" command. */ + +static void +unset_exec_wrapper_command (char *args, int from_tty) +{ + xfree (exec_wrapper); + exec_wrapper = NULL; +} + +void +_initialize_fork_child (void) +{ + add_setshow_filename_cmd ("exec-wrapper", class_run, &exec_wrapper, _("\ +Set a wrapper for running programs."), _("\ +Show the wrapper for running programs."), NULL, + NULL, NULL, + &setlist, &showlist); + + add_cmd ("exec-wrapper", class_run, unset_exec_wrapper_command, + _("Disable use of an execution wrapper."), + &unsetlist); +} Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.467 diff -u -p -r1.467 gdb.texinfo --- doc/gdb.texinfo 3 Feb 2008 18:55:27 -0000 1.467 +++ doc/gdb.texinfo 8 Feb 2008 18:46:42 -0000 @@ -1910,6 +1910,31 @@ these cases, using the @code{start} comm your program too late, as the program would have already completed the elaboration phase. Under these circumstances, insert breakpoints in your elaboration code before running your program. + +@kindex set exec-wrapper +@item set exec-wrapper @var{wrapper} +@itemx show exec-wrapper +@itemx unset exec-wrapper +If @samp{exec-wrapper} is set, the specified wrapper will be used to +launch programs for debugging. @value{GDBN} will start your program +with a shell command of the form @kbd{exec @var{wrapper} +@var{program}}. Quoting will be added to @var{program} and its +arguments, but not to @var{wrapper}, so you should add quotes if +appropriate for your shell. The wrapper will run until its first +debug trap before @value{GDBN} takes control. + +On Unix systems, a debug trap (@code{SIGTRAP}) is generated at the +@code{execve} system call. This allows any program which uses +@code{execve} to start another program to be used as a wrapper. For +example, you can use @code{env} to pass an environment variable to the +debugged program, without setting the variable to your shell's +environment: + +@smallexample +(@value{GDBP}) set exec-wrapper env 'LD_PRELOAD=libtest.so' +(@value{GDBP}) run +@end smallexample + @end table @node Arguments @@ -13074,6 +13099,27 @@ You can include @option{--debug} on the process. This option is intended for @code{gdbserver} development and for bug reports to the developers. +The @option{--wrapper} option specifies a wrapper to launch programs +for debugging. The option should be followed by the name of the +wrapper, then any command-line arguments to pass to the wrapper, then +@kbd{--} indicating the end of the wrapper arguments. + +@code{gdbserver} will run the specified wrapper program with a +combined command line including the wrapper arguments, then the name +of the program to debug, then any arguments to the program. The wrapper +will run until its first debug trap before @value{GDBN} gains control. + +On Unix systems, a debug trap (@code{SIGTRAP}) is generated at the +@code{execve} system call. This allows any program which uses +@code{execve} to start another program to be used as a wrapper. For +example, you can use @code{env} to pass an environment variable to the +debugged program, without setting the variable in @code{gdbserver}'s +environment: + +@smallexample +$ gdbserver --wrapper env LD_PRELOAD=libtest.so -- :2222 ./testprog +@end smallexample + @subsection Connecting to @code{gdbserver} Run @value{GDBN} on the host system. Index: gdbserver/server.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.c,v retrieving revision 1.63 diff -u -p -r1.63 server.c --- gdbserver/server.c 30 Jan 2008 00:51:50 -0000 1.63 +++ gdbserver/server.c 8 Feb 2008 18:46:42 -0000 @@ -41,7 +41,7 @@ static int attached; static int response_needed; static int exit_requested; -static char **program_argv; +static char **program_argv, **wrapper_argv; /* Enable miscellaneous debugging output. The name is historical - it was originally used to debug LinuxThreads support. */ @@ -81,16 +81,34 @@ target_running (void) } static int -start_inferior (char *argv[], char *statusptr) +start_inferior (char **argv, char *statusptr) { + char **new_argv = argv; attached = 0; + if (wrapper_argv != NULL) + { + int i, count = 1; + + for (i = 0; wrapper_argv[i] != NULL; i++) + count++; + for (i = 0; argv[i] != NULL; i++) + count++; + new_argv = alloca (sizeof (char *) * count); + count = 0; + for (i = 0; wrapper_argv[i] != NULL; i++) + new_argv[count++] = wrapper_argv[i]; + for (i = 0; argv[i] != NULL; i++) + new_argv[count++] = argv[i]; + new_argv[count] = NULL; + } + #ifdef SIGTTOU signal (SIGTTOU, SIG_DFL); signal (SIGTTIN, SIG_DFL); #endif - signal_pid = create_inferior (argv[0], argv); + signal_pid = create_inferior (new_argv[0], new_argv); /* FIXME: we don't actually know at this point that the create actually succeeded. We won't know that until we wait. */ @@ -107,6 +125,33 @@ start_inferior (char *argv[], char *stat atexit (restore_old_foreground_pgrp); #endif + if (wrapper_argv != NULL) + { + struct thread_resume resume_info; + int sig; + + resume_info.thread = -1; + resume_info.step = 0; + resume_info.sig = 0; + resume_info.leave_stopped = 0; + + sig = mywait (statusptr, 0); + if (*statusptr != 'T') + return sig; + + do + { + (*the_target->resume) (&resume_info); + + sig = mywait (statusptr, 0); + if (*statusptr != 'T') + return sig; + } + while (sig != TARGET_SIGNAL_TRAP); + + return sig; + } + /* Wait till we are at 1st instruction in program, return signal number (assuming success). */ return mywait (statusptr, 0); @@ -1001,7 +1046,8 @@ gdbserver_usage (void) "HOST:PORT to listen for a TCP connection.\n" "\n" "Options:\n" - " --debug\t\tEnable debugging output.\n"); + " --debug\t\tEnable debugging output.\n" + " --wrapper WRAPPER --\tRun WRAPPER to start new programs.\n"); } #undef require_running @@ -1045,6 +1091,23 @@ main (int argc, char *argv[]) attach = 1; else if (strcmp (*next_arg, "--multi") == 0) multi_mode = 1; + else if (strcmp (*next_arg, "--wrapper") == 0) + { + next_arg++; + + wrapper_argv = next_arg; + while (*next_arg != NULL && strcmp (*next_arg, "--") != 0) + next_arg++; + + if (next_arg == wrapper_argv || *next_arg == NULL) + { + gdbserver_usage (); + exit (1); + } + + /* Consume the "--". */ + *next_arg = NULL; + } else if (strcmp (*next_arg, "--debug") == 0) debug_threads = 1; else