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>
35 #include <sys/reason.h>
43 #include "os/assumes.h"
44 #include <os/debug_private.h>
46 #include <os/log_private.h>
47 #include <os/reason_private.h>
49 #include <CrashReporterClient.h>
50 #define os_set_crash_message(arg) CRSetCrashLogMessage(arg)
52 #define OSX_ASSUMES_LOG_REDIRECT_SECT_NAME "__osx_log_func"
53 #define os_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
55 static bool _os_should_abort_on_assumes
= false;
58 _os_basename(const char *p
)
60 return ((strrchr(p
, '/') ? : p
- 1) + 1);
64 _os_get_build(char *build
, size_t sz
)
66 /* Get the build every time. We used to cache it, but if PID 1 experiences
67 * an assumes() failure before the build has been set, that would mean that
68 * all future failures would get bad build info. So we fetch it every time.
69 * Since assumes() failures are on the slow path anyway, not a huge deal.
71 int mib
[] = { CTL_KERN
, KERN_OSVERSION
};
74 int r
= sysctl(mib
, 2, build
, &sz
, NULL
, 0);
75 if (r
== 0 && sz
== 1) {
76 (void)strlcpy(build
, "99Z999", oldsz
);
78 #if TARGET_IPHONE_SIMULATOR
79 char *simVersion
= getenv("SIMULATOR_RUNTIME_BUILD_VERSION");
81 strlcat(build
, " ", oldsz
);
82 strlcat(build
, simVersion
, oldsz
);
88 _os_get_image_uuid(void *hdr
, uuid_t uuid
)
91 struct mach_header_64
*hdr32or64
= (struct mach_header_64
*)hdr
;
93 struct mach_header
*hdr32or64
= (struct mach_header
*)hdr
;
97 size_t next
= sizeof(*hdr32or64
);
98 struct load_command
*cur
= NULL
;
99 for (i
= 0; i
< hdr32or64
->ncmds
; i
++) {
100 cur
= (struct load_command
*)((uintptr_t)hdr32or64
+ next
);
101 if (cur
->cmd
== LC_UUID
) {
102 struct uuid_command
*cmd
= (struct uuid_command
*)cur
;
103 uuid_copy(uuid
, cmd
->uuid
);
107 next
+= cur
->cmdsize
;
110 if (i
== hdr32or64
->ncmds
) {
116 _os_abort_on_assumes_once(void)
118 /* Embedded boot-args can get pretty long. Let's just hope this is big
122 size_t len
= sizeof(bootargs
) - 1;
124 if (sysctlbyname("kern.bootargs", bootargs
, &len
, NULL
, 0) == 0) {
125 if (strnstr(bootargs
, "-os_assumes_fatal", len
)) {
126 _os_should_abort_on_assumes
= true;
132 _os_abort_on_assumes(void)
134 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
138 if (getenv("OS_ASSUMES_FATAL")) {
141 pthread_once(&once
, _os_abort_on_assumes_once
);
142 result
= _os_should_abort_on_assumes
;
145 if (getenv("OS_ASSUMES_FATAL_PID1")) {
154 typedef struct mach_header_64 os_mach_header
;
156 typedef struct mach_header os_mach_header
;
160 _os_find_log_redirect_func(os_mach_header
*hdr
)
162 os_redirect_t result
= NULL
;
165 unsigned long size
= 0;
166 uint8_t *data
= getsectiondata(hdr
, OS_ASSUMES_REDIRECT_SEG
, OS_ASSUMES_REDIRECT_SECT
, &size
);
168 data
= getsectiondata(hdr
, "__TEXT", OSX_ASSUMES_LOG_REDIRECT_SECT_NAME
, &size
);
170 if (data
&& size
< sizeof(name
) - 2) {
171 (void)strlcpy(name
, (const char *)data
, size
+ 1);
172 result
= dlsym(RTLD_DEFAULT
, name
);
174 } else if (size
== sizeof(struct _os_redirect_assumes_s
)) {
175 struct _os_redirect_assumes_s
*redirect
= (struct _os_redirect_assumes_s
*)data
;
176 result
= redirect
->redirect
;
183 _os_log_redirect(void *hdr
, const char *msg
)
187 os_redirect_t redirect_func
= _os_find_log_redirect_func(hdr
);
189 result
= redirect_func(msg
);
195 __attribute__((always_inline
))
197 _os_construct_message(uint64_t code
, _SIMPLE_STRING asl_message
, Dl_info
*info
, char *buff
, size_t sz
)
199 const char *image_name
= NULL
;
200 uintptr_t offset
= 0;
201 uuid_string_t uuid_str
;
203 void *ret
= __builtin_return_address(0);
204 if (dladdr(ret
, info
)) {
206 _os_get_image_uuid(info
->dli_fbase
, uuid
);
208 uuid_unparse(uuid
, uuid_str
);
209 image_name
= _os_basename(info
->dli_fname
);
211 offset
= ret
- info
->dli_fbase
;
215 (void)snprintf(sig
, sizeof(sig
), "%s:%lu", uuid_str
, offset
);
218 (void)snprintf(result
, sizeof(result
), "0x%llx", code
);
221 size_t bsz
= sizeof(build
);
222 _os_get_build(build
, bsz
);
224 (void)snprintf(buff
, sz
, "assertion failed: %s: %s + %lu [%s]: %s", build
, image_name
, offset
, uuid_str
, result
);
226 _simple_asl_msg_set(asl_message
, "com.apple.message.domain", "com.apple.assumes.failure");
227 _simple_asl_msg_set(asl_message
, "com.apple.message.signature", sig
);
228 _simple_asl_msg_set(asl_message
, "com.apple.message.signature2", result
);
229 _simple_asl_msg_set(asl_message
, "com.apple.message.signature3", image_name
);
230 _simple_asl_msg_set(asl_message
, "com.apple.message.summarize", "YES");
233 #pragma mark Internal Implementations
235 os_crash_callback_t _os_crash_callback
= NULL
;
237 __attribute__((always_inline
))
239 _os_crash_impl(const char *message
) {
240 os_set_crash_message(message
);
241 if (!_os_crash_callback
) {
242 _os_crash_callback
= dlsym(RTLD_MAIN_ONLY
, "os_crash_function");
244 if (_os_crash_callback
) {
245 _os_crash_callback(message
);
249 __attribute__((always_inline
))
251 _os_crash_fmt_impl(os_log_pack_t pack
, size_t pack_size
)
254 * We put just the format string into the CrashReporter buffer so that we
255 * can get at least that on customer builds.
257 const char *message
= pack
->olp_format
;
258 _os_crash_impl(message
);
260 char *(*_os_log_pack_send_and_compose
)(os_log_pack_t
, os_log_t
,
261 os_log_type_t
, char *, size_t) = NULL
;
262 _os_log_pack_send_and_compose
= dlsym(RTLD_DEFAULT
, "os_log_pack_send_and_compose");
263 if (!_os_log_pack_send_and_compose
) return false;
265 os_log_t __os_log_default
= NULL
;
266 __os_log_default
= dlsym(RTLD_DEFAULT
, "_os_log_default");
267 if (!__os_log_default
) return false;
269 char *composed
= _os_log_pack_send_and_compose(pack
, __os_log_default
,
270 OS_LOG_TYPE_FAULT
, NULL
, 0);
272 abort_with_payload(OS_REASON_LIBSYSTEM
, OS_REASON_LIBSYSTEM_CODE_FAULT
, pack
, pack_size
, composed
, 0);
275 __attribute__((always_inline
))
277 _os_assumes_log_impl(uint64_t code
)
279 char message
[256] = "";
281 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
284 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
285 if (!_os_log_redirect(info
.dli_fbase
, message
)) {
286 _os_debug_log_error_str(message
);
287 _simple_asl_msg_set(asl_message
, "Level", "Error");
288 _simple_asl_msg_set(asl_message
, "Message", "");
289 _simple_asl_send(asl_message
);
292 _simple_sfree(asl_message
);
295 if (_os_abort_on_assumes()) {
300 __attribute__((always_inline
))
302 _os_assert_log_impl(uint64_t code
)
306 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
310 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
311 if (!_os_log_redirect(info
.dli_fbase
, message
)) {
312 _os_debug_log_error_str(message
);
313 _simple_asl_msg_set(asl_message
, "Level", "Error");
314 _simple_asl_msg_set(asl_message
, "Message", "");
315 _simple_asl_send(asl_message
);
318 _simple_sfree(asl_message
);
319 result
= strdup(message
);
325 __attribute__((always_inline
))
327 _os_assumes_log_ctx_impl(os_log_callout_t callout
, void *ctx
, uint64_t code
)
329 char message
[256] = "";
331 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
334 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
336 (void)callout(asl_message
, ctx
, message
);
337 _simple_sfree(asl_message
);
340 if (_os_abort_on_assumes()) {
345 __attribute__((always_inline
))
347 _os_assert_log_ctx_impl(os_log_callout_t callout
, void *ctx
, uint64_t code
)
351 _SIMPLE_STRING asl_message
= _simple_asl_msg_new();
355 _os_construct_message(code
, asl_message
, &info
, message
, sizeof(message
));
357 (void)callout(asl_message
, ctx
, message
);
358 _simple_sfree(asl_message
);
359 result
= strdup(message
);
364 #pragma mark Public Interfaces
365 void _os_crash(const char *message
)
367 _os_crash_impl(message
);
370 void _os_crash_fmt(os_log_pack_t pack
, size_t pack_size
)
372 _os_crash_fmt_impl(pack
, pack_size
);
376 _os_assumes_log(uint64_t code
)
378 _os_assumes_log_impl(code
);
382 _os_assert_log(uint64_t code
)
384 return _os_assert_log_impl(code
);
388 _os_assumes_log_ctx(os_log_callout_t callout
, void *ctx
, uint64_t code
)
390 _os_assumes_log_ctx_impl(callout
, ctx
, code
);
394 _os_assert_log_ctx(os_log_callout_t callout
, void *ctx
, uint64_t code
)
396 return _os_assert_log_ctx_impl(callout
, ctx
, code
);
400 _os_avoid_tail_call(void)