I decompile a VC++ application in IDA 7, and I often find vftables referencing the same function multiple times, like the one I called "Object__pure" here, part of a "heavy-base" Object
class inherited by almost every other class in the application:
seg002:008F4748 const Object::`vftable' dd offset Object__free ; DATA XREF: Object__ctor+12↑o Object__dtor+A↑o Object__copy+16↑o
seg002:008F474C dd offset Object__vsub_7D0990
seg002:008F4750 dd offset Object__vsub_7D09A0
seg002:008F4754 dd offset Object__pure
seg002:008F4758 dd offset Object__pure
seg002:008F475C dd offset Object__pure
seg002:008F4760 dd offset Object__pure
seg002:008F4764 dd offset Object__vsub_47B660
Child classes in my executable inheriting from Object
typically have their own custom functions instead of those "pure" (how I called them) ones in their vtable.
Not really knowing what that could be, I gave it the name "pure", thinking about it like a virtual or pure virtual call. The function itself does nothing other than calling a completely empty Object__vsub_80CD50
:
seg000:007E4580 Object__pure proc near ; CODE XREF: <lots!>
seg000:007E4580
seg000:007E4580 a1 = dword ptr -4
seg000:007E4580 arg_0= dword ptr 8
seg000:007E4580
seg000:007E4580 000 push ebp
seg000:007E4581 004 mov ebp, esp
seg000:007E4583 004 push ecx
seg000:007E4584 008 mov [ebp+a1], ecx
seg000:007E4587 008 mov eax, [ebp+arg_0]
seg000:007E458A 008 push eax
seg000:007E458B 00C mov ecx, [ebp+a1] ; this
seg000:007E458E 00C call Object__vsub_80CD50 ; Call Procedure
seg000:007E4593 00C mov esp, ebp
seg000:007E4595 004 pop ebp
seg000:007E4596 000 retn 4 ; Return Near from Procedure
seg000:007E4596 Object__pure endp
...
seg000:0080CD50 Object__vsub_80CD50 proc near ; CODE XREF: <lots again!>
seg000:0080CD50
seg000:0080CD50 var_4= dword ptr -4
seg000:0080CD50
seg000:0080CD50 000 push ebp
seg000:0080CD51 004 mov ebp, esp
seg000:0080CD53 004 push ecx
seg000:0080CD54 008 mov [ebp+var_4], ecx
seg000:0080CD57 008 mov esp, ebp
seg000:0080CD59 004 pop ebp
seg000:0080CD5A 000 retn 4 ; Return Near from Procedure
seg000:0080CD5A Object__vsub_80CD50 endp
Why is such a function referenced multiple times? Is it due to optimization, unifying functions that do nothing? Are those functions typically virtual / pure virtual?
retf
; a 'pure' virtual function may output a warning.vsub_80CD50
) even effectively doing nothing if interpreted like a__thiscall
(which it seems to be), just pushing over thethis
pointer in ecx, but then returning immediately?retn *immediate*
, so it also removes a value from the stack. If you look up where it is called from, you will see an extra argument is pushed – apart from initializingecx
(and possibly other registers).