Merge "Make abort messages available to debuggerd."
diff --git a/libc/bionic/__stack_chk_fail.cpp b/libc/bionic/__stack_chk_fail.cpp
index d5031ac..8b9ed5e 100644
--- a/libc/bionic/__stack_chk_fail.cpp
+++ b/libc/bionic/__stack_chk_fail.cpp
@@ -32,6 +32,5 @@
 #include "libc_logging.h"
 
 void __stack_chk_fail() {
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", "stack corruption detected");
-  abort();
+  __libc_fatal("stack corruption detected");
 }
diff --git a/libc/bionic/assert.cpp b/libc/bionic/assert.cpp
index 6f221a5..84024c7 100644
--- a/libc/bionic/assert.cpp
+++ b/libc/bionic/assert.cpp
@@ -28,27 +28,16 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/types.h>
 #include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
+
 #include "libc_logging.h"
 
-// We log to stderr for the benefit of "adb shell" users, and the log for the benefit
-// of regular app developers who want to see their asserts.
-
 void __assert(const char* file, int line, const char* failed_expression) {
-  const char* fmt = "%s:%d: assertion \"%s\" failed\n";
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", fmt, file, line, failed_expression);
-  fprintf(stderr, fmt, file, line, failed_expression);
-  abort();
+  __libc_fatal("%s:%d: assertion \"%s\" failed", file, line, failed_expression);
   /* NOTREACHED */
 }
 
 void __assert2(const char* file, int line, const char* function, const char* failed_expression) {
-  const char* fmt = "%s:%d: %s: assertion \"%s\" failed\n";
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", fmt, file, line, function, failed_expression);
-  fprintf(stderr, fmt, file, line, function, failed_expression);
-  abort();
+  __libc_fatal("%s:%d: %s: assertion \"%s\" failed", file, line, function, failed_expression);
   /* NOTREACHED */
 }
diff --git a/libc/bionic/dlmalloc.c b/libc/bionic/dlmalloc.c
index cf09aac..51c62a7 100644
--- a/libc/bionic/dlmalloc.c
+++ b/libc/bionic/dlmalloc.c
@@ -16,15 +16,7 @@
 
 #include "dlmalloc.h"
 
-#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <linux/ashmem.h>
-
-#include <private/libc_logging.h>
+#include "private/libc_logging.h"
 
 // Send dlmalloc errors to the log.
 static void __bionic_heap_corruption_error(const char* function);
@@ -37,15 +29,12 @@
 #include "../upstream-dlmalloc/malloc.c"
 
 static void __bionic_heap_corruption_error(const char* function) {
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", "@@@ ABORTING: heap corruption detected by %s",
-                    function);
-  abort();
+  __libc_fatal("@@@ ABORTING: heap corruption detected by %s", function);
 }
 
 static void __bionic_heap_usage_error(const char* function, void* address) {
-  __libc_format_log(ANDROID_LOG_FATAL, "libc",
-                    "@@@ ABORTING: invalid address or address of corrupt block %p passed to %s",
-                    address, function);
+  __libc_fatal("@@@ ABORTING: invalid address or address of corrupt block %p passed to %s",
+               address, function);
   // So that we can get a memory dump around the specific address.
   *((int**) 0xdeadbaad) = (int*) address;
 }
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 33ec1db..1fc490e 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -45,6 +45,7 @@
 #include "private/KernelArgumentBlock.h"
 #include "pthread_internal.h"
 
+extern "C" abort_msg_t** __abort_message_ptr;
 extern "C" unsigned __get_sp(void);
 extern "C" int __system_properties_init(void);
 
@@ -96,6 +97,7 @@
   errno = 0;
   __libc_auxv = args.auxv;
   __progname = args.argv[0] ? args.argv[0] : "<unknown>";
+  __abort_message_ptr = args.abort_message_ptr;
 
   // AT_RANDOM is a pointer to 16 bytes of randomness on the stack.
   __stack_chk_guard = *reinterpret_cast<uintptr_t*>(getauxval(AT_RANDOM));
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index 755dc81..8de1192 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -31,228 +31,96 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <pthread.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-
-/*** Generic output sink
- ***/
-
-struct Out {
-  void *opaque;
-  void (*send)(void *opaque, const char *data, int len);
-};
-
-static void out_send(Out *o, const char *data, size_t len) {
-    o->send(o->opaque, data, (int)len);
-}
-
-static void
-out_send_repeat(Out *o, char ch, int count)
-{
-    char pad[8];
-    const int padSize = (int)sizeof(pad);
-
-    memset(pad, ch, sizeof(pad));
-    while (count > 0) {
-        int avail = count;
-        if (avail > padSize) {
-            avail = padSize;
-        }
-        o->send(o->opaque, pad, avail);
-        count -= avail;
-    }
-}
-
-/* forward declaration */
-static void out_vformat(Out* o, const char* format, va_list args);
-
-/*** Bounded buffer output
- ***/
-
-struct BufOut {
-  Out out[1];
-  char *buffer;
-  char *pos;
-  char *end;
-  int total;
-};
-
-static void buf_out_send(void *opaque, const char *data, int len) {
-    BufOut *bo = reinterpret_cast<BufOut*>(opaque);
-
-    if (len < 0) {
-        len = strlen(data);
-    }
-
-    bo->total += len;
-
-    while (len > 0) {
-        int avail = bo->end - bo->pos;
-        if (avail == 0)
-            break;
-        if (avail > len)
-            avail = len;
-        memcpy(bo->pos, data, avail);
-        bo->pos += avail;
-        bo->pos[0] = '\0';
-        len -= avail;
-    }
-}
-
-static Out*
-buf_out_init(BufOut *bo, char *buffer, size_t size)
-{
-    if (size == 0)
-        return NULL;
-
-    bo->out->opaque = bo;
-    bo->out->send   = buf_out_send;
-    bo->buffer      = buffer;
-    bo->end         = buffer + size - 1;
-    bo->pos         = bo->buffer;
-    bo->pos[0]      = '\0';
-    bo->total       = 0;
-
-    return bo->out;
-}
-
-static int
-buf_out_length(BufOut *bo)
-{
-    return bo->total;
-}
-
-static int
-vformat_buffer(char *buff, size_t buf_size, const char *format, va_list args)
-{
-    BufOut bo;
-    Out *out;
-
-    out = buf_out_init(&bo, buff, buf_size);
-    if (out == NULL)
-        return 0;
-
-    out_vformat(out, format, args);
-
-    return buf_out_length(&bo);
-}
-
-int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
-  va_list args;
-  va_start(args, format);
-  int result = vformat_buffer(buffer, buffer_size, format, args);
-  va_end(args);
-  return result;
-}
-
-
-/*** File descriptor output
- ***/
-
-struct FdOut {
-  Out out[1];
-  int fd;
-  int total;
-};
-
-static void
-fd_out_send(void *opaque, const char *data, int len)
-{
-    FdOut *fdo = reinterpret_cast<FdOut*>(opaque);
-
-    if (len < 0)
-        len = strlen(data);
-
-    while (len > 0) {
-        int ret = write(fdo->fd, data, len);
-        if (ret < 0) {
-            if (errno == EINTR)
-                continue;
-            break;
-        }
-        data += ret;
-        len -= ret;
-        fdo->total += ret;
-    }
-}
-
-static Out*
-fd_out_init(FdOut *fdo, int  fd)
-{
-    fdo->out->opaque = fdo;
-    fdo->out->send = fd_out_send;
-    fdo->fd = fd;
-    fdo->total = 0;
-
-    return fdo->out;
-}
-
-static int
-fd_out_length(FdOut *fdo)
-{
-    return fdo->total;
-}
-
-
-int __libc_format_fd(int fd, const char* format, ...) {
-  FdOut fdo;
-  Out* out = fd_out_init(&fdo, fd);
-  if (out == NULL) {
-    return 0;
-  }
-
-  va_list args;
-  va_start(args, format);
-  out_vformat(out, format, args);
-  va_end(args);
-
-  return fd_out_length(&fdo);
-}
-
-/*** Log output
- ***/
-
-#include <unistd.h>
-#include <fcntl.h>
+#include <sys/mman.h>
 #include <sys/uio.h>
+#include <unistd.h>
 
+static pthread_mutex_t gAbortMsgLock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t gLogInitializationLock = PTHREAD_MUTEX_INITIALIZER;
 
-int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) {
-  char buf[1024];
-  int buf_strlen = vformat_buffer(buf, sizeof(buf), fmt, args);
+__LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common.
 
-  static int main_log_fd = -1;
-  if (main_log_fd == -1) {
-    ScopedPthreadMutexLocker locker(&gLogInitializationLock);
-    main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));
-    if (main_log_fd == -1) {
-      return -1;
+// Must be kept in sync with frameworks/base/core/java/android/util/EventLog.java.
+enum AndroidEventLogType {
+  EVENT_TYPE_INT      = 0,
+  EVENT_TYPE_LONG     = 1,
+  EVENT_TYPE_STRING   = 2,
+  EVENT_TYPE_LIST     = 3,
+};
+
+struct BufferOutputStream {
+ public:
+  BufferOutputStream(char* buffer, size_t size) : total(0) {
+    buffer_ = buffer;
+    end_ = buffer + size - 1;
+    pos_ = buffer_;
+    pos_[0] = '\0';
+  }
+
+  ~BufferOutputStream() {
+  }
+
+  void Send(const char* data, int len) {
+    if (len < 0) {
+      len = strlen(data);
+    }
+
+    while (len > 0) {
+      int avail = end_ - pos_;
+      if (avail == 0) {
+        break;
+      }
+      if (avail > len) {
+        avail = len;
+      }
+      memcpy(pos_, data, avail);
+      pos_ += avail;
+      pos_[0] = '\0';
+      len -= avail;
+      total += avail;
     }
   }
 
-  struct iovec vec[3];
-  vec[0].iov_base = &priority;
-  vec[0].iov_len = 1;
-  vec[1].iov_base = const_cast<char*>(tag);
-  vec[1].iov_len = strlen(tag) + 1;
-  vec[2].iov_base = const_cast<char*>(buf);
-  vec[2].iov_len = buf_strlen + 1;
+  int total;
 
-  return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
-}
+ private:
+  char* buffer_;
+  char* pos_;
+  char* end_;
+};
 
-int __libc_format_log(int priority, const char* tag, const char* format, ...) {
-  va_list args;
-  va_start(args, format);
-  int result = __libc_format_log_va_list(priority, tag, format, args);
-  va_end(args);
-  return result;
-}
+struct FdOutputStream {
+ public:
+  FdOutputStream(int fd) : total(0), fd_(fd) {
+  }
+
+  void Send(const char* data, int len) {
+    if (len < 0) {
+      len = strlen(data);
+    }
+
+    while (len > 0) {
+      int rc = TEMP_FAILURE_RETRY(write(fd_, data, len));
+      if (rc == -1) {
+        break;
+      }
+      data += rc;
+      len -= rc;
+      total += rc;
+    }
+  }
+
+  int total;
+
+ private:
+  int fd_;
+};
 
 /*** formatted output implementation
  ***/
@@ -263,9 +131,7 @@
  *
  * NOTE: Does *not* handle a sign prefix.
  */
-static unsigned
-parse_decimal(const char *format, int *ppos)
-{
+static unsigned parse_decimal(const char *format, int *ppos) {
     const char* p = format + *ppos;
     unsigned result = 0;
 
@@ -273,8 +139,9 @@
         int ch = *p;
         unsigned d = (unsigned)(ch - '0');
 
-        if (d >= 10U)
+        if (d >= 10U) {
             break;
+        }
 
         result = result*10 + d;
         p++;
@@ -341,10 +208,25 @@
   format_unsigned(buf, buf_size, value, base, caps);
 }
 
+template <typename Out>
+static void SendRepeat(Out& o, char ch, int count) {
+  char pad[8];
+  memset(pad, ch, sizeof(pad));
+
+  const int pad_size = static_cast<int>(sizeof(pad));
+  while (count > 0) {
+    int avail = count;
+    if (avail > pad_size) {
+      avail = pad_size;
+    }
+    o.Send(pad, avail);
+    count -= avail;
+  }
+}
+
 /* Perform formatted output to an output target 'o' */
-static void
-out_vformat(Out *o, const char *format, va_list args)
-{
+template <typename Out>
+static void out_vformat(Out& o, const char* format, va_list args) {
     int nn = 0;
 
     for (;;) {
@@ -371,7 +253,7 @@
         } while (1);
 
         if (mm > nn) {
-            out_send(o, format+nn, mm-nn);
+            o.Send(format+nn, mm-nn);
             nn = mm;
         }
 
@@ -387,7 +269,7 @@
             c = format[nn++];
             if (c == '\0') {  /* single trailing '%' ? */
                 c = '%';
-                out_send(o, &c, 1);
+                o.Send(&c, 1);
                 return;
             }
             else if (c == '0') {
@@ -508,28 +390,74 @@
 
         if (slen < width && !padLeft) {
             char padChar = padZero ? '0' : ' ';
-            out_send_repeat(o, padChar, width - slen);
+            SendRepeat(o, padChar, width - slen);
         }
 
-        out_send(o, str, slen);
+        o.Send(str, slen);
 
         if (slen < width && padLeft) {
             char padChar = padZero ? '0' : ' ';
-            out_send_repeat(o, padChar, width - slen);
+            SendRepeat(o, padChar, width - slen);
         }
     }
 }
 
-// must be kept in sync with frameworks/base/core/java/android/util/EventLog.java
-enum AndroidEventLogType {
-  EVENT_TYPE_INT      = 0,
-  EVENT_TYPE_LONG     = 1,
-  EVENT_TYPE_STRING   = 2,
-  EVENT_TYPE_LIST     = 3,
-};
+int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
+  BufferOutputStream os(buffer, buffer_size);
+  va_list args;
+  va_start(args, format);
+  out_vformat(os, format, args);
+  va_end(args);
+  return os.total;
+}
+
+int __libc_format_fd(int fd, const char* format, ...) {
+  FdOutputStream os(fd);
+  va_list args;
+  va_start(args, format);
+  out_vformat(os, format, args);
+  va_end(args);
+  return os.total;
+}
+
+static int __libc_write_log(int priority, const char* tag, const char* msg) {
+  static int main_log_fd = -1;
+  if (main_log_fd == -1) {
+    ScopedPthreadMutexLocker locker(&gLogInitializationLock);
+    main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));
+    if (main_log_fd == -1) {
+      return -1;
+    }
+  }
+
+  iovec vec[3];
+  vec[0].iov_base = &priority;
+  vec[0].iov_len = 1;
+  vec[1].iov_base = const_cast<char*>(tag);
+  vec[1].iov_len = strlen(tag) + 1;
+  vec[2].iov_base = const_cast<char*>(msg);
+  vec[2].iov_len = strlen(msg) + 1;
+
+  return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
+}
+
+int __libc_format_log_va_list(int priority, const char* tag, const char* format, va_list args) {
+  char buffer[1024];
+  BufferOutputStream os(buffer, sizeof(buffer));
+  out_vformat(os, format, args);
+  return __libc_write_log(priority, tag, buffer);
+}
+
+int __libc_format_log(int priority, const char* tag, const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  int result = __libc_format_log_va_list(priority, tag, format, args);
+  va_end(args);
+  return result;
+}
 
 static int __libc_android_log_event(int32_t tag, char type, const void* payload, size_t len) {
-  struct iovec vec[3];
+  iovec vec[3];
   vec[0].iov_base = &tag;
   vec[0].iov_len = sizeof(tag);
   vec[1].iov_base = &type;
@@ -554,9 +482,45 @@
 }
 
 void __fortify_chk_fail(const char *msg, uint32_t tag) {
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", "FORTIFY_SOURCE: %s. Calling abort().\n", msg);
   if (tag != 0) {
     __libc_android_log_event_uid(tag);
   }
+  __libc_fatal("FORTIFY_SOURCE: %s. Calling abort().", msg);
+}
+
+void __libc_fatal(const char* format, ...) {
+  char msg[1024];
+  BufferOutputStream os(msg, sizeof(msg));
+  va_list args;
+  va_start(args, format);
+  out_vformat(os, format, args);
+  va_end(args);
+
+  // TODO: log to stderr for the benefit of "adb shell" users.
+
+  // Log to the log for the benefit of regular app developers (whose stdout and stderr are closed).
+  __libc_write_log(ANDROID_LOG_FATAL, "libc", msg);
+
+  __libc_set_abort_message(msg);
+
   abort();
 }
+
+void __libc_set_abort_message(const char* msg) {
+  size_t size = sizeof(abort_msg_t) + strlen(msg) + 1;
+  void* map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+  if (map == MAP_FAILED) {
+    return;
+  }
+
+  if (__abort_message_ptr != NULL) {
+    ScopedPthreadMutexLocker locker(&gAbortMsgLock);
+    if (*__abort_message_ptr != NULL) {
+      munmap(*__abort_message_ptr, (*__abort_message_ptr)->size);
+    }
+    abort_msg_t* new_abort_message = reinterpret_cast<abort_msg_t*>(map);
+    new_abort_message->size = size;
+    strcpy(new_abort_message->msg, msg);
+    *__abort_message_ptr = new_abort_message;
+  }
+}
diff --git a/libc/bionic/malloc_debug_qemu.cpp b/libc/bionic/malloc_debug_qemu.cpp
index 08225c1..34ddb87 100644
--- a/libc/bionic/malloc_debug_qemu.cpp
+++ b/libc/bionic/malloc_debug_qemu.cpp
@@ -600,7 +600,7 @@
         error_log("Unable to open /dev/qemu_trace");
         return -1;
     } else {
-        qtrace = mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+        qtrace = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
         close(fd);
 
         if (qtrace == MAP_FAILED) {
diff --git a/libc/bionic/system_properties.c b/libc/bionic/system_properties.c
index c9cf2f7..0587430 100644
--- a/libc/bionic/system_properties.c
+++ b/libc/bionic/system_properties.c
@@ -106,7 +106,7 @@
         goto cleanup;
     }
 
-    prop_area *pa = mmap(0, fd_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
+    prop_area *pa = mmap(NULL, fd_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
 
     if (pa == MAP_FAILED) {
         goto cleanup;
@@ -150,7 +150,7 @@
     while(count--) {
         unsigned entry = *toc++;
         if(TOC_NAME_LEN(entry) != len) continue;
-        
+
         pi = TOC_TO_INFO(pa, entry);
         if(memcmp(name, pi->name, len)) continue;
 
@@ -163,7 +163,7 @@
 int __system_property_read(const prop_info *pi, char *name, char *value)
 {
     unsigned serial, len;
-    
+
     for(;;) {
         serial = pi->serial;
         while(SERIAL_DIRTY(serial)) {
diff --git a/libc/private/KernelArgumentBlock.h b/libc/private/KernelArgumentBlock.h
index d777267..14eca06 100644
--- a/libc/private/KernelArgumentBlock.h
+++ b/libc/private/KernelArgumentBlock.h
@@ -21,6 +21,8 @@
 #include <stdint.h>
 #include <sys/auxv.h>
 
+struct abort_msg_t;
+
 // When the kernel starts the dynamic linker, it passes a pointer to a block
 // of memory containing argc, the argv array, the environment variable array,
 // and the array of ELF aux vectors. This class breaks that block up into its
@@ -67,6 +69,8 @@
   char** envp;
   Elf32_auxv_t* auxv;
 
+  abort_msg_t** abort_message_ptr;
+
  private:
   // Disallow copy and assignment.
   KernelArgumentBlock(const KernelArgumentBlock&);
diff --git a/libc/private/libc_logging.h b/libc/private/libc_logging.h
index 4c9dc21..281bad3 100644
--- a/libc/private/libc_logging.h
+++ b/libc/private/libc_logging.h
@@ -67,6 +67,20 @@
   ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
 };
 
+struct abort_msg_t {
+  size_t size;
+  char msg[0];
+};
+
+__LIBC_HIDDEN__ void __libc_set_abort_message(const char* msg);
+
+//
+// Formats a message to the log (priority 'fatal'), then aborts.
+//
+
+__LIBC_HIDDEN__ __noreturn void __libc_fatal(const char* format, ...)
+    __attribute__((__format__(printf, 1, 2)));
+
 //
 // Formatting routines for the C library's internal debugging.
 // Unlike the usual alternatives, these don't allocate.
diff --git a/linker/debugger.cpp b/linker/debugger.cpp
index 6fddb1c..a7c0591 100644
--- a/linker/debugger.cpp
+++ b/linker/debugger.cpp
@@ -52,8 +52,12 @@
 
 /* message sent over the socket */
 struct debugger_msg_t {
-    debugger_action_t action;
-    pid_t tid;
+  // version 1 included:
+  debugger_action_t action;
+  pid_t tid;
+
+  // version 2 added:
+  uintptr_t abort_msg_address;
 };
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
@@ -154,14 +158,14 @@
     sigemptyset(&newact.sa_mask);
 
     if (sigaction(signum, &newact, &oldact) < 0) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed testing for SA_SIGINFO: %s",
+      __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
                         strerror(errno));
-      return 0;
+      return false;
     }
     bool ret = (oldact.sa_flags & SA_SIGINFO) != 0;
 
     if (sigaction(signum, &oldact, NULL) == -1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "Restore failed in test for SA_SIGINFO: %s",
+      __libc_format_log(ANDROID_LOG_WARN, "libc", "Restore failed in test for SA_SIGINFO: %s",
                         strerror(errno));
     }
     return ret;
@@ -186,19 +190,17 @@
     int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);
 
     if (s >= 0) {
-        /* debugger knows our pid from the credentials on the
-         * local socket but we need to tell it our tid.  It
-         * is paranoid and will verify that we are giving a tid
-         * that's actually in our process
-         */
-        int  ret;
+        // debuggerd knows our pid from the credentials on the
+        // local socket but we need to tell it the tid of the crashing thread.
+        // debuggerd will be paranoid and verify that we sent a tid
+        // that's actually in our process.
         debugger_msg_t msg;
         msg.action = DEBUGGER_ACTION_CRASH;
         msg.tid = tid;
-        ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
+        msg.abort_msg_address = reinterpret_cast<uintptr_t>(gAbortMessage);
+        int ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
         if (ret == sizeof(msg)) {
-            /* if the write failed, there is no point to read on
-             * the file descriptor. */
+            // if the write failed, there is no point trying to read a response.
             ret = TEMP_FAILURE_RETRY(read(s, &tid, 1));
             int saved_errno = errno;
             notify_gdb_of_libraries();
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 3afd314..47c45eb 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -105,6 +105,8 @@
 
 __LIBC_HIDDEN__ int gLdDebugVerbosity;
 
+__LIBC_HIDDEN__ abort_msg_t* gAbortMessage = NULL; // For debuggerd.
+
 enum RelocationKind {
     kRelocAbsolute = 0,
     kRelocRelative,
@@ -171,8 +173,7 @@
  */
 extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity();
 
-static r_debug _r_debug = {1, NULL, &rtld_db_dlactivity,
-                                  RT_CONSISTENT, 0};
+static r_debug _r_debug = {1, NULL, &rtld_db_dlactivity, RT_CONSISTENT, 0};
 static link_map_t* r_debug_tail = 0;
 
 static pthread_mutex_t gDebugMutex = PTHREAD_MUTEX_INITIALIZER;
@@ -1815,8 +1816,8 @@
 
   Elf32_Addr linker_addr = args.getauxval(AT_BASE);
 
-  Elf32_Ehdr *elf_hdr = (Elf32_Ehdr*) linker_addr;
-  Elf32_Phdr *phdr = (Elf32_Phdr*)((unsigned char*) linker_addr + elf_hdr->e_phoff);
+  Elf32_Ehdr* elf_hdr = (Elf32_Ehdr*) linker_addr;
+  Elf32_Phdr* phdr = (Elf32_Phdr*)((unsigned char*) linker_addr + elf_hdr->e_phoff);
 
   soinfo linker_so;
   memset(&linker_so, 0, sizeof(soinfo));
@@ -1841,6 +1842,7 @@
 
   // We have successfully fixed our own relocations. It's safe to run
   // the main part of the linker now.
+  args.abort_message_ptr = &gAbortMessage;
   Elf32_Addr start_address = __linker_init_post_relocation(args, linker_addr);
 
   set_soinfo_pool_protection(PROT_READ);
diff --git a/linker/linker.h b/linker/linker.h
index 6196bec..61d623a 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -186,6 +186,7 @@
 Elf32_Sym* dlsym_handle_lookup(soinfo* si, const char* name);
 
 void debuggerd_init();
+extern "C" abort_msg_t* gAbortMessage;
 extern "C" void notify_gdb_of_libraries();
 
 char* linker_get_error_buffer();