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 #include -#include -#include + #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 cf09aacf0..51c62a707 100644 --- a/libc/bionic/dlmalloc.c +++ b/libc/bionic/dlmalloc.c @@ -16,15 +16,7 @@ #include "dlmalloc.h" -#include -#include -#include -#include -#include - -#include - -#include +#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] : ""; + __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(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 #include +#include #include #include #include #include #include -#include - -/*** 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(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(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 -#include +#include #include +#include +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(tag); - vec[1].iov_len = strlen(tag) + 1; - vec[2].iov_base = const_cast(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 @@ 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 +static void SendRepeat(Out& o, char ch, int count) { + char pad[8]; + memset(pad, ch, sizeof(pad)); + + const int pad_size = static_cast(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 +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(tag); + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = const_cast(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(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 #include +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. diff --git a/linker/debugger.cpp b/linker/debugger.cpp index 6fddb1c75..a7c0591d9 100644 --- a/linker/debugger.cpp +++ b/linker/debugger.cpp @@ -52,8 +52,12 @@ enum debugger_action_t { /* 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 @@ static bool haveSiginfo(int signum) { 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 @@ void debuggerd_signal_handler(int n, siginfo_t* info, void*) { 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(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 3afd31405..47c45eb0b 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -105,6 +105,8 @@ static soinfo* gLdPreloads[LDPRELOAD_MAX + 1]; __LIBC_HIDDEN__ int gLdDebugVerbosity; +__LIBC_HIDDEN__ abort_msg_t* gAbortMessage = NULL; // For debuggerd. + enum RelocationKind { kRelocAbsolute = 0, kRelocRelative, @@ -171,8 +173,7 @@ size_t linker_get_error_buffer_size() { */ 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 @@ extern "C" Elf32_Addr __linker_init(void* raw_args) { 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 @@ extern "C" Elf32_Addr __linker_init(void* raw_args) { // 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 6196bec02..61d623a96 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -186,6 +186,7 @@ Elf32_Sym* dladdr_find_symbol(soinfo* si, const void* addr); 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();