Merge "Add libdl_android to the Runtime APEX."
diff --git a/.clang-format b/.clang-format
index b8c6428..7630d16 100644
--- a/.clang-format
+++ b/.clang-format
@@ -12,3 +12,4 @@
PenaltyExcessCharacter: 32
Cpp11BracedListStyle: false
+IncludeBlocks: Preserve
\ No newline at end of file
diff --git a/libc/Android.bp b/libc/Android.bp
index 53a26a6..c5ea4c5 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -142,19 +142,21 @@
}
// ========================================================
-// libc_stack_protector.a - stack protector code
+// libc_bootstrap.a - -fno-stack-protector and -ffreestanding
// ========================================================
//
-// Code that implements the stack protector (or that runs
-// before TLS has been set up) needs to be compiled with
-// -fno-stack-protector, since it accesses the stack canary
-// TLS slot.
+// Code that implements the stack protector (or that runs before TLS has been set up) needs to be
+// compiled with -fno-stack-protector, since it accesses the stack canary TLS slot. In the linker,
+// some of this code runs before ifunc resolvers have made string.h functions work, so compile with
+// -ffreestanding.
cc_library_static {
srcs: [
"bionic/__libc_init_main_thread.cpp",
"bionic/__stack_chk_fail.cpp",
+ "bionic/bionic_call_ifunc_resolver.cpp",
+ "bionic/getauxval.cpp",
],
arch: {
arm64: {
@@ -172,20 +174,25 @@
},
defaults: ["libc_defaults"],
- cflags: ["-fno-stack-protector"],
- name: "libc_stack_protector",
+ cflags: ["-fno-stack-protector", "-ffreestanding"],
+ name: "libc_bootstrap",
}
-// libc_init_static.cpp also needs to be built without stack protector,
-// because it's responsible for setting up TLS for static executables.
-// This isn't the case for dynamic executables because the dynamic linker
-// has already set up the main thread's TLS.
+// libc_init_static.cpp and libc_init_dynamic.cpp need to be built without stack protector.
+// libc_init_static.cpp sets up TLS for static executables, and libc_init_dynamic.cpp initializes
+// the stack protector global variable.
cc_library_static {
name: "libc_init_static",
defaults: ["libc_defaults"],
srcs: ["bionic/libc_init_static.cpp"],
- cflags: ["-fno-stack-protector"],
+ cflags: [
+ "-fno-stack-protector",
+
+ // Compile libc_init_static.cpp with -ffreestanding, because some of its code is called
+ // from the linker before ifunc resolvers have made string.h functions available.
+ "-ffreestanding",
+ ],
}
cc_library_static {
@@ -784,12 +791,6 @@
cc_library_static {
defaults: ["libc_defaults"],
srcs: [
- // The data that backs getauxval is initialized in the libc init
- // functions which are invoked by the linker. If this file is included
- // in libc_ndk.a, only one of the copies of the global data will be
- // initialized, resulting in nullptr dereferences.
- "bionic/getauxval.cpp",
-
// These require getauxval, which isn't available on older platforms.
"bionic/sysconf.cpp",
"bionic/vdso.cpp",
@@ -1084,7 +1085,6 @@
"bionic/atof.cpp",
"bionic/bionic_allocator.cpp",
"bionic/bionic_arc4random.cpp",
- "bionic/bionic_call_ifunc_resolver.cpp",
"bionic/bionic_futex.cpp",
"bionic/bionic_netlink.cpp",
"bionic/bionic_systrace.cpp",
@@ -1427,6 +1427,7 @@
whole_static_libs: [
"libc_bionic_ndk",
+ "libc_bootstrap",
"libc_fortify",
"libc_freebsd",
"libc_freebsd_large_stack",
@@ -1434,7 +1435,6 @@
"libc_netbsd",
"libc_openbsd_large_stack",
"libc_openbsd_ndk",
- "libc_stack_protector",
"libc_syscalls",
"libc_tzcode",
"libm",
@@ -1458,6 +1458,7 @@
whole_static_libs: [
"libc_bionic",
"libc_bionic_ndk",
+ "libc_bootstrap",
"libc_dns",
"libc_fortify",
"libc_freebsd",
@@ -1467,7 +1468,6 @@
"libc_openbsd",
"libc_openbsd_large_stack",
"libc_openbsd_ndk",
- "libc_stack_protector",
"libc_syscalls",
"libc_tzcode",
"libstdc++",
@@ -1495,11 +1495,11 @@
}
// ========================================================
-// libc_common_static.a For static binaries.
+// libc_static_dispatch.a
// ========================================================
cc_library_static {
defaults: ["libc_defaults"],
- name: "libc_common_static",
+ name: "libc_static_dispatch",
arch: {
x86: {
@@ -1512,18 +1512,14 @@
srcs: ["arch-arm64/static_function_dispatch.S"],
},
},
-
- whole_static_libs: [
- "libc_common",
- ],
}
// ========================================================
-// libc_common_shared.a For shared libraries.
+// libc_dynamic_dispatch.a
// ========================================================
cc_library_static {
defaults: ["libc_defaults"],
- name: "libc_common_shared",
+ name: "libc_dynamic_dispatch",
cflags: [
"-ffreestanding",
@@ -1541,9 +1537,31 @@
srcs: ["arch-arm64/dynamic_function_dispatch.cpp"],
},
},
+}
+
+// ========================================================
+// libc_common_static.a For static binaries.
+// ========================================================
+cc_library_static {
+ defaults: ["libc_defaults"],
+ name: "libc_common_static",
whole_static_libs: [
"libc_common",
+ "libc_static_dispatch",
+ ],
+}
+
+// ========================================================
+// libc_common_shared.a For shared libraries.
+// ========================================================
+cc_library_static {
+ defaults: ["libc_defaults"],
+ name: "libc_common_shared",
+
+ whole_static_libs: [
+ "libc_common",
+ "libc_dynamic_dispatch",
],
}
@@ -1567,19 +1585,16 @@
// libc_nomalloc.a
// ========================================================
//
-// This is a version of the static C library that does not
-// include malloc. It's useful in situations when the user wants
-// to provide their own malloc implementation, or wants to
-// explicitly disallow the use of malloc, such as in the
-// dynamic linker.
+// This is a version of the static C library used by the dynamic linker that exclude malloc. It also
+// excludes functions selected using ifunc's (e.g. for string.h). Link in either
+// libc_static_dispatch or libc_dynamic_dispatch to provide those functions.
cc_library_static {
name: "libc_nomalloc",
defaults: ["libc_defaults"],
- cflags: ["-DLIBC_STATIC"],
whole_static_libs: [
- "libc_common_static",
+ "libc_common",
"libc_init_static",
"libc_unwind_static",
],
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 6e1b0de..94cf1f8 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -57,7 +57,9 @@
//
// This is in a file by itself because it needs to be built with
// -fno-stack-protector because it's responsible for setting up the main
-// thread's TLS (which stack protector relies on).
+// thread's TLS (which stack protector relies on). It's also built with
+// -ffreestanding because the early init function runs in the linker before
+// ifunc resolvers have run.
// Do enough setup to:
// - Let the dynamic linker invoke system calls (and access errno)
@@ -65,7 +67,8 @@
// - Allow the stack protector to work (with a zero cookie)
// Avoid doing much more because, when this code is called within the dynamic
// linker, the linker binary hasn't been relocated yet, so certain kinds of code
-// are hazardous, such as accessing non-hidden global variables.
+// are hazardous, such as accessing non-hidden global variables or calling
+// string.h functions.
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
extern "C" void __libc_init_main_thread_early(const KernelArgumentBlock& args,
bionic_tcb* temp_tcb) {
@@ -80,6 +83,23 @@
main_thread.set_cached_pid(main_thread.tid);
}
+// This code is used both by each new pthread and the code that initializes the main thread.
+void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread) {
+#ifdef TLS_SLOT_SELF
+ // On x86, slot 0 must point to itself so code can read the thread pointer by
+ // loading %fs:0 or %gs:0.
+ tcb->tls_slot(TLS_SLOT_SELF) = &tcb->tls_slot(TLS_SLOT_SELF);
+#endif
+ tcb->tls_slot(TLS_SLOT_THREAD_ID) = thread;
+}
+
+void __init_tcb_dtv(bionic_tcb* tcb) {
+ // Initialize the DTV slot to a statically-allocated empty DTV. The first
+ // access to a dynamic TLS variable allocates a new DTV.
+ static const TlsDtv zero_dtv = {};
+ __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
+}
+
// Finish initializing the main thread.
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
extern "C" void __libc_init_main_thread_late() {
diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp
index 8522835..437de78 100644
--- a/libc/bionic/bionic_call_ifunc_resolver.cpp
+++ b/libc/bionic/bionic_call_ifunc_resolver.cpp
@@ -30,14 +30,32 @@
#include <sys/auxv.h>
#include <sys/ifunc.h>
+#include "private/bionic_auxv.h"
+
+// This code is called in the linker before it has been relocated, so minimize calls into other
+// parts of Bionic. In particular, we won't ever have two ifunc resolvers called concurrently, so
+// initializing the ifunc resolver argument doesn't need to be thread-safe.
+
ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) {
#if defined(__aarch64__)
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*);
- static __ifunc_arg_t arg = { sizeof(__ifunc_arg_t), getauxval(AT_HWCAP), getauxval(AT_HWCAP2) };
+ static __ifunc_arg_t arg;
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ arg._size = sizeof(__ifunc_arg_t);
+ arg._hwcap = getauxval(AT_HWCAP);
+ arg._hwcap2 = getauxval(AT_HWCAP2);
+ }
return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(arg._hwcap | _IFUNC_ARG_HWCAP, &arg);
#elif defined(__arm__)
typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long);
- static unsigned long hwcap = getauxval(AT_HWCAP);
+ static unsigned long hwcap;
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ hwcap = getauxval(AT_HWCAP);
+ }
return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap);
#else
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
diff --git a/libc/bionic/getauxval.cpp b/libc/bionic/getauxval.cpp
index c8f867b..f865f97 100644
--- a/libc/bionic/getauxval.cpp
+++ b/libc/bionic/getauxval.cpp
@@ -36,7 +36,6 @@
// This function needs to be safe to call before TLS is set up, so it can't
// access errno or the stack protector.
-__attribute__((no_stack_protector))
__LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool& exists) {
for (ElfW(auxv_t)* v = __libc_shared_globals()->auxv; v->a_type != AT_NULL; ++v) {
if (v->a_type == type) {
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index 7d04457..ef488ee 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -140,30 +140,32 @@
return -1;
}
- MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\"");
+ fflush(fp);
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
// Dump all of the large allocations in the arenas.
for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
struct mallinfo mi = je_mallinfo_arena_info(i);
if (mi.hblkhd != 0) {
- MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i);
+ MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
{
- MallocXmlElem(fp, "allocated-large").Contents("%zu", mi.ordblks);
- MallocXmlElem(fp, "allocated-huge").Contents("%zu", mi.uordblks);
- MallocXmlElem(fp, "allocated-bins").Contents("%zu", mi.fsmblks);
+ MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
+ MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
+ MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
size_t total = 0;
for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
struct mallinfo mi = je_mallinfo_bin_info(i, j);
if (mi.ordblks != 0) {
- MallocXmlElem bin_elem(fp, "bin", "nr=\"%d\"", j);
- MallocXmlElem(fp, "allocated").Contents("%zu", mi.ordblks);
- MallocXmlElem(fp, "nmalloc").Contents("%zu", mi.uordblks);
- MallocXmlElem(fp, "ndalloc").Contents("%zu", mi.fordblks);
+ MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
+ MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
+ MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
+ MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
total += mi.ordblks;
}
}
- MallocXmlElem(fp, "bins-total").Contents("%zu", total);
+ MallocXmlElem(fd, "bins-total").Contents("%zu", total);
}
}
}
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 0b74023..28c0b0c 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -231,6 +231,9 @@
g_target_sdk_version = target;
}
+// This function is called in the dynamic linker before ifunc resolvers have run, so this file is
+// compiled with -ffreestanding to avoid implicit string.h function calls. (It shouldn't strictly
+// be necessary, though.)
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals() {
static libc_shared_globals globals;
return &globals;
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 1dc1066..03af2d9 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -54,31 +54,12 @@
void __init_user_desc(struct user_desc*, bool, void*);
#endif
-// This code is used both by each new pthread and the code that initializes the main thread.
-__attribute__((no_stack_protector))
-void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread) {
-#ifdef TLS_SLOT_SELF
- // On x86, slot 0 must point to itself so code can read the thread pointer by
- // loading %fs:0 or %gs:0.
- tcb->tls_slot(TLS_SLOT_SELF) = &tcb->tls_slot(TLS_SLOT_SELF);
-#endif
- tcb->tls_slot(TLS_SLOT_THREAD_ID) = thread;
-}
-
__attribute__((no_stack_protector))
void __init_tcb_stack_guard(bionic_tcb* tcb) {
// GCC looks in the TLS for the stack guard on x86, so copy it there from our global.
tcb->tls_slot(TLS_SLOT_STACK_GUARD) = reinterpret_cast<void*>(__stack_chk_guard);
}
-__attribute__((no_stack_protector))
-void __init_tcb_dtv(bionic_tcb* tcb) {
- // Initialize the DTV slot to a statically-allocated empty DTV. The first
- // access to a dynamic TLS variable allocates a new DTV.
- static const TlsDtv zero_dtv = {};
- __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
-}
-
void __init_bionic_tls_ptrs(bionic_tcb* tcb, bionic_tls* tls) {
tcb->thread()->bionic_tls = tls;
tcb->tls_slot(TLS_SLOT_BIONIC_TLS) = tls;
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index 4e1aa61..d0c11d2 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -470,7 +470,7 @@
break;
}
- struct addrinfo* ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+ ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
if (ai == NULL) {
break;
}
diff --git a/libc/include/search.h b/libc/include/search.h
index 97fdeda..7a75404 100644
--- a/libc/include/search.h
+++ b/libc/include/search.h
@@ -6,30 +6,56 @@
* $FreeBSD: release/9.0.0/include/search.h 105250 2002-10-16 14:29:23Z robert $
*/
-#ifndef _SEARCH_H_
-#define _SEARCH_H_
+#pragma once
+
+/**
+ * @file search.h
+ * @brief Queues, hash tables, trees, and linear array searches.
+ */
#include <sys/cdefs.h>
#include <sys/types.h>
+/** See hsearch()/hsearch_r(). */
typedef enum {
FIND,
ENTER
} ACTION;
+/** See hsearch()/hsearch_r(). */
typedef struct entry {
+ /** The string key. */
char* key;
+ /** The associated data. */
void* data;
} ENTRY;
+/**
+ * Constants given to the twalk() visitor.
+ * Note that the constant names are misleading.
+ */
typedef enum {
+ /**
+ * If this is the first visit to a non-leaf node.
+ * Use this for *preorder* traversal.
+ */
preorder,
+ /**
+ * If this is the second visit to a non-leaf node.
+ * Use this for *inorder* traversal.
+ */
postorder,
+ /**
+ * If this is the third visit to a non-leaf node.
+ * Use this for *postorder* traversal.
+ */
endorder,
+ /** If this is the first and only visit to a leaf node. */
leaf
} VISIT;
#if defined(__USE_BSD) || defined(__USE_GNU)
+/** The hash table type for hcreate_r()/hdestroy_r()/hsearch_r(). */
struct hsearch_data {
struct __hsearch* __hsearch;
};
@@ -37,30 +63,157 @@
__BEGIN_DECLS
+/**
+ * [insque(3)](http://man7.org/linux/man-pages/man3/insque.3.html) inserts
+ * an item in a queue (an intrusive doubly-linked list).
+ *
+ * Available since API level 21.
+ */
void insque(void* __element, void* __previous) __INTRODUCED_IN(21);
+
+/**
+ * [remque(3)](http://man7.org/linux/man-pages/man3/remque.3.html) removes
+ * an item from a queue (an intrusive doubly-linked list).
+ *
+ * Available since API level 21.
+ */
void remque(void* __element) __INTRODUCED_IN(21);
-int hcreate(size_t) __INTRODUCED_IN(28);
+/**
+ * [hcreate(3)](http://man7.org/linux/man-pages/man3/hcreate.3.html)
+ * initializes the global hash table, with space for at least `__n` elements.
+ *
+ * See hcreate_r() if you need more than one hash table.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+int hcreate(size_t __n) __INTRODUCED_IN(28);
+
+/**
+ * [hdestroy(3)](http://man7.org/linux/man-pages/man3/hdestroy.3.html) destroys
+ * the global hash table.
+ *
+ * See hdestroy_r() if you need more than one hash table.
+ *
+ * Available since API level 28.
+ */
void hdestroy(void) __INTRODUCED_IN(28);
-ENTRY* hsearch(ENTRY, ACTION) __INTRODUCED_IN(28);
+
+/**
+ * [hsearch(3)](http://man7.org/linux/man-pages/man3/hsearch.3.html) finds or
+ * inserts `__entry` in the global hash table, based on `__action`.
+ *
+ * See hsearch_r() if you need more than one hash table.
+ *
+ * Returns a pointer to the entry on success, and returns NULL and sets
+ * `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+ENTRY* hsearch(ENTRY __entry, ACTION __action) __INTRODUCED_IN(28);
#if defined(__USE_BSD) || defined(__USE_GNU)
-int hcreate_r(size_t, struct hsearch_data*) __INTRODUCED_IN(28);
-void hdestroy_r(struct hsearch_data*) __INTRODUCED_IN(28);
-int hsearch_r(ENTRY, ACTION, ENTRY**, struct hsearch_data*) __INTRODUCED_IN(28);
+
+/**
+ * [hcreate_r(3)](http://man7.org/linux/man-pages/man3/hcreate_r.3.html)
+ * initializes a hash table `__table` with space for at least `__n` elements.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+int hcreate_r(size_t __n, struct hsearch_data* __table) __INTRODUCED_IN(28);
+
+/**
+ * [hdestroy_r(3)](http://man7.org/linux/man-pages/man3/hdestroy_r.3.html) destroys
+ * the hash table `__table`.
+ *
+ * Available since API level 28.
+ */
+void hdestroy_r(struct hsearch_data* __table) __INTRODUCED_IN(28);
+
+/**
+ * [hsearch_r(3)](http://man7.org/linux/man-pages/man3/hsearch_r.3.html) finds or
+ * inserts `__entry` in the hash table `__table`, based on `__action`.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ * A pointer to the entry is returned in `*__result`.
+ *
+ * Available since API level 28.
+ */
+int hsearch_r(ENTRY __entry, ACTION __action, ENTRY** __result, struct hsearch_data* __table) __INTRODUCED_IN(28);
+
#endif
-void* lfind(const void* __key, const void* __base, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*))
- __INTRODUCED_IN(21);
-void* lsearch(const void* __key, void* __base, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*))
- __INTRODUCED_IN(21);
+/**
+ * [lfind(3)](http://man7.org/linux/man-pages/man3/lfind.3.html) brute-force
+ * searches the unsorted array `__array` (of `__count` items each of size `__size`)
+ * for `__key`, using `__comparator`.
+ *
+ * See bsearch() if you have a sorted array.
+ *
+ * Returns a pointer to the matching element on success, or NULL on failure.
+ *
+ * Available since API level 21.
+ */
+void* lfind(const void* __key, const void* __array, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(21);
-void* tdelete(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void tdestroy(void* __root, void (*__free_fn)(void*)) __INTRODUCED_IN(16);
-void* tfind(const void* __key, void* const* __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void* tsearch(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void twalk(const void* __root, void (*__visitor)(const void*, VISIT, int)) __INTRODUCED_IN(21);
+/**
+ * [lsearch(3)](http://man7.org/linux/man-pages/man3/lsearch.3.html) brute-force
+ * searches the unsorted array `__array` (of `__count` items each of size `__size`)
+ * for `__key`, using `__comparator`.
+ *
+ * Unlike lfind(), on failure lsearch() will *insert* `__key` at the end of
+ * `__array` and increment `*__count`.
+ *
+ * Returns a pointer to the matching element on success, or to the newly-added
+ * element on failure.
+ *
+ * Available since API level 21.
+ */
+void* lsearch(const void* __key, void* __array, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(21);
+
+/**
+ * [tdelete(3)](http://man7.org/linux/man-pages/man3/tdelete.3.html) searches
+ * for and removes an element in the tree `*__root_ptr`. The search is performed
+ * using `__comparator`.
+ *
+ * Returns a pointer to the parent of the deleted node, or NULL on failure.
+ */
+void* tdelete(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [tdestroy(3)](http://man7.org/linux/man-pages/man3/tdestroy.3.html) destroys
+ * the hash table `__root` using `__free_fn` on each node.
+ */
+void tdestroy(void* __root, void (*__free_fn)(void*));
+
+/**
+ * [tfind(3)](http://man7.org/linux/man-pages/man3/tfind.3.html) searches
+ * for an element in the tree `*__root_ptr`. The search is performed using
+ * `__comparator`.
+ *
+ * Returns a pointer to the matching node, or NULL on failure.
+ */
+void* tfind(const void* __key, void* const* __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [tsearch(3)](http://man7.org/linux/man-pages/man3/tsearch.3.html) searches
+ * for an element in the tree `*__root_ptr`. The search is performed using
+ * `__comparator`.
+ *
+ * Unlike tfind(), on failure tsearch() will *insert* `__key` into the tree.
+ *
+ * Returns a pointer to the matching node, or to the newly-added node.
+ */
+void* tsearch(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [twalk(3)](http://man7.org/linux/man-pages/man3/twalk.3.html) calls
+ * `__visitor` on every node in the tree.
+ */
+void twalk(const void* __root, void (*__visitor)(const void*, VISIT, int));
__END_DECLS
-
-#endif
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index ec7e42d..b1e28b7 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -554,7 +554,7 @@
return pointers_.count(pointer) != 0;
}
-void PointerData::DumpLiveToFile(FILE* fp) {
+void PointerData::DumpLiveToFile(int fd) {
std::vector<ListInfoType> list;
std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
@@ -566,13 +566,13 @@
total_memory += info.size * info.num_allocations;
}
- fprintf(fp, "Total memory: %zu\n", total_memory);
- fprintf(fp, "Allocation records: %zd\n", list.size());
- fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
- fprintf(fp, "\n");
+ dprintf(fd, "Total memory: %zu\n", total_memory);
+ dprintf(fd, "Allocation records: %zd\n", list.size());
+ dprintf(fd, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
+ dprintf(fd, "\n");
for (const auto& info : list) {
- fprintf(fp, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
+ dprintf(fd, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
info.num_allocations);
FrameInfoType* frame_info = info.frame_info;
if (frame_info != nullptr) {
@@ -580,22 +580,22 @@
if (frame_info->frames[i] == 0) {
break;
}
- fprintf(fp, " %" PRIxPTR, frame_info->frames[i]);
+ dprintf(fd, " %" PRIxPTR, frame_info->frames[i]);
}
}
- fprintf(fp, "\n");
+ dprintf(fd, "\n");
if (info.backtrace_info != nullptr) {
- fprintf(fp, " bt_info");
+ dprintf(fd, " bt_info");
for (const auto& frame : *info.backtrace_info) {
- fprintf(fp, " {");
+ dprintf(fd, " {");
if (frame.map_info != nullptr && !frame.map_info->name.empty()) {
- fprintf(fp, "\"%s\"", frame.map_info->name.c_str());
+ dprintf(fd, "\"%s\"", frame.map_info->name.c_str());
} else {
- fprintf(fp, "\"\"");
+ dprintf(fd, "\"\"");
}
- fprintf(fp, " %" PRIx64, frame.rel_pc);
+ dprintf(fd, " %" PRIx64, frame.rel_pc);
if (frame.function_name.empty()) {
- fprintf(fp, " \"\" 0}");
+ dprintf(fd, " \"\" 0}");
} else {
char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr,
nullptr);
@@ -605,11 +605,11 @@
} else {
name = frame.function_name.c_str();
}
- fprintf(fp, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
+ dprintf(fd, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
free(demangled_name);
}
}
- fprintf(fp, "\n");
+ dprintf(fd, "\n");
}
}
}
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index c7958f3..78f0ed8 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -152,7 +152,7 @@
static void GetAllocList(std::vector<ListInfoType>* list);
static void LogLeaks();
- static void DumpLiveToFile(FILE* fp);
+ static void DumpLiveToFile(int fd);
static void GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
size_t* backtrace_size);
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index c030d54..3c0e630 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -785,16 +785,23 @@
if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
return g_dispatch->malloc_info(options, fp);
}
+
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
- MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
+ // Avoid any issues where allocations are made that will be freed
+ // in the fclose.
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "malloc", "version=\"debug-malloc-1\"");
std::vector<ListInfoType> list;
PointerData::GetAllocList(&list);
size_t alloc_num = 0;
for (size_t i = 0; i < list.size(); i++) {
- MallocXmlElem alloc(fp, "allocation", "nr=\"%zu\"", alloc_num);
+ MallocXmlElem alloc(fd, "allocation", "nr=\"%zu\"", alloc_num);
size_t total = 1;
size_t size = list[i].size;
@@ -802,8 +809,8 @@
i++;
total++;
}
- MallocXmlElem(fp, "size").Contents("%zu", list[i].size);
- MallocXmlElem(fp, "total").Contents("%zu", total);
+ MallocXmlElem(fd, "size").Contents("%zu", list[i].size);
+ MallocXmlElem(fd, "total").Contents("%zu", total);
alloc_num++;
}
return 0;
@@ -905,25 +912,28 @@
static std::mutex g_dump_lock;
-static void write_dump(FILE* fp) {
- fprintf(fp, "Android Native Heap Dump v1.2\n\n");
+static void write_dump(int fd) {
+ dprintf(fd, "Android Native Heap Dump v1.2\n\n");
std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
- fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
+ dprintf(fd, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
- PointerData::DumpLiveToFile(fp);
+ PointerData::DumpLiveToFile(fd);
- fprintf(fp, "MAPS\n");
+ dprintf(fd, "MAPS\n");
std::string content;
if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
- fprintf(fp, "Could not open /proc/self/maps\n");
+ dprintf(fd, "Could not open /proc/self/maps\n");
} else {
- fprintf(fp, "%s", content.c_str());
+ dprintf(fd, "%s", content.c_str());
}
- fprintf(fp, "END\n");
+ dprintf(fd, "END\n");
}
bool debug_write_malloc_leak_info(FILE* fp) {
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
@@ -933,7 +943,8 @@
return false;
}
- write_dump(fp);
+ write_dump(fileno(fp));
+
return true;
}
@@ -943,13 +954,13 @@
std::lock_guard<std::mutex> guard(g_dump_lock);
- FILE* fp = fopen(file_name, "w+e");
- if (fp == nullptr) {
+ int fd = open(file_name, O_RDWR | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+ if (fd == -1) {
error_log("Unable to create file: %s", file_name);
return;
}
error_log("Dumping to file: %s\n", file_name);
- write_dump(fp);
- fclose(fp);
+ write_dump(fd);
+ close(fd);
}
diff --git a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
index 0716758..67bb8d9 100644
--- a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
@@ -37,6 +37,7 @@
#include <time.h>
#include <unistd.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -174,6 +175,7 @@
}
static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
+ std::vector<const char*> no_match_strings = std::vector<const char*>{},
time_t timeout_seconds = kTimeoutSeconds) {
std::string log_str;
time_t start = time(nullptr);
@@ -181,12 +183,18 @@
while (true) {
GetLogStr(pid, &log_str);
found_all = true;
+ // Look for the expected strings.
for (auto str : match_strings) {
if (log_str.find(str) == std::string::npos) {
found_all = false;
break;
}
}
+
+ // Verify the unexpected strings are not present.
+ for (auto str : no_match_strings) {
+ ASSERT_TRUE(log_str.find(str) == std::string::npos) << "Unexpectedly found '" << str << "' in log output:\n" << log_str;
+ }
if (found_all) {
return;
}
@@ -194,7 +202,7 @@
break;
}
}
- ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" + log_str;
+ ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" << log_str;
}
TEST(MallocTests, DISABLED_smoke) {}
@@ -464,3 +472,45 @@
<< "Found crash in log.\nLog message: " << log_str;
}
}
+
+TEST(MallocTests, DISABLED_write_leak_info) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ FILE* fp = fdopen(tf.fd, "w+");
+ if (fp == nullptr) {
+ printf("Unable to create %s\n", tf.path);
+ _exit(1);
+ }
+ tf.release();
+
+ void* ptr = malloc(1000);
+ if (ptr == nullptr) {
+ printf("malloc failed\n");
+ _exit(1);
+ }
+ memset(ptr, 0, 1000);
+
+ android_mallopt(M_WRITE_MALLOC_LEAK_INFO_TO_FILE, fp, sizeof(fp));
+
+ fclose(fp);
+
+ free(ptr);
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_no_header) {
+ pid_t pid;
+ ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace", &pid, 0));
+
+ ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+
+ std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_header) {
+ pid_t pid;
+ ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace guard", &pid, 0));
+
+ ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+ std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 0238d10..70457b9 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -27,6 +27,8 @@
#include <algorithm>
#include <memory>
+#include <string>
+#include <string_view>
#include <thread>
#include <vector>
#include <utility>
@@ -73,6 +75,9 @@
void* debug_valloc(size_t);
#endif
+bool debug_write_malloc_leak_info(FILE*);
+void debug_dump_heap(const char*);
+
__END_DECLS
constexpr char DIVIDER[] =
@@ -1302,6 +1307,10 @@
}
static std::string SanitizeHeapData(const std::string& data) {
+ if (data.empty()) {
+ return data;
+ }
+
// Remove the map data since it's not consistent.
std::string sanitized;
bool skip_map_data = false;
@@ -1329,7 +1338,7 @@
sanitized += line + '\n';
}
}
- return sanitized;
+ return android::base::Trim(sanitized);
}
void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
@@ -1402,9 +1411,7 @@
z 1 sz 40 num 1 bt 300 400
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1463,9 +1470,7 @@
z 0 sz 300 num 1 bt 100 200
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1513,9 +1518,7 @@
z 0 sz 300 num 2 bt 100 200
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1575,9 +1578,7 @@
bt_info {"" 100 "fake1" a} {"" 200 "fake2" 14}
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -2472,15 +2473,19 @@
TEST_F(MallocDebugTest, malloc_info_no_pointer_tracking) {
Init("fill");
- char* buffer;
- size_t size;
- FILE* memstream = open_memstream(&buffer, &size);
- ASSERT_TRUE(memstream != nullptr);
- ASSERT_EQ(0, debug_malloc_info(0, memstream));
- ASSERT_EQ(0, fclose(memstream));
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_TRUE(root != nullptr);
ASSERT_STREQ("malloc", root->Name());
@@ -2501,17 +2506,21 @@
std::unique_ptr<void, decltype(debug_free)*> ptr4(debug_malloc(1200), debug_free);
ASSERT_TRUE(ptr4.get() != nullptr);
- char* buffer;
- size_t size;
- FILE* memstream = open_memstream(&buffer, &size);
- ASSERT_TRUE(memstream != nullptr);
- ASSERT_EQ(0, debug_malloc_info(0, memstream));
- ASSERT_EQ(0, fclose(memstream));
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
- SCOPED_TRACE(testing::Message() << "Output:\n" << buffer);
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+
+ SCOPED_TRACE(testing::Message() << "Output:\n" << contents);
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_TRUE(root != nullptr);
ASSERT_STREQ("malloc", root->Name());
@@ -2548,3 +2557,134 @@
ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
ASSERT_EQ(1, val);
}
+
+static void AllocPtrsWithBacktrace(std::vector<void*>* ptrs) {
+ backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc});
+ void* ptr = debug_malloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 1024);
+ ptrs->push_back(ptr);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002});
+ ptr = debug_malloc(500);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 500);
+ ptrs->push_back(ptr);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0x104});
+ ptr = debug_malloc(100);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 100);
+ ptrs->push_back(ptr);
+}
+
+static constexpr std::string_view kDumpInfo = R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
+
+Total memory: 1624
+Allocation records: 3
+Backtrace size: 16
+
+z 0 sz 1024 num 1 bt f e d c
+z 0 sz 500 num 1 bt bc000 bc001 bc002
+z 0 sz 100 num 1 bt 104
+MAPS
+MAP_DATA
+END)";
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ FILE* fp = fopen(tf.path, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+
+ fclose(fp);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected(kDumpInfo);
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info_extra_data) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ FILE* fp = fopen(tf.path, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ fprintf(fp, "This message should appear before the output.\n");
+ ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+ fprintf(fp, "This message should appear after the output.\n");
+
+ fclose(fp);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected = "This message should appear before the output.\n"
+ + std::string(kDumpInfo)
+ + "\nThis message should appear after the output.";
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, dump_heap) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ debug_dump_heap(tf.path);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected(kDumpInfo);
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string expected_log = std::string("6 malloc_debug Dumping to file: ") + tf.path + "\n\n";
+ ASSERT_EQ(expected_log, getFakeLogPrint());
+}
diff --git a/libc/private/MallocXmlElem.h b/libc/private/MallocXmlElem.h
index 04d3eee..a367972 100644
--- a/libc/private/MallocXmlElem.h
+++ b/libc/private/MallocXmlElem.h
@@ -18,38 +18,39 @@
#include <stdarg.h>
#include <stdio.h>
+#include <unistd.h>
#include <private/bionic_macros.h>
class MallocXmlElem {
public:
// Name must be valid throughout lifetime of the object.
- explicit MallocXmlElem(FILE* fp, const char* name,
- const char* attr_fmt = nullptr, ...) : fp_(fp), name_(name) {
- fprintf(fp, "<%s", name_);
+ explicit MallocXmlElem(int fd, const char* name,
+ const char* attr_fmt = nullptr, ...) : fd_(fd), name_(name) {
+ dprintf(fd_, "<%s", name_);
if (attr_fmt != nullptr) {
va_list args;
va_start(args, attr_fmt);
- fputc(' ', fp_);
- vfprintf(fp_, attr_fmt, args);
+ write(fd_, " ", 1);
+ vdprintf(fd_, attr_fmt, args);
va_end(args);
}
- fputc('>', fp_);
+ write(fd_, ">", 1);
}
~MallocXmlElem() noexcept {
- fprintf(fp_, "</%s>", name_);
+ dprintf(fd_, "</%s>", name_);
}
void Contents(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
- vfprintf(fp_, fmt, args);
+ vdprintf(fd_, fmt, args);
va_end(args);
}
private:
- FILE* fp_;
+ int fd_;
const char* name_;
BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(MallocXmlElem);
diff --git a/libc/private/ScopedFd.h b/libc/private/ScopedFd.h
index 9b1cd9d..1cec916 100644
--- a/libc/private/ScopedFd.h
+++ b/libc/private/ScopedFd.h
@@ -46,7 +46,7 @@
}
void reset(int fd = -1) {
- if (fd != -1) {
+ if (fd_ != -1) {
ErrnoRestorer e;
close(fd_);
}
diff --git a/linker/Android.bp b/linker/Android.bp
index bb9d26d..1800bdb 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -1,22 +1,6 @@
-cc_library_static {
- name: "liblinker_malloc",
- defaults: ["linux_bionic_supported"],
- recovery_available: true,
- native_bridge_supported: true,
-
- srcs: [
- "linker_memory.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- // We need to access Bionic private headers in the linker.
- include_dirs: ["bionic/libc"],
-
- static_libs: ["libasync_safe", "libbase"],
-}
+// ========================================================
+// linker_wrapper - Linux Bionic (on the host)
+// ========================================================
// This is used for bionic on (host) Linux to bootstrap our linker embedded into
// a binary.
@@ -66,6 +50,103 @@
include_dirs: ["bionic/libc"],
}
+// ========================================================
+// linker default configuration
+// ========================================================
+
+// Configuration for the linker binary and any of its static libraries.
+cc_defaults {
+ name: "linker_defaults",
+ arch: {
+ arm: {
+ cflags: ["-D__work_around_b_24465209__"],
+ },
+ x86: {
+ cflags: ["-D__work_around_b_24465209__"],
+ },
+ },
+
+ cflags: [
+ "-fno-stack-protector",
+ "-Wstrict-overflow=5",
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Wextra",
+ "-Wunused",
+ "-Werror",
+ ],
+
+ // TODO: split out the asflags.
+ asflags: [
+ "-fno-stack-protector",
+ "-Wstrict-overflow=5",
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Wextra",
+ "-Wunused",
+ "-Werror",
+ ],
+
+ product_variables: {
+ debuggable: {
+ cppflags: ["-DUSE_LD_CONFIG_FILE"],
+ },
+ },
+
+ cppflags: ["-Wold-style-cast"],
+
+ static_libs: [
+ "libziparchive",
+ "libbase",
+ "libz",
+
+ "libasync_safe",
+
+ "liblog",
+ ],
+
+ // We need to access Bionic private headers in the linker.
+ include_dirs: ["bionic/libc"],
+}
+
+// ========================================================
+// linker components
+// ========================================================
+
+// Enable a module on all targets the linker runs on (ordinary Android targets, Linux Bionic, and
+// native bridge implementations).
+cc_defaults {
+ name: "linker_all_targets",
+ defaults: ["linux_bionic_supported"],
+ recovery_available: true,
+ native_bridge_supported: true,
+}
+
+cc_library_static {
+ name: "liblinker_main",
+ defaults: ["linker_defaults", "linker_all_targets"],
+ srcs: ["linker_main.cpp"],
+
+ // Ensure that the compiler won't insert string function calls before ifuncs are resolved.
+ cflags: ["-ffreestanding"],
+}
+
+cc_library_static {
+ name: "liblinker_malloc",
+ defaults: ["linker_defaults", "linker_all_targets"],
+ srcs: ["linker_memory.cpp"],
+}
+
+cc_library_static {
+ name: "liblinker_debuggerd_stub",
+ defaults: ["linker_defaults", "linker_all_targets"],
+ srcs: ["linker_debuggerd_stub.cpp"],
+}
+
+// ========================================================
+// template for the linker binary
+// ========================================================
+
filegroup {
name: "linker_sources",
srcs: [
@@ -79,7 +160,6 @@
"linker_globals.cpp",
"linker_libc_support.c",
"linker_libcxx_support.cpp",
- "linker_main.cpp",
"linker_namespaces.cpp",
"linker_logger.cpp",
"linker_mapped_file_fragment.cpp",
@@ -137,30 +217,50 @@
],
}
-filegroup {
- name: "linker_version_script",
- srcs: ["linker.generic.map"],
-}
-
-filegroup {
- name: "linker_version_script_arm",
- srcs: ["linker.arm.map"],
-}
-
cc_defaults {
- name: "linker_defaults",
+ name: "linker_version_script_overlay",
+ arch: {
+ arm: { version_script: "linker.arm.map" },
+ arm64: { version_script: "linker.generic.map" },
+ x86: { version_script: "linker.generic.map" },
+ x86_64: { version_script: "linker.generic.map" },
+ mips: { version_script: "linker.generic.map" },
+ mips64: { version_script: "linker.generic.map" },
+ },
+}
+
+// A template for the linker binary. May be inherited by native bridge implementations.
+cc_defaults {
+ name: "linker_bin_template",
+ defaults: ["linker_defaults"],
+
+ srcs: [":linker_sources"],
+
arch: {
arm: {
- cflags: ["-D__work_around_b_24465209__"],
+ srcs: [":linker_sources_arm"],
+ static_libs: ["libunwind_llvm"],
+ },
+ arm64: {
+ srcs: [":linker_sources_arm64"],
},
x86: {
- cflags: ["-D__work_around_b_24465209__"],
+ srcs: [":linker_sources_x86"],
+ },
+ x86_64: {
+ srcs: [":linker_sources_x86_64"],
+ },
+ mips: {
+ srcs: [":linker_sources_mips"],
+ },
+ mips64: {
+ srcs: [":linker_sources_mips64"],
},
},
- // -shared is used to overwrite the -Bstatic and -static
- // flags triggered by LOCAL_FORCE_STATIC_EXECUTABLE.
- // This dynamic linker is actually a shared object linked with static libraries.
+ // -shared is used to overwrite the -Bstatic and -static flags triggered by enabling
+ // static_executable. This dynamic linker is actually a shared object linked with static
+ // libraries.
ldflags: [
"-shared",
"-Wl,-Bsymbolic",
@@ -168,35 +268,6 @@
"-Wl,-soname,ld-android.so",
],
- cflags: [
- "-fno-stack-protector",
- "-Wstrict-overflow=5",
- "-fvisibility=hidden",
- "-Wall",
- "-Wextra",
- "-Wunused",
- "-Werror",
- ],
-
- // TODO: split out the asflags.
- asflags: [
- "-fno-stack-protector",
- "-Wstrict-overflow=5",
- "-fvisibility=hidden",
- "-Wall",
- "-Wextra",
- "-Wunused",
- "-Werror",
- ],
-
- product_variables: {
- debuggable: {
- cppflags: ["-DUSE_LD_CONFIG_FILE"],
- },
- },
-
- cppflags: ["-Wold-style-cast"],
-
// we are going to link libc++_static manually because
// when stl is not set to "none" build system adds libdl
// to the list of static libraries which needs to be
@@ -222,58 +293,15 @@
sanitize: {
hwaddress: false,
},
-}
-
-cc_binary {
- defaults: ["linux_bionic_supported", "linker_defaults"],
- srcs: [ ":linker_sources" ],
-
- arch: {
- arm: {
- srcs: [ ":linker_sources_arm" ],
- version_script: ":linker_version_script_arm",
- static_libs: ["libunwind_llvm"],
- },
- arm64: {
- srcs: [":linker_sources_arm64"],
- version_script: ":linker_version_script",
- },
- x86: {
- srcs: [":linker_sources_x86"],
- version_script: ":linker_version_script",
- },
- x86_64: {
- srcs: [":linker_sources_x86_64"],
- version_script: ":linker_version_script",
- },
- mips: {
- srcs: [":linker_sources_mips"],
- version_script: ":linker_version_script",
- },
- mips64: {
- srcs: [":linker_sources_mips64"],
- version_script: ":linker_version_script",
- },
- },
-
- // We need to access Bionic private headers in the linker.
- include_dirs: ["bionic/libc"],
static_libs: [
- "libc_nomalloc",
- "libm",
- "libziparchive",
- "libbase",
- "libz",
-
- "libasync_safe",
-
- "liblog",
- "libc++_static",
-
- // Important: The liblinker_malloc should be the last library in the list
- // to overwrite any other malloc implementations by other static libraries.
+ "liblinker_main",
"liblinker_malloc",
+
+ "libc++_static",
+ "libc_nomalloc",
+ "libc_dynamic_dispatch",
+ "libm",
],
// Ensure that if the linker needs __gnu_Unwind_Find_exidx, then the linker will have a
@@ -287,9 +315,25 @@
// linker stops linking against libgcc.a's arm32 unwinder.
whole_static_libs: ["libc_unwind_static"],
+ system_shared_libs: [],
+
+ // Opt out of native_coverage when opting out of system_shared_libs
+ native_coverage: false,
+}
+
+// ========================================================
+// linker[_asan][64] binary
+// ========================================================
+
+cc_binary {
name: "linker",
+ defaults: [
+ "linker_bin_template",
+ "linux_bionic_supported",
+ "linker_version_script_overlay",
+ ],
+
symlinks: ["linker_asan"],
- recovery_available: true,
multilib: {
lib32: {
cflags: ["-DLIB_PATH=\"lib\""],
@@ -299,28 +343,38 @@
suffix: "64",
},
},
- system_shared_libs: [],
- // Opt out of native_coverage when opting out of system_shared_libs
- native_coverage: false,
+ compile_multilib: "both",
+ xom: false,
+
+ recovery_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.runtime",
+ ],
target: {
android: {
+ srcs: [
+ "linker_debuggerd_android.cpp",
+ ],
static_libs: [
"libc++demangle",
"libdebuggerd_handler_fallback",
],
},
+ linux_bionic: {
+ static_libs: [
+ "liblinker_debuggerd_stub",
+ ],
+ }
},
- compile_multilib: "both",
- xom: false,
-
- apex_available: [
- "//apex_available:platform",
- "com.android.runtime",
- ],
}
+// ========================================================
+// assorted modules
+// ========================================================
+
sh_binary {
name: "ldd",
src: "ldd",
@@ -347,25 +401,11 @@
// for x86, exclude libgcc_eh.a for the same reasons as above
arch: {
- arm: {
- version_script: "linker.arm.map",
- },
- arm64: {
- version_script: "linker.generic.map",
- },
x86: {
ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
- version_script: "linker.generic.map",
},
x86_64: {
ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
- version_script: "linker.generic.map",
- },
- mips: {
- version_script: "linker.generic.map",
- },
- mips64: {
- version_script: "linker.generic.map",
},
},
@@ -379,7 +419,7 @@
stl: "none",
name: "ld-android",
- defaults: ["linux_bionic_supported"],
+ defaults: ["linux_bionic_supported", "linker_version_script_overlay"],
recovery_available: true,
native_bridge_supported: true,
diff --git a/linker/linker.cpp b/linker/linker.cpp
index eedce70..1393eb5 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -3162,7 +3162,10 @@
TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n",
reinterpret_cast<void*>(reloc),
reinterpret_cast<void*>(load_bias + addend));
- {
+ // In the linker, ifuncs are called as soon as possible so that string functions work.
+ // We must not call them again. (e.g. On arm32, resolving an ifunc changes the meaning of
+ // the addend from a resolver function to the implementation.)
+ if (!is_linker()) {
#if !defined(__LP64__)
// When relocating dso with text_relocation .text segment is
// not executable. We need to restore elf flags for this
diff --git a/linker/linker_debuggerd.h b/linker/linker_debuggerd.h
new file mode 100644
index 0000000..d701879
--- /dev/null
+++ b/linker/linker_debuggerd.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+void linker_debuggerd_init();
diff --git a/linker/linker_debuggerd_android.cpp b/linker/linker_debuggerd_android.cpp
new file mode 100644
index 0000000..b8c82f9
--- /dev/null
+++ b/linker/linker_debuggerd_android.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "linker_debuggerd.h"
+
+#include "debuggerd/handler.h"
+#include "private/bionic_globals.h"
+
+#include "linker_gdb_support.h"
+
+void linker_debuggerd_init() {
+ debuggerd_callbacks_t callbacks = {
+ .get_abort_message = []() {
+ return __libc_shared_globals()->abort_msg;
+ },
+ .post_dump = ¬ify_gdb_of_libraries,
+ };
+ debuggerd_init(&callbacks);
+}
diff --git a/linker/linker_debuggerd_stub.cpp b/linker/linker_debuggerd_stub.cpp
new file mode 100644
index 0000000..631e6e4
--- /dev/null
+++ b/linker/linker_debuggerd_stub.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "linker_debuggerd.h"
+
+void linker_debuggerd_init() {
+}
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index bea2e3c..8ba947f 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -32,6 +32,7 @@
#include <sys/auxv.h>
#include "linker_debug.h"
+#include "linker_debuggerd.h"
#include "linker_cfi.h"
#include "linker_gdb_support.h"
#include "linker_globals.h"
@@ -39,6 +40,8 @@
#include "linker_tls.h"
#include "linker_utils.h"
+#include "private/bionic_auxv.h"
+#include "private/bionic_call_ifunc_resolver.h"
#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
@@ -46,9 +49,6 @@
#include "android-base/unique_fd.h"
#include "android-base/strings.h"
#include "android-base/stringprintf.h"
-#ifdef __ANDROID__
-#include "debuggerd/handler.h"
-#endif
#include <async_safe/log.h>
#include <bionic/libc_init_common.h>
@@ -311,15 +311,7 @@
__system_properties_init(); // may use 'environ'
// Register the debuggerd signal handler.
-#ifdef __ANDROID__
- debuggerd_callbacks_t callbacks = {
- .get_abort_message = []() {
- return __libc_shared_globals()->abort_msg;
- },
- .post_dump = ¬ify_gdb_of_libraries,
- };
- debuggerd_init(&callbacks);
-#endif
+ linker_debuggerd_init();
g_linker_logger.ResetState();
@@ -575,6 +567,39 @@
}
}
+// TODO: There is a similar ifunc resolver calling loop in libc_init_static.cpp, but that version
+// uses weak symbols, which don't work in the linker prior to its relocation. This version also
+// supports a load bias. When we stop supporting the gold linker in the NDK, then maybe we can use
+// non-weak definitions and merge the two loops.
+#if defined(USE_RELA)
+extern __LIBC_HIDDEN__ ElfW(Rela) __rela_iplt_start[], __rela_iplt_end[];
+
+static void call_ifunc_resolvers(ElfW(Addr) load_bias) {
+ for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
+ ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset + load_bias);
+ ElfW(Addr) resolver = r->r_addend + load_bias;
+ *offset = __bionic_call_ifunc_resolver(resolver);
+ }
+}
+#else
+extern __LIBC_HIDDEN__ ElfW(Rel) __rel_iplt_start[], __rel_iplt_end[];
+
+static void call_ifunc_resolvers(ElfW(Addr) load_bias) {
+ for (ElfW(Rel) *r = __rel_iplt_start; r != __rel_iplt_end; ++r) {
+ ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset + load_bias);
+ ElfW(Addr) resolver = *offset + load_bias;
+ *offset = __bionic_call_ifunc_resolver(resolver);
+ }
+}
+#endif
+
+// Usable before ifunc resolvers have been called. This function is compiled with -ffreestanding.
+static void linker_memclr(void* dst, size_t cnt) {
+ for (size_t i = 0; i < cnt; ++i) {
+ reinterpret_cast<char*>(dst)[i] = '\0';
+ }
+}
+
// Detect an attempt to run the linker on itself. e.g.:
// /system/bin/linker64 /system/bin/linker64
// Use priority-1 to run this constructor before other constructors.
@@ -608,7 +633,8 @@
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
// Initialize TLS early so system calls and errno work.
KernelArgumentBlock args(raw_args);
- bionic_tcb temp_tcb = {};
+ bionic_tcb temp_tcb __attribute__((uninitialized));
+ linker_memclr(&temp_tcb, sizeof(temp_tcb));
__libc_init_main_thread_early(args, &temp_tcb);
// When the linker is run by itself (rather than as an interpreter for
@@ -626,11 +652,15 @@
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
+ // string.h functions must not be used prior to calling the linker's ifunc resolvers.
+ const ElfW(Addr) load_bias = get_elf_exec_load_bias(elf_hdr);
+ call_ifunc_resolvers(load_bias);
+
soinfo tmp_linker_so(nullptr, nullptr, nullptr, 0, 0);
tmp_linker_so.base = linker_addr;
tmp_linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
- tmp_linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
+ tmp_linker_so.load_bias = load_bias;
tmp_linker_so.dynamic = nullptr;
tmp_linker_so.phdr = phdr;
tmp_linker_so.phnum = elf_hdr->e_phnum;
diff --git a/tests/leak_test.cpp b/tests/leak_test.cpp
index 3cc1a0a..e0a3d57 100644
--- a/tests/leak_test.cpp
+++ b/tests/leak_test.cpp
@@ -127,22 +127,20 @@
// http://b/36045112
TEST(pthread_leak, detach) {
LeakChecker lc;
- constexpr int kThreadCount = 100;
- // Devices with low power cores/low number of cores can not finish test in time hence decreasing
- // threads count to 90.
- // http://b/129924384.
- int threads_count = (sysconf(_SC_NPROCESSORS_CONF) > 2) ? kThreadCount : (kThreadCount - 10);
+ // Ancient devices with only 2 cores need a lower limit.
+ // http://b/129924384 and https://issuetracker.google.com/142210680.
+ const int thread_count = (sysconf(_SC_NPROCESSORS_CONF) > 2) ? 100 : 50;
for (size_t pass = 0; pass < 1; ++pass) {
- struct thread_data { pthread_barrier_t* barrier; pid_t* tid; } threads[kThreadCount] = {};
+ struct thread_data { pthread_barrier_t* barrier; pid_t* tid; } threads[thread_count];
pthread_barrier_t barrier;
- ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, threads_count + 1), 0);
+ ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, thread_count + 1), 0);
// Start child threads.
- pid_t tids[kThreadCount];
- for (int i = 0; i < threads_count; ++i) {
+ pid_t tids[thread_count];
+ for (int i = 0; i < thread_count; ++i) {
threads[i] = {&barrier, &tids[i]};
const auto thread_function = +[](void* ptr) -> void* {
thread_data* data = static_cast<thread_data*>(ptr);
@@ -158,7 +156,7 @@
pthread_barrier_wait(&barrier);
ASSERT_EQ(pthread_barrier_destroy(&barrier), 0);
- WaitUntilAllThreadsExited(tids, threads_count);
+ WaitUntilAllThreadsExited(tids, thread_count);
// A native bridge implementation might need a warm up pass to reach a steady state.
// http://b/37920774.
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 0407553..ebbd247 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -358,15 +358,20 @@
TEST(malloc, malloc_info) {
#ifdef __BIONIC__
SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
- char* buf;
- size_t bufsize;
- FILE* memstream = open_memstream(&buf, &bufsize);
- ASSERT_NE(nullptr, memstream);
- ASSERT_EQ(0, malloc_info(0, memstream));
- ASSERT_EQ(0, fclose(memstream));
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_NE(nullptr, root);
@@ -416,17 +421,21 @@
#ifdef __BIONIC__
SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
- char* buf;
- size_t bufsize;
- FILE* memstream = open_memstream(&buf, &bufsize);
- ASSERT_NE(nullptr, memstream);
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
size_t mallinfo_before_allocated_bytes = mallinfo().uordblks;
- ASSERT_EQ(0, malloc_info(0, memstream));
+ ASSERT_EQ(0, malloc_info(0, fp));
size_t mallinfo_after_allocated_bytes = mallinfo().uordblks;
- ASSERT_EQ(0, fclose(memstream));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
size_t total_allocated_bytes = 0;
auto root = doc.FirstChildElement();
diff --git a/tests/system_properties_test2.cpp b/tests/system_properties_test2.cpp
index b9936dd..0953bde 100644
--- a/tests/system_properties_test2.cpp
+++ b/tests/system_properties_test2.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -24,6 +22,10 @@
#include <sstream>
#include <string>
+#include <gtest/gtest.h>
+
+#include "utils.h"
+
#if defined(__BIONIC__)
#include <sys/system_properties.h>
int64_t NanoTime() {
@@ -128,6 +130,29 @@
#endif // __BIONIC__
}
+TEST(properties, no_fd_leaks) {
+#if defined(__BIONIC__)
+ FdLeakChecker leak_checker;
+ std::stringstream ss;
+ ss << "debug.test." << getpid() << "." << NanoTime() << ".";
+ const std::string property_prefix = ss.str();
+ const std::string property_name = property_prefix + "property1";
+
+ for (size_t i = 0; i < 100; ++i) {
+ char propvalue[PROP_VALUE_MAX];
+ ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value1"));
+ ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
+ ASSERT_STREQ("value1", propvalue);
+
+ ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value2"));
+ ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
+ ASSERT_STREQ("value2", propvalue);
+ }
+#else // __BIONIC__
+ GTEST_SKIP() << "bionic-only test";
+#endif // __BIONIC__
+}
+
TEST(properties, empty_value) {
#if defined(__BIONIC__)
char propvalue[PROP_VALUE_MAX];
@@ -136,13 +161,13 @@
ss << "debug.test." << getpid() << "." << NanoTime() << "." << "property_empty";
const std::string property_name = ss.str();
- for (size_t i=0; i<1000; ++i) {
+ for (size_t i = 0; i < 1000; ++i) {
ASSERT_EQ(0, __system_property_set(property_name.c_str(), ""));
ASSERT_EQ(0, __system_property_get(property_name.c_str(), propvalue));
ASSERT_STREQ("", propvalue);
}
-#else // __BIONIC__
- GTEST_SKIP() << "bionic-only test";
+#else // __BIONIC__
+ GTEST_SKIP() << "bionic-only test";
#endif // __BIONIC__
}
diff --git a/tests/utils.h b/tests/utils.h
index 1f4cece..cfc68c9 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -16,6 +16,7 @@
#pragma once
+#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -253,3 +254,29 @@
std::string output_;
};
#endif
+
+class FdLeakChecker {
+ public:
+ FdLeakChecker() {
+ }
+
+ ~FdLeakChecker() {
+ size_t end_count = CountOpenFds();
+ EXPECT_EQ(start_count_, end_count);
+ }
+
+ private:
+ static size_t CountOpenFds() {
+ auto fd_dir = std::unique_ptr<DIR, decltype(&closedir)>{ opendir("/proc/self/fd"), closedir };
+ size_t count = 0;
+ dirent* de = nullptr;
+ while ((de = readdir(fd_dir.get())) != nullptr) {
+ if (de->d_type == DT_LNK) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ size_t start_count_ = CountOpenFds();
+};
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
index d2c50a9..bad63ad 100644
--- a/tools/versioner/src/Driver.cpp
+++ b/tools/versioner/src/Driver.cpp
@@ -126,14 +126,7 @@
cmd.push_back("-DANDROID");
cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
- // FIXME: Re-enable FORTIFY properly once our clang in external/ is new enough
- // to support diagnose_if without giving us syntax errors.
-#if 0
cmd.push_back("-D_FORTIFY_SOURCE=2");
-#else
- cmd.push_back("-D_FORTIFY_SOURCE=0");
- cmd.push_back("-D__BIONIC_DECLARE_FORTIFY_HELPERS");
-#endif
cmd.push_back("-D_GNU_SOURCE");
cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));