Looking for C++ object in a memory dump

Published

January 15, 2018

When analyzing the core dump of a C++ based, long-running, server application it may be helpful to know the exact state of some objects created by the process. The question, then is, how to find that object. Core files consist of the recorded state of the working memory. The task may be not trivial if the process uses lots of memory.

I’ll assume that the C++ object has some virtual method. In that case, the object must contain a virtual pointer to the V-table of a class. By using the nm tool, it is easy to determine the address of a V-table. I can use that address, to determine exact locations in a core dump, of all the objects of that class, as all those objects will contain an address to V-table.

To demonstrate how the procedure works, let’s use the following, code below as “server application”. We are looking for an object t.

class test {
public:
  virtual ~test(){}
};
int main() {
  test t;
  abort();
  return 0;
}

As mentioned, the nm command shows an address of test class V-table.

$ nm -C myapp | grep "vtable for test"
0000000000400980 V vtable for test

Ok, so the V-table address is 0x400980. Now I need to find a V-pointer in the compiled binary. The value of a V-pointer is an address to the V-table + 16 (on a 64-bit system). To understand where this +16 comes from, we need to understand how the layout of the V-table looks like.

V-Table

The graph above shows 5 segments. Typically, on a 64-bit system, each of those segments is 8 bytes long (4 on a 32-bit system). V-table starts with an empty segment, storing value 0x00. The following segment contains an address to the typeinfo object of the class (used by the typeid function). The next segment is an address to the first virtual function declared in the class - in our case, it is an address to the destructor of the test class. The V-pointer stores address to this function, which is a reason why the value of V-pointer is an address of V-table+16 (V-table + 2 segments).

The next step in my investigation is to determine an address of the V-pointer in a program binary. Address of a V-table is 0x400980, so the address too look for is a value 0x00400980 + 0x10 = 0x00400990.

> hexdump -C myapp | grep "90 09 40 00"
00001860  48 c7 00 90 09 40 00 5d  c3 90 90 90 90 90 90 90  |H....@.]........|
00002860  48 c7 00 90 09 40 00 5d  c3 90 90 90 90 90 90 90  |H....@.]........|
0005e410  90 09 40 00 00 00 00 00  00 00 00 00 00 00 00 00  |..@.............|

We have got 3 possible places where an object may be located. I’ll use 3-rd for further description. I know that is the one I’m looking for, but normally at this point, one needs to somehow determine which object is the interesting one by examinating all of them. The address of this object is 0x005e410.

Now we need to find out what’s the address of this object in a core file. To do it you need to do some calculations, because:

object address in a core file = offset to the V-pointer from program binary + VMA address - VMA offset

VMA address and VMA offset we can get by using objdump or readelf commands.

> objdump -h corefile
Sections:
Idx Name          Size      VMA               LMA               File off  Algn
...
36 load26        00001000  00007f7fbc5c5000  0000000000000000  0003e000  2**12
                 CONTENTS, ALLOC, LOAD
37 load27        00022000  00007fff4f143000  0000000000000000  0003f000  2**12
                 CONTENTS, ALLOC, LOAD
38 load28        00001000  00007fff4f1f9000  0000000000000000  00061000  2**12
                 CONTENTS, ALLOC, LOAD, READONLY, CODE

Section 37, starting with load 27 is the interesting one. That’s because the V-pointer offset value from the program binary is 0x005e410. This value is between 0x3f000 (“File off” column for section 37) and 0x61000 (“File off” column for section 38). VMA address value for this section is 00007fff4f143000, VMA offset value is 0003f000. According to the formula above address of the object will be:

0x5e410 + 0x7fff4f143000 - 0x3f000 = 0x7FFF4F162410

Let’s now check with GDB if the described procedure is correct:

> gdb myapp core
(gdb) p &t
$1 = (test *) 0x7fff4f162410

As we see address of the t variable is the same as what I have got from the calculation so the procedure is correct.