Reputation: 61
There are 3 components involved
loader.so
-Bsymbolic
, overrides puts
and loads other.so
puts
, and can't be modifiedHow can i have other.so
use the overridden puts
from loader.so
?
Note that i want puts
to be overridden only from loader.so
onwards (included other.so
), the main program should be unaffected
Sample code follows
main.c
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char *argv[]){
dlopen("./loader.so", RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);
puts("Normal");
return 0;
}
loader.c
#include <stdio.h>
#include <dlfcn.h>
extern int puts(const char *s){
fputs("Hooked: ", stdout);
fputs(s, stdout);
fputc('\n', stdout);
return 0;
}
__attribute__((constructor))
void ctor(void) {
puts("Something");
void *other = dlopen("./other.so", RTLD_NOW);
}
other.c
#include <stdio.h>
__attribute__((constructor))
void ctor(void) {
puts("Hello!");
}
make.sh
#!/bin/bash
gcc main.c -o main -ldl
gcc loader.c -fPIC -shared -Wl,-Bsymbolic -o loader.so
gcc other.c -fPIC -shared -o other.so
Desired output
Hooked: Something
Hooked: Hello!
Normal
Actual output
Hooked: Something
Hello!
Normal
Upvotes: 4
Views: 1150
Reputation: 61
After playing with the problem a bit more, i have a solution that requires a bit of external help from patchelf
, so i'll wait to accept this solution in case there's a different approach to the problem.
This solution works by making a new shared object, shared.so
, with the modified puts
, as following
int puts(const char *s){
fputs("Hooked: ", stdout);
fputs(s, stdout);
fputc('\n', stdout);
return 0;
}
We then need to force other.so
to depend on this new shared object, and we can do this by using patchelf --add-needed shared.so other.so
This does involve a modification to other.so
, but it doesn't require a re-compilation from source (which makes this approach more feasible).
Now, when we load other.so
, we need to specify RTLD_DEEPBIND
inside loader.c
like this
void *other = dlopen("./libother.so", RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);
so that the search order won't start from the global context, but from the library itself.
Since other.so
doesn't define puts
, the direct dependencies will be looked up, and puts
will be found in shared.so
The properties of RTLD_DEEPBIND
make sure that even eventual LD_PRELOAD
ed objects are trumped over.
So if puts
is disabled inside a preloaded shared object we can work around that and call the real, unmodified puts
from glibc (and only for calls originating from other.so
).
We don't need any patchelf
or shared.so
if all we want is restore the original behaviour
Upvotes: 1
Reputation: 49181
Try adding flag -Wl,--no-as-needed
gcc loader.c -fPIC -shared -Wl,-Bsymbolic -Wl,--no-as-needed -o loader.so
I successfully hooked time related functions from C library in time-machine.
Upvotes: 0