Sometimes, the exact layout of objects in memory becomes very important. Some situations you may encounter:
- When overlaying different types as “views” of the same memory location, perhaps via
reinterpret_cast
,union
s, orvoid*
-casting. You want to know where the field in one view lands in another. - When examining a struct layout’s packing, to see if there is space being wasted.
- When looking at a crash at some offset like 0xc8, it’s common for that to be a NULL pointer dereference of a field of some structure, as if you did
MyStruct* foo = nullptr; return foo->field;
.
A very handy command for looking at the underlying storage of types is pahole
. But I’m usually in gdb already when I want to examine types. Plus, I somehow have never gotten comfortable with the separate pahole
command — possibly because it has a tendency to seg fault when I try running it. The latest gdb (8.1) also has it built-in if you run ptype/o typename
.
Tom Tromey implemented a simple version of pahole inside gdb using the Python scripting APIs. I stole1 his code, fixed some bugs, mucked with his output layout, added some more bugs, fixed some of those, and added it to my gdb startup script collection. I also abstracted out the type traversal and created an additional command to examine what is at a given offset.
offset
Let’s say we have a crash at the address 0x58. Suspecting a NULL pointer dereference while manipulating a js::RegExpShared
object, let’s look at what is at that offset:
(gdb) offset 0x58 js::RegExpShared Scanning byte offsets 88..95 overlap at byte 88..127 with this.tables : js::RegExpShared::JitCodeTables overlap at byte 88..95 with this.tables.mBegin : mozilla::UniquePtr*
By default, when you examine an offset, offset
will look at native word’s worth of memory. Here, I’m running on 64-bit, so we’re looking at the 8 bytes starting at 0x58 (aka 88 decimal). (You could do offset/1 88 js::RegExpShared
to only look at the single byte offset 88.)
From the above output, we can see that the field tables
overlaps the offset range being inspected. tables
itself is a js::RegExpShared::JitCodeTables
structure, and its mBegin
field occupies the relevant offsets.
Let’s look at another example:
(gdb) offset 184 JSContext Scanning byte offsets 184..191 overlap at byte 184..199 with this.kind_ : js::WriteOnceData overlap at byte 184..187 with this.kind_.value : js::ContextKind overlap at byte 188..187 with this.kind_.check : js::CheckUnprotected overlap at byte 188..191 with this.kind_ : <32-bit hole> in js::WriteOnceData before field 'nwrites'
This has some weirdnesses. First, notice the “byte 188..187” range of kind_.check. That’s an empty js::CheckUnprotected
struct2.
Next, we seem to have collided with a hole in the JSContext structure. We can use pahole
to look at the overall structure. But first, let’s switch to a simpler structure (JSContext is huge and the output would be pages long).
(gdb) offset 0 js::jit::ABIArg Scanning byte offsets 0..7 overlap at byte 0..3 with this.kind_ : js::jit::ABIArg::Kind overlap at byte 4..7 with this : <32-bit hole> in js::jit::ABIArg before field 'u'
Now we can get a higher-level view with pahole.
pahole
(gdb) pahole js::jit::ABIArg offset size 0 16 : struct js::jit::ABIArg { 0 4 : kind_ : js::jit::ABIArg::Kind 4 4 : --> 32 bit hole in js::jit::ABIArg <-- 8 8 : u : struct union {...} { 8 +0 1 : gpr_ : js::jit::Register::Code 8 +0 8 : fpu_ : js::jit::FloatRegister::Code 8 +0 4 : offset_ : uint32_t } union {...} } js::jit::ABIArg
This displays the full structure, with each field (or hole) displayed beside its offset and size within the overall type. Offsets of fields directly inside of the given type are given directly. Offsets within sub-structures are given as the offset of that structure within the outermost struct, plus an offset within the inner struct.
These outputs can get very noisy when they are deeply nested, possibly displaying a lot more data than you care about. You can clamp the depth of the tree with a /N
suffix option if you wish:
(gdb) pahole/1 js::jit::ABIArg offset size 0 16 : struct js::jit::ABIArg { 0 4 : kind_ : js::jit::ABIArg::Kind 4 4 : --> 32 bit hole in struct js::jit::ABIArg <-- 8 8 : u : union {...} } struct js::jit::ABIArg
Probably not useful with this example, since the interesting stuff is now hidden, but it makes large or deeply nested types much more readable.
Installing
If you would like to use this stuff, you can see the full directions for installing my random helper crap, or you can just grab the one file gdbinit.pahole.py and source it from your ~/.gdbinit:
source ~/path/to/gdbinit.pahole.py
Footnotes
1. "Stole" in the GPLv3 sense, that is -- tromey's original and my modified version are both released under the GPLv3 license.
2. Whether empty structs should even be displayed is questionable. What does that even mean?