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 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 } 71} 72 73static 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 101static 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 117static 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__ 140typedef struct mach_header_64 os_mach_header; 141#else 142typedef struct mach_header os_mach_header; 143#endif 144 145static 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 168static 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)) 182static 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)) 221static 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)) 245static 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)) 289static 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)) 308static 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 327void 328_os_assumes_log(uint64_t code) 329{ 330 _os_assumes_log_impl(code); 331} 332 333char * 334_os_assert_log(uint64_t code) 335{ 336 return _os_assert_log_impl(code); 337} 338 339void 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 345char * 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 351void 352_os_avoid_tail_call(void) 353{ 354 // no-op 355} 356 357#pragma mark Legacy 358void 359_osx_assumes_log(uint64_t code) 360{ 361 _os_assumes_log(code); 362} 363 364char * 365_osx_assert_log(uint64_t code) 366{ 367 return _os_assert_log_impl(code); 368} 369 370void 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 376char * 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 382void 383_osx_avoid_tail_call(void) 384{ 385 _os_avoid_tail_call(); 386} 387