2 * Copyright (c) 2011, 2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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>
34 #include <sys/types.h>
42 #include "os/assumes.h"
43 #include "gen/assumes.h"
44 #include <os/debug_private.h>
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))
49 static bool _os_should_abort_on_assumes
= false;
52 _os_basename(const char *p
)
54 return ((strrchr(p
, '/') ? : p
- 1) + 1);
58 _os_get_build(char *build
, size_t sz
)
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.
65 int mib
[] = { CTL_KERN
, KERN_OSVERSION
};
68 int r
= sysctl(mib
, 2, build
, &sz
, NULL
, 0);
69 if (r
== 0 && sz
== 1) {
70 (void)strlcpy(build
, "99Z999", oldsz
);
72 #if TARGET_IPHONE_SIMULATOR
73 char *simVersion
= getenv("SIMULATOR_RUNTIME_BUILD_VERSION");
75 strlcat(build
, " ", oldsz
);
76 strlcat(build
, simVersion
, oldsz
);
82 _os_get_image_uuid(void *hdr
, uuid_t uuid
)
85 struct mach_header_64
*hdr32or64
= (struct mach_header_64
*)hdr
;
87 struct mach_header
*hdr32or64
= (struct mach_header
*)hdr
;
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
);
101 next
+= cur
->cmdsize
;
104 if (i
== hdr32or64
->ncmds
) {
110 _os_abort_on_assumes_once(void)
112 /* Embedded boot-args can get pretty long. Let's just hope this is big
116 size_t len
= sizeof(bootargs
) - 1;
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;
126 _os_abort_on_assumes(void)
128 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
132 if (getenv("OS_ASSUMES_FATAL")) {
135 pthread_once(&once
, _os_abort_on_assumes_once
);
136 result
= _os_should_abort_on_assumes
;
139 if (getenv("OS_ASSUMES_FATAL_PID1")) {
148 typedef struct mach_header_64 os_mach_header
;
150 typedef struct mach_header os_mach_header
;
154 _os_find_log_redirect_func(os_mach_header
*hdr
)
156 os_redirect_t result
= NULL
;
159 unsigned long size
= 0;
160 uint8_t *data
= getsectiondata(hdr
, OS_ASSUMES_REDIRECT_SEG
, OS_ASSUMES_REDIRECT_SECT
, &size
);
162 data
= getsectiondata(hdr
, "__TEXT", OSX_ASSUMES_LOG_REDIRECT_SECT_NAME
, &size
);
164 if (data
&& size
< sizeof(name
) - 2) {
165 (void)strlcpy(name
, (const char *)data
, size
+ 1);
166 result
= dlsym(RTLD_DEFAULT
, name
);
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
;
177 _os_log_redirect(void *hdr
, const char *msg
)
181 os_redirect_t redirect_func
= _os_find_log_redirect_func(hdr
);
183 result
= redirect_func(msg
);
189 __attribute__((always_inline
))
191 _os_construct_message(uint64_t code
, _SIMPLE_STRING asl_message
, Dl_info
*info
, char *buff
, size_t sz
)
193 const char *image_name
= NULL
;
194 uintptr_t offset
= 0;
195 uuid_string_t uuid_str
;
197 void *ret
= __builtin_return_address(0);
198 if (dladdr(ret
, info
)) {
200 _os_get_image_uuid(info
->dli_fbase
, uuid
);
202 uuid_unparse(uuid
, uuid_str
);
203 image_name
= _os_basename(info
->dli_fname
);
205 offset
= ret
- info
->dli_fbase
;
209 (void)snprintf(sig
, sizeof(sig
), "%s:%lu", uuid_str
, offset
);
212 (void)snprintf(result
, sizeof(result
), "0x%llx", code
);
215 size_t bsz
= sizeof(build
);
216 _os_get_build(build
, bsz
);
218 (void)snprintf(buff
, sz
, "assertion failed: %s: %s + %lu [%s]: %s", build
, image_name
, offset
, uuid_str
, result
);
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");
227 #pragma mark Internal Implementations
229 os_crash_callback_t _os_crash_callback
= NULL
;
231 __attribute__((always_inline
))
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");
238 if (_os_crash_callback
) {
239 _os_crash_callback(message
);
243 __attribute__((always_inline
))
245 _os_assumes_log_impl(uint64_t code
)
247 char message
[256] = "";
249 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
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
);
260 _simple_sfree(asl_message
);
263 if (_os_abort_on_assumes()) {
268 __attribute__((always_inline
))
270 _os_assert_log_impl(uint64_t code
)
274 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
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
);
286 _simple_sfree(asl_message
);
287 result
= strdup(message
);
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
297 (void)pthread_getname_np(pthread_self(), name
, sizeof(name
));
300 if (strlen(name
) == 0) {
301 (void)snprintf(newname
, sizeof(newname
), "[Fatal bug: 0x%llx]", code
);
303 (void)snprintf(newname
, sizeof(newname
), "%s [Fatal bug: 0x%llx]", name
, code
);
306 (void)pthread_setname_np(newname
);
312 __attribute__((always_inline
))
314 _os_assumes_log_ctx_impl(os_log_callout_t callout
, void *ctx
, uint64_t code
)
316 char message
[256] = "";
318 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
321 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
323 (void)callout(asl_message
, ctx
, message
);
324 _simple_sfree(asl_message
);
327 if (_os_abort_on_assumes()) {
332 __attribute__((always_inline
))
334 _os_assert_log_ctx_impl(os_log_callout_t callout
, void *ctx
, uint64_t code
)
338 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
342 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
344 (void)callout(asl_message
, ctx
, message
);
345 _simple_sfree(asl_message
);
346 result
= strdup(message
);
351 #pragma mark Public Interfaces
352 void _os_crash(const char *message
)
354 _os_crash_impl(message
);
358 _os_assumes_log(uint64_t code
)
360 _os_assumes_log_impl(code
);
364 _os_assert_log(uint64_t code
)
366 return _os_assert_log_impl(code
);
370 _os_assumes_log_ctx(os_log_callout_t callout
, void *ctx
, uint64_t code
)
372 _os_assumes_log_ctx_impl(callout
, ctx
, code
);
376 _os_assert_log_ctx(os_log_callout_t callout
, void *ctx
, uint64_t code
)
378 return _os_assert_log_ctx_impl(callout
, ctx
, code
);
382 _os_avoid_tail_call(void)
389 _osx_assumes_log(uint64_t code
)
391 _os_assumes_log(code
);
395 _osx_assert_log(uint64_t code
)
397 return _os_assert_log_impl(code
);
401 _osx_assumes_log_ctx(osx_log_callout_t callout
, void *ctx
, uint64_t code
)
403 _os_assumes_log_ctx_impl(callout
, ctx
, code
);
407 _osx_assert_log_ctx(osx_log_callout_t callout
, void *ctx
, uint64_t code
)
409 return _os_assert_log_ctx_impl(callout
, ctx
, code
);
413 _osx_avoid_tail_call(void)
415 _os_avoid_tail_call();