-
Notifications
You must be signed in to change notification settings - Fork 349
Description
Hi @wolfcw
I'm having trouble getting libfaketime to work with MacOS 12, possibly due to changes in how dyld works. I am using an M1 mac, but it doesn't seem to be related to the architecture.
Repro (from libfaketime repo root):
make
cat <<EOF > monterepro.c
#include <stdio.h>
#include <time.h>
#include <dlfcn.h>
static int (*next_clock_gettime) (clockid_t clk_id, struct timespec *tp);
int main()
{
// This will print 2021
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
printf(ctime(&now));
// clock_gettime was loaded from /usr/lib/system/libsystem_c.dylib
Dl_info dl_info;
dladdr((void *)clock_gettime, &dl_info);
printf("%s\n", dl_info.dli_fname);
// Libfaketime itself works -- the block below prints 2029
void* handle = dlopen("./src/libfaketime.1.dylib", RTLD_LAZY);
next_clock_gettime = dlsym(handle, "clock_gettime");
next_clock_gettime(CLOCK_REALTIME, &now);
printf(ctime(&now));
}
EOF
clang -o monterepro monterepro.c
DYLD_INSERT_LIBRARIES=./src/libfaketime.1.dylib DYLD_FORCE_FLAT_NAMESPACE=YES FAKETIME='@2029-08-14 21:59:54' ./monterepro
At the end of the output, you should see:
Thu Dec 9 xx:xx:xx 2021
/usr/lib/system/libsystem_c.dylib
Tue Aug 14 21:59:54 2029
It looks like not matter what I do, the clock functions are loaded from libsystem_c.dylib. Running with DYLD_PRINT_LIBRARIES=YES
shows that libfaketime is indeed being loaded:
> libfaketime(master): DYLD_INSERT_LIBRARIES=./src/libfaketime.1.dylib DYLD_PRINT_LIBRARIES=YES DYLD_FORCE_FLAT_NAMESPACE=YES FAKETIME='@2029-08-14 21:59:54' ./monterepro
dyld[70537]: <31AA8C31-5441-300A-8FA6-F2000B296E25> /Users/ronrother/libfaketime/monterepro
dyld[70537]: <9905DE48-8EA9-3106-966C-E80EC7BA33B9> /Users/ronrother/libfaketime/src/libfaketime.1.dylib
...
But DYLD_PRINT_BINDINGS
suggests libfaketime is not taking precedence:
> libfaketime(master): DYLD_INSERT_LIBRARIES=./src/libfaketime.1.dylib DYLD_PRINT_BINDINGS=YES DYLD_FORCE_FLAT_NAMESPACE=YES FAKETIME='@2029-08-14 21:59:54' ./monterepro
...
dyld[77121]: <monterepro/bind#0> -> 0x1bc324d3c (libsystem_c.dylib/_clock_gettime)
...
This style of interposition still works with functions from other libraries, but doesn't seem to work with functions from libsystem.
For the record:
I had success using this approach: http://toves.freeshell.org/interpose/. By copying this DYLD_INTERPOSE macro from https://opensource.apple.com/source/dyld/dyld-97.1/include/mach-o/dyld-interposing.h.auto.html, renaming clock_gettime
in libfaketime.c
to interposed_clock_gettime
, applying DYLD_INTERPOSE
to it and calling clock_gettime
directly instead of using real_clock_gettime
, I got it to work (as a POC):
...
#define DYLD_INTERPOSE(_replacment,_replacee) \
__attribute__((used)) static struct{ const void* replacment; const void* replacee; } _interpose_##_replacee \
__attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacment, (const void*)(unsigned long)&_replacee };
# renamed from clock_gettime
int interposed_clock_gettime(clockid_t clk_id, struct timespec *tp)
{
...
DONT_FAKE_TIME(result = (*clock_gettime)(clk_id, tp));
...
}
DYLD_INTERPOSE(interposed_clock_gettime, clock_gettime);
But it looks like this approach would require a large refactor, and I'm not sure how far it would be in terms of backwards-compatibility.