Skip to content

Commit cb1c60a

Browse files
committed
Add macOS support.
1 parent 1955b9d commit cb1c60a

File tree

4 files changed

+163
-40
lines changed

4 files changed

+163
-40
lines changed

nix-trace/test.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
run() {
44
rm -f test-tmp/log
5-
LD_PRELOAD=$PWD/build/trace-nix.so TRACE_NIX=test-tmp/log \
5+
DYLD_INSERT_LIBRARIES=$PWD/build/trace-nix.so LD_PRELOAD=$PWD/build/trace-nix.so TRACE_NIX=test-tmp/log \
66
nix-shell --run : -p -- "$@" 2>/dev/null
77
}
88

99
run_without_p() {
1010
rm -f test-tmp/log
11-
LD_PRELOAD=$PWD/build/trace-nix.so TRACE_NIX=test-tmp/log \
11+
DYLD_INSERT_LIBRARIES=$PWD/build/trace-nix.so LD_PRELOAD=$PWD/build/trace-nix.so TRACE_NIX=test-tmp/log \
1212
nix-shell --run : -- "$@" 2>/dev/null
1313
}
1414

nix-trace/trace-nix.c

Lines changed: 158 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <errno.h>
77
#include <fcntl.h>
88
#include <limits.h>
9-
#include <pthread.h>
109
#include <stdarg.h>
1110
#include <stdio.h>
1211
#include <stdlib.h>
@@ -16,16 +15,34 @@
1615
#include <sys/types.h>
1716
#include <unistd.h>
1817

19-
static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
2018
static FILE *log_f = NULL;
2119
static const char *pwd = NULL;
2220

21+
#ifdef __APPLE__
22+
23+
struct dyld_interpose {
24+
const void * replacement;
25+
const void * replacee;
26+
};
27+
28+
#define WRAPPER(RET, FUN) static RET _cns_wrapper_##FUN
29+
#define REAL(FUN) FUN
30+
#define DEF_WRAPPER(FUN) \
31+
__attribute__((used)) static struct dyld_interpose _cns_interpose_##FUN \
32+
__attribute__((section("__DATA,__interpose"))) = { &_cns_wrapper_##FUN, &FUN };
33+
34+
#else /* __APPLE__ */
35+
2336
static int (*real___lxstat)(int ver, const char *path, struct stat *buf) = NULL;
2437
static int (*real_open)(const char *path, int flags, ...) = NULL;
2538
static DIR *(*real_opendir)(const char *name) = NULL;
2639

40+
#define WRAPPER(RET, FUN) RET FUN
2741
#define REAL(FUN) \
2842
(real_##FUN == NULL ? (real_##FUN = dlsym(RTLD_NEXT, #FUN)) : real_##FUN)
43+
#define DEF_WRAPPER(FUN)
44+
45+
#endif /* __APPLE__ */
2946

3047
#define FATAL() \
3148
do { \
@@ -36,8 +53,36 @@ static DIR *(*real_opendir)(const char *name) = NULL;
3653

3754
#define LEN 16
3855

56+
// Locks
57+
58+
#ifdef __APPLE__
59+
60+
#include <dispatch/dispatch.h>
61+
static dispatch_semaphore_t print_mutex;
62+
static dispatch_semaphore_t buf_mutex;
63+
#define INIT_MUTEX(MUTEX) \
64+
MUTEX = dispatch_semaphore_create(1)
65+
#define LOCK(MUTEX) \
66+
dispatch_semaphore_wait(MUTEX, DISPATCH_TIME_FOREVER)
67+
#define UNLOCK(MUTEX) \
68+
dispatch_semaphore_signal(MUTEX)
69+
70+
#else /* __APPLE__ */
71+
72+
#include <pthread.h>
73+
static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
74+
static pthread_mutex_t buf_mutex = PTHREAD_MUTEX_INITIALIZER;
75+
#define INIT_MUTEX(MUTEX)
76+
#define LOCK(MUTEX) \
77+
pthread_mutex_lock(&MUTEX)
78+
#define UNLOCK(MUTEX) \
79+
pthread_mutex_unlock(&MUTEX)
80+
81+
#endif /* __APPLE__ */
82+
3983
// Predeclarations
4084

85+
static void print_stat(int result, const char *path, struct stat *sb);
4186
static void convert_digest(char [static LEN*2+1], const uint8_t [static LEN]);
4287
static int enable(const char *);
4388
static void hash_dir(char [static LEN*2+1], DIR *);
@@ -47,10 +92,65 @@ static int strcmp_qsort(const void *, const void *);
4792

4893
////////////////////////////////////////////////////////////////////////////////
4994

95+
#ifdef __APPLE__
96+
97+
/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */
98+
/*
99+
* Copyright (c) 2008 Otto Moerbeek <[email protected]>
100+
*
101+
* Permission to use, copy, modify, and distribute this software for any
102+
* purpose with or without fee is hereby granted, provided that the above
103+
* copyright notice and this permission notice appear in all copies.
104+
*
105+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
107+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
108+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
109+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
110+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
111+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
112+
*/
113+
114+
#include <sys/types.h>
115+
#include <errno.h>
116+
#include <stdint.h>
117+
#include <stdlib.h>
118+
119+
/*
120+
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
121+
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
122+
*/
123+
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
124+
125+
static void *reallocarray(void *optr, size_t nmemb, size_t size) {
126+
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
127+
nmemb > 0 && SIZE_MAX / nmemb < size) {
128+
errno = ENOMEM;
129+
return NULL;
130+
}
131+
return realloc(optr, size * nmemb);
132+
}
133+
134+
static const char *get_current_dir_name() {
135+
char *buf = NULL;
136+
size_t bufsize = 64 * sizeof(char);
137+
do {
138+
buf = realloc(buf, bufsize);
139+
if (buf == NULL)
140+
FATAL();
141+
if (getcwd(buf, bufsize))
142+
return buf;
143+
bufsize *= 2;
144+
} while (errno == ERANGE);
145+
FATAL();
146+
}
147+
#endif /* __APPLE__ */
148+
50149
static void __attribute__((constructor)) init() {
51-
// Remove ourselves from LD_PRLOAD. We do not want to log child processes.
150+
// Remove ourselves from LD_PRELOAD and DYLD_INSERT_LIBRARIES. We do not want to log child processes.
52151
// TODO: use `ld.so --preload` instead
53152
unsetenv("LD_PRELOAD");
153+
unsetenv("DYLD_INSERT_LIBRARIES");
54154

55155
const char *fname = getenv("TRACE_NIX");
56156
if (fname != NULL) {
@@ -65,42 +165,32 @@ static void __attribute__((constructor)) init() {
65165
FATAL();
66166
}
67167
unsetenv("TRACE_NIX");
168+
169+
INIT_MUTEX(print_mutex);
170+
INIT_MUTEX(buf_mutex);
68171
}
69172

70-
int __lxstat(int ver, const char *path, struct stat *sb) {
71-
static char *buf = NULL;
72-
static off_t buf_len = 0;
173+
#ifdef __APPLE__
73174

74-
int result = REAL(__lxstat)(ver, path, sb);
175+
WRAPPER(int, lstat)(const char *path, struct stat *sb) {
176+
int result = REAL(lstat)(path, sb);
177+
print_stat(result, path, sb);
178+
return result;
179+
}
180+
DEF_WRAPPER(lstat);
75181

76-
if (enable(path)) {
77-
if (result != 0) {
78-
print_log('s', path, "-");
79-
} else if (S_ISLNK(sb->st_mode)) {
80-
pthread_mutex_lock(&mutex);
81-
if (buf_len < sb->st_size + 2) {
82-
buf_len = sb->st_size + 2;
83-
buf = realloc(buf, buf_len);
84-
if (buf == NULL)
85-
FATAL();
86-
}
87-
ssize_t link_len = readlink(path, buf+1, sb->st_size);
88-
if (link_len < 0 || link_len != sb->st_size)
89-
FATAL();
90-
buf[0] = 'l';
91-
buf[sb->st_size+1] = 0;
92-
print_log('s', path, buf);
93-
pthread_mutex_unlock(&mutex);
94-
} else if (S_ISDIR(sb->st_mode)) {
95-
print_log('s', path, "d");
96-
} else {
97-
print_log('s', path, "+");
98-
}
99-
}
182+
#else /* __APPLE__ */
183+
184+
WRAPPER(int, __lxstat)(int ver, const char *path, struct stat *sb) {
185+
int result = REAL(__lxstat)(ver, path, sb);
186+
print_stat(result, path, sb);
100187
return result;
101-
}
188+
}
189+
DEF_WRAPPER(__lxstat)
190+
191+
#endif /* __APPLE__ */
102192

103-
int open(const char *path, int flags, ...) {
193+
WRAPPER(int, open)(const char *path, int flags, ...) {
104194
va_list args;
105195
va_start(args, flags);
106196
int mode = va_arg(args, int);
@@ -120,8 +210,9 @@ int open(const char *path, int flags, ...) {
120210

121211
return fd;
122212
}
213+
DEF_WRAPPER(open)
123214

124-
DIR *opendir(const char *path) {
215+
WRAPPER(DIR *, opendir)(const char *path) {
125216
DIR *dirp = REAL(opendir)(path);
126217
if (enable(path)) {
127218
if (dirp == NULL) {
@@ -134,6 +225,7 @@ DIR *opendir(const char *path) {
134225
}
135226
return dirp;
136227
}
228+
DEF_WRAPPER(opendir)
137229

138230
////////////////////////////////////////////////////////////////////////////////
139231

@@ -164,8 +256,38 @@ static int enable(const char *path) {
164256
return 1;
165257
}
166258

259+
static void print_stat(int result, const char *path, struct stat *sb) {
260+
static char *buf = NULL;
261+
static off_t buf_len = 0;
262+
263+
if (enable(path)) {
264+
if (result != 0) {
265+
print_log('s', path, "-");
266+
} else if (S_ISLNK(sb->st_mode)) {
267+
LOCK(buf_mutex);
268+
if (buf_len < sb->st_size + 2) {
269+
buf_len = sb->st_size + 2;
270+
buf = realloc(buf, buf_len);
271+
if (buf == NULL)
272+
FATAL();
273+
}
274+
ssize_t link_len = readlink(path, buf+1, sb->st_size);
275+
if (link_len < 0 || link_len != sb->st_size)
276+
FATAL();
277+
buf[0] = 'l';
278+
buf[sb->st_size+1] = 0;
279+
print_log('s', path, buf);
280+
UNLOCK(buf_mutex);
281+
} else if (S_ISDIR(sb->st_mode)) {
282+
print_log('s', path, "d");
283+
} else {
284+
print_log('s', path, "+");
285+
}
286+
}
287+
}
288+
167289
static void print_log(char op, const char *path, const char *result) {
168-
pthread_mutex_lock(&mutex);
290+
LOCK(print_mutex);
169291
fprintf(
170292
log_f,
171293
"%c" "%s%s" "%s%c" "%s%c",
@@ -175,7 +297,7 @@ static void print_log(char op, const char *path, const char *result) {
175297
result, (char)0
176298
);
177299
fflush(log_f);
178-
pthread_mutex_unlock(&mutex);
300+
UNLOCK(print_mutex);
179301
}
180302

181303
static void hash_file(char digest_s[static LEN*2+1], int fd) {

shell.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ let main = import ./default.nix { inherit pkgs; };
33
in with pkgs;
44
mkShell {
55
buildInputs = main.buildInputs ++ main.nativeBuildInputs
6-
++ [ cargo-edit niv nixfmt rustfmt shellcheck ];
6+
++ [ cargo-edit niv nixfmt rustfmt shellcheck time ];
77
inherit (main) BLAKE3_CSRC;
88
CNS_IN_NIX_SHELL = "1";
99
}

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ struct NixShellOutput {
101101
}
102102

103103
fn minimal_essential_path() -> OsString {
104-
let required_binaries = ["tar", "gzip", "git"];
104+
let required_binaries = ["tar", "gzip", "git", "nix-shell", "rm"];
105105

106106
fn which_dir(binary: &&str) -> Option<PathBuf> {
107107
std::env::var_os("PATH")
@@ -232,6 +232,7 @@ fn run_nix_shell(inp: &NixShellInput) -> NixShellOutput {
232232
.env_clear()
233233
.envs(&inp.env)
234234
.env("LD_PRELOAD", env!("CNS_TRACE_NIX_SO"))
235+
.env("DYLD_INSERT_LIBRARIES", env!("CNS_TRACE_NIX_SO"))
235236
.env("TRACE_NIX", trace_file.path())
236237
.stdin(Stdio::null())
237238
.status()

0 commit comments

Comments
 (0)