Static type time checking during compilation of virtual functions in C ++

Background

Recently, my colleague ran into a problem when using the old version of the header file for the library. As a result, the code created to call the virtual function in C ++ referred to an incorrect offset in the virtual function lookup table for the class (vtable).

Unfortunately, this error was not detected at compile time.

Question

All common functions involve using their crippled names, which provide the correct function (including the correct overload option) chosen by the linker. Similarly, you can imagine that an object file or library may contain symbolic information about the functions in the vtable for the C ++ class.

Is it possible to allow a C ++ compiler (e.g. g++ or Visual Studio) to check calls to a virtual function during binding?

Example

Here is a simple test example. Imagine this simple header file and its associated implementation:

Base.hpp:

 #ifndef BASE_HPP #define BASE_HPP namespace Test { class Base { public: virtual int f() const = 0; virtual int g() const = 0; virtual int h() const = 0; }; class BaseFactory { public: static const Base* createBase(); }; } #endif 

Derived.cpp:

 #include "Base.hpp" #include <iostream> using namespace std; namespace Test { class Derived : public Base { public: virtual int f() const { cout << "Derived::f()" << endl; return 1; } virtual int g() const { cout << "Derived::g()" << endl; return 2; } virtual int h() const { cout << "Derived::h()" << endl; return 3; } }; const Base* BaseFactory::createBase() { return new Derived(); } } 

Now imagine that the program uses the wrong / old version of the header file, where there is no virtual function in the middle:

BaseWrong.hpp

 #ifndef BASEWRONG_HPP #define BASEWRONG_HPP namespace Test { class Base { public: virtual int f() const = 0; // Missing: virtual int g() const = 0; virtual int h() const = 0; }; class BaseFactory { public: static const Base* createBase(); }; } #endif 

So, we have the main program:

main.cpp

 // Including the _wrong_ version of the header! #include "BaseWrong.hpp" #include <iostream> using namespace std; int main() { const Test::Base* base = Test::BaseFactory::createBase(); const int fres = base->f(); cout << "f() returned: " << fres << endl; const int hres = base->h(); cout << "h() returned: " << hres << endl; return 0; } 

When we compile the “library” using the correct header, and then compile and link the main program using the wrong header ...

 $ g++ -c Derived.cpp $ g++ Main.cpp Derived.o -o Main 

... then the virtual call to h() uses the wrong index in the vtable, so the call really goes to g() :

 $ ./Main Derived::f() f() returned: 1 Derived::g() h() returned: 2 

In this small example, the functions g() and h() have the same signature, so the “only” thing that goes wrong is the wrong function called (which in itself is bad and can completely disappear), but if the signatures are different, which can (and, apparently, this) lead to damage to the stack - for example, when calling a function in a DLL on Windows that uses the Pascal calling convention (the caller pushes the arguments and the caller calls them before returning).

+6
source share
1 answer

The short answer to your question is no. The main problem is that the offsets of the called functions are computed at compile time; therefore, if your caller code was compiled with a (wrong) header file that included "virtual int g () const", then your main.o will have all references to h () offset to the presence of g (). But your library was compiled with the correct header file, so there is no g () function, so the offset h () in Derived.o will be different from the main .o

This is not a question of invoking typechecking on virtual functions - it is a “restriction” based on the fact that the C ++ compiler performs the calculation of the offset of the compile-time function, not the runtime.

You can work around this problem by using dl_open instead of direct function calls and dynamically linking your library, instead of statically linking it.

0
source

All Articles