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.
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.