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 <os/debug_private.h>
45 #define OSX_ASSUMES_LOG_REDIRECT_SECT_NAME "__osx_log_func"
46 #define os_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
48 static bool _os_should_abort_on_assumes
= false;
51 _os_basename(const char *p
)
53 return ((strrchr(p
, '/') ? : p
- 1) + 1);
57 _os_get_build(char *build
, size_t sz
)
59 /* Get the build every time. We used to cache it, but if PID 1 experiences
60 * an assumes() failure before the build has been set, that would mean that
61 * all future failures would get bad build info. So we fetch it every time.
62 * Since assumes() failures are on the slow path anyway, not a huge deal.
64 int mib
[] = { CTL_KERN
, KERN_OSVERSION
};
67 int r
= sysctl(mib
, 2, build
, &sz
, NULL
, 0);
68 if (r
== 0 && sz
== 1) {
69 (void)strlcpy(build
, "99Z999", oldsz
);
71 #if TARGET_IPHONE_SIMULATOR
72 char *simVersion
= getenv("SIMULATOR_RUNTIME_BUILD_VERSION");
74 strlcat(build
, " ", oldsz
);
75 strlcat(build
, simVersion
, oldsz
);
81 _os_get_image_uuid(void *hdr
, uuid_t uuid
)
84 struct mach_header_64
*hdr32or64
= (struct mach_header_64
*)hdr
;
86 struct mach_header
*hdr32or64
= (struct mach_header
*)hdr
;
90 size_t next
= sizeof(*hdr32or64
);
91 struct load_command
*cur
= NULL
;
92 for (i
= 0; i
< hdr32or64
->ncmds
; i
++) {
93 cur
= (struct load_command
*)((uintptr_t)hdr32or64
+ next
);
94 if (cur
->cmd
== LC_UUID
) {
95 struct uuid_command
*cmd
= (struct uuid_command
*)cur
;
96 uuid_copy(uuid
, cmd
->uuid
);
100 next
+= cur
->cmdsize
;
103 if (i
== hdr32or64
->ncmds
) {
109 _os_abort_on_assumes_once(void)
111 /* Embedded boot-args can get pretty long. Let's just hope this is big
115 size_t len
= sizeof(bootargs
) - 1;
117 if (sysctlbyname("kern.bootargs", bootargs
, &len
, NULL
, 0) == 0) {
118 if (strnstr(bootargs
, "-os_assumes_fatal", len
)) {
119 _os_should_abort_on_assumes
= true;
125 _os_abort_on_assumes(void)
127 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
131 if (getenv("OS_ASSUMES_FATAL")) {
134 pthread_once(&once
, _os_abort_on_assumes_once
);
135 result
= _os_should_abort_on_assumes
;
138 if (getenv("OS_ASSUMES_FATAL_PID1")) {
147 typedef struct mach_header_64 os_mach_header
;
149 typedef struct mach_header os_mach_header
;
153 _os_find_log_redirect_func(os_mach_header
*hdr
)
155 os_redirect_t result
= NULL
;
158 unsigned long size
= 0;
159 uint8_t *data
= getsectiondata(hdr
, OS_ASSUMES_REDIRECT_SEG
, OS_ASSUMES_REDIRECT_SECT
, &size
);
161 data
= getsectiondata(hdr
, "__TEXT", OSX_ASSUMES_LOG_REDIRECT_SECT_NAME
, &size
);
163 if (data
&& size
< sizeof(name
) - 2) {
164 (void)strlcpy(name
, (const char *)data
, size
+ 1);
165 result
= dlsym(RTLD_DEFAULT
, name
);
167 } else if (size
== sizeof(struct _os_redirect_assumes_s
)) {
168 struct _os_redirect_assumes_s
*redirect
= (struct _os_redirect_assumes_s
*)data
;
169 result
= redirect
->redirect
;
176 _os_log_redirect(void *hdr
, const char *msg
)
180 os_redirect_t redirect_func
= _os_find_log_redirect_func(hdr
);
182 result
= redirect_func(msg
);
188 __attribute__((always_inline
))
190 _os_construct_message(uint64_t code
, _SIMPLE_STRING asl_message
, Dl_info
*info
, char *buff
, size_t sz
)
192 const char *image_name
= NULL
;
193 uintptr_t offset
= 0;
194 uuid_string_t uuid_str
;
196 void *ret
= __builtin_return_address(0);
197 if (dladdr(ret
, info
)) {
199 _os_get_image_uuid(info
->dli_fbase
, uuid
);
201 uuid_unparse(uuid
, uuid_str
);
202 image_name
= _os_basename(info
->dli_fname
);
204 offset
= ret
- info
->dli_fbase
;
208 (void)snprintf(sig
, sizeof(sig
), "%s:%lu", uuid_str
, offset
);
211 (void)snprintf(result
, sizeof(result
), "0x%llx", code
);
214 size_t bsz
= sizeof(build
);
215 _os_get_build(build
, bsz
);
217 (void)snprintf(buff
, sz
, "assertion failed: %s: %s + %lu [%s]: %s", build
, image_name
, offset
, uuid_str
, result
);
219 _simple_asl_msg_set(asl_message
, "com.apple.message.domain", "com.apple.assumes.failure");
220 _simple_asl_msg_set(asl_message
, "com.apple.message.signature", sig
);
221 _simple_asl_msg_set(asl_message
, "com.apple.message.signature2", result
);
222 _simple_asl_msg_set(asl_message
, "com.apple.message.signature3", image_name
);
223 _simple_asl_msg_set(asl_message
, "com.apple.message.summarize", "YES");
226 #pragma mark Internal Implementations
228 os_crash_callback_t _os_crash_callback
= NULL
;
230 __attribute__((always_inline
))
232 _os_crash_impl(const char *message
) {
233 os_set_crash_message(message
);
234 if (!_os_crash_callback
) {
235 _os_crash_callback
= dlsym(RTLD_MAIN_ONLY
, "os_crash_function");
237 if (_os_crash_callback
) {
238 _os_crash_callback(message
);
242 __attribute__((always_inline
))
244 _os_assumes_log_impl(uint64_t code
)
246 char message
[256] = "";
248 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
251 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
252 if (!_os_log_redirect(info
.dli_fbase
, message
)) {
253 _os_debug_log_error_str(message
);
254 _simple_asl_msg_set(asl_message
, "Level", "Error");
255 _simple_asl_msg_set(asl_message
, "Message", "");
256 _simple_asl_send(asl_message
);
259 _simple_sfree(asl_message
);
262 if (_os_abort_on_assumes()) {
267 __attribute__((always_inline
))
269 _os_assert_log_impl(uint64_t code
)
273 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
277 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
278 if (!_os_log_redirect(info
.dli_fbase
, message
)) {
279 _os_debug_log_error_str(message
);
280 _simple_asl_msg_set(asl_message
, "Level", "Error");
281 _simple_asl_msg_set(asl_message
, "Message", "");
282 _simple_asl_send(asl_message
);
285 _simple_sfree(asl_message
);
286 result
= strdup(message
);
289 #if LIBC_NO_LIBCRASHREPORTERCLIENT
290 /* There is no crash report information facility on embedded, which is
291 * really regrettable. Fortunately, all we need to capture is the value
292 * which tripped up the assertion. We can just stuff that into the thread's
296 (void)pthread_getname_np(pthread_self(), name
, sizeof(name
));
299 if (strlen(name
) == 0) {
300 (void)snprintf(newname
, sizeof(newname
), "[Fatal bug: 0x%llx]", code
);
302 (void)snprintf(newname
, sizeof(newname
), "%s [Fatal bug: 0x%llx]", name
, code
);
305 (void)pthread_setname_np(newname
);
311 __attribute__((always_inline
))
313 _os_assumes_log_ctx_impl(os_log_callout_t callout
, void *ctx
, uint64_t code
)
315 char message
[256] = "";
317 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
320 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
322 (void)callout(asl_message
, ctx
, message
);
323 _simple_sfree(asl_message
);
326 if (_os_abort_on_assumes()) {
331 __attribute__((always_inline
))
333 _os_assert_log_ctx_impl(os_log_callout_t callout
, void *ctx
, uint64_t code
)
337 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
341 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
343 (void)callout(asl_message
, ctx
, message
);
344 _simple_sfree(asl_message
);
345 result
= strdup(message
);
350 #pragma mark Public Interfaces
351 void _os_crash(const char *message
)
353 _os_crash_impl(message
);
357 _os_assumes_log(uint64_t code
)
359 _os_assumes_log_impl(code
);
363 _os_assert_log(uint64_t code
)
365 return _os_assert_log_impl(code
);
369 _os_assumes_log_ctx(os_log_callout_t callout
, void *ctx
, uint64_t code
)
371 _os_assumes_log_ctx_impl(callout
, ctx
, code
);
375 _os_assert_log_ctx(os_log_callout_t callout
, void *ctx
, uint64_t code
)
377 return _os_assert_log_ctx_impl(callout
, ctx
, code
);
381 _os_avoid_tail_call(void)