Bug 560741 - gdb gets confused about class pointer offsets with C++ multiple inheritance and vtables
Summary: gdb gets confused about class pointer offsets with C++ multiple inheritance a...
Alias: None
Product: Fedora
Classification: Fedora
Component: gdb
Version: 12
Hardware: All
OS: Linux
Target Milestone: ---
Assignee: Tom Tromey
QA Contact: Fedora Extras Quality Assurance
Depends On:
TreeView+ depends on / blocked
Reported: 2010-02-01 18:37 UTC by Boris Zbarsky
Modified: 2014-08-11 05:46 UTC (History)
5 users (show)

Fixed In Version: gdb-
Doc Type: Bug Fix
Doc Text:
Clone Of:
Last Closed: 2010-03-01 00:15:11 UTC

Attachments (Terms of Use)
Testcase (321 bytes, text/plain)
2010-02-01 18:38 UTC, Boris Zbarsky
no flags Details

Description Boris Zbarsky 2010-02-01 18:37:31 UTC
Description of problem: gdb gets confused about class pointer offsets with C++ multiple inheritance and vtables; reports bogus data.

Version-Release number of selected component (if applicable): gdb-7.0.1-26.fc12

How reproducible: Every time

Steps to Reproduce:
1. Compile the attached test.cpp with |g++ -g test.cpp|.
2. Run |gdb ./a.out|.  The following steps to reproduce are a transcript of a gdb session:

(gdb) break test.cpp:28
Breakpoint 1 at 0x400728: file test.cpp, line 29.
(gdb) run
Starting program: /home/bzbarsky/a.out 
Breakpoint 1, main () at test.cpp:29
29        return 0;
(gdb) p b
$1 = (B *) 0x601050
(gdb) p (class C*)b
$2 = (C *) 0x601030
(gdb) p (class A*)$2
$3 = (A *) 0x601030
(gdb) p (class B*)$2
$4 = (B *) 0x601040

Note that $4 does not equal $1.  In particular, when downcasting from B* to C*, the offset gdb seems to use is 2x what it should be.  This is very consistent (in my original situation in Mozilla code, the offset used was 176 instead of the correct 88).  As a consequence of this, you get this behavior:

(gdb) set print object on
(gdb) p b
$5 = (C *) 0x601040
(gdb) p/x b->mA
$6 = 0x31
(gdb) p/x b->mB
$7 = 0xaaaaaaaa
Actual results: When downcasting to a base class to a derived class when the base class is not the first base class the derived class inherits from, the offset used is 2x the correct one.  As a result, the wrong derived class pointer is obtained and incorrect member variable values are read.

Expected results:  In this case, the B* pointer should be offset from the C* pointer by 16 bytes.

Additional info: If I take out either of the virtual functions in the testcase, things start working correctly in terms of the pointers, though if I take out the virtual function b() then gdb claims there is no mA member in |b| when set print object on.

Comment 1 Boris Zbarsky 2010-02-01 18:38:20 UTC
Created attachment 388111 [details]

Comment 2 Tom Tromey 2010-02-02 18:26:50 UTC
I could only reproduce the second part of this bug.

On my x86 F12 box, with gdb-7.0.1-30 and gcc-c++-4.4.2-20,
(A*)b == (B*)b == b.

I was able to reproduce the b->mA bug.  This is actually a bit of
a strange gdb extension.

Are you using the system g++?  What version is the RPM?

Here's a transcript of my debug session:

Script started on Tue 02 Feb 2010 11:19:43 AM MST
parfait. gdb -nx ./pr
GNU gdb (GDB) Fedora (7.0.1-30.fc12)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /tmp/pr...done.
(gdb) b 28
Breakpoint 1 at 0x804859a: file rh560741.cc, line 28.
(gdb) r
Starting program: /tmp/pr 

Breakpoint 1, main () at rh560741.cc:28
28	  return 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.11.1-1.i686 libgcc-4.4.2-20.fc12.i686 libstdc++-4.4.2-20.fc12.i686
(gdb) p b
$1 = (B *) 0x804a010
(gdb) p (A*)b
$2 = (A *) 0x804a010
(gdb) p (B*)b
$3 = (B *) 0x804a010
(gdb) p (C*)b
$4 = (C *) 0x804a000
(gdb) p *b
$5 = {_vptr.B = 0x8048708, mB = -1145324613}
(gdb) set print object on
(gdb) p *b
$6 = (C) {<A> = {_vptr.A = 0x80486f8, mA = -1431655766}, <B> = {
    _vptr.B = 0x8048708, mB = -1145324613}, <No data fields>}
(gdb) p /x b->mA
$7 = 0x19
(gdb) p /x b->B
$8 = 0xaaaaaaaa

Comment 3 Boris Zbarsky 2010-02-02 20:38:06 UTC
> Are you using the system g++?  What version is the RPM?


~% rpm -q gdb
~% rpm -q gcc-c++

This is the system gcc.  It looks like I forgot to mention in comment 0 that this is a 64bit system, which of course matters if looking at the exact values of the pointer offsets (though not if you look at whether casting |b| to C* and then back to B* recovers the original pointer).

Looking at your debug session, it's pretty clearly showing the bug, if you're on a 32-bit system.  On a 32-bit system, casting |b| to |C*| should result in a pointer that's pointing to 8 bytes less than |b|, whereas your debug session shows it pointing to 16 bytes less than |b|.  In particular, casting your $4 to B* will almost certainly give 0x804a008 instead of the original |b|.

Comment 4 Tom Tromey 2010-02-02 20:58:42 UTC
Oops, thanks for pointing that out.
I am looking into it.

Comment 5 Tom Tromey 2010-02-02 23:43:13 UTC
I checked in a fix upstream: http://sourceware.org/ml/gdb-cvs/2010-02/msg00017.html

I am not sure when this will show up in a Fedora package; that is up
to Jan.

Comment 6 Boris Zbarsky 2010-02-03 02:55:27 UTC
Hmm.  Does that fix work if the code is compiled with -fno-rtti (which is in fact my situation)?

Note that in this case there is in fact no virtual inheritance, so looking at the destination type should presumably work ok, right?

Comment 7 Tom Tromey 2010-02-03 18:28:49 UTC
I tried the test case with -fno-rtti and it worked fine.
The gdb "RTTI" stuff actually works using knowledge of the ABI,
it doesn't rely on having the RTTI data compiled in.

Comment 8 Boris Zbarsky 2010-02-03 18:48:14 UTC
Ah, ok.  Excellent.

Will this bug get updated if that patch does make it downstream?

Comment 9 Jan Kratochvil 2010-02-03 21:08:25 UTC
(In reply to comment #8)
> Will this bug get updated if that patch does make it downstream?    

It looks to be present in Rawhide, forgot to close it:

Comment 10 Boris Zbarsky 2010-02-03 21:20:59 UTC
Jan, are there any plans to backport to FC12, by any chance?  This is making gdb pretty difficult to use for any codebase where multiple inheritance from classes with virtual functions is used (Gecko in my case).... And this certainly used to work in older gdb versions.

Comment 11 Jan Kratochvil 2010-02-03 21:33:07 UTC
OK (unaware why/if it regressed).

Comment 12 Tom Tromey 2010-02-03 21:46:29 UTC
I'm surprised to hear it used to work, since AFAICT this area hasn't
had much love in recent times.
You may want to delay pushing it into F12 (if you decide to do that),
because the follow-on bug you found is showing that the rot is deeper
than I thought: http://sourceware.org/ml/gdb-patches/2010-02/msg00059.html

Comment 13 Boris Zbarsky 2010-02-03 23:22:28 UTC
> I'm surprised to hear it used to work

Well, more precisely it works in gdb 6.3.50-20050815 on Mac (which I realize is more or less a fork) and I _think_ it worked in the system gdb shipped with FC9.

So I'm not claiming it's a particularly recent regression, by any means...

Thank you again for looking into this!

Comment 14 Tom Tromey 2010-02-08 16:06:02 UTC
I checked in the fix for the regression that Jan found.
(That is, checked in upstream.)

Comment 15 Jan Kratochvil 2010-03-01 00:15:11 UTC
Sorry but backporting these changes is IMO not acceptable for F-12 stable release, it would nullify the meaning on F-12 testing.

Feel free to use even the binary .fc13 *.rpm packages from:

(the last fix is still not there, there will be a new .fc13 rebase these days)

Note You need to log in before you can comment on or make changes to this bug.