]> git.saurik.com Git - apple/libc.git/blobdiff - gen/assumes.c
Libc-825.24.tar.gz
[apple/libc.git] / gen / assumes.c
index 076e59cb3808abd727d192eb7077e29d39d4fdcc..444c629cd2a5ce01f594a1f6513a4dcb715aecd4 100644 (file)
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/arch.h>
+#include <mach-o/getsect.h>
+#include <pthread.h>
 #include <sys/types.h>
 #include <execinfo.h>
 #include <stdio.h>
 #include <dlfcn.h>
 #include <asl.h>
 #include <errno.h>
+#include <pthread.h>
 #include "assumes.h"
 
-/* TODO: Re-enable after deciding how best to interact with this symbol. */
-/*
- * NOTE: 8006611: converted to using libCrashReporterClient.a, so shouldn't
- * use __crashreporter_info__.
- */
-#if 0
-static char __crashreporter_info_buff__[2048];
-const char *__crashreporter_info__ = &__crashreporter_info_buff__[0];
-asm (".desc __crashreporter_info__, 0x10");
-#endif
-
+#define OSX_ASSUME_LOG_REDIRECT_SECT_NAME "__osx_log_func"
 #define osx_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
 
+static bool _osx_should_abort_on_assumes = false;
+
 static const char *
 _osx_basename(const char *p)
 {
        return ((strrchr(p, '/') ? : p - 1) + 1);
 }
 
-static char *
-_osx_get_build(void)
+static void
+_osx_get_build(char *build, size_t sz)
 {
-       static char s_build[16];
-       static long s_once = 0;
-       if (osx_atomic_cmpxchg(&s_once, 0, 1)) {
-               int mib[] = { CTL_KERN, KERN_OSVERSION };
-               size_t sz = sizeof(s_build);
+       /* Get the build every time. We used to cache it, but if PID 1 experiences
+        * an assumes() failure before the build has been set, that would mean that
+        * all future failures would get bad build info. So we fetch it every time.
+        * Since assumes() failures are on the slow path anyway, not a huge deal.
+        */
+       int mib[] = { CTL_KERN, KERN_OSVERSION };
 
-               (void)sysctl(mib, 2, s_build, &sz, NULL, 0);
+       size_t oldsz = sz;
+       int r = sysctl(mib, 2, build, &sz, NULL, 0);
+       if (r == 0 && sz == 1) {
+               (void)strlcpy(build, "99Z999", oldsz);
        }
-       
-       return s_build;
 }
 
 static void
 _osx_get_image_uuid(void *hdr, uuid_t uuid)
 {
 #if __LP64__
-       struct mach_header_64 *_hdr = (struct mach_header_64 *)hdr;
+       struct mach_header_64 *hdr32or64 = (struct mach_header_64 *)hdr;
 #else
-       struct mach_header *_hdr = (struct mach_header *)hdr;
+       struct mach_header *hdr32or64 = (struct mach_header *)hdr;
 #endif /* __LP64__ */
 
        size_t i = 0;
-       size_t next = sizeof(*_hdr);
+       size_t next = sizeof(*hdr32or64);
        struct load_command *cur = NULL;
-       for (i = 0; i < _hdr->ncmds; i++) {
-               cur = (struct load_command *)((uintptr_t)_hdr + next);
+       for (i = 0; i < hdr32or64->ncmds; i++) {
+               cur = (struct load_command *)((uintptr_t)hdr32or64 + next);
                if (cur->cmd == LC_UUID) {
                        struct uuid_command *cmd = (struct uuid_command *)cur;
                        uuid_copy(uuid, cmd->uuid);
                        break;
                }
+
                next += cur->cmdsize;
        }
-       
-       if (i == _hdr->ncmds) {
+
+       if (i == hdr32or64->ncmds) {
                uuid_clear(uuid);
        }
 }
 
-void
-_osx_assumes_log(uint64_t code)
+static void
+_osx_abort_on_assumes_once(void)
 {
-       Dl_info info;
+       /* Embedded boot-args can get pretty long. Let's just hope this is big
+        * enough.
+        */
+       char bootargs[2048];
+       size_t len = sizeof(bootargs) - 1;
 
+       if (sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) == 0) {
+               if (strnstr(bootargs, "-osx_assumes_fatal", len)) {
+                       _osx_should_abort_on_assumes = true;
+               }
+       }
+}
+
+static bool
+_osx_abort_on_assumes(void)
+{
+       static pthread_once_t once = PTHREAD_ONCE_INIT;
+       bool result = false;
+
+       if (getpid() != 1) {
+               if (getenv("OSX_ASSUMES_FATAL")) {
+                       result = true;
+               } else {
+                       pthread_once(&once, _osx_abort_on_assumes_once);
+                       result = _osx_should_abort_on_assumes;
+               }
+       } else {
+               if (getenv("OSX_ASSUMES_FATAL_PID1")) {
+                       result = true;
+               }
+       }
+
+       return result;
+}
+
+#if __LP64__
+static osx_redirect_t
+_osx_find_log_redirect_func(struct mach_header_64 *hdr)
+{
+       osx_redirect_t result = NULL;
+
+       char name[128];
+       unsigned long size = 0;
+       uint8_t *data = getsectiondata(hdr, "__TEXT", OSX_ASSUME_LOG_REDIRECT_SECT_NAME, &size);
+       if (data && size < sizeof(name) - 2) {
+               /* Ensure NUL termination. */
+               (void)strlcpy(name, (const char *)data, size + 1);
+               result = dlsym(RTLD_DEFAULT, name);
+       }
+
+       return result;
+}
+#else
+static osx_redirect_t
+_osx_find_log_redirect_func(struct mach_header *hdr)
+{
+       osx_redirect_t result = NULL;
+
+       char name[128];
+       unsigned long size = 0;
+       uint8_t *data = getsectiondata(hdr, "__TEXT", OSX_ASSUME_LOG_REDIRECT_SECT_NAME, &size);
+       if (data && size < sizeof(name) - 2) {
+               (void)strlcpy(name, (const char *)data, size + 1);
+               result = dlsym(RTLD_DEFAULT, name);
+       }
+
+       return result;
+}
+#endif
+
+static bool
+_osx_log_redirect(void *hdr, const char *msg)
+{
+       bool result = false;
+
+       osx_redirect_t redirect_func = _osx_find_log_redirect_func(hdr);
+       if (redirect_func) {
+               result = redirect_func(msg);
+       }
+
+       return result;
+}
+
+__attribute__((always_inline))
+static void
+_osx_construct_message(const char *prefix, uint64_t code, aslmsg asl_message, Dl_info *info, char *buff, size_t sz)
+{
        const char *image_name = NULL;
        uintptr_t offset = 0;
        uuid_string_t uuid_str;
-       
-       void *arr[2];
-       /* Get our caller's address so we can look it up with dladdr(3) and
-        * get info about the image.
-        */
-       if (backtrace(arr, 2) == 2) {
-               /* dladdr(3) returns non-zero on success... for some reason. */
-               if (dladdr(arr[1], &info)) {
-                       uuid_t uuid;
-                       _osx_get_image_uuid(info.dli_fbase, uuid);
-
-                       uuid_unparse(uuid, uuid_str);
-                       image_name = _osx_basename(info.dli_fname);
-                       
-                       offset = arr[1] - info.dli_fbase;
-               }
-       } else {
-               uuid_t null_uuid;
-               uuid_string_t uuid_str;
+
+       void *ret = __builtin_return_address(0);
+       if (dladdr(ret, info)) {
+               uuid_t uuid;
+               _osx_get_image_uuid(info->dli_fbase, uuid);
                
-               uuid_clear(null_uuid);
-               uuid_unparse(null_uuid, uuid_str);
+               uuid_unparse(uuid, uuid_str);
+               image_name = _osx_basename(info->dli_fname);
                
-               image_name = "unknown";
+               offset = ret - info->dli_fbase;
        }
-       
+
        char name[256];
        (void)snprintf(name, sizeof(name), "com.apple.assumes.%s", image_name);
-       
+
        char sig[64];
        (void)snprintf(sig, sizeof(sig), "%s:%lu", uuid_str, offset);
 
        char result[24];
        (void)snprintf(result, sizeof(result), "0x%llx", code);
 
-       char *prefix = "Bug";
-       char message[1024];
-       (void)snprintf(message, sizeof(message), "%s: %s: %s + %lu [%s]: %s", prefix, _osx_get_build(), image_name, offset, uuid_str, result);
-
-       aslmsg msg = asl_new(ASL_TYPE_MSG);
-       if (msg != NULL) {
-               /* MessageTracer messages aren't logged to the regular syslog store, so
-                * we pre-log the message without any MessageTracer attributes so that
-                * we can see it in our regular syslog.
-                */
-               (void)asl_log(NULL, msg, ASL_LEVEL_ERR, "%s", message);
-       
-               (void)asl_set(msg, "com.apple.message.domain", name);
-               (void)asl_set(msg, "com.apple.message.signature", sig);
-               (void)asl_set(msg, "com.apple.message.value", result);
-
-               (void)asl_log(NULL, msg, ASL_LEVEL_ERR, "%s", message);
-               asl_free(msg);
-       }
+       char build[16];
+       size_t bsz = sizeof(build);
+       _osx_get_build(build, bsz);
+
+       (void)snprintf(buff, sz, "%s: %s: %s + %lu [%s]: %s", prefix, build, image_name, offset, uuid_str, result);
+
+       (void)asl_set(asl_message, "com.apple.message.domain", name);
+       (void)asl_set(asl_message, "com.apple.message.signature", sig);
+       (void)asl_set(asl_message, "com.apple.message.value", result);
 }
 
-/* For osx_assert(). We need to think more about how best to set the __crashreporter_info__ string.
- * For example, calling into two functions will basically smash the register state at the time of
- * the assertion failure, causing potentially valuable information to be lost. Also, just setting
- * the __crashreporter_info__ to a static string only involves one instruction, whereas a function
- * call involves... well, more.
- */
-#if 0
 void
-osx_hardware_trap(const char *fmt, ...)
+_osx_assumes_log(uint64_t code)
 {
-       va_list ap;
-       va_start(ap, fmt);
-       
-       osx_hardware_trapv(fmt, ap);
-       
-       va_end(ap);
+       aslmsg asl_message = asl_new(ASL_TYPE_MSG);
+       if (asl_message) {
+               Dl_info info;
+               char message[256];
+               _osx_construct_message("Bug", code, asl_message, &info, message, sizeof(message));
+               if (!_osx_log_redirect(info.dli_fbase, message)) {
+                       /* MessageTracer messages aren't logged to the regular syslog store,
+                        * so we log the message without any MessageTracer attributes so
+                        * that we can see it in our regular syslog.
+                        */
+                       (void)asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", message);
+                       (void)asl_log(NULL, asl_message, ASL_LEVEL_ERR, "%s", message);
+               }
+
+               asl_free(asl_message);
+       }
+
+       if (_osx_abort_on_assumes()) {
+               __builtin_trap();
+       }
+}
+
+char *
+_osx_assert_log(uint64_t code)
+{
+       char *result = NULL;
+
+       aslmsg asl_message = asl_new(ASL_TYPE_MSG);
+       if (asl_message) {
+               Dl_info info;
+               char message[256];
+               _osx_construct_message("Fatal bug", code, asl_message, &info, message, sizeof(message));
+               if (!_osx_log_redirect(info.dli_fbase, message)) {
+                       (void)asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s", message);
+                       (void)asl_log(NULL, asl_message, ASL_LEVEL_ERR, "%s", message);
+               }
+               
+               asl_free(asl_message);
+               result = strdup(message);
+       }
+
+#if LIBC_NO_LIBCRASHREPORTERCLIENT
+       /* There is no crash report information facility on embedded, which is
+        * really regrettable. Fortunately, all we need to capture is the value
+        * which tripped up the assertion. We can just stuff that into the thread's 
+        * name.
+        */
+       char name[64];
+       (void)pthread_getname_np(pthread_self(), name, sizeof(name));
+
+       char newname[64];
+       if (strlen(name) == 0) {
+               (void)snprintf(newname, sizeof(newname), "[Fatal bug: 0x%llx]", code);
+       } else {
+               (void)snprintf(newname, sizeof(newname), "%s [Fatal bug: 0x%llx]", name, code);
+       }
+
+       (void)pthread_setname_np(newname);
+#endif
+
+       return result;
 }
 
 void
-osx_hardware_trapv(const char *fmt, va_list ap)
+_osx_assumes_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
+{
+       aslmsg asl_message = asl_new(ASL_TYPE_MSG);
+       if (asl_message) {
+               Dl_info info;
+               char message[256];
+               _osx_construct_message("Bug", code, asl_message, &info, message, sizeof(message));
+
+               (void)callout(asl_message, ctx, message);
+               asl_free(asl_message);
+       }
+
+       if (_osx_abort_on_assumes()) {
+               __builtin_trap();
+       }
+}
+
+char *
+_osx_assert_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
 {
-       (void)vsnprintf(__crashreporter_info_buff__, sizeof(__crashreporter_info_buff__), fmt, ap);
-       fflush(NULL);
-       __builtin_trap();
+       char *result = NULL;
+
+       aslmsg asl_message = asl_new(ASL_TYPE_MSG);
+       if (asl_message) {
+               Dl_info info;
+               char message[256];
+               _osx_construct_message("Fatal bug", code, asl_message, &info, message, sizeof(message));
+
+               (void)callout(asl_message, ctx, message);
+               asl_free(asl_message);
+               result = strdup(message);
+       }
+}
+
+extern void
+_osx_avoid_tail_call(void)
+{
+       // no-op
 }
-#endif