Skip to content

Instantly share code, notes, and snippets.

@gnarayan81
Last active September 8, 2024 00:17
Show Gist options
  • Save gnarayan81/2446cf392f012f31a2b3 to your computer and use it in GitHub Desktop.
Save gnarayan81/2446cf392f012f31a2b3 to your computer and use it in GitHub Desktop.
Viewing object layout in clang.
// The intent of this class is to display the layout of an object in
// memory.
// Compile with:
// clang -cc1 -x c++ -v -fdump-record-layouts test.cc -emit-llvm -o /dev/null
//
//
//*** Dumping AST Record Layout
//0 | struct Top
//0 | int a
//| [sizeof=4, dsize=4, align=4
//| nvsize=4, nvalign=4]
//*** Dumping AST Record Layout
//0 | struct Left
//0 | struct Top (base)
//0 | int a
//4 | int l
//| [sizeof=8, dsize=8, align=4
//| nvsize=8, nvalign=4]
//*** Dumping AST Record Layout
//0 | struct Right
//0 | struct Top (base)
//0 | int a
//4 | int r
//| [sizeof=8, dsize=8, align=4
//| nvsize=8, nvalign=4]
//*** Dumping AST Record Layout
//0 | struct Bottom
//0 | struct Left (base)
//0 | struct Top (base)
//0 | int a
//4 | int l
//8 | struct Right (base)
//8 | struct Top (base)
//8 | int a
//12 | int r
//16 | int b
//| [sizeof=20, dsize=20, align=4
//| nvsize=20, nvalign=4]
#undef CHECKING_LAYOUT
#ifndef CHECKING_LAYOUT
# include <stdio.h>
#endif
struct Top {
const int a = 16;
};
struct Left : virtual public Top {
const int l = 32;
};
struct Right : virtual public Top {
const int r = 64;
};
struct Bottom : public Left, public Right {
const int b = 128;
};
int main() {
Bottom b;
Left* l = &b;
Right* r = &b;
#ifndef CHECKING_LAYOUT
printf("&b = %p\n", &b);
printf(" l = %p\n", l);
printf(" r = %p\n", r);
#endif
// On a single run,
// &b = 0x7fff59df1988
// l = 0x7fff59df1988
// r = 0x7fff59df1990
// ...
// Clearly l and r are separated by h'8 bytes. Does this reconcile
// with what we have from the AST ?
// ...
//*** Dumping AST Record Layout
//0 | struct Bottom
//0 | struct Left (base)
//0 | struct Top (base)
//0 | int a
//4 | int l
//8 | struct Right (base)
//8 | struct Top (base)
//8 | int a
//12 | int r
//16 | int b
//| [sizeof=20, dsize=20, align=4
//| nvsize=20, nvalign=4]
// ...
// That sure seems to be the case, if you pay attention to struct Right
// at offset 8.
// Now what is of interest is that 'int a', while quoted twice
// has storage only 8 bytes in, while, rather confusingly, int r
// is at 12 bytes in. Now consider,
#ifndef CHECKING_LAYOUT
printf(" &(l->a) = %p\n", &(l->a));
printf(" &(r->a) = %p\n", &(r->a));
#endif
// &b = 0x7ffffdb5bda8
// l = 0x7ffffdb5bda8
// r = 0x7ffffdb5bdb0
// &(l->a) = 0x7ffffdb5bda8
// &(r->a) = 0x7ffffdb5bdb0
// ...
// Okay now, l->a is at an offset of 0 from &b, which is fine, from the
// AST record on top. r->a is at an offset of 8 bytes from the top, which
// is expected since it is at the base of 'b'.
// ...
// The problem comes in when trying to access top.
//Top* t1 = (Top*) &b;
//Top* t2 = (Top*) &b;
// Clang now whinges:
// ...
// object_memory_layout.cc:112:19: error: ambiguous conversion from derived class 'Bottom' to base class 'Top':
// struct Bottom -> struct Left -> struct Top
//struct Bottom -> struct Right -> struct Top
// Top* t1 = (Top*) &b;
// ^~
// object_memory_layout.cc:113:19: error: ambiguous conversion from derived class 'Bottom' to base class 'Top':
// struct Bottom -> struct Left -> struct Top
// struct Bottom -> struct Right -> struct Top
// Top* t2 = (Top*) &b;
// ...
// In fact, it is quite explicitly saying that it doesn't know how to
// get to Top, due to the ugly diamond.
// ...
// It is of course possible to do this:
Top* t1 = l;
Top* t2 = r;
// And that's fine, because the conversion is no longer ambiguous.
// ...
// The point that is made here is that the conversion ambiguity arises
// from the table layout above. Notice that getting to top could be
// either offset 0 or offset 8.
// A suggested solution to this would be to simply have a single
// copy of Top in the object, via virtual inheritance.
// ...
// With that done.
//*** Dumping AST Record Layout
//0 | struct Bottom
//0 | struct Left (primary base)
//0 | (Left vtable pointer)
//8 | int l
//16 | struct Right (base)
//16 | (Right vtable pointer)
//24 | int r
//28 | int b
//32 | struct Top (virtual base)
//32 | int a
//| [sizeof=40, dsize=36, align=8
//| nvsize=32, nvalign=8]
// Okay, now there's vtable pointers. The vtable pointer should provide
// the offset necessary to get to Top.
// Let's get that value.
#ifndef CHECKING_LAYOUT
printf(" left vtable pointer: *(%p) => *(%016llx) => %u\n", &b,
*((unsigned long long *) &b), *((unsigned *)(*((unsigned long long *) &b))));
unsigned long long *rvt = ((unsigned long long *) &b) + 0x2;
printf("right vtable pointer: *(%p) => *(%016llx) => %#x\n", rvt,
*rvt, *((unsigned *)(*rvt)));
// Oddly, the right vptr is busted ? The left one is okay, but it still
// is off by a factor of 2.
// left vtable pointer: *(0x7fff0c6add50) => *(0000000000400ae8) => 16
// right vtable pointer: *(0x7fff0c6add60) => *(0000000000400b00) => 0x400ae8
#endif
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment