This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: Workaround for virtual function/dllimport bug, also patch guidance request
If I understand your question, you want to know how MSVC++ handles virtual
functions marked as "dllimport". Having implemented some level of
compatability with VC++ in CodeWarrior, this is what I've observed.
There are two possible ways the function can be marked as dllimport.
1) the member function itself was marked
class foo { __declspec(dllimport) virtual void bar(); }
2) the entire class was marked
__declspec(dllimport) class foo2 { virtual void bar(); }
In both cases, a call to bar through a pointer to foo would go through the
vtable with no changes in the generated code. However, case 1 will have the
vtable be staticly linked while case 2 will have the vtable be imported from
the DLL. A constructor for foo in case 1 would resolve the vtable to
??_7foo@@6B@, but case 2 would be an indirect reference through
__imp_??_7foo2@@6B@ to get the address of the vtable plus an offset of 8 to
skip over the RTTI pointers.
Here is a disassembly of the two constructors from the CodeWarrior 2.3.1
compiler to show the difference. Also included is the disassembly of a
function that accesses foo->bar(). Oh, this is VC++/MASM style assembly, so
reverse the fields mentally if needed.
============================================================================
====
SECTION SIZE = 0x00000017; NAME = .text
DEFINED SYMBOLS:
name = ??0foo@@QAE@XZ
unmangled name = foo::foo()
offset = 0x00000000; type = 0x0020; class = 0x0002
00000000: 55 push ebp
00000001: 89 E5 mov ebp,esp
00000003: 83 EC 08 sub esp,8
00000006: 89 4D FC mov dword ptr [ebp-4],ecx
00000009: 8B 45 FC mov eax,dword ptr [ebp-4]
0000000C: C7 00 08 00 00 00 mov dword ptr [eax],offset
??_7foo@@6B@+8
00000012: 8B 45 FC mov eax,dword ptr [ebp-4]
00000015: C9 leave
00000016: C3 ret near
============================================================================
====
SECTION SIZE = 0x0000001C; NAME = .text
DEFINED SYMBOLS:
name = ??0foo2@@QAE@XZ
unmangled name = foo2::foo2()
offset = 0x00000000; type = 0x0020; class = 0x0002
00000000: 55 push ebp
00000001: 89 E5 mov ebp,esp
00000003: 83 EC 08 sub esp,8
00000006: 89 4D FC mov dword ptr [ebp-4],ecx
00000009: 8B 15 00 00 00 00 mov edx,dword ptr __imp_??_7foo2@@6B@
0000000F: 83 C2 08 add edx,8
00000012: 8B 45 FC mov eax,dword ptr [ebp-4]
00000015: 89 10 mov dword ptr [eax],edx
00000017: 8B 45 FC mov eax,dword ptr [ebp-4]
0000001A: C9 leave
0000001B: C3 ret near
============================================================================
====
SECTION SIZE = 0x00000020; NAME = .text
DEFINED SYMBOLS:
name = ?foobar@@YAXXZ
unmangled name = void foobar()
offset = 0x00000000; type = 0x0020; class = 0x0002
00000000: 55 push ebp
00000001: 89 E5 mov ebp,esp
00000003: 56 push esi
00000004: 8B 0D 00 00 00 00 mov ecx,dword ptr ?f@@3PAUfoo@@A
0000000A: 8B 31 mov esi,dword ptr [ecx]
0000000C: FF 16 call dword ptr [esi] near
0000000E: 8B 0D 00 00 00 00 mov ecx,dword ptr ?f2@@3PAUfoo2@@A
00000014: 8B 31 mov esi,dword ptr [ecx]
00000016: FF 16 call dword ptr [esi] near
00000018: EB 00 jmp $+2 ; --> 0x001a
0000001A: 8D 65 FC lea esp,dword ptr [ebp-4]
0000001D: 5E pop esi
0000001E: 5D pop ebp
0000001F: C3 ret near
When I modified the code to be dllexport (the code within the DLL), the
generated constructor code was the same, since accessing the vtable acts
like the static case from inside the DLL.
I'm at home, so I cannot verify if VC++ does exactly the same thing, but I
know we're very close in this area... I've personally had to look at several
bug reports about these kind of things.