Search code examples
cdebuggingassemblygdbcallstack

Does GDB shows wrong data when run over an compiler optimized binary image?


I am having a code like -

100 static void
101 func (<struct type1> *arg1, <struct type2> **arg2)
102 {
103   <struct type2> *var1;
...
...
...
112    var1 = *arg2; /* arg2 is no where else used in this function */
113    if (!var1) {
114        return;
115    }
...
...   /* var1 has been referenced to read its properties and to set its properties */
120
...
...
125    my_free();       /* crash happened at this line - my_free() would call __free() */
...
...
130
     }  

The program has crashed and created a core like -

#2  0x000055ea29cceb76 in func [__be_func...] (arg1=arg1@entry=0x7f8bcc440098, arg2=arg2@entry=0x0) at myfile.c:121

You could notice that the second argument arg2 is shown by GDB as 0x0. However, if it was NULL then program could have crashed at line#112 itself.

So I decided to disassemble the code and below is the excerpt after disassembling the above function -

(gdb) disass
Dump of assembler code for function func:
   0x000055ea29cce790 <+0>:     push   %rbp
   0x000055ea29cce791 <+1>:     mov    %rsp,%rbp
   0x000055ea29cce794 <+4>:     push   %r15
   0x000055ea29cce796 <+6>:     push   %r14
   0x000055ea29cce798 <+8>:     push   %r13
   0x000055ea29cce79a <+10>:    push   %r12
   0x000055ea29cce79c <+12>:    push   %rbx
   0x000055ea29cce79d <+13>:    sub    $0x38,%rsp
   0x000055ea29cce7a1 <+17>:    mov    %rdi,%r14
   0x000055ea29cce7a4 <+20>:    movabs $0x8000000000000000,%r15
   0x000055ea29cce7ae <+30>:    addq   $0x1,0x1864a08a(%rip) 
   0x000055ea29cce7b6 <+38>:    lea    -0x1(%r15),%rdx
   0x000055ea29cce7ba <+42>:    mov    %rdi,-0x38(%rbp)
   0x000055ea29cce7be <+46>:    and    %rdx,%r14
   0x000055ea29cce7c1 <+49>:    and    %rdx,%rsi
   0x000055ea29cce7c4 <+52>:    mov    $0xffffffffffffff7f,%r13
   0x000055ea29cce7cb <+59>:    and    0x50(%r14),%r13
   0x000055ea29cce7cf <+63>:    movl   $0x1000000,-0x50(%rbp)
   0x000055ea29cce7d6 <+70>:    movl   $0x0,-0x48(%rbp)
   0x000055ea29cce7dd <+77>:    mov    (%rsi),%rcx
   0x000055ea29cce7e0 <+80>:    bswap  %rcx
   0x000055ea29cce7e3 <+83>:    mov    %rcx,%rbx
   0x000055ea29cce7e6 <+86>:    mov    %rdx,-0x40(%rbp)
   0x000055ea29cce7ea <+90>:    and    %rdx,%rbx
   0x000055ea29cce7ed <+93>:    je     0x55ea29ccebcb
   0x000055ea29cce7f3 <+99>:    bswap  %r13
   0x000055ea29cce7f6 <+102>:   testb  $0x20,0x10d0(%r14)
   0x000055ea29cce7fe <+110>:   jne    0x55ea29cce80b
   0x000055ea29cce800 <+112>:   cmpl   $0x0,0x48(%r14)
   0x000055ea29cce805 <+117>:   je     0x55ea29cce9e1
   0x000055ea29cce80b <+123>:   mov    %rsi,-0x58(%rbp)
   0x000055ea29cce80f <+127>:   mov    0x1210(%r14),%r11
   0x000055ea29cce816 <+134>:   bswap  %r11
   ...
   ...
   ...
   ...

My analysis is - Since arg2 is only used at line# 112, may be due to compiler optimisation, it is not saved onto the stack frame and just held in register %rsi. And once after the execution of line# 112, the value in %rsi has got changed and arg2 is lost indefinitely. If my analysis is correct, shouldn't GDB shown arg2 as <optimized out> in the callstack rather than as 0x0?


Edit#1 to my analysis - when I dumped the stackframe of the function func

(gdb) i r rbp
rbp            0x7f8bcd651730      0x7f8bcd651730
(gdb) x/13xg 0x7F8BCD6516D0
0x7f8bcd6516d0: 0x0000000b00000006      
                0x00007f8bcd651748   <--- stored register %rsi value, which represents the 2nd argument - ie `arg2`
0x7f8bcd6516e0: 0x0000000001000000
                0x0000000000000000
0x7f8bcd6516f0: 0x7fffffffffffffff      
                0x80007f8bcc440098   <--- stored register %rdi value, which represents the 1st argument - ie `arg1` 
0x7f8bcd651700: 0x80007f8bd51a6230      
                0x00007f8bd51a6248
0x7f8bcd651710: 0x00007f8bcd651748      
                0x00007f8bd51a6230
0x7f8bcd651720: 0x80007f8bcc440098      
                0x0000000000000000
0x7f8bcd651730: 0x00007f8bcd651780 

As you can see the 2nd argument arg2 is not really NULL.

Important thing to notice is - 1st argument is stored as 0x80007f8bcc440098, whereas 2nd argument is stored as 0x00007f8bcd651748. The difference is that 1st argument's MSB bit is set to 1. Why is GDB not printing rightly when the address's MSB is not set to 1?


Solution

  • It appear to be an issue with compiler. Output of info address arg2 showed that 2nd argument arg2 doesn't contains an entry in stack location.