]> git.saurik.com Git - apple/libc.git/blame - os/assumes.c
Libc-1158.1.2.tar.gz
[apple/libc.git] / os / assumes.c
CommitLineData
6465356a
A
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
23e20b00 24#include <TargetConditionals.h>
6465356a
A
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"
23e20b00 43#include <os/debug_private.h>
6465356a
A
44
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))
47
48static bool _os_should_abort_on_assumes = false;
49
50static const char *
51_os_basename(const char *p)
52{
53 return ((strrchr(p, '/') ? : p - 1) + 1);
54}
55
56static void
57_os_get_build(char *build, size_t sz)
58{
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.
63 */
64 int mib[] = { CTL_KERN, KERN_OSVERSION };
65
66 size_t oldsz = sz;
67 int r = sysctl(mib, 2, build, &sz, NULL, 0);
68 if (r == 0 && sz == 1) {
69 (void)strlcpy(build, "99Z999", oldsz);
70 }
23e20b00
A
71#if TARGET_IPHONE_SIMULATOR
72 char *simVersion = getenv("SIMULATOR_RUNTIME_BUILD_VERSION");
73 if (simVersion) {
74 strlcat(build, " ", oldsz);
75 strlcat(build, simVersion, oldsz);
76 }
77#endif
6465356a
A
78}
79
80static void
81_os_get_image_uuid(void *hdr, uuid_t uuid)
82{
83#if __LP64__
84 struct mach_header_64 *hdr32or64 = (struct mach_header_64 *)hdr;
85#else
86 struct mach_header *hdr32or64 = (struct mach_header *)hdr;
87#endif /* __LP64__ */
88
89 size_t i = 0;
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);
97 break;
98 }
99
100 next += cur->cmdsize;
101 }
102
103 if (i == hdr32or64->ncmds) {
104 uuid_clear(uuid);
105 }
106}
107
108static void
109_os_abort_on_assumes_once(void)
110{
111 /* Embedded boot-args can get pretty long. Let's just hope this is big
112 * enough.
113 */
114 char bootargs[2048];
115 size_t len = sizeof(bootargs) - 1;
116
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;
120 }
121 }
122}
123
124static bool
125_os_abort_on_assumes(void)
126{
127 static pthread_once_t once = PTHREAD_ONCE_INIT;
128 bool result = false;
129
130 if (getpid() != 1) {
131 if (getenv("OS_ASSUMES_FATAL")) {
132 result = true;
133 } else {
134 pthread_once(&once, _os_abort_on_assumes_once);
135 result = _os_should_abort_on_assumes;
136 }
137 } else {
138 if (getenv("OS_ASSUMES_FATAL_PID1")) {
139 result = true;
140 }
141 }
142
143 return result;
144}
145
146#if __LP64__
147typedef struct mach_header_64 os_mach_header;
148#else
149typedef struct mach_header os_mach_header;
150#endif
151
152static os_redirect_t
153_os_find_log_redirect_func(os_mach_header *hdr)
154{
155 os_redirect_t result = NULL;
156
157 char name[128];
158 unsigned long size = 0;
159 uint8_t *data = getsectiondata(hdr, OS_ASSUMES_REDIRECT_SEG, OS_ASSUMES_REDIRECT_SECT, &size);
160 if (!data) {
161 data = getsectiondata(hdr, "__TEXT", OSX_ASSUMES_LOG_REDIRECT_SECT_NAME, &size);
162
163 if (data && size < sizeof(name) - 2) {
164 (void)strlcpy(name, (const char *)data, size + 1);
165 result = dlsym(RTLD_DEFAULT, name);
166 }
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;
170 }
171
172 return result;
173}
174
175static bool
176_os_log_redirect(void *hdr, const char *msg)
177{
178 bool result = false;
179
180 os_redirect_t redirect_func = _os_find_log_redirect_func(hdr);
181 if (redirect_func) {
182 result = redirect_func(msg);
183 }
184
185 return result;
186}
187
188__attribute__((always_inline))
189static void
190_os_construct_message(uint64_t code, _SIMPLE_STRING asl_message, Dl_info *info, char *buff, size_t sz)
191{
192 const char *image_name = NULL;
193 uintptr_t offset = 0;
194 uuid_string_t uuid_str;
195
196 void *ret = __builtin_return_address(0);
197 if (dladdr(ret, info)) {
198 uuid_t uuid;
199 _os_get_image_uuid(info->dli_fbase, uuid);
200
201 uuid_unparse(uuid, uuid_str);
202 image_name = _os_basename(info->dli_fname);
203
204 offset = ret - info->dli_fbase;
205 }
206
207 char sig[64];
208 (void)snprintf(sig, sizeof(sig), "%s:%lu", uuid_str, offset);
209
210 char result[24];
211 (void)snprintf(result, sizeof(result), "0x%llx", code);
212
23e20b00 213 char build[32];
6465356a
A
214 size_t bsz = sizeof(build);
215 _os_get_build(build, bsz);
216
217 (void)snprintf(buff, sz, "assertion failed: %s: %s + %lu [%s]: %s", build, image_name, offset, uuid_str, result);
218
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");
224}
225
226#pragma mark Internal Implementations
5f125488
A
227
228os_crash_callback_t _os_crash_callback = NULL;
229
230__attribute__((always_inline))
231static inline void
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");
236 }
237 if (_os_crash_callback) {
238 _os_crash_callback(message);
239 }
240}
241
6465356a
A
242__attribute__((always_inline))
243static inline void
244_os_assumes_log_impl(uint64_t code)
245{
5f125488
A
246 char message[256] = "";
247
6465356a
A
248 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
249 if (asl_message) {
250 Dl_info info;
6465356a
A
251 _os_construct_message(code, asl_message, &info, message, sizeof(message));
252 if (!_os_log_redirect(info.dli_fbase, message)) {
23e20b00 253 _os_debug_log_error_str(message);
6465356a
A
254 _simple_asl_msg_set(asl_message, "Level", "Error");
255 _simple_asl_msg_set(asl_message, "Message", "");
256 _simple_asl_send(asl_message);
257 }
258
259 _simple_sfree(asl_message);
260 }
261
262 if (_os_abort_on_assumes()) {
5f125488 263 os_crash(message);
6465356a
A
264 }
265}
266
267__attribute__((always_inline))
268static inline char *
269_os_assert_log_impl(uint64_t code)
270{
271 char *result = NULL;
272
273 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
274 if (asl_message) {
275 Dl_info info;
276 char message[256];
277 _os_construct_message(code, asl_message, &info, message, sizeof(message));
278 if (!_os_log_redirect(info.dli_fbase, message)) {
23e20b00 279 _os_debug_log_error_str(message);
6465356a
A
280 _simple_asl_msg_set(asl_message, "Level", "Error");
281 _simple_asl_msg_set(asl_message, "Message", "");
282 _simple_asl_send(asl_message);
283 }
284
285 _simple_sfree(asl_message);
286 result = strdup(message);
287 }
288
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
293 * name.
294 */
295 char name[64];
296 (void)pthread_getname_np(pthread_self(), name, sizeof(name));
297
298 char newname[64];
299 if (strlen(name) == 0) {
300 (void)snprintf(newname, sizeof(newname), "[Fatal bug: 0x%llx]", code);
301 } else {
302 (void)snprintf(newname, sizeof(newname), "%s [Fatal bug: 0x%llx]", name, code);
303 }
304
305 (void)pthread_setname_np(newname);
306#endif
307
308 return result;
309}
310
311__attribute__((always_inline))
312static inline void
313_os_assumes_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
314{
5f125488
A
315 char message[256] = "";
316
6465356a
A
317 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
318 if (asl_message) {
319 Dl_info info;
6465356a
A
320 _os_construct_message(code, asl_message, &info, message, sizeof(message));
321
322 (void)callout(asl_message, ctx, message);
323 _simple_sfree(asl_message);
324 }
325
326 if (_os_abort_on_assumes()) {
5f125488 327 os_crash(message);
6465356a
A
328 }
329}
330
331__attribute__((always_inline))
332static inline char *
333_os_assert_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
334{
335 char *result = NULL;
336
337 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
338 if (asl_message) {
339 Dl_info info;
340 char message[256];
341 _os_construct_message(code, asl_message, &info, message, sizeof(message));
342
343 (void)callout(asl_message, ctx, message);
344 _simple_sfree(asl_message);
345 result = strdup(message);
346 }
347 return result;
348}
349
350#pragma mark Public Interfaces
5f125488
A
351void _os_crash(const char *message)
352{
353 _os_crash_impl(message);
354}
355
6465356a
A
356void
357_os_assumes_log(uint64_t code)
358{
359 _os_assumes_log_impl(code);
360}
361
362char *
363_os_assert_log(uint64_t code)
364{
365 return _os_assert_log_impl(code);
366}
367
368void
369_os_assumes_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
370{
371 _os_assumes_log_ctx_impl(callout, ctx, code);
372}
373
374char *
375_os_assert_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
376{
377 return _os_assert_log_ctx_impl(callout, ctx, code);
378}
379
380void
381_os_avoid_tail_call(void)
382{
383 // no-op
384}