1 /*
2 * Copyright (c) 2011, 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <uuid/uuid.h>
25 #include <sys/types.h>
26 #include <sys/sysctl.h>
27 #include <mach/boolean.h>
28
29 #if 0
30 #include <mach-o/loader.h>
31 #include <mach-o/fat.h>
32 #include <mach-o/arch.h>
33 #include <mach-o/getsect.h>
34 #endif
35 #include <pthread.h>
36 #include <sys/types.h>
37 #include <execinfo.h>
38 #include <stdio.h>
39 #include <dlfcn.h>
40 #include <_simple.h>
41 #include <errno.h>
42 #include <pthread.h>
43 #include <string.h>
44 #include "os/assumes.h"
45 #include "gen/assumes.h"
46
47
48 #define OSX_ASSUMES_LOG_REDIRECT_SECT_NAME "__osx_log_func"
49 #define os_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
50 #if 0
51 static bool _os_should_abort_on_assumes = false;
52 #endif
53 void
_os_avoid_tail_call(void)54 _os_avoid_tail_call(void)
55 {
56 // no-op
57 }
58
59 #pragma mark Public Interfaces
60 void
_os_assumes_log(uint64_t code __unused)61 _os_assumes_log(uint64_t code __unused)
62 {
63 #ifdef notyet
64 _os_assumes_log_impl(code);
65 #endif
66 }
67
68 void
_os_assumes_log_ctx(os_log_callout_t callout __unused,void * ctx __unused,uint64_t code __unused)69 _os_assumes_log_ctx(os_log_callout_t callout __unused, void *ctx __unused, uint64_t code __unused)
70 {
71 #ifdef notyet
72 _os_assumes_log_ctx_impl(callout, ctx, code);
73 #endif
74 }
75
76 char *
_os_assert_log(uint64_t code __unused)77 _os_assert_log(uint64_t code __unused)
78 {
79 #ifdef notyet
80 return _os_assert_log_impl(code);
81 #endif
82 return strdup("implement _os_assert_log");
83 }
84
85 #if 0
86
87 static const char *
88 _os_basename(const char *p)
89 {
90 return ((strrchr(p, '/') ? : p - 1) + 1);
91 }
92 #if 0
93 static void
94 _os_get_build(char *build, size_t sz)
95 {
96 /* Get the build every time. We used to cache it, but if PID 1 experiences
97 * an assumes() failure before the build has been set, that would mean that
98 * all future failures would get bad build info. So we fetch it every time.
99 * Since assumes() failures are on the slow path anyway, not a huge deal.
100 */
101 int mib[] = { CTL_KERN, KERN_OSVERSION };
102
103 size_t oldsz = sz;
104 int r = sysctl(mib, 2, build, &sz, NULL, 0);
105 if (r == 0 && sz == 1) {
106 (void)strlcpy(build, "99Z999", oldsz);
107 }
108 }
109
110 static void
111 _os_get_image_uuid(void *hdr, uuid_t uuid)
112 {
113 #if __LP64__
114 struct mach_header_64 *hdr32or64 = (struct mach_header_64 *)hdr;
115 #else
116 struct mach_header *hdr32or64 = (struct mach_header *)hdr;
117 #endif /* __LP64__ */
118
119 size_t i = 0;
120 size_t next = sizeof(*hdr32or64);
121 struct load_command *cur = NULL;
122 for (i = 0; i < hdr32or64->ncmds; i++) {
123 cur = (struct load_command *)((uintptr_t)hdr32or64 + next);
124 if (cur->cmd == LC_UUID) {
125 struct uuid_command *cmd = (struct uuid_command *)cur;
126 uuid_copy(uuid, cmd->uuid);
127 break;
128 }
129
130 next += cur->cmdsize;
131 }
132
133 if (i == hdr32or64->ncmds) {
134 uuid_clear(uuid);
135 }
136 }
137
138 static void
139 _os_abort_on_assumes_once(void)
140 {
141 /* Embedded boot-args can get pretty long. Let's just hope this is big
142 * enough.
143 */
144 char bootargs[2048];
145 size_t len = sizeof(bootargs) - 1;
146
147 if (sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) == 0) {
148 if (strnstr(bootargs, "-os_assumes_fatal", len)) {
149 _os_should_abort_on_assumes = true;
150 }
151 }
152 }
153 #endif
154 static bool
155 _os_abort_on_assumes(void)
156 {
157 #if 0
158 static pthread_once_t once = PTHREAD_ONCE_INIT;
159 #endif
160 bool result = false;
161
162 if (getpid() != 1) {
163 if (getenv("OS_ASSUMES_FATAL")) {
164 result = true;
165 } else {
166 _os_should_abort_on_assumes = true;
167 /* pthread_once(&once, _os_abort_on_assumes_once); */
168 result = _os_should_abort_on_assumes;
169 }
170 } else {
171 if (getenv("OS_ASSUMES_FATAL_PID1")) {
172 result = true;
173 }
174 }
175
176 return result;
177 }
178
179 #if 0
180 #if __LP64__
181 typedef struct mach_header_64 os_mach_header;
182 #else
183 typedef struct mach_header os_mach_header;
184 #endif
185
186 static os_redirect_t
187 _os_find_log_redirect_func(os_mach_header *hdr)
188 {
189 os_redirect_t result = NULL;
190
191 char name[128];
192 unsigned long size = 0;
193 uint8_t *data = getsectiondata(hdr, OS_ASSUMES_REDIRECT_SEG, OS_ASSUMES_REDIRECT_SECT, &size);
194 if (!data) {
195 data = getsectiondata(hdr, "__TEXT", OSX_ASSUMES_LOG_REDIRECT_SECT_NAME, &size);
196
197 if (data && size < sizeof(name) - 2) {
198 (void)strlcpy(name, (const char *)data, size + 1);
199 result = dlsym(RTLD_DEFAULT, name);
200 }
201 } else if (size == sizeof(struct _os_redirect_assumes_s)) {
202 struct _os_redirect_assumes_s *redirect = (struct _os_redirect_assumes_s *)data;
203 result = redirect->redirect;
204 }
205
206 return result;
207 }
208 }
209
210 static bool
211 _os_log_redirect(void *hdr, const char *msg)
212 {
213 bool result = false;
214
215 os_redirect_t redirect_func = _os_find_log_redirect_func(hdr);
216 if (redirect_func) {
217 result = redirect_func(msg);
218 }
219
220 return result;
221 }
222
223 __attribute__((always_inline))
224 static void
225 _os_construct_message(uint64_t code, _SIMPLE_STRING asl_message, Dl_info *info, char *buff, size_t sz)
226 {
227 const char *image_name = NULL;
228 uintptr_t offset = 0;
229 uuid_string_t uuid_str;
230
231 void *ret = __builtin_return_address(0);
232 if (dladdr(ret, info)) {
233 uuid_t uuid;
234 _os_get_image_uuid(info->dli_fbase, uuid);
235
236 uuid_unparse(uuid, uuid_str);
237 image_name = _os_basename(info->dli_fname);
238
239 offset = ret - info->dli_fbase;
240 }
241
242 char sig[64];
243 (void)snprintf(sig, sizeof(sig), "%s:%lu", uuid_str, offset);
244
245 char result[24];
246 (void)snprintf(result, sizeof(result), "0x%llx", code);
247
248 char build[16];
249 size_t bsz = sizeof(build);
250 _os_get_build(build, bsz);
251
252 (void)snprintf(buff, sz, "assertion failed: %s: %s + %lu [%s]: %s", build, image_name, offset, uuid_str, result);
253
254 _simple_asl_msg_set(asl_message, "com.apple.message.domain", "com.apple.assumes.failure");
255 _simple_asl_msg_set(asl_message, "com.apple.message.signature", sig);
256 _simple_asl_msg_set(asl_message, "com.apple.message.signature2", result);
257 _simple_asl_msg_set(asl_message, "com.apple.message.signature3", image_name);
258 _simple_asl_msg_set(asl_message, "com.apple.message.summarize", "YES");
259 }
260 #endif
261 #pragma mark Internal Implementations
262 __attribute__((always_inline))
263 static inline void
264 _os_assumes_log_impl(uint64_t code)
265 {
266 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
267 if (asl_message) {
268 Dl_info info;
269 char message[256];
270 _os_construct_message(code, asl_message, &info, message, sizeof(message));
271 if (!_os_log_redirect(info.dli_fbase, message)) {
272 _os_trace_error_str(message);
273 _simple_asl_msg_set(asl_message, "Level", "Error");
274 _simple_asl_msg_set(asl_message, "Message", "");
275 _simple_asl_send(asl_message);
276 }
277
278 _simple_sfree(asl_message);
279 }
280
281 if (_os_abort_on_assumes()) {
282 __builtin_trap();
283 }
284 }
285
286 __attribute__((always_inline))
287 static inline char *
288 _os_assert_log_impl(uint64_t code)
289 {
290 char *result = NULL;
291
292 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
293 if (asl_message) {
294 Dl_info info;
295 char message[256];
296 _os_construct_message(code, asl_message, &info, message, sizeof(message));
297 if (!_os_log_redirect(info.dli_fbase, message)) {
298 _os_trace_error_str(message);
299 _simple_asl_msg_set(asl_message, "Level", "Error");
300 _simple_asl_msg_set(asl_message, "Message", "");
301 _simple_asl_send(asl_message);
302 }
303
304 _simple_sfree(asl_message);
305 result = strdup(message);
306 }
307
308 #if LIBC_NO_LIBCRASHREPORTERCLIENT
309 /* There is no crash report information facility on embedded, which is
310 * really regrettable. Fortunately, all we need to capture is the value
311 * which tripped up the assertion. We can just stuff that into the thread's
312 * name.
313 */
314 char name[64];
315 (void)pthread_getname_np(pthread_self(), name, sizeof(name));
316
317 char newname[64];
318 if (strlen(name) == 0) {
319 (void)snprintf(newname, sizeof(newname), "[Fatal bug: 0x%llx]", code);
320 } else {
321 (void)snprintf(newname, sizeof(newname), "%s [Fatal bug: 0x%llx]", name, code);
322 }
323
324 (void)pthread_setname_np(newname);
325 #endif
326
327 return result;
328 }
329
330 __attribute__((always_inline))
331 static inline void
332 _os_assumes_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
333 {
334 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
335 if (asl_message) {
336 Dl_info info;
337 char message[256];
338 _os_construct_message(code, asl_message, &info, message, sizeof(message));
339
340 (void)callout(asl_message, ctx, message);
341 _simple_sfree(asl_message);
342 }
343
344 if (_os_abort_on_assumes()) {
345 __builtin_trap();
346 }
347 }
348
349 __attribute__((always_inline))
350 static inline char *
351 _os_assert_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
352 {
353 char *result = NULL;
354
355 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
356 if (asl_message) {
357 Dl_info info;
358 char message[256];
359 _os_construct_message(code, asl_message, &info, message, sizeof(message));
360
361 (void)callout(asl_message, ctx, message);
362 _simple_sfree(asl_message);
363 result = strdup(message);
364 }
365 return result;
366 }
367
368 #pragma mark Public Interfaces
369 void
370 _os_assumes_log(uint64_t code)
371 {
372 _os_assumes_log_impl(code);
373 }
374
375 char *
376 _os_assert_log(uint64_t code)
377 {
378 return _os_assert_log_impl(code);
379 }
380
381 void
382 _os_assumes_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
383 {
384 _os_assumes_log_ctx_impl(callout, ctx, code);
385 }
386
387 char *
388 _os_assert_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
389 {
390 return _os_assert_log_ctx_impl(callout, ctx, code);
391 }
392
393 #pragma mark Legacy
394 void
395 _osx_assumes_log(uint64_t code)
396 {
397 _os_assumes_log(code);
398 }
399
400 char *
401 _osx_assert_log(uint64_t code)
402 {
403 return _os_assert_log_impl(code);
404 }
405
406 void
407 _osx_assumes_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
408 {
409 _os_assumes_log_ctx_impl(callout, ctx, code);
410 }
411
412 char *
413 _osx_assert_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
414 {
415 return _os_assert_log_ctx_impl(callout, ctx, code);
416 }
417
418 void
419 _osx_avoid_tail_call(void)
420 {
421 _os_avoid_tail_call();
422 }
423
424 #endif
425