this repo has no description
1#include <stdio.h>
2#include <string.h>
3#include <sys/types.h>
4#include <dirent.h>
5#include <dlfcn.h>
6#include <stdbool.h>
7#include <stdlib.h>
8
9#include <mach/mach.h>
10
11#include <darling/emulation/common/simple.h>
12#include "xtracelib.h"
13#include "mach_trace.h"
14#include "bsd_trace.h"
15#include "xtrace/xtrace-mig-types.h"
16#include "tls.h"
17#include "string.h"
18
19#define XTRACE_MIG_DIR_PATH "/usr/lib/darling/xtrace-mig"
20
21static size_t subsystems_cnt = 0;
22static struct xtrace_mig_subsystem** subsystems = NULL;
23
24static mach_port_name_t host_port;
25
26extern "C"
27void xtrace_setup_mig_tracing(void)
28{
29 // This runs before the syscall tracing is enabled, so we can
30 // freely use libSystem and make syscalls.
31
32 host_port = mach_host_self();
33
34 DIR* xtrace_mig_dir = opendir(XTRACE_MIG_DIR_PATH);
35 if (xtrace_mig_dir == NULL)
36 {
37 perror("xtrace: failed to open " XTRACE_MIG_DIR_PATH);
38 return;
39 }
40 // Count the number of files, and allocate this many subsystem pointers;
41 for (struct dirent* dirent; (dirent = readdir(xtrace_mig_dir)) != NULL; subsystems_cnt++);
42 subsystems = (xtrace_mig_subsystem**)malloc(subsystems_cnt * sizeof(struct xtrace_mig_subsystem*));
43
44 rewinddir(xtrace_mig_dir);
45 for (size_t i = 0; i < subsystems_cnt; i++)
46 {
47 struct dirent* dirent = readdir(xtrace_mig_dir);
48 if (dirent == NULL)
49 {
50 perror("xtrace: readdir");
51 subsystems_cnt = i;
52 break;
53 }
54 if (dirent->d_type != DT_REG)
55 {
56 subsystems[i] = NULL;
57 continue;
58 }
59 size_t path_size = strlen(XTRACE_MIG_DIR_PATH) + 1 + strlen(dirent->d_name) + 1;
60 char path[path_size];
61 strcpy(path, XTRACE_MIG_DIR_PATH "/");
62 strcat(path, dirent->d_name);
63
64 void* dylib_handle = dlopen(path, RTLD_LOCAL);
65 if (dylib_handle == NULL)
66 {
67 xtrace_error("xtrace: failed to dlopen %s: %s\n", path, dlerror());
68 subsystems[i] = NULL;
69 continue;
70 }
71 subsystems[i] = (struct xtrace_mig_subsystem*) dlsym(dylib_handle, "xtrace_mig_subsystem");
72 if (subsystems[i] == NULL)
73 {
74 xtrace_error("xtrace: failed to dlsym(%s, \"xtrace_mig_subsystem\"): %s\n", path, dlerror());
75 // Leave NULL subsystem in place and continue.
76 }
77 }
78
79 closedir(xtrace_mig_dir);
80}
81
82DEFINE_XTRACE_TLS_VAR(bool, is_first_arg, false, NULL);
83
84#define BEFORE if (!get_is_first_arg()) xtrace_string_append_c_string(log, ", ")
85#define AFTER set_is_first_arg(false)
86
87XTRACE_HIDDEN extern "C"
88void add_raw_arg(xtrace_string_t log, const char* format, ...)
89{
90 va_list vl;
91 va_start(vl, format);
92
93 BEFORE;
94 xtrace_string_append_format_valist(log, format, vl);
95 AFTER;
96
97 va_end(vl);
98}
99
100XTRACE_HIDDEN extern "C"
101void add_num_arg(xtrace_string_t log, unsigned long long n)
102{
103 BEFORE;
104 xtrace_string_append_format(log, "%llu", n);
105 AFTER;
106}
107
108XTRACE_HIDDEN extern "C"
109void add_ptr_arg(xtrace_string_t log, void* ptr)
110{
111 BEFORE;
112 xtrace_string_append_format(log, "%p", ptr);
113 AFTER;
114}
115
116XTRACE_HIDDEN extern "C"
117void add_string_arg(xtrace_string_t log, const char* s)
118{
119 BEFORE;
120 xtrace_print_string_literal(xtrace::String::to_cxx_ptr(log), s);
121 AFTER;
122}
123
124XTRACE_HIDDEN extern "C"
125void add_bytes_arg(xtrace_string_t log, const void* bytes, unsigned long cnt)
126{
127 BEFORE;
128 const unsigned char* b = (const unsigned char*) bytes;
129 xtrace_string_append_c_string(log, "bytes ");
130 for (int i = 0; i < cnt; i++)
131 xtrace_string_append_format(log, "%x", b[i]);
132 AFTER;
133}
134
135XTRACE_HIDDEN extern "C"
136void add_return_code_arg(xtrace_string_t log, kern_return_t code)
137{
138 BEFORE;
139 xtrace_print_kern_return(xtrace::String::to_cxx_ptr(log), code);
140 AFTER;
141}
142
143XTRACE_HIDDEN extern "C"
144void add_port_arg(xtrace_string_t log, mach_port_name_t port_name, mach_msg_type_name_t disposition)
145{
146 BEFORE;
147 xtrace_string_append_format(log, "%s %u", xtrace_msg_type_to_str(disposition, 0), port_name);
148 AFTER;
149}
150
151XTRACE_HIDDEN extern "C"
152void add_ool_mem_arg(xtrace_string_t log, const void* ptr, unsigned long size)
153{
154 BEFORE;
155 xtrace_string_append_format(log, "mem [%p; %lu]", ptr, size);
156 AFTER;
157}
158
159XTRACE_HIDDEN extern "C"
160void add_ool_ports_arg(xtrace_string_t log, const void* ptr, unsigned long cnt, mach_msg_type_name_t disposition)
161{
162 BEFORE;
163 xtrace_string_append_format(log, "%s [%p; x%lu]", xtrace_msg_type_to_str(disposition, 0), ptr, cnt);
164 AFTER;
165}
166
167static unsigned long long read_integer(const void* ptr, unsigned int size)
168{
169 switch (size / 8)
170 {
171 case 1:
172 return *(unsigned char*) ptr;
173 case sizeof(short):
174 return *(unsigned short*) ptr;
175 case sizeof(int):
176 return *(unsigned int*) ptr;
177/*
178 case sizeof(long):
179 return *(unsigned long*) ptr;
180*/
181 case sizeof(long long):
182 return *(unsigned long long*) ptr;
183 default:
184 return -1;
185 }
186}
187
188XTRACE_HIDDEN extern "C"
189void add_struct_arg(xtrace_string_t log, const void* ptr, unsigned long cnt, unsigned long item_size)
190{
191 BEFORE;
192 unsigned char* p = (unsigned char*) ptr;
193 xtrace_string_append_c_string(log, "{");
194 for (unsigned long i = 0; i < cnt; i++)
195 {
196 if (i != 0)
197 xtrace_string_append_c_string(log, ", ");
198 xtrace_string_append_format(log, "%llu", read_integer((void*) p, item_size));
199 p += item_size;
200 }
201 xtrace_string_append_c_string(log, "}");
202 AFTER;
203}
204
205XTRACE_HIDDEN extern "C"
206void add_array_arg(xtrace_string_t log, const void* ptr, unsigned long cnt, unsigned long item_size)
207{
208 BEFORE;
209 unsigned char* p = (unsigned char*) ptr;
210 xtrace_string_append_c_string(log, "[");
211 for (unsigned long i = 0; i < cnt; i++)
212 {
213 if (i != 0)
214 xtrace_string_append_c_string(log, ", ");
215 xtrace_string_append_format(log, "%llu", read_integer((void*) p, item_size));
216 p += item_size;
217 }
218 xtrace_string_append_c_string(log, "]");
219 AFTER;
220}
221
222XTRACE_HIDDEN extern "C"
223void set_return_code(xtrace_string_t log, kern_return_t code)
224{
225 BEFORE;
226 xtrace_print_kern_return(xtrace::String::to_cxx_ptr(log), code);
227 AFTER;
228}
229
230#undef BEFORE
231#undef AFTER
232
233XTRACE_HIDDEN extern "C"
234const struct xtrace_mig_callbacks callbacks = {
235 .add_raw_arg = add_raw_arg,
236 .add_num_arg = add_num_arg,
237 .add_ptr_arg = add_ptr_arg,
238 .add_string_arg = add_string_arg,
239 .add_bytes_arg = add_bytes_arg,
240 .add_return_code_arg = add_return_code_arg,
241 .add_port_arg = add_port_arg,
242 .add_ool_mem_arg = add_ool_mem_arg,
243 .add_ool_ports_arg = add_ool_ports_arg,
244 .add_struct_arg = add_struct_arg,
245 .add_array_arg = add_array_arg,
246 .set_return_code = set_return_code,
247 // String Functions
248 .xtrace_string_construct = xtrace_string_construct,
249 .xtrace_string_destruct = xtrace_string_destruct,
250 .xtrace_string_clear = xtrace_string_clear,
251 .xtrace_string_c_str = xtrace_string_c_str,
252 // Logging Functions
253 .xtrace_log = xtrace_log
254};
255
256static const struct xtrace_mig_routine_desc* find_routine(mach_msg_id_t id, const struct xtrace_mig_subsystem* s, int* out_is_reply)
257{
258 if (s == NULL)
259 return NULL;
260
261 const struct xtrace_mig_routine_desc* r = NULL;
262 int is_reply;
263
264 if (s->base <= id && id < (s->base + s->routine_cnt))
265 {
266 r = &s->routines[id - s->base];
267 is_reply = 0;
268 }
269 else if (s->base + 100 <= id && id < (s->base + 100 + s->routine_cnt))
270 {
271 r = &s->routines[id - (s->base + 100)];
272 is_reply = 1;
273 }
274
275 if (r == NULL)
276 return NULL;
277 if (!r->present)
278 return NULL;
279 if (is_reply && !r->reply_present)
280 return NULL;
281
282 *out_is_reply = is_reply;
283 return r;
284}
285
286static int filter(const struct xtrace_mig_subsystem* s, mach_port_name_t request_port)
287{
288 if (s == NULL)
289 return 0;
290
291 // mach_host.defs and job.defs use the same msgids,
292 // so use the request port to distinguish them.
293 if (request_port == bootstrap_port)
294 return strncmp(s->name, "job", 3) == 0;
295 if (request_port == host_port)
296 return strncmp(s->name, "host", 4) == 0;
297
298 return 1;
299}
300
301static int find_subsystem(
302 mach_msg_id_t id,
303 mach_port_name_t request_port,
304 int do_filter,
305 const struct xtrace_mig_subsystem** out_s,
306 const struct xtrace_mig_routine_desc** out_r,
307 int* out_is_reply
308)
309{
310 // First, check if it matches any routine that has both a request and a reply.
311 // The reason for doing it like this is that many subsystems are actually
312 // "reply" and "forward" versions of other subsystems/routines, consisting only
313 // of simpleroutines; and we want to find the original ones if possible.
314 for (size_t i = 0; i < subsystems_cnt; i++)
315 {
316 if (do_filter && !filter(subsystems[i], request_port))
317 continue;
318
319 const struct xtrace_mig_routine_desc* r = find_routine(id, subsystems[i], out_is_reply);
320 if (r != NULL && r->reply_present)
321 {
322 *out_s = subsystems[i];
323 *out_r = r;
324 return 1;
325 }
326 }
327
328 // Now, just see if it matches anything.
329 for (size_t i = 0; i < subsystems_cnt; i++)
330 {
331 if (do_filter && !filter(subsystems[i], request_port))
332 continue;
333
334 const struct xtrace_mig_routine_desc* r = find_routine(id, subsystems[i], out_is_reply);
335 if (r != NULL)
336 {
337 *out_s = subsystems[i];
338 *out_r = r;
339 return 1;
340 }
341 }
342
343 return 0;
344}
345
346void xtrace_print_mig_message(xtrace::String* log, const mach_msg_header_t* message, mach_port_name_t request_port)
347{
348 if (message == NULL)
349 return;
350
351 const struct xtrace_mig_subsystem* s;
352 const struct xtrace_mig_routine_desc* r;
353 int is_reply;
354
355 int res = find_subsystem(message->msgh_id, request_port, 1, &s, &r, &is_reply);
356 if (!res)
357 res = find_subsystem(message->msgh_id, request_port, 0, &s, &r, &is_reply);
358 if (!res)
359 return;
360
361 if (!is_reply)
362 log->append_format("%s::%s(", s->name, r->name);
363 else
364 {
365 xtrace_set_gray_color(log);
366 log->append_format("%s::%s() -> ", s->name, r->name);
367 xtrace_reset_color(log);
368 }
369
370 set_is_first_arg(true);
371 r->routine(message, is_reply, &callbacks);
372
373 if (!is_reply)
374 log->append(")");
375 else
376 log->append(" ");
377}