From 30d7defcf5bedb3b1bad66662510cb19a6de8635 Mon Sep 17 00:00:00 2001 From: usertam Date: Sun, 8 Jun 2025 18:33:33 +0800 Subject: [PATCH 1/2] libfaketime.c: get rid of stat64 things on aarch64-darwin To give more context, stat64 is a child of large-file support (LFS) back in 1996, during the transition from 32-bit to 64-bit. People wanted 64-bit inodes in 32-bit systems, hence stat and stat64. Nowadays where everything is 64-bit, stat64 is mostly just an alias to stat, as stat is already native 64-bit. On modern implementations like musl, stat64 is even dropped entirely as a sane default. We observe the same in darwin's stat.h: #if !__DARWIN_ONLY_64_BIT_INO_T struct stat64 __DARWIN_STRUCT_STAT64; #endif /* !__DARWIN_ONLY_64_BIT_INO_T */ Because struct stat64 doesn't ever exist on aarch64-darwin, and we don't have to worry about people using stat64 calls, we can safely remove all stat64 bloat, according to __DARWIN_ONLY_64_BIT_INO_T. I nuked fake_stat64buf because only STAT64_HANDLER is using it, and only non-darwin stat64 things use that handler. I didn't do more because people might still use stat64 things on x86_64 (on glibc) and other older 32-bit platforms, and we still need to hook those. A loose follow up to PR #453. Fixes the remaining clang warnings on aarch64-darwin. --- src/libfaketime.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/libfaketime.c b/src/libfaketime.c index a456995..4133173 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -208,10 +208,12 @@ static int (*real_xstat) (int, const char *, struct stat *); static int (*real_fxstat) (int, int, struct stat *); static int (*real_fxstatat) (int, int, const char *, struct stat *, int); static int (*real_lxstat) (int, const char *, struct stat *); +#if !defined(__APPLE__) || !__DARWIN_ONLY_64_BIT_INO_T static int (*real_xstat64) (int, const char *, struct stat64 *); static int (*real_fxstat64) (int, int , struct stat64 *); static int (*real_fxstatat64) (int, int , const char *, struct stat64 *, int); static int (*real_lxstat64) (int, const char *, struct stat64 *); +#endif #ifdef STATX_TYPE static int (*real_statx) (int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf); #endif @@ -983,6 +985,7 @@ static inline void fake_statbuf (struct stat *buf) { #endif } +#ifndef __APPLE__ static inline void fake_stat64buf (struct stat64 *buf) { #ifndef st_atime lock_for_stat(); @@ -992,18 +995,13 @@ static inline void fake_stat64buf (struct stat64 *buf) { unlock_for_stat(); #else lock_for_stat(); -#ifndef __APPLE__ - fake_clock_gettime(CLOCK_REALTIME, &buf->st_ctim); - fake_clock_gettime(CLOCK_REALTIME, &buf->st_atim); - fake_clock_gettime(CLOCK_REALTIME, &buf->st_mtim); -#else fake_clock_gettime(CLOCK_REALTIME, &buf->st_ctimespec); fake_clock_gettime(CLOCK_REALTIME, &buf->st_atimespec); fake_clock_gettime(CLOCK_REALTIME, &buf->st_mtimespec); -#endif unlock_for_stat(); #endif } +#endif /* macOS dyld interposing uses the function's real name instead of real_name */ #ifdef MACOS_DYLD_INTERPOSE @@ -1113,25 +1111,19 @@ int __lxstat (int ver, const char *path, struct stat *buf) { STAT_HANDLER(lxstat, buf, ver, path, buf); } -#endif -#ifndef __APPLE__ /* Contributed by Philipp Hachtmann in version 0.6 */ int __xstat64 (int ver, const char *path, struct stat64 *buf) { STAT64_HANDLER(xstat64, buf, ver, path, buf); } -#endif -#ifndef __APPLE__ /* Contributed by Philipp Hachtmann in version 0.6 */ int __fxstat64 (int ver, int fildes, struct stat64 *buf) { STAT64_HANDLER(fxstat64, buf, ver, fildes, buf); } -#endif -#ifndef __APPLE__ /* Added in v0.8 as suggested by Daniel Kahn Gillmor */ #ifndef NO_ATFILE int __fxstatat64 (int ver, int fildes, const char *filename, struct stat64 *buf, int flag) @@ -1139,16 +1131,14 @@ int __fxstatat64 (int ver, int fildes, const char *filename, struct stat64 *buf, STAT64_HANDLER(fxstatat64, buf, ver, fildes, filename, buf, flag); } #endif -#endif -#ifndef __APPLE__ /* Contributed by Philipp Hachtmann in version 0.6 */ int __lxstat64 (int ver, const char *path, struct stat64 *buf) { STAT64_HANDLER(lxstat64, buf, ver, path, buf); } -#endif -#endif +#endif /* ifndef __APPLE__ */ +#endif /* ifdef FAKE_STAT */ #ifdef STATX_TYPE static inline void fake_statx_timestamp(struct statx_timestamp* p) @@ -2689,10 +2679,12 @@ static void ftpl_really_init(void) real_fxstat = dlsym(RTLD_NEXT, "__fxstat"); real_fxstatat = dlsym(RTLD_NEXT, "__fxstatat"); real_lxstat = dlsym(RTLD_NEXT, "__lxstat"); +#if !defined(__APPLE__) || !__DARWIN_ONLY_64_BIT_INO_T real_xstat64 = dlsym(RTLD_NEXT,"__xstat64"); real_fxstat64 = dlsym(RTLD_NEXT, "__fxstat64"); real_fxstatat64 = dlsym(RTLD_NEXT, "__fxstatat64"); real_lxstat64 = dlsym(RTLD_NEXT, "__lxstat64"); +#endif #ifdef STATX_TYPE real_statx = dlsym(RTLD_NEXT, "statx"); #endif From 77ae25f52999d4b8169cae1649a236a996839940 Mon Sep 17 00:00:00 2001 From: usertam Date: Sun, 8 Jun 2025 21:20:04 +0800 Subject: [PATCH 2/2] README.OSX: document about the new arm64e ABI --- README.OSX | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.OSX b/README.OSX index e13a980..9690fcb 100644 --- a/README.OSX +++ b/README.OSX @@ -166,3 +166,50 @@ The environment variable FAKETIME can be changed at application run-time and always takes precedence over other user-controlled settings. It can be re-set to 0 (zero) to work around potential incompatibilities or if you do not want libfaketime applied to your software. + +5) Working with the new arm64e system binaries in Apple Silicon +--------------------------------------------------------------- + +Since Apple Silicon, Apple started shipping system binaries compiled against +the `arm64e` ABI. This new ABI enforces Pointer Authentication Codes (PACs), +and enforces assembly instructions to sign and check pointer signatures to +prevent malicious control flow altering. + + $ file /bin/date + /bin/date: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e] + /bin/date (for architecture x86_64): Mach-O 64-bit executable x86_64 + /bin/date (for architecture arm64e): Mach-O 64-bit executable arm64e + +Most importantly, the new `arm64e` ABI is incompatible with the normal `arm64` +ABI we are used to; this is done so that everything `arm64e` is PAC-enforced. + +As a result, this will happen when we try to hook naive `arm64` libfaketime on +system binaries (and vice versa with `arm64e` libfaketime on `arm64` binaries): + + $ DYLD_INSERT_LIBRARIES=libfaketime.1.dylib /bin/date + dyld[5788]: terminating because inserted dylib 'libfaketime.1.dylib' could not be loaded: + tried: 'libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e')) + +Since PR #497, we now compile libfaketime with a fat library/binary setup, so +that we support both ABIs at the same time: + + $ file libfaketime.1.dylib + libfaketime.1.dylib: Mach-O universal binary with 2 architectures: [arm64:Mach-O 64-bit dynamically linked shared library arm64] [arm64e:Mach-O 64-bit dynamically linked shared library arm64e] + libfaketime.1.dylib (for architecture arm64): Mach-O 64-bit dynamically linked shared library arm64 + libfaketime.1.dylib (for architecture arm64e): Mach-O 64-bit dynamically linked shared library arm64e + +Unfortunately, Apple does not support running third-party `arm64e` code yet, +since the ABI is still unstable. This means that you cannot use libfaketime +on system `arm64e` binaries out of the box, at the time of writing. + +If you really need to, you may disable SIP in the recovery terminal: + + (in recovery) # csrutil disable + +And enable the experimental ABI after boot: + + (in regular boot) $ sudo nvram boot-args=-arm64e_preview_abi + +Then `arm64e` should work as-is. This use case is rather uncommon since most +userspace binaries will remain `arm64` for the time being, until Apple really +doubles down on `arm64e`. Regardless, we should be prepared for that.