#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