summaryrefslogtreecommitdiffstats
path: root/libc
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2013-04-04 13:46:46 -0700
committerElliott Hughes <enh@google.com>2013-04-05 11:24:19 -0700
commit0d787c1fa18c6a1f29ef9840e28a68cf077be1de (patch)
tree3f8efc05d80658c9be48513238104a5de01a9951 /libc
parent014c75c78bdcad8acde8d0abb2450aa1f78e1a1f (diff)
downloadbionic-0d787c1fa18c6a1f29ef9840e28a68cf077be1de.tar.gz
bionic-0d787c1fa18c6a1f29ef9840e28a68cf077be1de.tar.bz2
bionic-0d787c1fa18c6a1f29ef9840e28a68cf077be1de.zip
Make abort messages available to debuggerd.
This adds __libc_fatal, cleans up the internal logging code a bit more, and switches suitable callers over to __libc_fatal. In addition to logging, __libc_fatal stashes the message somewhere that the debuggerd signal handler can find it before calling abort. In the debuggerd signal handler, we pass this address to debuggerd so that it can come back with ptrace to read the message and present it to the user. Bug: 8531731 Change-Id: I416ec1da38a8a1b0d0a582ccd7c8aaa681ed4a29
Diffstat (limited to 'libc')
-rw-r--r--libc/bionic/__stack_chk_fail.cpp3
-rw-r--r--libc/bionic/assert.cpp17
-rw-r--r--libc/bionic/dlmalloc.c19
-rw-r--r--libc/bionic/libc_init_common.cpp2
-rw-r--r--libc/bionic/libc_logging.cpp394
-rw-r--r--libc/bionic/malloc_debug_qemu.cpp2
-rw-r--r--libc/bionic/system_properties.c6
-rw-r--r--libc/private/KernelArgumentBlock.h4
-rw-r--r--libc/private/libc_logging.h14
9 files changed, 211 insertions, 250 deletions
diff --git a/libc/bionic/__stack_chk_fail.cpp b/libc/bionic/__stack_chk_fail.cpp
index d5031ac7b..8b9ed5e6c 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 6f221a56a..84024c7c2 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.
+#include "libc_logging.h"
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 cf09aacf0..51c62a707 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 @@ static void __bionic_heap_usage_error(const char* function, void* address);
#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 33ec1db35..1fc490e23 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 @@ void __libc_init_common(KernelArgumentBlock& args) {
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 755dc8111..8de1192be 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 <sys/mman.h>
+#include <sys/uio.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);
+static pthread_mutex_t gAbortMsgLock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t gLogInitializationLock = PTHREAD_MUTEX_INITIALIZER;
-/*** Bounded buffer output
- ***/
+__LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common.
-struct BufOut {
- Out out[1];
- char *buffer;
- char *pos;
- char *end;
- int total;
+// 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,
};
-static void buf_out_send(void *opaque, const char *data, int len) {
- BufOut *bo = reinterpret_cast<BufOut*>(opaque);
+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);
+ 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;
+ 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;
}
-}
-
-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;
-}
+ private:
+ char* buffer_;
+ char* pos_;
+ char* end_;
+};
-int __libc_format_fd(int fd, const char* format, ...) {
- FdOut fdo;
- Out* out = fd_out_init(&fdo, fd);
- if (out == NULL) {
- return 0;
+struct FdOutputStream {
+ public:
+ FdOutputStream(int fd) : total(0), fd_(fd) {
}
- 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/uio.h>
-
-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);
+ void Send(const char* data, int len) {
+ if (len < 0) {
+ len = strlen(data);
+ }
- 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;
+ while (len > 0) {
+ int rc = TEMP_FAILURE_RETRY(write(fd_, data, len));
+ if (rc == -1) {
+ break;
+ }
+ data += rc;
+ len -= rc;
+ total += rc;
}
}
- 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;
-
- return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
-}
+ int total;
-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;
-}
+ private:
+ int fd_;
+};
/*** formatted output implementation
***/
@@ -263,9 +131,7 @@ int __libc_format_log(int priority, const char* tag, const char* format, ...) {
*
* 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 @@ parse_decimal(const char *format, int *ppos)
int ch = *p;
unsigned d = (unsigned)(ch - '0');
- if (d >= 10U)
+ if (d >= 10U) {
break;
+ }
result = result*10 + d;
p++;
@@ -341,10 +208,25 @@ static void format_integer(char* buf, size_t buf_size, uint64_t value, char conv
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 @@ out_vformat(Out *o, const char *format, va_list args)
} while (1);
if (mm > nn) {
- out_send(o, format+nn, mm-nn);
+ o.Send(format+nn, mm-nn);
nn = mm;
}
@@ -387,7 +269,7 @@ out_vformat(Out *o, const char *format, va_list args)
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 @@ out_vformat(Out *o, const char *format, va_list args)
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 __libc_android_log_event_uid(int32_t tag) {
}
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 08225c1e1..34ddb8751 100644
--- a/libc/bionic/malloc_debug_qemu.cpp
+++ b/libc/bionic/malloc_debug_qemu.cpp
@@ -600,7 +600,7 @@ int malloc_debug_initialize() {
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 c9cf2f75f..0587430a5 100644
--- a/libc/bionic/system_properties.c
+++ b/libc/bionic/system_properties.c
@@ -106,7 +106,7 @@ int __system_properties_init(void)
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 @@ const prop_info *__system_property_find(const char *name)
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 @@ const prop_info *__system_property_find(const char *name)
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 d77726733..14eca06a1 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 @@ class KernelArgumentBlock {
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 4c9dc2190..281bad3ab 100644
--- a/libc/private/libc_logging.h
+++ b/libc/private/libc_logging.h
@@ -67,6 +67,20 @@ enum {
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.