type
status
date
slug
summary
tags
category
icon
password
At the start, let's think about a simple class named Person. It has a function PrintType that prints the string "Person".
When this code is compiled, the compiler creates instructions for it in assembly language. Here, it sets up a 'this' pointer and then calls the Person::PrintType function.
The important thing to note here is the E8 type call instruction. This instruction uses relative addressing, an approach that calculates the location of the function to be called based on its distance from the current instruction. So, the function address is determined and hardcoded into the code.
Now, let's change the PrintType function to be a virtual function. This means the function can be redefined in any class that inherits from it. So we now have a base class Object with virtual functions, and a Person class that overrides these functions.
When we call the PrintType function now, the compiler uses a different strategy to find the function.
Now, it uses a FF type call instruction. This instruction is more complex, and it's used for calling virtual functions. This instruction uses indirect addressing, which means the function address to be called is looked up from the address provided after the FF code. In this scenario, the function address is looked up from the EAX register. Guess what is stored in the EAX register at the moment? It's the second virtual function stored in the Person's virtual table.
💡
When a class has virtual functions, the compiler adds a virtual table for this class and all its derived classes. This is a list of all the virtual functions that the class has. Each object of a class with virtual functions has a pointer to its virtual table, usually stored as the first member of the object. The virtual table itself is an array where each entry is a function pointer pointing to the most derived function for that object.

virtual table setup

In C++, when a class has one or more virtual functions, the compiler automatically adds a hidden member variable to the class. This variable is a pointer, often called the virtual pointer, which points to a static array of function pointers, called the vtable (or virtual table). Each class with virtual functions has its own vtable, and all objects of that class have a virtual pointer that point to that virtual table.
Now, in the case of a constructor, the compiler generates assembly code that, among other things, initializes this vptr. The vptr is typically initialized at the beginning of the constructor, before any user code is executed but after the initialization of its base class, to ensure that the vtable is correctly set up even if a virtual function is called during the constructor.
Note that in the case of multiple inheritance, the derived class would have multiple virtual tables, one for each of its base class.

virtual table usage

When calling virtual functions, the vtable is utilized. Take Person::PrintType as an example, here's an explanation of the assembly code for the call instruction:
  1. mov eax, dword ptr [p]: This moves the address of the Person object into the eax register.
  1. mov edx, dword ptr [eax]: Pass the value of the virtual pointer of p , which is the memory address of its virtual table, into edx register.
  1. mov ecx, dword ptr [p]: Setup this pointer for function call.
  1. mov eax, dword ptr [edx + 4]: The second function pointer from the vtable (which edx points to) is moved to the eax register. In this case, this should be the address of the Person::PrintType function.
  1. call eax: This calls the function whose address is stored in eax, which should be Person::PrintType.
Win32 Event QueueInjecting a DLL into a Windows Executable By Modifying PE Import Table
Zack Yang
Zack Yang
Just a humble bounty hunter🥷
公告
type
status
date
slug
summary
tags
category
icon
password
Hey there, welcome to my blog 👋
-- 这个博客写些什么 --
定期技术分享🤖
不定期发疯文学🤡