Skip to content
Klemens Morgenstern edited this page Mar 23, 2018 · 3 revisions

Symbols in C

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:

basics-foo-bar

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.

Mangled names

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.

What this tool does

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.