GDB and C Preprocessor Macro
C preprocessor macro are quite convenient while programming, however they can quickly become a burden while compiling and debugging. Consider this little C program:
#define str(s) #s #define xstr(s) str(s) #define A 10 #define B 15 #ifndef C #define C 15 #endif #define MIN(x, y) (x > y ? y : x) int printf (const char * format, ... ); void main(int argc, char **argv) { printf("test1 " xstr(A) " vs " xstr(B) " -> %d\n", MIN(A, B)); #undef A #undef B #define A 0 printf("test2 " xstr(A) " vs " xstr(C) " -> %d\n", MIN(A, C)); }
It outputs, as expected:
$ gcc test.c && ./a.out test1 10 vs 15 -> 10 test2 0 vs 15 -> 0 $ gcc test.c -DC=-5 && ./a.out test1 10 vs 15 -> 10 test2 0 vs -5 -> -5
Let's add a -g in the compiler flags and see how we can follow its execution with GDB.
Debugging macros with GDB, the hard way
$ gcc test.c -DC=-5 -g && gdb ./a.out GNU gdb (GDB) 7.8.1 Reading symbols from ./a.out...done. (gdb) start Starting program: /tmp/a.out Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe998) at test.c:15 15 printf("test1 " xstr(A) " vs " xstr(B) " -> %d\n", MIN(A, B)); (gdb) print A No symbol "A" in current context. (gdb) p B No symbol "B" in current context.
Okay, so we can't go anywhere so easily. So let's continue the hard way:
(gdb) x/4i $pc # print 4 i-instructions after the current address of the program counter/instruction pointer $pc => 0x400515 <main+15>: mov $0xa,%esi 0x40051a <main+20>: mov $0x4005c4,%edi 0x40051f <main+25>: mov $0x0,%eax 0x400524 <main+30>: callq 0x4003e0 <printf@plt> (gdb) p/d 0xa # print hexadecimal number 0xa in d-igits $2 = 10 (gdb) p (char *) 0x4005c4 # cast this address in char* and print it $3 = 0x4005c4 "test1 10 vs 15 -> %d\n"
There we are, the processor is going to execute printf("test1 10 vs 15 -> %d\n", 10). Naively I expected to see the comparison between 10 and 15, but the compiler optimizes that automatically, even with -O0 flag. Change one operand to a 'real' C variable to see the comparison in the code.
That works, but that's a bit hardcore, and not easy to apply in all the situation. Nonetheless, it can be useful for instance to see which functions(s) will be called by a preprocessor macro function.
What can we do to better understand such pieces of code ?
First, let's remember how the preprocessor works: it preprocesses the source file, before passing it to the actual C compiler.
So let's intercept this step.
Debugging macros with GCC, an easier way
GCC can dump this intermediate step with the flag -E, or directly apply cpp to your file:
$ cpp test.c -DC=-5 # OR # gcc -E test.c -DC=-5 # 1 "test.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 1 "<command-line>" 2 # 1 "test.c" # 12 "test.c" int printf (const char * format, ... ); void main(int argc, char **argv) { printf("test1 " "10" " vs " "15" " -> %d\n", (10 > 15 ? 15 : 10)); printf("test2 " "0" " vs " "-5" " -> %d\n", (0 > -5 ? -5 : 0)); }
And there we can see what is exactly fed to the C compiler. Note that the comparison is still here, so it's (obviously) not the preprocessor which optimized it out.
But this is not very interactive ... and we said that we want to use GDB!
Debugging macros with GDB, the nice way
The lasts versions of GCC+GDB (I can't say since when it's in place, just that it works with my up-to-date Archlinux) can respectively include and interpret preprocessor macro definitions inside the binary's debugging information. It's not included in the standard -g debugging flag, but in -g3, certainly because of the quantity of information that need to be included with dozens of header files are included in a C file.
$ gcc -g3 test.c -DC=-5 && gdb a.out GNU gdb (GDB) 7.8.1 Reading symbols from a.out...done. (gdb) info macro A The symbol `A' has no definition as a C/C++ preprocessor macro at <user-defined>:-1
Argl, what's that? all the article was a lie? all of that to come to 'A' has no definition as a C/C++ preprocessor macro? That's what I though until yesterday. I tried to play with macros in GDB a couple of time, but always failed this hard. Until I read this forum post, that says in a more polite way: RTFM!
gdb uses the current listing position to decide which macro definitions are in scope.
Oh, yes, indeed, it makes sense ... !
(gdb) start Temporary breakpoint 1 at 0x400515: file test.c, line 15. Starting program: /tmp/a.out Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe998) at test.c:15 15 printf("test1 " xstr(A) " vs " xstr(B) " -> %d\n", MIN(A, B)); (gdb) info macro A Defined at /tmp/test.c:4 #define A 10 (gdb) next test1 10 vs 15 -> 10 19 printf("test2 " xstr(A) " vs " xstr(C) " -> %d\n", MIN(A, C)); (gdb) info macro A Defined at /tmp/test.c:18 #define A 0 (gdb) info macro C Defined at /tmp/test.c:0 -DC=-5 (gdb) info macro MIN Defined at /tmp/test.c:11 #define MIN(x, y) (x > y ? y : x)
GDB can also expand macro functions:
(gdb) macro expand MIN(A, B) expands to: (10 > 15 ? 15 : 10) (gdb) macro expand MIN(A, MIN(A, 15)) expands to: (10 > (10 > 15 ? 15 : 10) ? (10 > 15 ? 15 : 10) : 10) (gdb) p (10 > (10 > 15 ? 15 : 10) ? (10 > 15 ? 15 : 10) : 10) $1 = 10
There we are, and that's working well, cool :-) (if you run gcc -E test.c -DC=-5 -g3 you'll see that the preprocessor passes the #defines to the compiler, including the builtin ones.
However, I couldn't get macro define|list|undef to work. According to the help, GDB should be able to change the value of macro-definitions, but that seems very hard to implement, maybe that's why it doesn't work ...
(gdb) help macro define Define a new C/C++ preprocessor macro. The GDB command `macro define DEFINITION' is equivalent to placing a preprocessor directive of the form `#define DEFINITION' such that the definition is visible in all the inferior's source files. For example: (gdb) macro define PI (3.1415926) (gdb) macro define MIN(x,y) ((x) < (y) ? (x) : (y)) (gdb) start Temporary breakpoint 1 at 0x400515: file test.c, line 15. Starting program: /tmp/a.out Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe998) at test.c:15 15 printf("test1 " xstr(A) " vs " xstr(B) " -> %d\n", MIN(A, B)); (gdb) macro define A -5 (gdb) macro define B -10 (gdb) n test1 10 vs 15 -> 10
ratmice :
I believe that 'macro define' and friends only work with GDB's expression parsing
and do not affect the inferior process.
so they are only really useful for cases where the process being debugged lacks the -g3,
so when you 'macro define MIN(x,y)...', you could then 'macro expand MIN(-1, -2)',
and substitute variables from your program, but not much else.
Kevin :
@ratmice :
indeed, you're certainly right:
(gdb) macro define min(a, b)(a<b)
(gdb) macro define y 50
(gdb) macro expand min(x, y)
expands to: (5<50)
and then, evaluate it 'manually':
(gdb) print (5<50)
$1 = 1
ratmice :
@Kevin : Sorry I didn't do a very good job of explaining what i meant by GDB's expression parsing,
(gdb) help print
Print value of expression EXP.
so as well as macro expand, after macro define you should be able to do stuff like:
(gdb) macro define min(a, b)(a<b)
(gdb) p min(5,6)
$1 = 1
(gdb) p min(7,5)
$3 = 0
and even mix them with calls to the inferior:
(gdb) call (void) printf("%d\n", min(5,6))
1
(gdb)
and copy/paste code calling macros in your sources into gdb,
(gdb) macro define MIN(x, y) (x > y ? y : x)
(gdb) macro define A 10
(gdb) macro define B 15
(gdb) macro define str(s) #s
(gdb) macro define xstr(s) str(s)
(gdb) call (void) printf("test1 " xstr(A) " vs " xstr(B) " -> %d\n", MIN(A, B))
test1 10 vs 15 -> 10
(gdb)
This obviously much less convenient though than with -g3, especially when macros are being redefined as in your example.