-
Notifications
You must be signed in to change notification settings - Fork 0
Basics
To explain the implementation we'll go through the previously named example, but regard is as C, so we have no name mangling.
//foo.c
void foo(int i) { bar(i); }
//bar.c
void bar(int i) { }
If we compile and link those two source files into a result.lib
the entry points will look like this:
Now when we want to wrap a function in C we can use the name directly, i.e. pass --wrap=bar
to the linker. So our stub will look like this:
//wrap.c
#include <stdio.h>
void __wrap__bar(int val) {printf("Hello world %i\n", val);};
The prefixes __wrap__
and __real__
are generated by the linker.
This will yield us the following entry points for our binary.
As noted before: gcc does only allow this wrapping when the symbol has externa linkage, i.e. is defined in another compile unit.
With C++ the names become mangled, so overloading is possible. To give a few examples, let's see how those symbols look:
C++ Name | Mangled Name |
---|---|
void bar(int) |
_ZN3barEKi |
void foo(double&) |
_ZN3fooERKd |
void f(bar_t&) |
_ZN3barERKN5bar_tE |
void f(my_namespace::foo_t&) |
_ZN3barERKN12my_namespace5bar_tE |
Those simple examples demonstrate how it is difficult to maintain a table of wraps when using C++.
This gets even worse, when considering that a type maybe an alias, e.g. bar_t
might be an alias for my_namespace::foo_t
.
It is obviously possible to do this manually, but this will be hard to maintain, when used in tests on a regular basis.
We provide a solution to this problem, by letting the compiler generate as much information as possible and letting our tool connect the dots.
This tool provides a header to declare wrapping functions which will generate an exported symbol our tool uses to generate the required wrapping function, that connects to the stub. This is done by reading the outline[footnote the mangled as well as the demangled] of the binary with the declared stub and the one of the function to be stubbed. That way our tool can assure that there is a connection.
Taking our example from above we move to C++ code, so that we get the following:
//foo.cpp
void foo(int i) { bar(i); }
//bar.cpp
void bar(int i) { }
Our stub is declared utilizing our macros provided by cpp/wrap.hpp
.
//wrap.cpp
#include <cpp/wrap.hpp>
#include <iostream>
CPP_WRAP_FN_FIX(bar, void, (int val))
{
std::cout << "Hello world " << val << std::endl;
};
Now the build process will look like this[footnote Please note that you can let cpp-wrap call nm
as well.]
The header uses __CPP__
as a seperator, which means that this byte sequence must not be part of your identifiers.