Merge commit 'goog/eclair-plus-aosp'
diff --git a/libc/Android.mk b/libc/Android.mk
index bafc118..7f50390 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -521,17 +521,12 @@
LOCAL_SRC_FILES := \
$(libc_arch_static_src_files) \
bionic/dlmalloc.c \
+ bionic/malloc_debug_common.c \
bionic/libc_init_static.c
-LOCAL_CFLAGS := $(libc_common_cflags)
-
-ifeq ($(WITH_MALLOC_CHECK_LIBC_A),true)
- LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
- LOCAL_SRC_FILES += bionic/malloc_leak.c.arm
-endif
-
+LOCAL_CFLAGS := $(libc_common_cflags) \
+ -DLIBC_STATIC
LOCAL_C_INCLUDES := $(libc_common_c_includes)
-
LOCAL_MODULE := libc
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
LOCAL_SYSTEM_SHARED_LIBRARIES :=
@@ -550,7 +545,7 @@
LOCAL_SRC_FILES := \
$(libc_arch_dynamic_src_files) \
bionic/dlmalloc.c \
- bionic/malloc_leak.c.arm \
+ bionic/malloc_debug_common.c \
bionic/libc_init_dynamic.c
LOCAL_MODULE:= libc
@@ -570,8 +565,16 @@
include $(BUILD_SHARED_LIBRARY)
+# For all builds, except for the -user build we will enable memory
+# allocation checking (including memory leaks, buffer overwrites, etc.)
+# Note that all these checks are also controlled by env. settings
+# that can enable, or disable specific checks. Note also that some of
+# the checks are available only in emulator and are implemeted in
+# libc_malloc_qemu_instrumented.so.
+ifneq ($(TARGET_BUILD_VARIANT),user)
+
# ========================================================
-# libc_debug.so
+# libc_malloc_debug_leak.so
# ========================================================
include $(CLEAR_VARS)
@@ -582,30 +585,49 @@
LOCAL_C_INCLUDES := $(libc_common_c_includes)
LOCAL_SRC_FILES := \
- $(libc_arch_dynamic_src_files) \
- bionic/dlmalloc.c \
- bionic/malloc_leak.c.arm \
- bionic/libc_init_dynamic.c
+ bionic/malloc_debug_leak.c
-LOCAL_MODULE:= libc_debug
+LOCAL_MODULE:= libc_malloc_debug_leak
-# WARNING: The only library libc.so should depend on is libdl.so! If you add other libraries,
-# make sure to add -Wl,--exclude-libs=libgcc.a to the LOCAL_LDFLAGS for those libraries. This
-# ensures that symbols that are pulled into those new libraries from libgcc.a are not declared
-# external; if that were the case, then libc would not pull those symbols from libgcc.a as it
-# should, instead relying on the external symbols from the dependent libraries. That would
-# create an "cloaked" dependency on libgcc.a in libc though the libraries, which is not what
-# you wanted!
-
-LOCAL_SHARED_LIBRARIES := libdl
+LOCAL_SHARED_LIBRARIES := libc
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
LOCAL_SYSTEM_SHARED_LIBRARIES :=
# Don't prelink
LOCAL_PRELINK_MODULE := false
# Don't install on release build
-LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_TAGS := eng debug
include $(BUILD_SHARED_LIBRARY)
+
+# ========================================================
+# libc_malloc_debug_qemu.so
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := \
+ $(libc_common_cflags) \
+ -DMALLOC_QEMU_INSTRUMENT
+
+LOCAL_C_INCLUDES := $(libc_common_c_includes)
+
+LOCAL_SRC_FILES := \
+ bionic/malloc_debug_qemu.c
+
+LOCAL_MODULE:= libc_malloc_debug_qemu
+
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
+LOCAL_SYSTEM_SHARED_LIBRARIES :=
+# Don't prelink
+LOCAL_PRELINK_MODULE := false
+# Don't install on release build
+LOCAL_MODULE_TAGS := eng debug
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif #!user
+
+
# ========================================================
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libc/arch-arm/bionic/__get_pc.S b/libc/arch-arm/bionic/__get_pc.S
index d1377c7..4fc8929 100644
--- a/libc/arch-arm/bionic/__get_pc.S
+++ b/libc/arch-arm/bionic/__get_pc.S
@@ -26,6 +26,7 @@
* SUCH DAMAGE.
*/
.global __get_pc
+.type __get_pc, %function
__get_pc:
mov r0, pc
diff --git a/libc/arch-arm/bionic/__get_sp.S b/libc/arch-arm/bionic/__get_sp.S
index 9acaf3d..0a313a3 100644
--- a/libc/arch-arm/bionic/__get_sp.S
+++ b/libc/arch-arm/bionic/__get_sp.S
@@ -26,6 +26,7 @@
* SUCH DAMAGE.
*/
.global __get_sp
+.type __get_sp, %function
__get_sp:
mov r0, sp
diff --git a/libc/arch-arm/bionic/atomics_arm.S b/libc/arch-arm/bionic/atomics_arm.S
index 0cd0b92..55c642f 100644
--- a/libc/arch-arm/bionic/atomics_arm.S
+++ b/libc/arch-arm/bionic/atomics_arm.S
@@ -28,11 +28,17 @@
#include <sys/linux-syscalls.h>
.global __atomic_cmpxchg
+.type __atomic_cmpxchg, %function
.global __atomic_swap
+.type __atomic_swap, %function
.global __atomic_dec
+.type __atomic_dec, %function
.global __atomic_inc
+.type __atomic_inc, %function
.global __futex_wait
+.type __futex_wait, %function
.global __futex_wake
+.type __futex_wake, %function
#define FUTEX_WAIT 0
#define FUTEX_WAKE 1
diff --git a/libc/bionic/dlmalloc.c b/libc/bionic/dlmalloc.c
index f6f878e..19fbb75 100644
--- a/libc/bionic/dlmalloc.c
+++ b/libc/bionic/dlmalloc.c
@@ -390,9 +390,9 @@
size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set
REALLOC_ZERO_BYTES_FREES default: not defined
- This should be set if a call to realloc with zero bytes should
- be the same as a call to free. Some people think it should. Otherwise,
- since this malloc returns a unique pointer for malloc(0), so does
+ This should be set if a call to realloc with zero bytes should
+ be the same as a call to free. Some people think it should. Otherwise,
+ since this malloc returns a unique pointer for malloc(0), so does
realloc(p, 0).
LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
@@ -671,7 +671,7 @@
/* ------------------- Declarations of public routines ------------------- */
/* Check an additional macro for the five primary functions */
-#if !defined(USE_DL_PREFIX) || !defined(MALLOC_LEAK_CHECK)
+#ifndef USE_DL_PREFIX
#define dlcalloc calloc
#define dlfree free
#define dlmalloc malloc
@@ -3627,7 +3627,7 @@
m->seg.sflags = mmap_flag;
m->magic = mparams.magic;
init_bins(m);
- if (is_global(m))
+ if (is_global(m))
init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
else {
/* Offset top by embedded malloc_state */
@@ -3778,7 +3778,7 @@
}
/* Unmap any unused mmapped segments */
- if (HAVE_MMAP)
+ if (HAVE_MMAP)
released += release_unused_segments(m);
/* On failure, disable autotrim to avoid repeated failed future calls */
@@ -3986,7 +3986,7 @@
while (a < alignment) a <<= 1;
alignment = a;
}
-
+
if (bytes >= MAX_REQUEST - alignment) {
if (m != 0) { /* Test isn't needed but avoids compiler warning */
MALLOC_FAILURE_ACTION;
@@ -5446,5 +5446,5 @@
Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu)
* Based loosely on libg++-1.2X malloc. (It retains some of the overall
structure of old version, but most details differ.)
-
+
*/
diff --git a/libc/bionic/dlmalloc.h b/libc/bionic/dlmalloc.h
index e5f7d4a..1b642d2 100644
--- a/libc/bionic/dlmalloc.h
+++ b/libc/bionic/dlmalloc.h
@@ -1,14 +1,14 @@
/*
Default header file for malloc-2.8.x, written by Doug Lea
and released to the public domain, as explained at
- http://creativecommons.org/licenses/publicdomain.
-
+ http://creativecommons.org/licenses/publicdomain.
+
last update: Mon Aug 15 08:55:52 2005 Doug Lea (dl at gee)
This header is for ANSI C/C++ only. You can set any of
the following #defines before including:
- * If USE_DL_PREFIX is defined, it is assumed that malloc.c
+ * If USE_DL_PREFIX is defined, it is assumed that malloc.c
was also compiled with this option, so all routines
have names starting with "dl".
@@ -34,7 +34,7 @@
#if !ONLY_MSPACES
/* Check an additional macro for the five primary functions */
-#if !defined(USE_DL_PREFIX) || !defined(MALLOC_LEAK_CHECK)
+#if !defined(USE_DL_PREFIX)
#define dlcalloc calloc
#define dlfree free
#define dlmalloc malloc
diff --git a/libc/bionic/libc_init_dynamic.c b/libc/bionic/libc_init_dynamic.c
index b479b27..682ebcf 100644
--- a/libc/bionic/libc_init_dynamic.c
+++ b/libc/bionic/libc_init_dynamic.c
@@ -52,8 +52,6 @@
#include "libc_init_common.h"
#include <bionic_tls.h>
-extern void malloc_debug_init();
-
/* We flag the __libc_preinit function as a constructor to ensure
* that its address is listed in libc.so's .init_array section.
* This ensures that the function is called by the dynamic linker
@@ -78,12 +76,11 @@
__libc_init_common(elfdata);
-#ifdef MALLOC_LEAK_CHECK
- /* setup malloc leak checker, requires system properties */
+ /* Setup malloc routines accordingly to the environment.
+ * Requires system properties
+ */
extern void malloc_debug_init(void);
malloc_debug_init();
-#endif
-
}
__noreturn void __libc_init(uintptr_t *elfdata,
diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c
index e6264bb..d097b6b 100644
--- a/libc/bionic/libc_init_static.c
+++ b/libc/bionic/libc_init_static.c
@@ -68,12 +68,6 @@
/* Initialize the C runtime environment */
__libc_init_common(elfdata);
-#ifdef MALLOC_LEAK_CHECK
- /* setup malloc leak checker, requires system properties */
- extern void malloc_debug_init(void);
- malloc_debug_init();
-#endif
-
/* Several Linux ABIs don't pass the onexit pointer, and the ones that
* do never use it. Therefore, we ignore it.
*/
diff --git a/libc/bionic/logd_write.c b/libc/bionic/logd_write.c
index 3336428..2c5bf42 100644
--- a/libc/bionic/logd_write.c
+++ b/libc/bionic/logd_write.c
@@ -66,7 +66,7 @@
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-log_channel_t log_channels[LOG_ID_MAX] = {
+static log_channel_t log_channels[LOG_ID_MAX] = {
{ __write_to_log_null, -1, NULL },
{ __write_to_log_init, -1, "/dev/"LOGGER_LOG_MAIN },
{ __write_to_log_init, -1, "/dev/"LOGGER_LOG_RADIO }
@@ -112,6 +112,7 @@
log_channels[log_id].logger =
(fd < 0) ? __write_to_log_null : __write_to_log_kernel;
+ log_channels[log_id].fd = fd;
pthread_mutex_unlock(&log_init_lock);
diff --git a/libc/bionic/malloc_debug_common.c b/libc/bionic/malloc_debug_common.c
new file mode 100644
index 0000000..4210915
--- /dev/null
+++ b/libc/bionic/malloc_debug_common.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Contains definition of global variables and implementation of routines
+ * that are used by malloc leak detection code and other components in
+ * the system. The trick is that some components expect these data and
+ * routines to be defined / implemented in libc.so library, regardless
+ * whether or not MALLOC_LEAK_CHECK macro is defined. To make things even
+ * more tricky, malloc leak detection code, implemented in
+ * libc_malloc_debug.so also requires access to these variables and routines
+ * (to fill allocation entry hash table, for example). So, all relevant
+ * variables and routines are defined / implemented here and exported
+ * to all, leak detection code and other components via dynamic (libc.so),
+ * or static (libc.a) linking.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include "dlmalloc.h"
+#include "malloc_debug_common.h"
+
+/*
+ * In a VM process, this is set to 1 after fork()ing out of zygote.
+ */
+int gMallocLeakZygoteChild = 0;
+
+pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER;
+HashTable gHashTable;
+
+// =============================================================================
+// output functions
+// =============================================================================
+
+static int hash_entry_compare(const void* arg1, const void* arg2)
+{
+ HashEntry* e1 = *(HashEntry**)arg1;
+ HashEntry* e2 = *(HashEntry**)arg2;
+
+ size_t nbAlloc1 = e1->allocations;
+ size_t nbAlloc2 = e2->allocations;
+ size_t size1 = e1->size & ~SIZE_FLAG_MASK;
+ size_t size2 = e2->size & ~SIZE_FLAG_MASK;
+ size_t alloc1 = nbAlloc1 * size1;
+ size_t alloc2 = nbAlloc2 * size2;
+
+ // sort in descending order by:
+ // 1) total size
+ // 2) number of allocations
+ //
+ // This is used for sorting, not determination of equality, so we don't
+ // need to compare the bit flags.
+ int result;
+ if (alloc1 > alloc2) {
+ result = -1;
+ } else if (alloc1 < alloc2) {
+ result = 1;
+ } else {
+ if (nbAlloc1 > nbAlloc2) {
+ result = -1;
+ } else if (nbAlloc1 < nbAlloc2) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+ return result;
+}
+
+/*
+ * Retrieve native heap information.
+ *
+ * "*info" is set to a buffer we allocate
+ * "*overallSize" is set to the size of the "info" buffer
+ * "*infoSize" is set to the size of a single entry
+ * "*totalMemory" is set to the sum of all allocations we're tracking; does
+ * not include heap overhead
+ * "*backtraceSize" is set to the maximum number of entries in the back trace
+ */
+void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+ size_t* infoSize, size_t* totalMemory, size_t* backtraceSize)
+{
+ // don't do anything if we have invalid arguments
+ if (info == NULL || overallSize == NULL || infoSize == NULL ||
+ totalMemory == NULL || backtraceSize == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&gAllocationsMutex);
+
+ if (gHashTable.count == 0) {
+ *info = NULL;
+ *overallSize = 0;
+ *infoSize = 0;
+ *totalMemory = 0;
+ *backtraceSize = 0;
+ goto done;
+ }
+
+ void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count);
+
+ // get the entries into an array to be sorted
+ int index = 0;
+ int i;
+ for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
+ HashEntry* entry = gHashTable.slots[i];
+ while (entry != NULL) {
+ list[index] = entry;
+ *totalMemory = *totalMemory +
+ ((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
+ index++;
+ entry = entry->next;
+ }
+ }
+
+ // XXX: the protocol doesn't allow variable size for the stack trace (yet)
+ *infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE);
+ *overallSize = *infoSize * gHashTable.count;
+ *backtraceSize = BACKTRACE_SIZE;
+
+ // now get A byte array big enough for this
+ *info = (uint8_t*)dlmalloc(*overallSize);
+
+ if (*info == NULL) {
+ *overallSize = 0;
+ goto done;
+ }
+
+ qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare);
+
+ uint8_t* head = *info;
+ const int count = gHashTable.count;
+ for (i = 0 ; i < count ; i++) {
+ HashEntry* entry = list[i];
+ size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries);
+ if (entrySize < *infoSize) {
+ /* we're writing less than a full entry, clear out the rest */
+ /* TODO: only clear out the part we're not overwriting? */
+ memset(head, 0, *infoSize);
+ } else {
+ /* make sure the amount we're copying doesn't exceed the limit */
+ entrySize = *infoSize;
+ }
+ memcpy(head, &(entry->size), entrySize);
+ head += *infoSize;
+ }
+
+ dlfree(list);
+
+done:
+ pthread_mutex_unlock(&gAllocationsMutex);
+}
+
+void free_malloc_leak_info(uint8_t* info)
+{
+ dlfree(info);
+}
+
+struct mallinfo mallinfo()
+{
+ return dlmallinfo();
+}
+
+void* valloc(size_t bytes) {
+ /* assume page size of 4096 bytes */
+ return memalign( getpagesize(), bytes );
+}
+
+/* Support for malloc debugging.
+ * Note that if USE_DL_PREFIX is not defined, it's assumed that memory
+ * allocation routines are implemented somewhere else, so all our custom
+ * malloc routines should not be compiled at all.
+ */
+#ifdef USE_DL_PREFIX
+
+/* Table for dispatching malloc calls, initialized with default dispatchers. */
+const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) =
+{
+ dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
+};
+
+/* Selector of dispatch table to use for dispatching malloc calls. */
+const MallocDebug* __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
+
+void* malloc(size_t bytes) {
+ return __libc_malloc_dispatch->malloc(bytes);
+}
+void free(void* mem) {
+ __libc_malloc_dispatch->free(mem);
+}
+void* calloc(size_t n_elements, size_t elem_size) {
+ return __libc_malloc_dispatch->calloc(n_elements, elem_size);
+}
+void* realloc(void* oldMem, size_t bytes) {
+ return __libc_malloc_dispatch->realloc(oldMem, bytes);
+}
+void* memalign(size_t alignment, size_t bytes) {
+ return __libc_malloc_dispatch->memalign(alignment, bytes);
+}
+
+/* We implement malloc debugging only in libc.so, so code bellow
+ * must be excluded if we compile this file for static libc.a
+ */
+#ifndef LIBC_STATIC
+#include <sys/system_properties.h>
+#include <dlfcn.h>
+#include "logd.h"
+
+// =============================================================================
+// log functions
+// =============================================================================
+
+#define debug_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ )
+#define error_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
+#define info_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
+
+/* Table for dispatching malloc calls, depending on environment. */
+static MallocDebug gMallocUse __attribute__((aligned(32))) = {
+ dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
+};
+
+extern char* __progname;
+
+/* Handle to shared library where actual memory allocation is implemented.
+ * This library is loaded and memory allocation calls are redirected there
+ * when libc.debug.malloc environment variable contains value other than
+ * zero:
+ * 1 - For memory leak detections.
+ * 5 - For filling allocated / freed memory with patterns defined by
+ * CHK_SENTINEL_VALUE, and CHK_FILL_FREE macros.
+ * 10 - For adding pre-, and post- allocation stubs in order to detect
+ * buffer overruns.
+ * 20 - For enabling emulator memory allocation instrumentation detecting
+ * memory leaks and buffer overruns.
+ * Actual functionality for debug levels 1-10 is implemented in
+ * libc_malloc_debug_leak.so, while functionality for debug level 20 is
+ * implemented in libc_malloc_debug_qemu.so and can be run inside the
+ * emulator only.
+ */
+static void* libc_malloc_impl_handle = NULL;
+
+/* Initializes memory allocation framework once per process. */
+static void malloc_init_impl(void)
+{
+ const char* so_name = NULL;
+ unsigned int debug_level = 0;
+ unsigned int qemu_running = 0;
+ int len;
+ char env[PROP_VALUE_MAX];
+
+ // Get custom malloc debug level.
+ len = __system_property_get("libc.debug.malloc", env);
+ if (len) {
+ debug_level = atoi(env);
+ }
+
+ /* Debug level 0 means that we should use dlxxx allocation
+ * routines (default).
+ */
+ if (!debug_level) {
+ return;
+ }
+
+ // Get emulator running status.
+ len = __system_property_get("ro.kernel.qemu", env);
+ if (len) {
+ qemu_running = atoi(env);
+ }
+
+ // Lets see which .so must be loaded for the requested debug level
+ switch (debug_level) {
+ case 1:
+ case 5:
+ case 10:
+ so_name = "/system/lib/libc_malloc_debug_leak.so";
+ break;
+ case 20:
+ // Quick check: debug level 20 can only be handled in emulator
+ if (!qemu_running) {
+ info_log("%s: Debug level %d can only be set in emulator\n",
+ __progname, debug_level);
+ return;
+ }
+ so_name = "/system/lib/libc_malloc_debug_qemu.so";
+ break;
+ default:
+ info_log("%s: Debug level %d is unknown\n",
+ __progname, debug_level);
+ return;
+ }
+
+ // Load .so that implements the required malloc debugging functionality.
+ libc_malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
+ if (libc_malloc_impl_handle == NULL) {
+ error_log("%s: Missing module %s required for malloc debug level %d\n",
+ __progname, so_name, debug_level);
+ return;
+ }
+
+ // Initialize malloc dispatch table with appropriate routines.
+ switch (debug_level) {
+ case 1:
+ __libc_android_log_print(ANDROID_LOG_INFO, "libc",
+ "%s using MALLOC_DEBUG = %d (leak checker)\n",
+ __progname, debug_level);
+ gMallocUse.malloc =
+ dlsym(libc_malloc_impl_handle, "leak_malloc");
+ gMallocUse.free =
+ dlsym(libc_malloc_impl_handle, "leak_free");
+ gMallocUse.calloc =
+ dlsym(libc_malloc_impl_handle, "leak_calloc");
+ gMallocUse.realloc =
+ dlsym(libc_malloc_impl_handle, "leak_realloc");
+ gMallocUse.memalign =
+ dlsym(libc_malloc_impl_handle, "leak_memalign");
+ break;
+ case 5:
+ __libc_android_log_print(ANDROID_LOG_INFO, "libc",
+ "%s using MALLOC_DEBUG = %d (fill)\n",
+ __progname, debug_level);
+ gMallocUse.malloc =
+ dlsym(libc_malloc_impl_handle, "fill_malloc");
+ gMallocUse.free =
+ dlsym(libc_malloc_impl_handle, "fill_free");
+ gMallocUse.calloc = dlcalloc;
+ gMallocUse.realloc =
+ dlsym(libc_malloc_impl_handle, "fill_realloc");
+ gMallocUse.memalign =
+ dlsym(libc_malloc_impl_handle, "fill_memalign");
+ break;
+ case 10:
+ __libc_android_log_print(ANDROID_LOG_INFO, "libc",
+ "%s using MALLOC_DEBUG = %d (sentinels, fill)\n",
+ __progname, debug_level);
+ gMallocUse.malloc =
+ dlsym(libc_malloc_impl_handle, "chk_malloc");
+ gMallocUse.free =
+ dlsym(libc_malloc_impl_handle, "chk_free");
+ gMallocUse.calloc =
+ dlsym(libc_malloc_impl_handle, "chk_calloc");
+ gMallocUse.realloc =
+ dlsym(libc_malloc_impl_handle, "chk_realloc");
+ gMallocUse.memalign =
+ dlsym(libc_malloc_impl_handle, "chk_memalign");
+ break;
+ case 20:
+ __libc_android_log_print(ANDROID_LOG_INFO, "libc",
+ "%s using MALLOC_DEBUG = %d (instrumented for emulator)\n",
+ __progname, debug_level);
+ gMallocUse.malloc =
+ dlsym(libc_malloc_impl_handle, "qemu_instrumented_malloc");
+ gMallocUse.free =
+ dlsym(libc_malloc_impl_handle, "qemu_instrumented_free");
+ gMallocUse.calloc =
+ dlsym(libc_malloc_impl_handle, "qemu_instrumented_calloc");
+ gMallocUse.realloc =
+ dlsym(libc_malloc_impl_handle, "qemu_instrumented_realloc");
+ gMallocUse.memalign =
+ dlsym(libc_malloc_impl_handle, "qemu_instrumented_memalign");
+ break;
+ default:
+ break;
+ }
+
+ // Make sure dispatch table is initialized
+ if ((gMallocUse.malloc == NULL) ||
+ (gMallocUse.free == NULL) ||
+ (gMallocUse.calloc == NULL) ||
+ (gMallocUse.realloc == NULL) ||
+ (gMallocUse.memalign == NULL)) {
+ error_log("%s: Cannot initialize malloc dispatch table for debug level"
+ " %d: %p, %p, %p, %p, %p\n",
+ __progname, debug_level,
+ gMallocUse.malloc, gMallocUse.free,
+ gMallocUse.calloc, gMallocUse.realloc,
+ gMallocUse.memalign);
+ dlclose(libc_malloc_impl_handle);
+ libc_malloc_impl_handle = NULL;
+ } else {
+ __libc_malloc_dispatch = &gMallocUse;
+ }
+}
+
+static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
+
+#endif // !LIBC_STATIC
+#endif // USE_DL_PREFIX
+
+/* Initializes memory allocation framework.
+ * This routine is called from __libc_init routines implemented
+ * in libc_init_static.c and libc_init_dynamic.c files.
+ */
+void malloc_debug_init(void)
+{
+ /* We need to initialize malloc iff we impelement here custom
+ * malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so
+ */
+#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
+ if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
+ error_log("Unable to initialize malloc_debug component.");
+ }
+#endif // USE_DL_PREFIX && !LIBC_STATIC
+}
diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h
new file mode 100644
index 0000000..a6301b3
--- /dev/null
+++ b/libc/bionic/malloc_debug_common.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Contains declarations of types and constants used by malloc leak
+ * detection code in both, libc and libc_malloc_debug libraries.
+ */
+#ifndef MALLOC_DEBUG_COMMON_H
+#define MALLOC_DEBUG_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HASHTABLE_SIZE 1543
+#define BACKTRACE_SIZE 32
+/* flag definitions, currently sharing storage with "size" */
+#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
+#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD)
+
+#define MAX_SIZE_T (~(size_t)0)
+
+// =============================================================================
+// Structures
+// =============================================================================
+
+typedef struct HashEntry HashEntry;
+struct HashEntry {
+ size_t slot;
+ HashEntry* prev;
+ HashEntry* next;
+ size_t numEntries;
+ // fields above "size" are NOT sent to the host
+ size_t size;
+ size_t allocations;
+ intptr_t backtrace[0];
+};
+
+typedef struct HashTable HashTable;
+struct HashTable {
+ size_t count;
+ HashEntry* slots[HASHTABLE_SIZE];
+};
+
+/* Entry in malloc dispatch table. */
+typedef struct MallocDebug MallocDebug;
+struct MallocDebug {
+ void* (*malloc)(size_t bytes);
+ void (*free)(void* mem);
+ void* (*calloc)(size_t n_elements, size_t elem_size);
+ void* (*realloc)(void* oldMem, size_t bytes);
+ void* (*memalign)(size_t alignment, size_t bytes);
+};
+
+#ifdef __cplusplus
+}; /* end of extern "C" */
+#endif
+
+#endif // MALLOC_DEBUG_COMMON_H
diff --git a/libc/bionic/malloc_leak.c b/libc/bionic/malloc_debug_leak.c
similarity index 63%
rename from libc/bionic/malloc_leak.c
rename to libc/bionic/malloc_debug_leak.c
index 305f954..7b8822c 100644
--- a/libc/bionic/malloc_leak.c
+++ b/libc/bionic/malloc_debug_leak.c
@@ -38,6 +38,7 @@
#include <stdarg.h>
#include <fcntl.h>
#include <unwind.h>
+#include <dlfcn.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -47,212 +48,37 @@
#include "dlmalloc.h"
#include "logd.h"
+#include "malloc_debug_common.h"
-// =============================================================================
-// Utilities directly used by Dalvik
-// =============================================================================
+// This file should be included into the build only when
+// MALLOC_LEAK_CHECK, or MALLOC_QEMU_INSTRUMENT, or both
+// macros are defined.
+#ifndef MALLOC_LEAK_CHECK
+#error MALLOC_LEAK_CHECK is not defined.
+#endif // !MALLOC_LEAK_CHECK
-#define HASHTABLE_SIZE 1543
-#define BACKTRACE_SIZE 32
-/* flag definitions, currently sharing storage with "size" */
-#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
-#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD)
-
-#define MAX_SIZE_T (~(size_t)0)
-
-/*
- * In a VM process, this is set to 1 after fork()ing out of zygote.
- */
-int gMallocLeakZygoteChild = 0;
-
-// =============================================================================
-// Structures
-// =============================================================================
-
-typedef struct HashEntry HashEntry;
-struct HashEntry {
- size_t slot;
- HashEntry* prev;
- HashEntry* next;
- size_t numEntries;
- // fields above "size" are NOT sent to the host
- size_t size;
- size_t allocations;
- intptr_t backtrace[0];
-};
-
-typedef struct HashTable HashTable;
-struct HashTable {
- size_t count;
- HashEntry* slots[HASHTABLE_SIZE];
-};
-
-static pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER;
-static HashTable gHashTable;
+// Global variables defined in malloc_debug_common.c
+extern int gMallocLeakZygoteChild;
+extern pthread_mutex_t gAllocationsMutex;
+extern HashTable gHashTable;
+extern const MallocDebug __libc_malloc_default_dispatch;
+extern const MallocDebug* __libc_malloc_dispatch;
// =============================================================================
// log functions
// =============================================================================
#define debug_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak", (format), ##__VA_ARGS__ )
+ __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ )
+#define error_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ )
+#define info_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ )
-// =============================================================================
-// output functions
-// =============================================================================
+static int gTrapOnError = 1;
-static int hash_entry_compare(const void* arg1, const void* arg2)
-{
- HashEntry* e1 = *(HashEntry**)arg1;
- HashEntry* e2 = *(HashEntry**)arg2;
-
- size_t nbAlloc1 = e1->allocations;
- size_t nbAlloc2 = e2->allocations;
- size_t size1 = e1->size & ~SIZE_FLAG_MASK;
- size_t size2 = e2->size & ~SIZE_FLAG_MASK;
- size_t alloc1 = nbAlloc1 * size1;
- size_t alloc2 = nbAlloc2 * size2;
-
- // sort in descending order by:
- // 1) total size
- // 2) number of allocations
- //
- // This is used for sorting, not determination of equality, so we don't
- // need to compare the bit flags.
- int result;
- if (alloc1 > alloc2) {
- result = -1;
- } else if (alloc1 < alloc2) {
- result = 1;
- } else {
- if (nbAlloc1 > nbAlloc2) {
- result = -1;
- } else if (nbAlloc1 < nbAlloc2) {
- result = 1;
- } else {
- result = 0;
- }
- }
- return result;
-}
-
-/*
- * Retrieve native heap information.
- *
- * "*info" is set to a buffer we allocate
- * "*overallSize" is set to the size of the "info" buffer
- * "*infoSize" is set to the size of a single entry
- * "*totalMemory" is set to the sum of all allocations we're tracking; does
- * not include heap overhead
- * "*backtraceSize" is set to the maximum number of entries in the back trace
- */
-void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
- size_t* infoSize, size_t* totalMemory, size_t* backtraceSize)
-{
- // don't do anything if we have invalid arguments
- if (info == NULL || overallSize == NULL || infoSize == NULL ||
- totalMemory == NULL || backtraceSize == NULL) {
- return;
- }
-
- pthread_mutex_lock(&gAllocationsMutex);
-
- if (gHashTable.count == 0) {
- *info = NULL;
- *overallSize = 0;
- *infoSize = 0;
- *totalMemory = 0;
- *backtraceSize = 0;
- goto done;
- }
-
- void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count);
-
- // debug_log("*****\ngHashTable.count = %d\n", gHashTable.count);
- // debug_log("list = %p\n", list);
-
- // get the entries into an array to be sorted
- int index = 0;
- int i;
- for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
- HashEntry* entry = gHashTable.slots[i];
- while (entry != NULL) {
- list[index] = entry;
- *totalMemory = *totalMemory +
- ((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
- index++;
- entry = entry->next;
- }
- }
-
- // debug_log("sorted list!\n");
- // XXX: the protocol doesn't allow variable size for the stack trace (yet)
- *infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE);
- *overallSize = *infoSize * gHashTable.count;
- *backtraceSize = BACKTRACE_SIZE;
-
- // debug_log("infoSize = 0x%x overall = 0x%x\n", *infoSize, *overallSize);
- // now get A byte array big enough for this
- *info = (uint8_t*)dlmalloc(*overallSize);
-
- // debug_log("info = %p\n", info);
- if (*info == NULL) {
- *overallSize = 0;
- goto done;
- }
-
- // debug_log("sorting list...\n");
- qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare);
-
- uint8_t* head = *info;
- const int count = gHashTable.count;
- for (i = 0 ; i < count ; i++) {
- HashEntry* entry = list[i];
- size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries);
- if (entrySize < *infoSize) {
- /* we're writing less than a full entry, clear out the rest */
- /* TODO: only clear out the part we're not overwriting? */
- memset(head, 0, *infoSize);
- } else {
- /* make sure the amount we're copying doesn't exceed the limit */
- entrySize = *infoSize;
- }
- memcpy(head, &(entry->size), entrySize);
- head += *infoSize;
- }
-
- dlfree(list);
-
-done:
- // debug_log("+++++ done!\n");
- pthread_mutex_unlock(&gAllocationsMutex);
-}
-
-void free_malloc_leak_info(uint8_t* info)
-{
- dlfree(info);
-}
-
-struct mallinfo mallinfo()
-{
- return dlmallinfo();
-}
-
-void* valloc(size_t bytes) {
- /* assume page size of 4096 bytes */
- return memalign( getpagesize(), bytes );
-}
-
-
-/*
- * Code guarded by MALLOC_LEAK_CHECK is only needed when malloc check is
- * enabled. Currently we exclude them in libc.so, and only include them in
- * libc_debug.so.
- */
-#ifdef MALLOC_LEAK_CHECK
#define MALLOC_ALIGNMENT 8
#define GUARD 0x48151642
-
#define DEBUG 0
// =============================================================================
@@ -407,13 +233,13 @@
if (state->count) {
intptr_t ip = (intptr_t)_Unwind_GetIP(context);
if (ip) {
- state->addrs[0] = ip;
+ state->addrs[0] = ip;
state->addrs++;
state->count--;
return _URC_NO_REASON;
}
}
- /*
+ /*
* If we run out of space to record the address or 0 has been seen, stop
* unwinding the stack.
*/
@@ -431,70 +257,6 @@
}
// =============================================================================
-// malloc leak function dispatcher
-// =============================================================================
-
-static void* leak_malloc(size_t bytes);
-static void leak_free(void* mem);
-static void* leak_calloc(size_t n_elements, size_t elem_size);
-static void* leak_realloc(void* oldMem, size_t bytes);
-static void* leak_memalign(size_t alignment, size_t bytes);
-
-static void* fill_malloc(size_t bytes);
-static void fill_free(void* mem);
-static void* fill_realloc(void* oldMem, size_t bytes);
-static void* fill_memalign(size_t alignment, size_t bytes);
-
-static void* chk_malloc(size_t bytes);
-static void chk_free(void* mem);
-static void* chk_calloc(size_t n_elements, size_t elem_size);
-static void* chk_realloc(void* oldMem, size_t bytes);
-static void* chk_memalign(size_t alignment, size_t bytes);
-
-typedef struct {
- void* (*malloc)(size_t bytes);
- void (*free)(void* mem);
- void* (*calloc)(size_t n_elements, size_t elem_size);
- void* (*realloc)(void* oldMem, size_t bytes);
- void* (*memalign)(size_t alignment, size_t bytes);
-} MallocDebug;
-
-static const MallocDebug gMallocEngineTable[] __attribute__((aligned(32))) =
-{
- { dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign },
- { leak_malloc, leak_free, leak_calloc, leak_realloc, leak_memalign },
- { fill_malloc, fill_free, dlcalloc, fill_realloc, fill_memalign },
- { chk_malloc, chk_free, chk_calloc, chk_realloc, chk_memalign }
-};
-
-enum {
- INDEX_NORMAL = 0,
- INDEX_LEAK_CHECK,
- INDEX_MALLOC_FILL,
- INDEX_MALLOC_CHECK,
-};
-
-static MallocDebug const * gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
-static int gMallocDebugLevel;
-static int gTrapOnError = 1;
-
-void* malloc(size_t bytes) {
- return gMallocDispatch->malloc(bytes);
-}
-void free(void* mem) {
- gMallocDispatch->free(mem);
-}
-void* calloc(size_t n_elements, size_t elem_size) {
- return gMallocDispatch->calloc(n_elements, elem_size);
-}
-void* realloc(void* oldMem, size_t bytes) {
- return gMallocDispatch->realloc(oldMem, bytes);
-}
-void* memalign(size_t alignment, size_t bytes) {
- return gMallocDispatch->memalign(alignment, bytes);
-}
-
-// =============================================================================
// malloc check functions
// =============================================================================
@@ -532,7 +294,9 @@
va_list args;
pthread_mutex_lock(&gAllocationsMutex);
- gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
+ {
+ const MallocDebug* current_dispatch = __libc_malloc_dispatch;
+ __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
va_start(args, format);
__libc_android_log_vprint(ANDROID_LOG_ERROR, "libc",
format, args);
@@ -541,7 +305,8 @@
if (gTrapOnError) {
__builtin_trap();
}
- gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK];
+ __libc_malloc_dispatch = current_dispatch;
+ }
pthread_mutex_unlock(&gAllocationsMutex);
}
@@ -574,7 +339,7 @@
buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) {
if (buf[i] != CHK_SENTINEL_VALUE) {
- assert_log_message(
+ assert_log_message(
"*** %s CHECK: buffer %p "
"corrupted %d bytes before allocation",
func, mem, CHK_SENTINEL_HEAD_SIZE-i);
@@ -590,7 +355,7 @@
buf = (char*)mem + bytes;
for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) {
if (buf[i] != CHK_SENTINEL_VALUE) {
- assert_log_message(
+ assert_log_message(
"*** %s CHECK: buffer %p, size=%lu, "
"corrupted %d bytes after allocation",
func, buffer, bytes, i+1);
@@ -743,11 +508,11 @@
intptr_t backtrace[BACKTRACE_SIZE];
size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
-
+
AllocationEntry* header = (AllocationEntry*)base;
header->entry = record_backtrace(backtrace, numEntries, bytes);
header->guard = GUARD;
-
+
// now increment base to point to after our header.
// this should just work since our header is 8 bytes.
base = (AllocationEntry*)base + 1;
@@ -765,7 +530,7 @@
// check the guard to make sure it is valid
AllocationEntry* header = (AllocationEntry*)mem - 1;
-
+
if (header->guard != GUARD) {
// could be a memaligned block
if (((void**)mem)[-1] == MEMALIGN_GUARD) {
@@ -773,7 +538,7 @@
header = (AllocationEntry*)mem - 1;
}
}
-
+
if (header->guard == GUARD || is_valid_entry(header->entry)) {
// decrement the allocations
HashEntry* entry = header->entry;
@@ -842,7 +607,7 @@
// need to make sure it's a power of two
if (alignment & (alignment-1))
alignment = 1L << (31 - __builtin_clz(alignment));
-
+
// here, aligment is at least MALLOC_ALIGNMENT<<1 bytes
// we will align by at least MALLOC_ALIGNMENT bytes
// and at most alignment-MALLOC_ALIGNMENT bytes
@@ -855,7 +620,7 @@
// align the pointer
ptr += ((-ptr) % alignment);
-
+
// there is always enough space for the base pointer and the guard
((void**)ptr)[-1] = MEMALIGN_GUARD;
((void**)ptr)[-2] = base;
@@ -864,60 +629,3 @@
}
return base;
}
-#endif /* MALLOC_LEAK_CHECK */
-
-// called from libc_init()
-extern char* __progname;
-
-void malloc_debug_init()
-{
- unsigned int level = 0;
-#ifdef MALLOC_LEAK_CHECK
- // if MALLOC_LEAK_CHECK is enabled, use level=1 by default
- level = 1;
-#endif
- char env[PROP_VALUE_MAX];
- int len = __system_property_get("libc.debug.malloc", env);
-
- if (len) {
- level = atoi(env);
-#ifndef MALLOC_LEAK_CHECK
- /* Alert the user that libc_debug.so needs to be installed as libc.so
- * when performing malloc checks.
- */
- if (level != 0) {
- __libc_android_log_print(ANDROID_LOG_INFO, "libc",
- "Malloc checks need libc_debug.so pushed to the device!\n");
-
- }
-#endif
- }
-
-#ifdef MALLOC_LEAK_CHECK
- gMallocDebugLevel = level;
- switch (level) {
- default:
- case 0:
- gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL];
- break;
- case 1:
- __libc_android_log_print(ANDROID_LOG_INFO, "libc",
- "%s using MALLOC_DEBUG = %d (leak checker)\n",
- __progname, level);
- gMallocDispatch = &gMallocEngineTable[INDEX_LEAK_CHECK];
- break;
- case 5:
- __libc_android_log_print(ANDROID_LOG_INFO, "libc",
- "%s using MALLOC_DEBUG = %d (fill)\n",
- __progname, level);
- gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_FILL];
- break;
- case 10:
- __libc_android_log_print(ANDROID_LOG_INFO, "libc",
- "%s using MALLOC_DEBUG = %d (sentinels, fill)\n",
- __progname, level);
- gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK];
- break;
- }
-#endif
-}
diff --git a/libc/bionic/malloc_debug_qemu.c b/libc/bionic/malloc_debug_qemu.c
new file mode 100644
index 0000000..b63ddb4
--- /dev/null
+++ b/libc/bionic/malloc_debug_qemu.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Contains implementation of memeory allocation routines instrumented for
+ * usage in the emulator to detect memory allocation violations, such as
+ * memory leaks, buffer overruns, etc.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include "dlmalloc.h"
+#include "logd.h"
+
+// This file should be included into the build only when
+// MALLOC_QEMU_INSTRUMENT macro is defined.
+#ifndef MALLOC_QEMU_INSTRUMENT
+#error MALLOC_QEMU_INSTRUMENT is not defined.
+#endif // MALLOC_QEMU_INSTRUMENT
+
+// =============================================================================
+// log functions
+// =============================================================================
+
+#define debug_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_qemu", (format), ##__VA_ARGS__ )
+#define error_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_qemu", (format), ##__VA_ARGS__ )
+#define info_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_INFO, "malloc_qemu", (format), ##__VA_ARGS__ )
+
+void* qemu_instrumented_malloc(size_t bytes)
+{
+ return dlmalloc(bytes);
+}
+
+void qemu_instrumented_free(void* mem)
+{
+ dlfree(mem);
+}
+
+void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
+{
+ return dlcalloc(n_elements, elem_size);
+}
+
+void* qemu_instrumented_realloc(void* mem, size_t bytes)
+{
+ return dlrealloc(mem, bytes);
+}
+
+void* qemu_instrumented_memalign(size_t alignment, size_t bytes)
+{
+ return dlmemalign(alignment, bytes);
+}
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 79e526b..791b260 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -112,6 +112,9 @@
* that does not match the previous one in _bf. When this happens,
* _ub._base becomes non-nil (i.e., a stream has ungetc() data iff
* _ub._base!=NULL) and _up and _ur save the current values of _p and _r.
+ *
+ * NOTE: if you change this structure, you also need to update the
+ * std() initializer in findfp.c.
*/
typedef struct __sFILE {
unsigned char *_p; /* current position in (some) buffer */
diff --git a/libc/include/time64.h b/libc/include/time64.h
index 9da4bc7..7ec05af 100644
--- a/libc/include/time64.h
+++ b/libc/include/time64.h
@@ -31,9 +31,12 @@
#ifndef TIME64_H
#define TIME64_H
+#include <sys/cdefs.h>
#include <time.h>
#include <stdint.h>
+__BEGIN_DECLS
+
typedef int64_t time64_t;
struct tm *gmtime64_r (const time64_t *, struct tm *);
@@ -51,4 +54,6 @@
time64_t mktime64 (const struct tm *);
time64_t timelocal64 (const struct tm *);
+__END_DECLS
+
#endif /* TIME64_H */
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 1ada37e..67cb5fe 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -185,6 +185,14 @@
extern pid_t tcgetpgrp(int fd);
extern int tcsetpgrp(int fd, pid_t _pid);
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+
__END_DECLS
#endif /* _UNISTD_H_ */
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index e2feb60..97e1b5c 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -100,6 +100,7 @@
extern size_t mbrlen(const char *, size_t, mbstate_t *);
extern size_t mbrtowc(wchar_t *, const char *, size_t, mbstate_t *);
extern size_t mbsrtowcs(wchar_t *, const char **, size_t, mbstate_t *);
+extern size_t mbstowcs(wchar_t *, const char *, size_t);
extern wint_t putwc(wchar_t, FILE *);
extern wint_t putwchar(wchar_t);
extern int swprintf(wchar_t *, size_t, const wchar_t *, ...);
@@ -130,6 +131,7 @@
extern double wcstod(const wchar_t *, wchar_t **);
extern wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);
extern long int wcstol(const wchar_t *, wchar_t **, int);
+extern size_t wcstombs(char *, const wchar_t *, size_t);
extern unsigned long int wcstoul(const wchar_t *, wchar_t **, int);
extern wchar_t *wcswcs(const wchar_t *, const wchar_t *);
extern int wcswidth(const wchar_t *, size_t);
diff --git a/libc/stdlib/wchar.c b/libc/stdlib/wchar.c
index d805333..7722b34 100644
--- a/libc/stdlib/wchar.c
+++ b/libc/stdlib/wchar.c
@@ -227,6 +227,11 @@
return len;
}
+size_t mbstowcs(wchar_t *dst, const char *src, size_t len)
+{
+ return mbsrtowcs(dst, &src, len, NULL);
+}
+
wint_t putwc(wchar_t wc, FILE *stream)
{
return fputc((char)wc, stream);
@@ -339,6 +344,11 @@
return len;
}
+size_t wcstombs(char *dst, const wchar_t *src, size_t len)
+{
+ return wcsrtombs(dst, &src, len, NULL);
+}
+
size_t wcsspn(const wchar_t *ws1, const wchar_t *ws2)
{
return strspn( (const char*)ws1, (const char*)ws2 );
diff --git a/linker/debugger.c b/linker/debugger.c
index 5bb065c..1bd3cc8 100644
--- a/linker/debugger.c
+++ b/linker/debugger.c
@@ -32,6 +32,7 @@
#include <ctype.h>
#include <signal.h>
#include <sys/mman.h>
+#include <errno.h>
#include "linker.h"
@@ -40,6 +41,11 @@
void notify_gdb_of_libraries();
+#define RETRY_ON_EINTR(ret,cond) \
+ do { \
+ ret = (cond); \
+ } while (ret < 0 && errno == EINTR)
+
void debugger_signal_handler(int n)
{
unsigned tid;
@@ -58,10 +64,15 @@
* is paranoid and will verify that we are giving a tid
* that's actually in our process
*/
- write(s, &tid, sizeof(unsigned));
+ int ret;
- read(s, &tid, 1);
- notify_gdb_of_libraries();
+ RETRY_ON_EINTR(ret, write(s, &tid, sizeof(unsigned)));
+ if (ret == sizeof(unsigned)) {
+ /* if the write failed, there is no point to read on
+ * the file descriptor. */
+ RETRY_ON_EINTR(ret, read(s, &tid, 1));
+ notify_gdb_of_libraries();
+ }
close(s);
}
diff --git a/linker/linker.c b/linker/linker.c
index 6d57cbc..d61d0d7 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008, 2009 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -68,7 +68,6 @@
*
* open issues / todo:
*
- * - should we do anything special for STB_WEAK symbols?
* - are we doing everything we should for ARM_COPY relocations?
* - cleaner error reporting
* - after linking, set as much stuff as possible to READONLY
@@ -258,7 +257,7 @@
if(strlen(name) >= SOINFO_NAME_LEN) {
DL_ERR("%5d library name %s too long", pid, name);
- return 0;
+ return NULL;
}
/* The freelist is populated when we call free_info(), which in turn is
@@ -406,20 +405,20 @@
s = symtab + n;
if(strcmp(strtab + s->st_name, name)) continue;
- /* only concern ourselves with global symbols */
+ /* only concern ourselves with global and weak symbol definitions */
switch(ELF32_ST_BIND(s->st_info)){
case STB_GLOBAL:
+ case STB_WEAK:
/* no section == undefined */
if(s->st_shndx == 0) continue;
- case STB_WEAK:
TRACE_TYPE(LOOKUP, "%5d FOUND %s in %s (%08x) %d\n", pid,
name, si->name, s->st_value, s->st_size);
return s;
}
}
- return 0;
+ return NULL;
}
static unsigned elfhash(const char *_name)
@@ -437,25 +436,23 @@
}
static Elf32_Sym *
-_do_lookup_in_so(soinfo *si, const char *name, unsigned *elf_hash)
-{
- if (*elf_hash == 0)
- *elf_hash = elfhash(name);
- return _elf_lookup (si, *elf_hash, name);
-}
-
-static Elf32_Sym *
_do_lookup(soinfo *si, const char *name, unsigned *base)
{
- unsigned elf_hash = 0;
+ unsigned elf_hash = elfhash(name);
Elf32_Sym *s;
unsigned *d;
soinfo *lsi = si;
/* Look for symbols in the local scope first (the object who is
* searching). This happens with C++ templates on i386 for some
- * reason. */
- s = _do_lookup_in_so(si, name, &elf_hash);
+ * reason.
+ *
+ * Notes on weak symbols:
+ * The ELF specs are ambigious about treatment of weak definitions in
+ * dynamic linking. Some systems return the first definition found
+ * and some the first non-weak definition. This is system dependent.
+ * Here we return the first definition found for simplicity. */
+ s = _elf_lookup(si, elf_hash, name);
if(s != NULL)
goto done;
@@ -465,12 +462,12 @@
if (!validate_soinfo(lsi)) {
DL_ERR("%5d bad DT_NEEDED pointer in %s",
pid, si->name);
- return 0;
+ return NULL;
}
DEBUG("%5d %s: looking up %s in %s\n",
pid, si->name, name, lsi->name);
- s = _do_lookup_in_so(lsi, name, &elf_hash);
+ s = _elf_lookup(lsi, elf_hash, name);
if(s != NULL)
goto done;
}
@@ -485,7 +482,7 @@
lsi = somain;
DEBUG("%5d %s: looking up %s in executable %s\n",
pid, si->name, name, lsi->name);
- s = _do_lookup_in_so(lsi, name, &elf_hash);
+ s = _elf_lookup(lsi, elf_hash, name);
}
#endif
@@ -498,7 +495,7 @@
return s;
}
- return 0;
+ return NULL;
}
/* This is used by dl_sym(). It performs symbol lookup only within the
@@ -506,15 +503,14 @@
*/
Elf32_Sym *lookup_in_library(soinfo *si, const char *name)
{
- unsigned unused = 0;
- return _do_lookup_in_so(si, name, &unused);
+ return _elf_lookup(si, elfhash(name), name);
}
/* This is used by dl_sym(). It performs a global symbol lookup.
*/
Elf32_Sym *lookup(const char *name, soinfo **found)
{
- unsigned elf_hash = 0;
+ unsigned elf_hash = elfhash(name);
Elf32_Sym *s = NULL;
soinfo *si;
@@ -522,7 +518,7 @@
{
if(si->flags & FLAG_ERROR)
continue;
- s = _do_lookup_in_so(si, name, &elf_hash);
+ s = _elf_lookup(si, elf_hash, name);
if (s != NULL) {
*found = si;
break;
@@ -535,7 +531,7 @@
return s;
}
- return 0;
+ return NULL;
}
#if 0
@@ -1241,10 +1237,64 @@
if(sym != 0) {
sym_name = (char *)(strtab + symtab[sym].st_name);
s = _do_lookup(si, sym_name, &base);
- if(s == 0) {
- DL_ERR("%5d cannot locate '%s'...", pid, sym_name);
- return -1;
- }
+ if(s == NULL) {
+ /* We only allow an undefined symbol if this is a weak
+ reference.. */
+ s = &symtab[sym];
+ if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+ DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name);
+ return -1;
+ }
+
+ /* IHI0044C AAELF 4.5.1.1:
+
+ Libraries are not searched to resolve weak references.
+ It is not an error for a weak reference to remain
+ unsatisfied.
+
+ During linking, the value of an undefined weak reference is:
+ - Zero if the relocation type is absolute
+ - The address of the place if the relocation is pc-relative
+ - The address of nominial base address if the relocation
+ type is base-relative.
+ */
+
+ switch (type) {
+#if defined(ANDROID_ARM_LINKER)
+ case R_ARM_JUMP_SLOT:
+ case R_ARM_GLOB_DAT:
+ case R_ARM_ABS32:
+ case R_ARM_RELATIVE: /* Don't care. */
+ case R_ARM_NONE: /* Don't care. */
+#elif defined(ANDROID_X86_LINKER)
+ case R_386_JUMP_SLOT:
+ case R_386_GLOB_DAT:
+ case R_386_32:
+ case R_386_RELATIVE: /* Dont' care. */
+#endif /* ANDROID_*_LINKER */
+ /* sym_addr was initialized to be zero above or relocation
+ code below does not care about value of sym_addr.
+ No need to do anything. */
+ break;
+
+#if defined(ANDROID_X86_LINKER)
+ case R_386_PC32:
+ sym_addr = reloc;
+ break;
+#endif /* ANDROID_X86_LINKER */
+
+#if defined(ANDROID_ARM_LINKER)
+ case R_ARM_COPY:
+ /* Fall through. Can't really copy if weak symbol is
+ not found in run-time. */
+#endif /* ANDROID_ARM_LINKER */
+ default:
+ DL_ERR("%5d unknown weak reloc type %d @ %p (%d)\n",
+ pid, type, rel, (int) (rel - start));
+ return -1;
+ }
+ } else {
+ /* We got a definition. */
#if 0
if((base == 0) && (si->base != 0)){
/* linking from libraries to main image is bad */
@@ -1253,20 +1303,11 @@
return -1;
}
#endif
- // st_shndx==SHN_UNDEF means an undefined symbol.
- // st_value should be 0 then, except that the low bit of st_value is
- // used to indicate whether the symbol points to an ARM or thumb function,
- // and should be ignored in the following check.
- if ((s->st_shndx == SHN_UNDEF) && ((s->st_value & ~1) != 0)) {
- DL_ERR("%5d In '%s', symbol=%s shndx=%d && value=0x%08x. We do not "
- "handle this yet", pid, si->name, sym_name, s->st_shndx,
- s->st_value);
- return -1;
- }
- sym_addr = (unsigned)(s->st_value + base);
+ sym_addr = (unsigned)(s->st_value + base);
+ }
COUNT_RELOC(RELOC_SYMBOL);
} else {
- s = 0;
+ s = NULL;
}
/* TODO: This is ugly. Split up the relocations by arch into
@@ -1916,7 +1957,7 @@
return 0;
fail:
- ERROR("failed to link %s\n", si->name);
+ DL_ERR("failed to link %s\n", si->name);
si->flags |= FLAG_ERROR;
return -1;
}