This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: Allow a wrapper when starting programs


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  <dan@codesourcery.com>

	* 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 <signal.h>
@@ -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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]