15 #define MAX_REDIRECTS 128
18 struct dyld_interpose
{
19 const void * replacement
;
20 const void * replacee
;
22 #define WRAPPER(ret, name) static ret _libredirect_wrapper_##name
23 #define LOOKUP_REAL(name) &name
24 #define WRAPPER_DEF(name) \
25 __attribute__((used)) static struct dyld_interpose _libredirect_interpose_##name \
26 __attribute__((section("__DATA,__interpose"))) = { &_libredirect_wrapper_##name, &name };
28 #define WRAPPER(ret, name) ret name
29 #define LOOKUP_REAL(name) dlsym(RTLD_NEXT, #name)
30 #define WRAPPER_DEF(name)
33 static int nrRedirects
= 0;
34 static char * from
[MAX_REDIRECTS
];
35 static char * to
[MAX_REDIRECTS
];
37 static int isInitialized
= 0;
39 // FIXME: might run too late.
40 static void init() __attribute__((constructor
));
44 if (isInitialized
) return;
46 char * spec
= getenv("NIX_REDIRECTS");
49 // Ensure we only run this code once.
50 // We do not do `unsetenv("NIX_REDIRECTS")` to ensure that redirects
51 // also get initialized for subprocesses.
54 char * spec2
= malloc(strlen(spec
) + 1);
57 char * pos
= spec2
, * eq
;
58 while ((eq
= strchr(pos
, '='))) {
60 from
[nrRedirects
] = pos
;
62 to
[nrRedirects
] = pos
;
64 if (nrRedirects
== MAX_REDIRECTS
) break;
65 char * end
= strchr(pos
, ':');
73 static const char * rewrite(const char * volatile path
, char * buf
)
75 // Marking the path volatile is needed so the the following check isn't
76 // optimized away by the compiler.
77 if (path
== NULL
) return path
;
79 for (int n
= 0; n
< nrRedirects
; ++n
) {
80 int len
= strlen(from
[n
]);
81 if (strncmp(path
, from
[n
], len
) != 0) continue;
82 if (snprintf(buf
, PATH_MAX
, "%s%s", to
[n
], path
+ len
) >= PATH_MAX
)
90 static char * rewrite_non_const(char * path
, char * buf
)
92 // as long as the argument `path` is non-const, we can consider discarding
93 // the const qualifier of the return value to be safe.
94 return (char *)rewrite(path
, buf
);
97 static int open_needs_mode(int flags
)
100 return (flags
& O_CREAT
) || (flags
& O_TMPFILE
) == O_TMPFILE
;
102 return flags
& O_CREAT
;
106 /* The following set of Glibc library functions is very incomplete -
107 it contains only what we needed for programs in Nixpkgs. Just add
108 more functions as needed. */
110 WRAPPER(int, open
)(const char * path
, int flags
, ...)
112 int (*open_real
) (const char *, int, ...) = LOOKUP_REAL(open
);
114 if (open_needs_mode(flags
)) {
117 mode
= va_arg(ap
, mode_t
);
121 return open_real(rewrite(path
, buf
), flags
, mode
);
125 // In musl libc, open64 is simply a macro for open
126 #if !defined(__APPLE__) && !defined(open64)
127 WRAPPER(int, open64
)(const char * path
, int flags
, ...)
129 int (*open64_real
) (const char *, int, mode_t
) = LOOKUP_REAL(open64
);
131 if (open_needs_mode(flags
)) {
134 mode
= va_arg(ap
, mode_t
);
138 return open64_real(rewrite(path
, buf
), flags
, mode
);
143 WRAPPER(int, openat
)(int dirfd
, const char * path
, int flags
, ...)
145 int (*openat_real
) (int, const char *, int, ...) = LOOKUP_REAL(openat
);
147 if (open_needs_mode(flags
)) {
150 mode
= va_arg(ap
, mode_t
);
154 return openat_real(dirfd
, rewrite(path
, buf
), flags
, mode
);
158 WRAPPER(FILE *, fopen
)(const char * path
, const char * mode
)
160 FILE * (*fopen_real
) (const char *, const char *) = LOOKUP_REAL(fopen
);
162 return fopen_real(rewrite(path
, buf
), mode
);
167 WRAPPER(FILE *, __nss_files_fopen
)(const char * path
)
169 FILE * (*__nss_files_fopen_real
) (const char *) = LOOKUP_REAL(__nss_files_fopen
);
171 return __nss_files_fopen_real(rewrite(path
, buf
));
173 WRAPPER_DEF(__nss_files_fopen
)
176 // In musl libc, fopen64 is simply a macro for fopen
177 #if !defined(__APPLE__) && !defined(fopen64)
178 WRAPPER(FILE *, fopen64
)(const char * path
, const char * mode
)
180 FILE * (*fopen64_real
) (const char *, const char *) = LOOKUP_REAL(fopen64
);
182 return fopen64_real(rewrite(path
, buf
), mode
);
188 WRAPPER(int, __xstat
)(int ver
, const char * path
, struct stat
* st
)
190 int (*__xstat_real
) (int ver
, const char *, struct stat
*) = LOOKUP_REAL(__xstat
);
192 return __xstat_real(ver
, rewrite(path
, buf
), st
);
198 WRAPPER(int, __xstat64
)(int ver
, const char * path
, struct stat64
* st
)
200 int (*__xstat64_real
) (int ver
, const char *, struct stat64
*) = LOOKUP_REAL(__xstat64
);
202 return __xstat64_real(ver
, rewrite(path
, buf
), st
);
204 WRAPPER_DEF(__xstat64
)
207 #if defined(__linux__) && defined(STATX_TYPE)
208 WRAPPER(int, statx
)(int dirfd
, const char * restrict pathname
, int flags
,
209 unsigned int mask
, struct statx
* restrict statxbuf
)
211 int (*statx_real
) (int, const char * restrict
, int,
212 unsigned int, struct statx
* restrict
) = LOOKUP_REAL(statx
);
214 return statx_real(dirfd
, rewrite(pathname
, buf
), flags
, mask
, statxbuf
);
219 WRAPPER(int, fstatat
)(int dirfd
, const char * pathname
, struct stat
* statbuf
, int flags
)
221 int (*fstatat_real
) (int, const char *, struct stat
*, int) = LOOKUP_REAL(fstatat
);
223 return fstatat_real(dirfd
, rewrite(pathname
, buf
), statbuf
, flags
);
225 WRAPPER_DEF(fstatat
);
227 // In musl libc, fstatat64 is simply a macro for fstatat
228 #if !defined(__APPLE__) && !defined(fstatat64)
229 WRAPPER(int, fstatat64
)(int dirfd
, const char * pathname
, struct stat64
* statbuf
, int flags
)
231 int (*fstatat64_real
) (int, const char *, struct stat64
*, int) = LOOKUP_REAL(fstatat64
);
233 return fstatat64_real(dirfd
, rewrite(pathname
, buf
), statbuf
, flags
);
235 WRAPPER_DEF(fstatat64
);
238 WRAPPER(int, stat
)(const char * path
, struct stat
* st
)
240 int (*__stat_real
) (const char *, struct stat
*) = LOOKUP_REAL(stat
);
242 return __stat_real(rewrite(path
, buf
), st
);
246 // In musl libc, stat64 is simply a macro for stat
247 #if !defined(__APPLE__) && !defined(stat64)
248 WRAPPER(int, stat64
)(const char * path
, struct stat64
* st
)
250 int (*stat64_real
) (const char *, struct stat64
*) = LOOKUP_REAL(stat64
);
252 return stat64_real(rewrite(path
, buf
), st
);
257 WRAPPER(int, access
)(const char * path
, int mode
)
259 int (*access_real
) (const char *, int mode
) = LOOKUP_REAL(access
);
261 return access_real(rewrite(path
, buf
), mode
);
265 WRAPPER(int, posix_spawn
)(pid_t
* pid
, const char * path
,
266 const posix_spawn_file_actions_t
* file_actions
,
267 const posix_spawnattr_t
* attrp
,
268 char * const argv
[], char * const envp
[])
270 int (*posix_spawn_real
) (pid_t
*, const char *,
271 const posix_spawn_file_actions_t
*,
272 const posix_spawnattr_t
*,
273 char * const argv
[], char * const envp
[]) = LOOKUP_REAL(posix_spawn
);
275 return posix_spawn_real(pid
, rewrite(path
, buf
), file_actions
, attrp
, argv
, envp
);
277 WRAPPER_DEF(posix_spawn
)
279 WRAPPER(int, posix_spawnp
)(pid_t
* pid
, const char * file
,
280 const posix_spawn_file_actions_t
* file_actions
,
281 const posix_spawnattr_t
* attrp
,
282 char * const argv
[], char * const envp
[])
284 int (*posix_spawnp_real
) (pid_t
*, const char *,
285 const posix_spawn_file_actions_t
*,
286 const posix_spawnattr_t
*,
287 char * const argv
[], char * const envp
[]) = LOOKUP_REAL(posix_spawnp
);
289 return posix_spawnp_real(pid
, rewrite(file
, buf
), file_actions
, attrp
, argv
, envp
);
291 WRAPPER_DEF(posix_spawnp
)
293 WRAPPER(int, execv
)(const char * path
, char * const argv
[])
295 int (*execv_real
) (const char * path
, char * const argv
[]) = LOOKUP_REAL(execv
);
297 return execv_real(rewrite(path
, buf
), argv
);
301 WRAPPER(int, execvp
)(const char * path
, char * const argv
[])
303 int (*_execvp
) (const char *, char * const argv
[]) = LOOKUP_REAL(execvp
);
305 return _execvp(rewrite(path
, buf
), argv
);
309 WRAPPER(int, execve
)(const char * path
, char * const argv
[], char * const envp
[])
311 int (*_execve
) (const char *, char * const argv
[], char * const envp
[]) = LOOKUP_REAL(execve
);
313 return _execve(rewrite(path
, buf
), argv
, envp
);
317 WRAPPER(DIR *, opendir
)(const char * path
)
320 DIR * (*_opendir
) (const char*) = LOOKUP_REAL(opendir
);
322 return _opendir(rewrite(path
, buf
));
326 #define SYSTEM_CMD_MAX 512
328 static char * replace_substring(char * source
, char * buf
, char * replace_string
, char * start_ptr
, char * suffix_ptr
) {
329 char head
[SYSTEM_CMD_MAX
] = {0};
330 strncpy(head
, source
, start_ptr
- source
);
332 char tail
[SYSTEM_CMD_MAX
] = {0};
333 if(suffix_ptr
< source
+ strlen(source
)) {
334 strcpy(tail
, suffix_ptr
);
337 sprintf(buf
, "%s%s%s", head
, replace_string
, tail
);
341 static char * replace_string(char * buf
, char * from
, char * to
) {
343 char * matches
[SYSTEM_CMD_MAX
];
344 int from_len
= strlen(from
);
345 for(int i
=0; i
<strlen(buf
); i
++){
346 char *cmp_start
= buf
+ i
;
347 if(strncmp(from
, cmp_start
, from_len
) == 0){
348 matches
[num_matches
] = cmp_start
;
352 int len_diff
= strlen(to
) - strlen(from
);
353 for(int n
= 0; n
< num_matches
; n
++) {
354 char replaced
[SYSTEM_CMD_MAX
];
355 replace_substring(buf
, replaced
, to
, matches
[n
], matches
[n
]+from_len
);
356 strcpy(buf
, replaced
);
357 for(int nn
= n
+1; nn
< num_matches
; nn
++) {
358 matches
[nn
] += len_diff
;
364 static void rewriteSystemCall(const char * command
, char * buf
) {
368 // The dyld environment variable is not inherited by the subprocess spawned
369 // by system(), so this hack redefines it.
371 dladdr(&rewriteSystemCall
, &info
);
372 p
= stpcpy(p
, "export DYLD_INSERT_LIBRARIES=");
373 p
= stpcpy(p
, info
.dli_fname
);
379 for (int n
= 0; n
< nrRedirects
; ++n
) {
380 replace_string(buf
, from
[n
], to
[n
]);
384 WRAPPER(int, system
)(const char *command
)
386 int (*_system
) (const char*) = LOOKUP_REAL(system
);
388 char newCommand
[SYSTEM_CMD_MAX
];
389 rewriteSystemCall(command
, newCommand
);
390 return _system(newCommand
);
394 WRAPPER(int, chdir
)(const char *path
)
396 int (*chdir_real
) (const char *) = LOOKUP_REAL(chdir
);
398 return chdir_real(rewrite(path
, buf
));
402 WRAPPER(int, mkdir
)(const char *path
, mode_t mode
)
404 int (*mkdir_real
) (const char *path
, mode_t mode
) = LOOKUP_REAL(mkdir
);
406 return mkdir_real(rewrite(path
, buf
), mode
);
410 WRAPPER(int, mkdirat
)(int dirfd
, const char *path
, mode_t mode
)
412 int (*mkdirat_real
) (int dirfd
, const char *path
, mode_t mode
) = LOOKUP_REAL(mkdirat
);
414 return mkdirat_real(dirfd
, rewrite(path
, buf
), mode
);
418 WRAPPER(int, unlink
)(const char *path
)
420 int (*unlink_real
) (const char *path
) = LOOKUP_REAL(unlink
);
422 return unlink_real(rewrite(path
, buf
));
426 WRAPPER(int, unlinkat
)(int dirfd
, const char *path
, int flags
)
428 int (*unlinkat_real
) (int dirfd
, const char *path
, int flags
) = LOOKUP_REAL(unlinkat
);
430 return unlinkat_real(dirfd
, rewrite(path
, buf
), flags
);
432 WRAPPER_DEF(unlinkat
)
434 WRAPPER(int, rmdir
)(const char *path
)
436 int (*rmdir_real
) (const char *path
) = LOOKUP_REAL(rmdir
);
438 return rmdir_real(rewrite(path
, buf
));
442 static void copy_temp_wildcard(char * dest
, char * src
, int suffixlen
) {
443 int dest_len
= strnlen(dest
, PATH_MAX
);
444 int src_len
= strnlen(src
, PATH_MAX
);
445 memcpy(dest
+ dest_len
- (6 + suffixlen
), src
+ src_len
- (6 + suffixlen
), 6);
448 WRAPPER(int, mkstemp
)(char *template)
450 int (*mkstemp_real
) (char *template) = LOOKUP_REAL(mkstemp
);
452 char * rewritten
= rewrite_non_const(template, buf
);
453 int retval
= mkstemp_real(rewritten
);
454 if (retval
>= 0 && rewritten
!= template) {
455 copy_temp_wildcard(template, rewritten
, 0);
461 WRAPPER(int, mkostemp
)(char *template, int flags
)
463 int (*mkostemp_real
) (char *template, int flags
) = LOOKUP_REAL(mkostemp
);
465 char * rewritten
= rewrite_non_const(template, buf
);
466 int retval
= mkostemp_real(rewritten
, flags
);
467 if (retval
>= 0 && rewritten
!= template) {
468 copy_temp_wildcard(template, rewritten
, 0);
472 WRAPPER_DEF(mkostemp
)
474 WRAPPER(int, mkstemps
)(char *template, int suffixlen
)
476 int (*mkstemps_real
) (char *template, int suffixlen
) = LOOKUP_REAL(mkstemps
);
478 char * rewritten
= rewrite_non_const(template, buf
);
479 int retval
= mkstemps_real(rewritten
, suffixlen
);
480 if (retval
>= 0 && rewritten
!= template) {
481 copy_temp_wildcard(template, rewritten
, suffixlen
);
485 WRAPPER_DEF(mkstemps
)
487 WRAPPER(int, mkostemps
)(char *template, int suffixlen
, int flags
)
489 int (*mkostemps_real
) (char *template, int suffixlen
, int flags
) = LOOKUP_REAL(mkostemps
);
491 char * rewritten
= rewrite_non_const(template, buf
);
492 int retval
= mkostemps_real(rewritten
, suffixlen
, flags
);
493 if (retval
>= 0 && rewritten
!= template) {
494 copy_temp_wildcard(template, rewritten
, suffixlen
);
498 WRAPPER_DEF(mkostemps
)
500 WRAPPER(char *, mkdtemp
)(char *template)
502 char * (*mkdtemp_real
) (char *template) = LOOKUP_REAL(mkdtemp
);
504 char * rewritten
= rewrite_non_const(template, buf
);
505 char * retval
= mkdtemp_real(rewritten
);
506 if (retval
== NULL
) {
509 if (rewritten
!= template) {
510 copy_temp_wildcard(template, rewritten
, 0);
516 WRAPPER(char *, mktemp
)(char *template)
518 char * (*mktemp_real
) (char *template) = LOOKUP_REAL(mktemp
);
520 char * rewritten
= rewrite_non_const(template, buf
);
521 char * retval
= mktemp_real(rewritten
);
522 if (retval
== NULL
) {
525 if (rewritten
!= template) {
526 copy_temp_wildcard(template, rewritten
, 0);