Reputation: 315
I have an application (A) which calls a third-party shared library (C). I want to write a library of my own (B) that intercepts calls from A to C, and in some cases replace the calls with my own code, in some cases do some extra processing, then call the matching function in C, and in some cases just forward the calls to C directly.
The application is open-source, so I could just change each call site to call a similarly-named function in B, then call the corresponding function in C when required, but that would be a lot of work and would make merging upstream changes in the application difficult. I do not have the source to the third-party library. If it was header-only then I could just use namespaces to achieve this but I'm not sure how to go about it when both my library and the third-party library seemingly need the exact same symbols defined.
Is there even any way to make this work? I'm primarily targeting OS X, but would like it to work on linux and then eventually Windows as well.
Upvotes: 5
Views: 2889
Reputation: 20741
In my case, I was working on a library that makes use of the BIO_...
interface of the OpenSSL library. The problem with the BIO_free_all()
function is that it calls shutdown()
on the sockets. This sends a FIN on the socket meaning that the other side closes (finishes).
The reason I needed to make sure that shutdown()
was not being called is because I BIO_accept()
the socket in a parent process and then use fork()
to handle the request. When that happens, I clean up the newly accepted BIO *
object in the parent which does not need it anymore. When doing so, it messes up the connection.
So the best way would be to intercept the shutdown()
function. Today I found out that if my library gets loaded first, then I can define a "do nothing" version of the function and intercept that function:
extern "C" int shutdown(int, int)
{
return 0;
}
In Linux, I can verify that my library is linked first with a simple objdump
command:
$ objdump -x executable | less
Then search for the "Dynamic Section". There make sure that the other libraries are after your library:
[...snip...]
Dynamic Section:
NEEDED libasan.so.6
NEEDED libreporter.so.1
NEEDED libcppprocess.so.1
NEEDED libeventdispatcher.so.1 <-- my library
NEEDED libadvgetopt.so.2
NEEDED libcppthread.so.1
NEEDED libaddr.so.1
NEEDED libutf8.so.1
NEEDED libssl.so.3
NEEDED libcrypto.so.3 <-- problem library
NEEDED libsnaplogger.so.1
[...snip...]
In other words, if you have the problem in a .so library, you can fix it directly in that library (with the caveat that people must make sure to link in the correct order). Otherwise, creating a .so library just for that purpose is also a solution, which works with executables as well, along the export LD_PRELOAD=/path/to/that/dot.so/library
.
Upvotes: 1
Reputation: 8923
One solution is as follows.
In a header file, #define
all functions you want to intercept to your wrapper functions.
#define foo wrap_foo
#define bar wrap_bar
Stick this in some header file that is included everywhere, so that all of the code includes this header. This header should get included before the header for the library that you are intercepting. gcc
also has an -include
option that causes a header file to be included in all sources.
The goal here is to replace all calls to actual functions with calls to the wrapper functions.
Now define your wrapper functions somewhere, and intercept the calls as you wish. This file should not include the wrapper header that you created earlier.
int wrap_foo() {
return foo();
}
int wrap_bar() {
return bar();
}
Link it all together.
Note that this will let you intercept calls to foo()
and bar()
etc. only in the code that is compiled including the wrapper header. Any pre-compiled code (in libraries or objects) will link to foo()
and bar()
etc. directly.
Upvotes: 3
Reputation: 1168
You can use LD_PRELOAD to point to your own shared library. With LD_PRELOAD all functions in your shared library will be called instead of functions with the same name in those other libraries.
If you wish to inject code and then call the original functions you will need to call dlsym to get the original function from the third party library.
There are some examples here: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
Upvotes: 7