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