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