c++gdb

Understanding GDB Output - Memory Content


I'm debugging a segfault in a C++ program, but first I am trying to get a better handle on using gdb to inspect the memory layout in the core file that is produced when the process segfaults. I'm using the following toy program, which intentionally segfaults, in order to understand gdb's output better.

int main() {
    int a = 4;
    int b = 12;
    *(int *)0 =11;
    return a + b;
}

When I open up the core file (using gdb my_executable path/to/core.pid) I can see the memory addresses that contain the values of a and b, which make sense, since sizeof(int) == 4:

(gdb) p &a
$5 = (int *) 0x7ffff940e078
(gdb) p &b
$6 = (int *) 0x7ffff940e07c

And when I ask for the memory contents starting at &a it sort of makes sense, except that I'm on an Intel machine which should be little endian, and it looks like these integers are big endian:

(gdb) x/4x &a
0x7ffff940e078: 0x00000004      0x0000000c      0xf940e120      0x00007fff
# (added by me) ---a==4---      --b==12---      other stuff ->

So, if I ask for the memory contents starting at the next address I would expect the following (all the bytes shift one to the left):

(gdb) x/4x 0x7ffff940e079
0x7ffff940e079: 0x00000400      0x00000cf9      0x40e12000      0x007fff..

But what gdb actually prints is this:

(gdb) x/4x 0x7ffff940e079
0x7ffff940e079: 0x0c000000      0x20000000      0xfff940e1      0xca00007f
#               --b==12?--      other stuff ->

It almost looks like we jumped forward 7 bytes, or that we jumped forward 4 bytes and now b is little endian. But the memory after that first 0x0c byte is different than it was in the last command (before it was 0xf940e... now it's 0x2000...). Am I accidentally reading the memory with different alignment? And any idea why the values appear to be big-endian?

I'm compiling the code with g++ version 13.3.0 on Ubuntu 24.04, in case that helps.


Solution

  • You seem to be misinterpreting the GDB debug output. When you give the instruction x/4x and gdb shows you 0x00000004, it doesn't mean the bytes are 00 00 00 04. Instead, it means that if you interpret the next 4 bytes as a hex value, it is 0x00000004. Since your system is little-endian, this means the actual bytes are

    04 00 00 00 0c 00 00 00 20 e1 40 f9 ff 7f 00 00 [ca]
    ----------- ----------- ----------- -----------
    0x00000004  0x0000000c  0xf940e120  0x00007fff
    

    You can confirm this by invoking x/17bx instead per the comment by @ssbssa, which should show you the bytes in the correct order. Here the prefix b stands for byte.

    Now if you shift by one byte, the bytes are regrouped and reinterpreted, giving the following result:

    [04] 00 00 00 0c 00 00 00 20 e1 40 f9 ff 7f 00 00 ca
         ----------- ----------- ----------- -----------
         0x0c000000  0x20000000  0xfff940e1  0xca00007f
    

    Which is consistent with your observation. Nothing weird such as "jumped forward 7 bytes, or that we jumped forward 4 bytes and now b is little endian" happens.

    I don't think this is unique to GDB and I believe other hex dumpers such as objdump also have similar behavior. See this answer for more info. Hex dumpers are likely designed this way so that a casual user not reading a misaligned address wouldn't need to worry about endianness.