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