2

The unreal engine source code has this bit in a validity check function:

if (*(void**)this == nullptr)
{
    UE_LOG(LogUObjectBase, Error, TEXT("Virtual functions table is invalid."));
    return false;
}

In this case this being a pointer to an instanced object of a class. I understand what the conversion and dereferencing does on a surface level but I am not quite clear how this helps check if the vtable is valid.

Zoe
  • 27,060
  • 21
  • 118
  • 148
  • 1
    You are asking about very technical code that's tailored and customized for specific compilers and operating systems. There is no guarantee, whatsoever, that this will work, as described, on other C++ compilers or operating systems. – Sam Varshavchik Dec 23 '21 at 14:55
  • 2
    I know just enough about Unreal Engine to know that a lot of C++-like concepts are built up from scratch. So while this question is asking about C++ "vtables", the code is really inspecting the first bytes of a `UObject`, expecting a data pointer to be there and expecting `nullptr` to have some significance. This is really a question about objects in a particular class hierarchy not shown here. The C++ language does not have "invalid vtables" or a byte pattern that signifies one. – Drew Dormann Dec 23 '21 at 14:58
  • @SamVarshavchik - UE compiles using msbuild and clang, for Windows, Linux, Mac, Android, iOS, all consoles etc. etc. This is very low-level code, there are no platform-specific overrides, so I am pretty sure it is actually cross-compatible. – Damir Halilovic Dec 24 '21 at 21:52

1 Answers1

3

There are two separate questions here: what does this code do, and does it work?

One common way that vtables are implemented is by storing a pointer to the vtable at the base of the object. So, for example, on a 32-bit machine, the first four bytes of the object would be a pointer to the vtable, and on a 64-bit machine the first eight bytes of the object would be a pointer to the vtable.

With that in mind, let’s think about what *(void**)this does. The this pointer points to the base of the object. We want to interpret the beginning of that object as a pointer to a vtable, so we want to get the value of the pointer at the base of the object. However, we don’t have a name for that pointer, so we can’t look it up by name, and because the vtable is set up by the compiler there is no C++ type that corresponds to “a vtable.” So instead, we’ll do the following. We’ll envision the pointer we want to read as being a void* (a pointer to “something whose type we don’t know.”) The this pointer is pointing right at that void*, so we’ll introduce a cast of (void**)this to say “pretend that this points at a void*.” We then dereference that pointer to read the void* that’s stored there, which is our vtable pointer. Collectively, that gives us *(void**)this.

The next question is why the null-check works. In a general, this safety check won’t work. It presumes that when space for an object is allocated, the memory is set to all 0s before the object is constructed. Assuming that’s the case, if the vtable pointer hasn’t been set up, then the bytes allocated to it would be 0s, which on some C++ implementations is treated as a null pointer. So the check you’ve listed here would then read the vtable pointer, see if it’s null, and then report an error if it is.

However, there’s a lot of assumptions there - that the memory is nulled out before the object is constructed, that the vtable is at the exact base of the object, etc. I’m not familiar with the specifics of the Unreal engine, but I assume it probably is set up to ensure these requirements are met.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Thank you for the detailed explanation. As explained in another comment, since UE compiles to so many platforms and this is very low-level code in terms of its hierarchy, I presume that it does work. I was not aware that vtables are stored at the base of the object. Knowing that makes this make sense. – Damir Halilovic Dec 24 '21 at 21:55