gdb supports on-demand compilation and code injection into programs running under gdb. GCC 5.0 or higher built with libcc1.so must be installed for this functionality to be enabled. This functionality is implemented with the following commands.
compile code
source-codecompile code -raw
– source-codeThe command allows you to specify source-code in two ways. The simplest method is to provide a single line of code to the command. E.g.:
compile code printf ("hello world\n");
If you specify options on the command line as well as source code, they may conflict. The ‘--’ delimiter can be used to separate options from actual source code. E.g.:
compile code -r -- printf ("hello world\n");
Alternatively you can enter source code as multiple lines of text. To enter this mode, invoke the ‘compile code’ command without any text following the command. This will start the multiple-line editor and allow you to type as many lines of source code as required. When you have completed typing, enter ‘end’ on its own line to exit the editor.
compile code >printf ("hello\n"); >printf ("world\n"); >end
Specifying ‘-raw’, prohibits gdb from wrapping the
provided source-code in a callable scope. In this case, you must
specify the entry point of the code by defining a function named
_gdb_expr_
. The ‘-raw’ code cannot access variables of the
inferior. Using ‘-raw’ option may be needed for example when
source-code requires ‘#include’ lines which may conflict with
inferior symbols otherwise.
compile file
filenamecompile file -raw
filenamecompile code
, but take the source code from filename.
compile file /home/user/example.c
compile print
exprcompile print /
f exprcompile print
compile print /
fThe process of compiling and injecting the code can be inspected using:
set debug compile
show debug compile
compile
commandgdb needs to specify the right compilation options for the code to be injected, in part to make its ABI compatible with the inferior and in part to make the injected code compatible with gdb's injecting process.
The options used, in increasing precedence:
gdbarch
)-m32
) or 64-bit
(-m64
) compilation option.
DW_AT_producer
part of DWARF debugging information according
to the gcc option -grecord-gcc-switches
. One has to
explicitly specify -g
during inferior compilation otherwise
gcc produces no DWARF. This feature is only relevant for
platforms where -g
produces DWARF by default, otherwise one may
try to enforce DWARF by using -gdwarf-4
.
set compile-args
You can override compilation options using the following command:
set compile-args
compile
commands. These options override any conflicting ones
from the target architecture and/or options stored during inferior
compilation.
show compile-args
compile
commandThere are a few caveats to keep in mind when using the compile
command. As the caveats are different per language, the table below
highlights specific issues on a per language basis.
compile
command will have much the same
access to variables and types as it normally would if it were part of
the program currently being debugged in gdb.
Below is a sample program that forms the basis of the examples that follow. This program has been compiled and loaded into gdb, much like any other normal debugging session.
void function1 (void) { int i = 42; printf ("function 1\n"); } void function2 (void) { int j = 12; function1 (); } int main(void) { int k = 6; int *p; function2 (); return 0; }
For the purposes of the examples in this section, the program above has
been compiled, loaded into gdb, stopped at the function
main
, and gdb is awaiting input from the user.
To access variables and types for any program in gdb, the
program must be compiled and packaged with debug information. The
compile
command is not an exception to this rule. Without debug
information, you can still use the compile
command, but you will
be very limited in what variables and types you can access.
So with that in mind, the example above has been compiled with debug
information enabled. The compile
command will have access to
all variables and types (except those that may have been optimized
out). Currently, as gdb has stopped the program in the
main
function, the compile
command would have access to
the variable k
. You could invoke the compile
command
and type some source code to set the value of k
. You can also
read it, or do anything with that variable you would normally do in
C
. Be aware that changes to inferior variables in the
compile
command are persistent. In the following example:
compile code k = 3;
the variable k
is now 3. It will retain that value until
something else in the example program changes it, or another
compile
command changes it.
Normal scope and access rules apply to source code compiled and
injected by the compile
command. In the example, the variables
j
and k
are not accessible yet, because the program is
currently stopped in the main
function, where these variables
are not in scope. Therefore, the following command
compile code j = 3;
will result in a compilation error message.
Once the program is continued, execution will bring these variables in
scope, and they will become accessible; then the code you specify via
the compile
command will be able to access them.
You can create variables and types with the compile
command as
part of your source code. Variables and types that are created as part
of the compile
command are not visible to the rest of the program for
the duration of its run. This example is valid:
compile code int ff = 5; printf ("ff is %d\n", ff);
However, if you were to type the following into gdb after that command has completed:
compile code printf ("ff is %d\n'', ff);
a compiler error would be raised as the variable ff
no longer
exists. Object code generated and injected by the compile
command is removed when its execution ends. Caution is advised
when assigning to program variables values of variables created by the
code submitted to the compile
command. This example is valid:
compile code int ff = 5; k = ff;
The value of the variable ff
is assigned to k
. The variable
k
does not require the existence of ff
to maintain the value
it has been assigned. However, pointers require particular care in
assignment. If the source code compiled with the compile
command
changed the address of a pointer in the example program, perhaps to a
variable created in the compile
command, that pointer would point
to an invalid location when the command exits. The following example
would likely cause issues with your debugged program:
compile code int ff = 5; p = &ff;
In this example, p
would point to ff
when the
compile
command is executing the source code provided to it.
However, as variables in the (example) program persist with their
assigned values, the variable p
would point to an invalid
location when the command exists. A general rule should be followed
in that you should either assign NULL
to any assigned pointers,
or restore a valid location to the pointer before the command exits.
Similar caution must be exercised with any structs, unions, and typedefs
defined in compile
command. Types defined in the compile
command will no longer be available in the next compile
command.
Therefore, if you cast a variable to a type defined in the
compile
command, care must be taken to ensure that any future
need to resolve the type can be achieved.
(gdb) compile code static struct a { int a; } v = { 42 }; argv = &v; (gdb) compile code printf ("%d\n", ((struct a *) argv)->a); gdb command line:1:36: error: dereferencing pointer to incomplete type ‘struct a’ Compilation failed. (gdb) compile code struct a { int a; }; printf ("%d\n", ((struct a *) argv)->a); 42
Variables that have been optimized away by the compiler are not
accessible to the code submitted to the compile
command.
Access to those variables will generate a compiler error which gdb
will print to the console.
compile
commandgdb needs to find gcc for the inferior being debugged which
may not be obvious for remote targets of different architecture than where
gdb is running. Environment variable PATH
(PATH
from
shell that executed gdb, not the one set by gdb
command set environment
). See Environment. PATH
on
gdb host is searched for gcc binary matching the
target architecture and operating system.
Specifically PATH
is searched for binaries matching regular expression
arch(-[^-]*)?-
os-gcc
according to the inferior target being
debugged. arch is processor name — multiarch is supported, so for
example both i386
and x86_64
targets look for pattern
(x86_64|i.86)
and both s390
and s390x
targets look
for pattern s390x?
. os is currently supported only for
pattern linux(-gnu)?
.