tl;dr If you need only MyVar to be set:
$ gdb -ex 'set exec-wrapper env -i MyVar="`...`"' \
...
# or
$ gdb -ex 'set startup-with-shell off' \
-ex 'with confirm off -- unset env' \
-ex 'set env MyVar = '"`...`" \
...
You can do it this way:
$ readelf -s `which env` | grep '\bmain\b'
$ cat print-env.gdb
set $i = 0
while environ[$i]
print environ[$i]
set $i = $i + 1
end
$ gdb -ex 'break main' \
-ex run \
-x print-env.gdb \
--args env -i MyVar=`echo test` ruby
...
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
Starting program: /usr/bin/env -i MyVar=test ruby
warning: Error disabling address space randomization: Operation not permitted
process 32829 is executing new program: /usr/local/bin/ruby
Breakpoint 1, main (argc=1, argv=0x7ffd3cbca038) at ./main.c:55
55 ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffd3cbcafd9 "MyVar=test"
(gdb)
But in this case it starts env, which exec's ruby. Also gdb can't find the main symbol in the env binary, so it asks if you want to set the breakpoint anyway.
If you use start in place of break main; run, the result is the same (except that the breakpoint is temporary).
If env has the main symbol, gdb first stops in env, then in ruby (after continue):
$ readelf -s ../busybox/env | grep '\bmain\b'
5553: 00000000000110b9 122 FUNC GLOBAL DEFAULT 12 main
$ gdb -ex 'b main' \
-ex r \
-ex continue \
-x print-env.gdb \
--args ../busybox/env -i MyVar=`echo test` ruby
...
Breakpoint 1 at 0x110b9: file libbb/appletlib.c, line 1034.
Starting program: /busybox/env -i MyVar=test ruby
warning: Error disabling address space randomization: Operation not permitted
Breakpoint 1, main (argc=32723, argv=0x7fff2421cf10) at libbb/appletlib.c:1034
1034 {
Continuing.
process 32937 is executing new program: /usr/local/bin/ruby
Breakpoint 1, main (argc=1, argv=0x7ffd9c5d5568) at ./main.c:55
55 ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffd9c5d6fd9 "MyVar=test"
(gdb)
A better way is to use the exec-wrapper option:
$ gdb -ex 'set exec-wrapper ../busybox/env -i MyVar='`echo test` \
-ex start \
-x print-env.gdb \
--args ruby
...
Temporary breakpoint 1 at 0x363ed: file ./main.c, line 55.
Starting program: /ruby/ruby
warning: Error disabling address space randomization: Operation not permitted
Temporary breakpoint 1, main (argc=1, argv=0x7ffd0ebeea28) at ./main.c:55
55 ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffd0ebeefe2 "MyVar=test"
(gdb)
In this case gdb won't try to stop in env.
If you're okay with the process receiving some extra variables:
$ gdb -ex 'with confirm off -- unset env' \
-ex 'set env MyVar = '`echo test` \
-ex start \
-x print-env.gdb \
--args ruby
...
Temporary breakpoint 1 at 0x363ed: file ./main.c, line 55.
Starting program: /ruby/ruby
warning: Error disabling address space randomization: Operation not permitted
Temporary breakpoint 1, main (argc=1, argv=0x7ffcbee70748) at ./main.c:55
55 ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffcbee70fd0 "SHLVL=1"
$2 = 0x7ffcbee70fd8 "MyVar=test"
$3 = 0x7ffcbee70fe3 "PWD=/ruby"
(gdb)
The extra variables are set by the shell that gdb uses to execute your program:
If a shell is available on your target, the shell is used to pass the arguments, so that you may use normal conventions (such as wildcard expansion or variable substitution) in describing the arguments. In Unix systems, you can control which shell is used with the SHELL environment variable. If you do not define SHELL, GDB uses the default shell (/bin/sh). You can disable use of any shell with the set startup-with-shell command (see below for details).
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Starting.html#Starting
So a way to get rid of them is:
$ gdb -ex 'set startup-with-shell off' \
-ex 'with confirm off -- unset env' \
-ex 'set env MyVar = '`echo test` \
-ex start \
-x print-env.gdb \
--args ruby
...
Temporary breakpoint 1 at 0x363ed: file ./main.c, line 55.
Starting program: /ruby/ruby
warning: Error disabling address space randomization: Operation not permitted
Temporary breakpoint 1, main (argc=1, argv=0x7ffce046e2f8) at ./main.c:55
55 ruby_set_debug_option(getenv("RUBY_DEBUG"));
$1 = 0x7ffce046ffe2 "MyVar=test"
(gdb)
In cases more complex then echo test if you rely on the env utility you can use ${var@Q}:
$ MyVar=`...`
$ gdb -ex "set exec-wrapper env -i MyVar=${MyVar@Q}" ...
printf %q:
$ printf -v MyVar %q "`...`"
$ gdb -ex "set exec-wrapper env -i MyVar=$MyVar" ...
or just put the command inside exec-wrapper:
$ gdb -ex 'set exec-wrapper env -i MyVar="`...`"' ...
If you choose set env, then just enclose the command in double quotes:
$ gdb -ex 'set env MyVar = '"`...`" ...
gdb will skip the leading whitespaces, and take the rest of the line for the value of the MyVar variable.