This question is about a (possible) implementation for virtual function calls (which I believe it is used by gcc
).
Consider the following scenarios:
- class F inherits from class D (and maybe others) which inherits from class B (not virtually). D overrides the virtual method
f()
declared in B; an object of type F is instantiated - class F inherits from class D (and maybe others) which inherits from class B (virtually). D overrides the virtual method
f()
declared in B; an object of type F is instantiated
(the only difference between these two scenarios is the way class B is inherited)
In scenario 1, in the vtable of object B, at the location destined for f()
there is now a (non-virtual) thunk that says:
if you want to call
f()
, first change thethis
pointer withoffset
(it is actually D that puts this thunk there)
In scenario 2, in the vtable of object B, at the location destined for f()
there is now a (virtual) thunk that says:
if you want to call
f()
, first change thethis
pointer with the value stored ataddr
(D cannot tell B exactly how much this
pointer needs to be adjusted because it does not know the position of the B object in the final memory layout of the F object)
These assumptions were made by looking at the output of g++ -fdump-class-hierarchy
in combination with g++ -S
. Are they correct?
Now my question is: why is a virtual thunk necessary? Why can't F put a non-virtual thunk in B's virtual table (at the location for f()
)? Because when an F object needs to be instantiated, the compiler knows that f()
was declared in B, but it was overridden in D. And it also knows the exact offset between the object B (-in-F) and the object D (-in-F) (which I think is the reason for the virtual thunk in the first place).
EDIT (added output of g++ -fdump-class-hierarchy
and g++ -S
)
Scenario 1:
g++ -fdump-class-hierarchy
:
Vtable for F
...
48 (int (*)(...))D::_ZThn8_N1D1fEv (de-mangled: non-virtual thunk to D::f())
g++ -S
:
_ZThn8_N1D1fEv:
.LFB16:
.cfi_startproc
subq $8, %rdi #,
jmp .LTHUNK0 #
.cfi_endproc
Scenario 2:
g++ -fdump-class-hierarchy
:
Vtable for F
...
64 (int (*)(...))D::_ZTv0_n24_N1D1fEv (de-mangled: virtual thunk to D::f())
g++ -S
:
_ZTv0_n24_N1D1fEv:
.LFB16:
.cfi_startproc
movq (%rdi), %r10 #,
addq -24(%r10), %rdi #,
jmp .LTHUNK0 #
.cfi_endproc