On ELF systems you can use elf_hook to temporarily replace the real versions of various functions with your own mocked versions.
It allows you to redirect calls to any function from within a shared library to your own arbitrary function.
- Create a shared library containing the code under test
- In your test load the shared library dynamically (
dlopen
)
- Redirect the symbols you want to mock to your test functions (
elf_hook
)
- Now any calls to the real function within the library (the code under test) will be redirected to your mocked function
A plus for this method is that you can still call the original function when required.
- If for some tests you want to have the call to, eg
getaddrinfo
, succeed, you can call the system version.
- In other tests you can use your own mocked version, eg
mocked_getaddrinfo
, and have it return whatever you want.
- You can create as many
mocked_getaddrinfo
functions as you want, to test multiple scenarios
elf_hook has the following signature:
void* elf_hook(char const* library_filename,
void const* library_address,
char const* function_name,
void const* substitution_address);
You would use it like this:
#include <dlfcn.h>
#include "elf_hook.h"
void do_stuff(); // from the library under test (do_stuff calls getaddrinfo)
// our mocked function which will alter the behaviour inside do_stuff()
int mocked_getaddrinfo(const char* node,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res)
{
// return a broken value to test a getaddrinfo failure
return 42;
}
// another version which actually calls the real function
int real_getaddrinfo(const char* node,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res)
{
// the real getaddrinfo is available to us here, we only replace it in the shared lib
return getaddrinfo(node, service, hints, res);
}
int main()
{
const char* lib_path = "path/to/library/under/test.so";
// load the library under test
void* lib_handle = dlopen(lib_path, RTLD_LAZY);
// test 1: getraddrinfo is broken
//--------------------------------
// replace getaddrinfo with our 'mocked_getaddrinfo' version
elf_hook(lib_path, LIBRARY_ADDRESS_BY_HANDLE(lib_handle),
"getaddrinfo", mocked_getaddrinfo);
// call a function in the library under test where getaddrinfo fails
do_stuff();
// test 2: getraddrinfo is the system version
//--------------------------------
// replace getaddrinfo with our 'real_getaddrinfo' version
elf_hook(lib_path, LIBRARY_ADDRESS_BY_HANDLE(lib_handle),
"getaddrinfo", real_getaddrinfo);
// call the same function in the library, now getaddrinfo works
do_stuff();
dlclose(lib_handle);
return 0;
}
Any call to getaddrinfo
from within the library under test will now call mocked_getaddrinfo
.
A comprehensive article by the elf_hook author, Anthony Shoumikhin, is here.