How to wrap functions with the `--wrap` option correctly?
As StoryTeller told me, I ignored the "undefined reference" requirement which I already posted above:
... Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.
To use the --wrap
option I rearranged my code example like this:
main.c:
#include <stdio.h>
extern int foo();
extern int __real_foo();
int __wrap_foo() {
printf("wrap foo\n");
return 0;
}
int main () {
printf("foo:");foo();
printf("wrapfoo:");__wrap_foo();
printf("realfoo:");__real_foo();
return 0;
}
foo.c:
#include <stdio.h>
int foo() {
printf("foo\n");
return 0;
}
Then compile:
gcc main.c foo.c -Wl,--wrap=foo -o main
And the the amazing output after running ./main
:
foo:wrap foo
wrapfoo:wrap foo
realfoo:foo
The trick is (correct me if I am wrong) that the reference of foo()
and __real_foo()
is not defined at compile time. I. E. they have **undefined references" which is the requierement for the linker to link foo()
to __wrap_foo()
and __real_foo()
to foo()
.
Is it possible to wrap a member function of a C++ class?
In C++, all symbols names got mangled to ensure uniqueness of the symbol names, when function names are overloaded, placed in classes or subclasses, inside namespaces, etc.
The linker has no knowledge of the C++ original symbol names and only handles mangled symbol names. So to wrap a C++ member function, you have to wrap the mangled function name.
Foo.hpp
class Foo
{
public:
Foo() {};
~Foo() {};
void test();
};
Foo.cpp
#include "Foo.hpp"
#include <cstdio>
void Foo::test()
{
printf("Original Foo:test(): this = %p\n", (void*)this);
}
main.cpp
#include "Foo.hpp"
#include <cstdio>
extern "C" void __real__ZN3Foo4testEv(Foo* This);
extern "C" void __wrap__ZN3Foo4testEv(Foo* This)
{
printf("Wrapped Foo:test(): this = %p\n", (void*)This);
__real__ZN3Foo4testEv(This);
}
int main()
{
Foo foo;
printf("Address of foo: %p\n", (void*)&foo);
foo.test();
}
Usage:
$ g++ -o foo main.cpp Foo.cpp -Wl,--wrap=_ZN3Foo4testEv; ./foo
Address of foo: 0xffffcc2f
Wrapped Foo:test(): this = 0xffffcc2f
Original Foo:test(): this = 0xffffcc2f
Note the signature of the wrapping function __wrap__ZN3Foo4testEv
: it needs to be declared extern "C"
to avoid itself to being mangled. And it has access to the this
as the first implicit argument.
If you need to call the original function, the same apply for the declaration of the real function __real__ZN3Foo4testEv
.
To find out the mangled name of a C++ function, there are several ways. One would consist of first building the project without wrapping, and creating a map file from the linker. In the map file, you should be able to find out the mangled name of the desired function.
gcc: error: unrecognized option --wrap
You can pass options to the linker with -Wl,<linker-option>
. Alternatively there's -Xlinker <option>
for some versions of gcc. Try gcc -Wl,--wrap=add ...
How does target_link_libraries(--wrap) work?
When target_link_libraries
gets arguments which start with -
, it treats them as linker command-line options and passes them untouched to the linker. This therefore has nothing to do with CMake and everything to do with ld. You can study this in ld reference documentation, such as this one:
--wrap=
symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to"__wrap_
symbol
"
. Any undefined reference to"__real_
symbol
"
will be resolved to symbol.This can be used to provide a wrapper for a system function. The wrapper function should be called
"__wrap_
symbol
"
. If it wishes to call the system function, it should call"__real_
symbol
"
.Here is a trivial example:
void *
__wrap_malloc (size_t c)
{
printf ("malloc called with %zu\n", c);
return __real_malloc (c);
}
If you link other code with this file using --wrap malloc, then all calls to
"malloc"
will call the function"__wrap_malloc"
instead. The call to"__real_malloc"
in"__wrap_malloc"
will call the real"malloc"
function.You may wish to provide a
"__real_malloc"
function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of"__real_malloc"
in the same file as"__wrap_malloc"
; if you do, the assembler may resolve the call before the linker has a chance to wrap it to"malloc"
.
C++ ld linker --wrap option does not work for internal function calls
Let's lint a little bit of fluff first. In UnitTestsMain.cpp
,
the declaration:
extern "C" int _ZN3Cam6myFuncEv();
is redundant. It simply instructs the C++ compiler that references to the function
of that prototype whose mangled name is _ZN3Cam6myFuncEv
are references to
an externally defined function of that name. This is exactly the same information,
just expressed differently, that that the compiler has already got from:
namespace Cam {
int myFunc();
...
}
when it #include
-ed CameraHandler.h
, because _ZN3Cam6myFuncEv()
is the mangled
form of Cam::myFunc
. The extern "C"
redeclaration of Cam::myFunc
is harmless
but contributes nothing either to compilation or linkage.
On to the main question: Why does your mock int __wrap__ZN3Cam6myFuncEv()
get called instead of int Cam::myFunc
in UnitTestsMain.cpp
:
int main(){
std::cout << Cam::myFunc() << std::endl;
std::cout << Cam::myFunc2() << std::endl;
return 0;
}
as you want; but your mock is not called for int Cam::myFunc
in CameraHandler.cpp
:
int Cam::myFunc2(){
return Cam::myFunc() + 11;
}
The answer lies in the documentation of the --wrap
linker option:
--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be
resolved to __wrap_symbol. Any undefined reference to __real_symbol will be
resolved to symbol.
Maybe you read it and didn't grok the significance of undefined reference.
This means that when --wrap=symbol
is in effect, and the linker applies it
to an object file containing undefined references to symbol
, it will replace them
with references to __wrap_symbol
, and undefined references to __real_symbol
,
in that object file, will be replaced with symbol
.
Now in UnitTestsMain.o
, compiled from UnitTestsMain.cpp
, the references to both Cam::myFunc()
and Cam::myFunc2()
are undefined. These functions are both defined in CameraHandler.cpp
,
compiled in CameraHandler.o
.
Therefore in the linkage of UnitTestsMain.o
, --wrap ZN3Cam6myFuncEv
will take effect and
replace the call to Cam::myFunc
( = ZN3Cam6myFuncEv
) with a call to __wrap_ZN3Cam6myFuncEv
.
The call to Cam::myFunc2()
( = ZN3Cam7myFunc2Ev
) is not wrapped and is unaffected: it will be
resolved to the definition to be found in CameraHandler.o
But in the linkage of CameraHandler.o
, both functions are defined, so --wrap
has
no effect. When Cam::myFunc2()
calls Cam::myFunc(), it calls ZN3Cam6myFuncEv
, not__wrap_ZN3Cam6myFuncEv
.
That explains why the program outputs:
999
12
and not:
999
1010
Can you make your mocking work as expected?
Yes. You just have to ensure that every call to Cam::myFunc
that you want to be
mocked is compiled into an object file that does not contain the (real) definition
of Cam::myFunc
. The obvious way to do that is to define Cam::myFunc
in its own
source file. Here's your example fixed:
CameraHandler.h
#ifndef CAMERAHANDLER_H
#define CAMERAHANDLER_H
namespace Cam {
int myFunc();
int myFunc2();
}
#endif
CameraHandlerMock.h
#ifndef CAMERAHANDLERMOCK_H
#define CAMERAHANDLERMOCK_H
extern "C" {
int __wrap__ZN3Cam6myFuncEv();
}
#endif
CameraHandler_myFunc.cpp
#include "CameraHandler.h"
using namespace Cam;
int Cam::myFunc() {
return 1;
}
CameraHandler_myFunc2.cpp
#include "CameraHandler.h"
using namespace Cam;
int Cam::myFunc2(){
return Cam::myFunc() + 11;
}
CameraHandlerMock.cpp
#include "CameraHandlerMock.h"
int __wrap__ZN3Cam6myFuncEv() {
return 999;
}
UnitTestsMain.cpp
#include <iostream>
#include "CameraHandler.h"
#include "CameraHandlerMock.h"
int main(){
std::cout << Cam::myFunc() << std::endl;
std::cout << Cam::myFunc2() << std::endl;
return 0;
}
Makefile
SRCS := UnitTestsMain.cpp CameraHandler_myFunc.cpp \
CameraHandler_myFunc2.cpp CameraHandlerMock.cpp
OBJS := $(SRCS:.cpp=.o)
LDFLAGS := -Wl,--wrap,_ZN3Cam6myFuncEv
.PHONY: unitTests clean
unitTests: testsMain
testsMain: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^
UnitTestsMain: CameraHandler.h CameraHandlerMock.h
CameraHandler_Func.o CameraHandler_Func2.o: CameraHandler.h
CameraHandlerMock.o: CameraHandlerMock.h
clean:
rm -f $(OBJS) testsMain
(Your production build is not considered at all in this example makefile)
With this, the test build runs like:
$ make
g++ -c -o UnitTestsMain.o UnitTestsMain.cpp
g++ -c -o CameraHandler_myFunc.o CameraHandler_myFunc.cpp
g++ -c -o CameraHandler_myFunc2.o CameraHandler_myFunc2.cpp
g++ -c -o CameraHandlerMock.o CameraHandlerMock.cpp
g++ -Wl,--wrap,_ZN3Cam6myFuncEv -o testsMain UnitTestsMain.o \
CameraHandler_myFunc.o CameraHandler_myFunc2.o CameraHandlerMock.o
and testsMain
does what you expect:
$ ./testsMain
999
1010
You can simplify both source files and the makefile somewhat if you rewrite CameraHandlerMock.cpp
as just:
extern "C" {
int __wrap__ZN3Cam6myFuncEv() {
return 999;
}
}
Then you have no need for the mock header file CameraHandlerMock.h
at all.
If you have a lot of functions you need to mock in this low-level way, it
may get tedious to define each one in its own source file. You may be aware
that there are higher-level, framework-supported mocking options, e.g. googlemock,
that have rich mocking capabilities and don't entail this tedium. It's fair to say, however, that they may
replace it with more complicated kinds of tedium.
Does gcc wrap option takes effects on function printf ?
If you want this to work properly for fprintf
, you need to also add the flag -fno-builtin-fprintf
to the command line. Othwise, gcc will optimize the call to fprintf
to instead call fwrite
, and the linker will not see a call to fprintf
to wrap.
In general, to properly wrap any function, you may need the corresponding -fno-builtin-
option as well.
Related Topics
Add Library Search Path to Clang
C++ Is It Necessary to Delete Dynamically Allocated Objects at the End of the Main Scope
Are Destructors Run When Calling Exit()
Documenting Preprocessor Defines in Doxygen
Xlib How Does This (Removing Window Decoration) Work
Remove Reference in Decltype (Return T Instead of T& Where T& Is the Decltype)
Why Doesn't the Program Crash When I Call a Member Function Through a Null Pointer in C++
Compiling Simple Static Opengl 4.0 Program Using Mingw, Freeglut and Glew
Use Static_Assert to Check Types Passed to MACro
Capturing H264 Stream with Opencv
How to Use a Constexpr Value in a Lambda Without Capturing It
Access Child Members Within Parent Class, C++
Template Specialization for Multiple Types
C++ -- Return X,Y; What Is the Point