summaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
Diffstat (limited to 'libc')
-rw-r--r--libc/Android.bp13
-rw-r--r--libc/arch-arm/bionic/bpabi.c43
-rw-r--r--libc/arch-arm/dynamic_function_dispatch.cpp12
-rw-r--r--libc/bionic/grp_pwd.cpp82
-rw-r--r--libc/bionic/libc_init_common.cpp2
-rw-r--r--libc/bionic/malloc_common.cpp9
-rw-r--r--libc/bionic/malloc_common_dynamic.cpp59
-rw-r--r--libc/bionic/pthread_getschedparam.cpp2
-rw-r--r--libc/bionic/pthread_setschedparam.cpp3
-rw-r--r--libc/bionic/threads.cpp32
-rw-r--r--libc/include/android/api-level.h3
-rw-r--r--libc/include/android/legacy_threads_inlines.h (renamed from libc/arch-arm/dynamic_function_wrapper.S)17
-rw-r--r--libc/include/bits/threads_inlines.h207
-rw-r--r--libc/include/threads.h228
-rw-r--r--libc/libc.map.txt36
-rw-r--r--libc/malloc_hooks/tests/malloc_hooks_tests.cpp23
-rw-r--r--libc/private/bionic_malloc.h36
-rw-r--r--libc/stdlib/atexit.c5
18 files changed, 680 insertions, 132 deletions
diff --git a/libc/Android.bp b/libc/Android.bp
index d8a1d70d5..174783fa6 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -762,12 +762,13 @@ cc_library_static {
"arch-arm/generic/bionic/strlen.c",
"arch-arm/bionic/__aeabi_read_tp.S",
- "arch-arm/bionic/atomics_arm.c",
"arch-arm/bionic/__bionic_clone.S",
+ "arch-arm/bionic/__restore.S",
"arch-arm/bionic/_exit_with_stack_teardown.S",
+ "arch-arm/bionic/atomics_arm.c",
+ "arch-arm/bionic/bpabi.c",
"arch-arm/bionic/libcrt_compat.c",
"arch-arm/bionic/popcount_tab.c",
- "arch-arm/bionic/__restore.S",
"arch-arm/bionic/setjmp.S",
"arch-arm/bionic/syscall.S",
"arch-arm/bionic/vfork.S",
@@ -1153,6 +1154,7 @@ cc_library_static {
"bionic/tdestroy.cpp",
"bionic/termios.cpp",
"bionic/thread_private.cpp",
+ "bionic/threads.cpp",
"bionic/timespec_get.cpp",
"bionic/tmpfile.cpp",
"bionic/umount.cpp",
@@ -1461,12 +1463,7 @@ cc_library_static {
srcs: ["arch-x86/dynamic_function_dispatch.cpp"],
},
arm: {
- srcs: [
- "arch-arm/dynamic_function_dispatch.cpp",
-
- // Workaround for b/120254692.
- "arch-arm/dynamic_function_wrapper.S",
- ],
+ srcs: ["arch-arm/dynamic_function_dispatch.cpp"],
},
},
diff --git a/libc/arch-arm/bionic/bpabi.c b/libc/arch-arm/bionic/bpabi.c
new file mode 100644
index 000000000..5c9cd99c6
--- /dev/null
+++ b/libc/arch-arm/bionic/bpabi.c
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+extern long long __divdi3(long long, long long);
+extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
+
+long long __gnu_ldivmod_helper(long long a, long long b, long long* remainder) {
+ long long quotient = __divdi3(a, b);
+ *remainder = a - b * quotient;
+ return quotient;
+}
+
+unsigned long long __gnu_uldivmod_helper(unsigned long long a, unsigned long long b,
+ unsigned long long* remainder) {
+ unsigned long long quotient = __udivdi3(a, b);
+ *remainder = a - b * quotient;
+ return quotient;
+}
diff --git a/libc/arch-arm/dynamic_function_dispatch.cpp b/libc/arch-arm/dynamic_function_dispatch.cpp
index 09fd8f30e..640f3306a 100644
--- a/libc/arch-arm/dynamic_function_dispatch.cpp
+++ b/libc/arch-arm/dynamic_function_dispatch.cpp
@@ -89,15 +89,11 @@ static int ifunc_close(int fd) {
return r0;
}
-#define DEFINE_IFUNC_WITH_SUFFIX(name, suffix) \
- name##_func name##suffix __attribute__((ifunc(#name "_resolver"))); \
+#define DEFINE_IFUNC(name) \
+ name##_func name __attribute__((ifunc(#name "_resolver"))); \
__attribute__((visibility("hidden"))) \
name##_func* name##_resolver()
-#define DEFINE_IFUNC(name) DEFINE_IFUNC_WITH_SUFFIX(name, )
-
-#define DEFINE_INTERNAL_IFUNC(name) DEFINE_IFUNC_WITH_SUFFIX(name, _internal)
-
#define DECLARE_FUNC(type, name) \
__attribute__((visibility("hidden"))) \
type name
@@ -291,7 +287,7 @@ DEFINE_IFUNC(__strcat_chk) {
}
typedef int strcmp_func(const char* __lhs, const char* __rhs);
-DEFINE_INTERNAL_IFUNC(strcmp) {
+DEFINE_IFUNC(strcmp) {
switch(get_cpu_variant()) {
case kCortexA9:
RETURN_FUNC(strcmp_func, strcmp_a9);
@@ -305,7 +301,7 @@ DEFINE_INTERNAL_IFUNC(strcmp) {
}
typedef size_t strlen_func(const char* __s);
-DEFINE_INTERNAL_IFUNC(strlen) {
+DEFINE_IFUNC(strlen) {
switch(get_cpu_variant()) {
case kCortexA9:
RETURN_FUNC(strlen_func, strlen_a9);
diff --git a/libc/bionic/grp_pwd.cpp b/libc/bionic/grp_pwd.cpp
index dadda49cb..ca6ea220c 100644
--- a/libc/bionic/grp_pwd.cpp
+++ b/libc/bionic/grp_pwd.cpp
@@ -163,37 +163,19 @@ static group* android_iinfo_to_group(group_state_t* state,
return gr;
}
-static passwd* android_id_to_passwd(passwd_state_t* state, unsigned id) {
+static const android_id_info* find_android_id_info(unsigned id) {
for (size_t n = 0; n < android_id_count; ++n) {
if (android_ids[n].aid == id) {
- return android_iinfo_to_passwd(state, android_ids + n);
+ return &android_ids[n];
}
}
return nullptr;
}
-static passwd* android_name_to_passwd(passwd_state_t* state, const char* name) {
+static const android_id_info* find_android_id_info(const char* name) {
for (size_t n = 0; n < android_id_count; ++n) {
if (!strcmp(android_ids[n].name, name)) {
- return android_iinfo_to_passwd(state, android_ids + n);
- }
- }
- return nullptr;
-}
-
-static group* android_id_to_group(group_state_t* state, unsigned id) {
- for (size_t n = 0; n < android_id_count; ++n) {
- if (android_ids[n].aid == id) {
- return android_iinfo_to_group(state, android_ids + n);
- }
- }
- return nullptr;
-}
-
-static group* android_name_to_group(group_state_t* state, const char* name) {
- for (size_t n = 0; n < android_id_count; ++n) {
- if (!strcmp(android_ids[n].name, name)) {
- return android_iinfo_to_group(state, android_ids + n);
+ return &android_ids[n];
}
}
return nullptr;
@@ -332,15 +314,9 @@ static id_t app_id_from_name(const char* name, bool is_group) {
} else if (end[1] == 'i' && isdigit(end[2])) {
// end will point to \0 if the strtoul below succeeds.
appid = strtoul(end+2, &end, 10) + AID_ISOLATED_START;
- } else {
- for (size_t n = 0; n < android_id_count; n++) {
- if (!strcmp(android_ids[n].name, end + 1)) {
- appid = android_ids[n].aid;
- // Move the end pointer to the null terminator.
- end += strlen(android_ids[n].name) + 1;
- break;
- }
- }
+ } else if (auto* android_id_info = find_android_id_info(end + 1); android_id_info != nullptr) {
+ appid = android_id_info->aid;
+ end += strlen(android_id_info->name) + 1;
}
// Check that the entire string was consumed by one of the 3 cases above.
@@ -370,11 +346,8 @@ static void print_app_name_from_uid(const uid_t uid, char* buffer, const int buf
if (appid >= AID_ISOLATED_START) {
snprintf(buffer, bufferlen, "u%u_i%u", userid, appid - AID_ISOLATED_START);
} else if (appid < AID_APP_START) {
- for (size_t n = 0; n < android_id_count; n++) {
- if (android_ids[n].aid == appid) {
- snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name);
- return;
- }
+ if (auto* android_id_info = find_android_id_info(appid); android_id_info != nullptr) {
+ snprintf(buffer, bufferlen, "u%u_%s", userid, android_id_info->name);
}
} else {
snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP_START);
@@ -391,11 +364,8 @@ static void print_app_name_from_gid(const gid_t gid, char* buffer, const int buf
} else if (appid >= AID_CACHE_GID_START && appid <= AID_CACHE_GID_END) {
snprintf(buffer, bufferlen, "u%u_a%u_cache", userid, appid - AID_CACHE_GID_START);
} else if (appid < AID_APP_START) {
- for (size_t n = 0; n < android_id_count; n++) {
- if (android_ids[n].aid == appid) {
- snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name);
- return;
- }
+ if (auto* android_id_info = find_android_id_info(appid); android_id_info != nullptr) {
+ snprintf(buffer, bufferlen, "u%u_%s", userid, android_id_info->name);
}
} else {
snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP_START);
@@ -520,12 +490,12 @@ passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function.
return nullptr;
}
- passwd* pw = android_id_to_passwd(state, uid);
- if (pw != nullptr) {
- return pw;
+ if (auto* android_id_info = find_android_id_info(uid); android_id_info != nullptr) {
+ return android_iinfo_to_passwd(state, android_id_info);
}
+
// Handle OEM range.
- pw = oem_id_to_passwd(uid, state);
+ passwd* pw = oem_id_to_passwd(uid, state);
if (pw != nullptr) {
return pw;
}
@@ -538,9 +508,8 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
return nullptr;
}
- passwd* pw = android_name_to_passwd(state, login);
- if (pw != nullptr) {
- return pw;
+ if (auto* android_id_info = find_android_id_info(login); android_id_info != nullptr) {
+ return android_iinfo_to_passwd(state, android_id_info);
}
if (vendor_passwd.FindByName(login, state)) {
@@ -550,7 +519,7 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
}
// Handle OEM range.
- pw = oem_id_to_passwd(oem_id_from_name(login), state);
+ passwd* pw = oem_id_to_passwd(oem_id_from_name(login), state);
if (pw != nullptr) {
return pw;
}
@@ -634,12 +603,12 @@ passwd* getpwent() {
}
static group* getgrgid_internal(gid_t gid, group_state_t* state) {
- group* grp = android_id_to_group(state, gid);
- if (grp != nullptr) {
- return grp;
+ if (auto* android_id_info = find_android_id_info(gid); android_id_info != nullptr) {
+ return android_iinfo_to_group(state, android_id_info);
}
+
// Handle OEM range.
- grp = oem_id_to_group(gid, state);
+ group* grp = oem_id_to_group(gid, state);
if (grp != nullptr) {
return grp;
}
@@ -655,9 +624,8 @@ group* getgrgid(gid_t gid) { // NOLINT: implementing bad function.
}
static group* getgrnam_internal(const char* name, group_state_t* state) {
- group* grp = android_name_to_group(state, name);
- if (grp != nullptr) {
- return grp;
+ if (auto* android_id_info = find_android_id_info(name); android_id_info != nullptr) {
+ return android_iinfo_to_group(state, android_id_info);
}
if (vendor_group.FindByName(name, state)) {
@@ -667,7 +635,7 @@ static group* getgrnam_internal(const char* name, group_state_t* state) {
}
// Handle OEM range.
- grp = oem_id_to_group(oem_id_from_name(name), state);
+ group* grp = oem_id_to_group(oem_id_from_name(name), state);
if (grp != nullptr) {
return grp;
}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index b229cda2f..1f099cfcc 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -232,6 +232,7 @@ static bool __is_unsafe_environment_variable(const char* name) {
"LD_AOUT_LIBRARY_PATH",
"LD_AOUT_PRELOAD",
"LD_AUDIT",
+ "LD_CONFIG_FILE",
"LD_DEBUG",
"LD_DEBUG_OUTPUT",
"LD_DYNAMIC_WEAK",
@@ -242,6 +243,7 @@ static bool __is_unsafe_environment_variable(const char* name) {
"LD_SHOW_AUXV",
"LD_USE_LOAD_BIAS",
"LIBC_DEBUG_MALLOC_OPTIONS",
+ "LIBC_HOOKS_ENABLE",
"LOCALDOMAIN",
"LOCPATH",
"MALLOC_CHECK_",
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index e9f9db20a..9dc4d12d1 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -32,14 +32,7 @@
// calls and add special debugging code to attempt to catch allocation
// errors. All of the debugging code is implemented in a separate shared
// library that is only loaded when the property "libc.debug.malloc.options"
-// is set to a non-zero value. There are two functions exported to
-// allow ddms, or other external users to get information from the debug
-// allocation.
-// get_malloc_leak_info: Returns information about all of the known native
-// allocations that are currently in use.
-// free_malloc_leak_info: Frees the data allocated by the call to
-// get_malloc_leak_info.
-// write_malloc_leak_info: Writes the leak info data to a file.
+// is set to a non-zero value.
#include <errno.h>
#include <stdint.h>
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 64f9f6f7b..599ac6a58 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -409,39 +409,29 @@ __LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
// =============================================================================
// Functions to support dumping of native heap allocations using malloc debug.
// =============================================================================
-
-// Retrieve native heap information.
-//
-// "*info" is set to a buffer we allocate
-// "*overall_size" is set to the size of the "info" buffer
-// "*info_size" is set to the size of a single entry
-// "*total_memory" is set to the sum of all allocations we're tracking; does
-// not include heap overhead
-// "*backtrace_size" is set to the maximum number of entries in the back trace
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size,
- size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
+bool GetMallocLeakInfo(android_mallopt_leak_info_t* leak_info) {
void* func = gFunctions[FUNC_GET_MALLOC_LEAK_INFO];
if (func == nullptr) {
- return;
+ errno = ENOTSUP;
+ return false;
}
- reinterpret_cast<get_malloc_leak_info_func_t>(func)(info, overall_size, info_size, total_memory,
- backtrace_size);
+ reinterpret_cast<get_malloc_leak_info_func_t>(func)(
+ &leak_info->buffer, &leak_info->overall_size, &leak_info->info_size,
+ &leak_info->total_memory, &leak_info->backtrace_size);
+ return true;
}
-extern "C" void free_malloc_leak_info(uint8_t* info) {
+bool FreeMallocLeakInfo(android_mallopt_leak_info_t* leak_info) {
void* func = gFunctions[FUNC_FREE_MALLOC_LEAK_INFO];
if (func == nullptr) {
- return;
+ errno = ENOTSUP;
+ return false;
}
- reinterpret_cast<free_malloc_leak_info_func_t>(func)(info);
+ reinterpret_cast<free_malloc_leak_info_func_t>(func)(leak_info->buffer);
+ return true;
}
-extern "C" void write_malloc_leak_info(FILE* fp) {
- if (fp == nullptr) {
- error_log("write_malloc_leak_info called with a nullptr");
- return;
- }
-
+bool WriteMallocLeakInfo(FILE* fp) {
void* func = gFunctions[FUNC_WRITE_LEAK_INFO];
bool written = false;
if (func != nullptr) {
@@ -453,7 +443,9 @@ extern "C" void write_malloc_leak_info(FILE* fp) {
fprintf(fp, "# adb shell stop\n");
fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
fprintf(fp, "# adb shell start\n");
+ errno = ENOTSUP;
}
+ return written;
}
// =============================================================================
@@ -484,6 +476,27 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
if (opcode == M_SET_ALLOCATION_LIMIT_BYTES) {
return LimitEnable(arg, arg_size);
}
+ if (opcode == M_WRITE_MALLOC_LEAK_INFO_TO_FILE) {
+ if (arg == nullptr || arg_size != sizeof(FILE*)) {
+ errno = EINVAL;
+ return false;
+ }
+ return WriteMallocLeakInfo(reinterpret_cast<FILE*>(arg));
+ }
+ if (opcode == M_GET_MALLOC_LEAK_INFO) {
+ if (arg == nullptr || arg_size != sizeof(android_mallopt_leak_info_t)) {
+ errno = EINVAL;
+ return false;
+ }
+ return GetMallocLeakInfo(reinterpret_cast<android_mallopt_leak_info_t*>(arg));
+ }
+ if (opcode == M_FREE_MALLOC_LEAK_INFO) {
+ if (arg == nullptr || arg_size != sizeof(android_mallopt_leak_info_t)) {
+ errno = EINVAL;
+ return false;
+ }
+ return FreeMallocLeakInfo(reinterpret_cast<android_mallopt_leak_info_t*>(arg));
+ }
return HeapprofdMallopt(opcode, arg, arg_size);
}
// =============================================================================
diff --git a/libc/bionic/pthread_getschedparam.cpp b/libc/bionic/pthread_getschedparam.cpp
index ed1853bdc..765287fa4 100644
--- a/libc/bionic/pthread_getschedparam.cpp
+++ b/libc/bionic/pthread_getschedparam.cpp
@@ -28,9 +28,11 @@
#include <errno.h>
+#include "private/bionic_defs.h"
#include "private/ErrnoRestorer.h"
#include "pthread_internal.h"
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_getschedparam(pthread_t t, int* policy, sched_param* param) {
ErrnoRestorer errno_restorer;
diff --git a/libc/bionic/pthread_setschedparam.cpp b/libc/bionic/pthread_setschedparam.cpp
index 8a0272868..656fe3208 100644
--- a/libc/bionic/pthread_setschedparam.cpp
+++ b/libc/bionic/pthread_setschedparam.cpp
@@ -30,9 +30,11 @@
#include <pthread.h>
#include <sched.h>
+#include "private/bionic_defs.h"
#include "private/ErrnoRestorer.h"
#include "pthread_internal.h"
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_setschedparam(pthread_t t, int policy, const sched_param* param) {
ErrnoRestorer errno_restorer;
@@ -42,6 +44,7 @@ int pthread_setschedparam(pthread_t t, int policy, const sched_param* param) {
return (sched_setscheduler(tid, policy, param) == -1) ? errno : 0;
}
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_setschedprio(pthread_t t, int priority) {
ErrnoRestorer errno_restorer;
diff --git a/libc/bionic/threads.cpp b/libc/bionic/threads.cpp
new file mode 100644
index 000000000..f59758013
--- /dev/null
+++ b/libc/bionic/threads.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 <threads.h>
+
+#define __BIONIC_THREADS_INLINE /* Out of line. */
+#include <bits/threads_inlines.h>
diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h
index a17585768..af7cde1e3 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -100,6 +100,9 @@ __BEGIN_DECLS
/** Names the "Q" API level (29), for comparisons against __ANDROID_API__. */
#define __ANDROID_API_Q__ 29
+/** Names the "R" API level (30), for comparisons against __ANDROID_API__. */
+#define __ANDROID_API_R__ 30
+
/**
* Returns the `targetSdkVersion` of the caller, or `__ANDROID_API_FUTURE__`
* if there is no known target SDK version (for code not running in the
diff --git a/libc/arch-arm/dynamic_function_wrapper.S b/libc/include/android/legacy_threads_inlines.h
index 1d2842b5c..e73ef37c7 100644
--- a/libc/arch-arm/dynamic_function_wrapper.S
+++ b/libc/include/android/legacy_threads_inlines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,12 +26,13 @@
* SUCH DAMAGE.
*/
-#include <private/bionic_asm.h>
+#pragma once
-#define FUNCTION_DELEGATE(name, impl) \
-ENTRY(name); \
- b impl; \
-END(name)
+#include <sys/cdefs.h>
-FUNCTION_DELEGATE(strcmp, strcmp_internal)
-FUNCTION_DELEGATE(strlen, strlen_internal)
+#if __ANDROID_API__ < __ANDROID_API_R__
+
+#define __BIONIC_THREADS_INLINE static __inline
+#include <bits/threads_inlines.h>
+
+#endif
diff --git a/libc/include/bits/threads_inlines.h b/libc/include/bits/threads_inlines.h
new file mode 100644
index 000000000..1130b3a55
--- /dev/null
+++ b/libc/include/bits/threads_inlines.h
@@ -0,0 +1,207 @@
+/*
+ * 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
+
+#include <threads.h>
+
+#include <errno.h>
+#include <sched.h>
+#include <stdlib.h>
+
+#if !defined(__BIONIC_THREADS_INLINE)
+#define __BIONIC_THREADS_INLINE static __inline
+#endif
+
+__BEGIN_DECLS
+
+static __inline int __bionic_thrd_error(int __pthread_code) {
+ switch (__pthread_code) {
+ case 0: return 0;
+ case ENOMEM: return thrd_nomem;
+ case ETIMEDOUT: return thrd_timedout;
+ case EBUSY: return thrd_busy;
+ default: return thrd_error;
+ }
+}
+
+__BIONIC_THREADS_INLINE void call_once(once_flag* __flag,
+ void (*__function)(void)) {
+ pthread_once(__flag, __function);
+}
+
+
+
+__BIONIC_THREADS_INLINE int cnd_broadcast(cnd_t* __cnd) {
+ return __bionic_thrd_error(pthread_cond_broadcast(__cnd));
+}
+
+__BIONIC_THREADS_INLINE void cnd_destroy(cnd_t* __cnd) {
+ pthread_cond_destroy(__cnd);
+}
+
+__BIONIC_THREADS_INLINE int cnd_init(cnd_t* __cnd) {
+ return __bionic_thrd_error(pthread_cond_init(__cnd, NULL));
+}
+
+__BIONIC_THREADS_INLINE int cnd_signal(cnd_t* __cnd) {
+ return __bionic_thrd_error(pthread_cond_signal(__cnd));
+}
+
+__BIONIC_THREADS_INLINE int cnd_timedwait(cnd_t* __cnd,
+ mtx_t* __mtx,
+ const struct timespec* __timeout) {
+ return __bionic_thrd_error(pthread_cond_timedwait(__cnd, __mtx, __timeout));
+}
+
+__BIONIC_THREADS_INLINE int cnd_wait(cnd_t* __cnd, mtx_t* __mtx) {
+ return __bionic_thrd_error(pthread_cond_wait(__cnd, __mtx));
+}
+
+
+
+__BIONIC_THREADS_INLINE void mtx_destroy(mtx_t* __mtx) {
+ pthread_mutex_destroy(__mtx);
+}
+
+__BIONIC_THREADS_INLINE int mtx_init(mtx_t* __mtx, int __type) {
+ int __pthread_type = (__type & mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE
+ : PTHREAD_MUTEX_NORMAL;
+ __type &= ~mtx_recursive;
+ if (__type != mtx_plain && __type != mtx_timed) return thrd_error;
+
+ pthread_mutexattr_t __attr;
+ pthread_mutexattr_init(&__attr);
+ pthread_mutexattr_settype(&__attr, __pthread_type);
+ return __bionic_thrd_error(pthread_mutex_init(__mtx, &__attr));
+}
+
+__BIONIC_THREADS_INLINE int mtx_lock(mtx_t* __mtx) {
+ return __bionic_thrd_error(pthread_mutex_lock(__mtx));
+}
+
+__BIONIC_THREADS_INLINE int mtx_timedlock(mtx_t* __mtx,
+ const struct timespec* __timeout) {
+ return __bionic_thrd_error(pthread_mutex_timedlock(__mtx, __timeout));
+}
+
+__BIONIC_THREADS_INLINE int mtx_trylock(mtx_t* __mtx) {
+ return __bionic_thrd_error(pthread_mutex_trylock(__mtx));
+}
+
+__BIONIC_THREADS_INLINE int mtx_unlock(mtx_t* __mtx) {
+ return __bionic_thrd_error(pthread_mutex_unlock(__mtx));
+}
+
+
+
+struct __bionic_thrd_data {
+ thrd_start_t __func;
+ void* __arg;
+};
+
+static inline void* __bionic_thrd_trampoline(void* __arg) {
+ struct __bionic_thrd_data __data =
+ *__BIONIC_CAST(static_cast, struct __bionic_thrd_data*, __arg);
+ free(__arg);
+ int __result = __data.__func(__data.__arg);
+ return __BIONIC_CAST(reinterpret_cast, void*,
+ __BIONIC_CAST(static_cast, uintptr_t, __result));
+}
+
+__BIONIC_THREADS_INLINE int thrd_create(thrd_t* __thrd,
+ thrd_start_t __func,
+ void* __arg) {
+ struct __bionic_thrd_data* __pthread_arg =
+ __BIONIC_CAST(static_cast, struct __bionic_thrd_data*,
+ malloc(sizeof(struct __bionic_thrd_data)));
+ __pthread_arg->__func = __func;
+ __pthread_arg->__arg = __arg;
+ int __result = __bionic_thrd_error(pthread_create(__thrd, NULL,
+ __bionic_thrd_trampoline,
+ __pthread_arg));
+ if (__result != thrd_success) free(__pthread_arg);
+ return __result;
+}
+
+__BIONIC_THREADS_INLINE thrd_t thrd_current(void) {
+ return pthread_self();
+}
+
+__BIONIC_THREADS_INLINE int thrd_detach(thrd_t __thrd) {
+ return __bionic_thrd_error(pthread_detach(__thrd));
+}
+
+__BIONIC_THREADS_INLINE int thrd_equal(thrd_t __lhs, thrd_t __rhs) {
+ return pthread_equal(__lhs, __rhs);
+}
+
+__BIONIC_THREADS_INLINE void thrd_exit(int __result) {
+ pthread_exit(__BIONIC_CAST(reinterpret_cast, void*,
+ __BIONIC_CAST(static_cast, uintptr_t, __result)));
+}
+
+__BIONIC_THREADS_INLINE int thrd_join(thrd_t __thrd, int* __result) {
+ void* __pthread_result;
+ if (pthread_join(__thrd, &__pthread_result) != 0) return thrd_error;
+ if (__result) {
+ *__result = __BIONIC_CAST(reinterpret_cast, intptr_t, __pthread_result);
+ }
+ return thrd_success;
+}
+
+__BIONIC_THREADS_INLINE int thrd_sleep(const struct timespec* __duration,
+ struct timespec* __remaining) {
+ int __rc = nanosleep(__duration, __remaining);
+ if (__rc == 0) return 0;
+ return (errno == EINTR) ? -1 : -2;
+}
+
+__BIONIC_THREADS_INLINE void thrd_yield(void) {
+ sched_yield();
+}
+
+
+
+__BIONIC_THREADS_INLINE int tss_create(tss_t* __key, tss_dtor_t __dtor) {
+ return __bionic_thrd_error(pthread_key_create(__key, __dtor));
+}
+
+__BIONIC_THREADS_INLINE void tss_delete(tss_t __key) {
+ pthread_key_delete(__key);
+}
+
+__BIONIC_THREADS_INLINE void* tss_get(tss_t __key) {
+ return pthread_getspecific(__key);
+}
+
+__BIONIC_THREADS_INLINE int tss_set(tss_t __key, void* __value) {
+ return __bionic_thrd_error(pthread_setspecific(__key, __value));
+}
+
+__END_DECLS
diff --git a/libc/include/threads.h b/libc/include/threads.h
new file mode 100644
index 000000000..2c43b0b65
--- /dev/null
+++ b/libc/include/threads.h
@@ -0,0 +1,228 @@
+/*
+ * 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
+
+/**
+ * @file threads.h
+ * @brief C11 threads.
+ */
+
+#include <sys/cdefs.h>
+
+#include <pthread.h>
+#include <time.h>
+
+#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
+#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
+
+/** The type for a condition variable. */
+typedef pthread_cond_t cnd_t;
+/** The type for a thread. */
+typedef pthread_t thrd_t;
+/** The type for a thread-specific storage key. */
+typedef pthread_key_t tss_t;
+/** The type for a mutex. */
+typedef pthread_mutex_t mtx_t;
+
+/** The type for a thread-specific storage destructor. */
+typedef void (*tss_dtor_t)(void*);
+/** The type of the function passed to thrd_create() to create a new thread. */
+typedef int (*thrd_start_t)(void*);
+
+/** The type used by call_once(). */
+typedef pthread_once_t once_flag;
+
+enum {
+ mtx_plain = 0x1,
+ mtx_recursive = 0x2,
+ mtx_timed = 0x4,
+};
+
+enum {
+ thrd_success = 0,
+ thrd_busy = 1,
+ thrd_error = 2,
+ thrd_nomem = 3,
+ thrd_timedout = 4,
+};
+
+#if !defined(__cplusplus)
+#define thread_local _Thread_local
+#endif
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= __ANDROID_API_R__
+// This file is implemented as static inlines before API level 30.
+
+/** Uses `__flag` to ensure that `__function` is called exactly once. */
+void call_once(once_flag* __flag, void (*__function)(void));
+
+
+
+/**
+ * Unblocks all threads blocked on `__cond`.
+ */
+int cnd_broadcast(cnd_t* __cond);
+
+/**
+ * Destroys a condition variable.
+ */
+void cnd_destroy(cnd_t* __cond);
+
+/**
+ * Creates a condition variable.
+ */
+int cnd_init(cnd_t* __cond);
+
+/**
+ * Unblocks one thread blocked on `__cond`.
+ */
+int cnd_signal(cnd_t* __cond);
+
+/**
+ * Unlocks `__mutex` and blocks until `__cond` is signaled or `__timeout` occurs.
+ */
+int cnd_timedwait(cnd_t* __cond, mtx_t* __mutex, const struct timespec* __timeout);
+
+/**
+ * Unlocks `__mutex` and blocks until `__cond` is signaled.
+ */
+int cnd_wait(cnd_t* __cond, mtx_t* __mutex);
+
+
+
+/**
+ * Destroys a mutex.
+ */
+void mtx_destroy(mtx_t* __mutex);
+
+/**
+ * Creates a mutex.
+ */
+int mtx_init(mtx_t* __mutex, int __type);
+
+/**
+ * Blocks until `__mutex` is acquired.
+ */
+int mtx_lock(mtx_t* __mutex);
+
+/**
+ * Blocks until `__mutex` is acquired or `__timeout` expires.
+ */
+int mtx_timedlock(mtx_t* __mutex, const struct timespec* __timeout);
+
+/**
+ * Acquires `__mutex` or returns `thrd_busy`.
+ */
+int mtx_trylock(mtx_t* __mutex);
+
+/**
+ * Unlocks `__mutex`.
+ */
+int mtx_unlock(mtx_t* __mutex);
+
+
+
+/**
+ * Creates a new thread running `__function(__arg)`, and sets `*__thrd` to
+ * the new thread.
+ */
+int thrd_create(thrd_t* __thrd, thrd_start_t __function, void* __arg);
+
+/**
+ * Returns the `thrd_t` corresponding to the caller.
+ */
+thrd_t thrd_current(void);
+
+/**
+ * Tells the OS to automatically dispose of `__thrd` when it exits.
+ */
+int thrd_detach(thrd_t __thrd);
+
+/**
+ * Tests whether two threads are the same thread.
+ */
+int thrd_equal(thrd_t __lhs, thrd_t __rhs);
+
+/**
+ * Terminates the calling thread, setting its result to `__result`.
+ */
+void thrd_exit(int __result) __noreturn;
+
+/**
+ * Blocks until `__thrd` terminates. If `__result` is not null, `*__result`
+ * is set to the exiting thread's result.
+ */
+int thrd_join(thrd_t __thrd, int* __result);
+
+/**
+ * Blocks the caller for at least `__duration` unless a signal is delivered.
+ * If a signal causes the sleep to end early and `__remaining` is not null,
+ * `*__remaining` is set to the time remaining.
+ *
+ * Returns 0 on success, or -1 if a signal was delivered.
+ */
+int thrd_sleep(const struct timespec* __duration, struct timespec* __remaining);
+
+/**
+ * Request that other threads should be scheduled.
+ */
+void thrd_yield(void);
+
+
+
+/**
+ * Creates a thread-specific storage key with the associated destructor (which
+ * may be null).
+ */
+int tss_create(tss_t* __key, tss_dtor_t __dtor);
+
+/**
+ * Destroys a thread-specific storage key.
+ */
+void tss_delete(tss_t __key);
+
+/**
+ * Returns the value for the current thread held in the thread-specific storage
+ * identified by `__key`.
+ */
+void* tss_get(tss_t __key);
+
+/**
+ * Sets the current thread's value for the thread-specific storage identified
+ * by `__key` to `__value`.
+ */
+int tss_set(tss_t __key, void* __value);
+
+#endif
+
+__END_DECLS
+
+#include <android/legacy_threads_inlines.h>
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 55ca9dcfb..4a734fc20 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1473,18 +1473,42 @@ LIBC_Q { # introduced=Q
malloc_enable; # apex
malloc_iterate; # apex
- # Used by libmediautils
- write_malloc_leak_info; # apex
- free_malloc_leak_info; # apex
- get_malloc_leak_info; # apex
-
# Used by libandroid_net
android_getaddrinfofornet; # apex
- # Used by libandroid_runtime and libmedia
+ # Used by libandroid_runtime, libmedia and libmediautils
android_mallopt; # apex
} LIBC_P;
+LIBC_R { # introduced=R
+ global:
+ call_once;
+ cnd_broadcast;
+ cnd_destroy;
+ cnd_init;
+ cnd_signal;
+ cnd_timedwait;
+ cnd_wait;
+ mtx_destroy;
+ mtx_init;
+ mtx_lock;
+ mtx_timedlock;
+ mtx_trylock;
+ mtx_unlock;
+ thrd_create;
+ thrd_current;
+ thrd_detach;
+ thrd_equal;
+ thrd_exit;
+ thrd_join;
+ thrd_sleep;
+ thrd_yield;
+ tss_create;
+ tss_delete;
+ tss_get;
+ tss_set;
+} LIBC_Q;
+
LIBC_PRIVATE {
global:
___Unwind_Backtrace; # arm
diff --git a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
index 0d23a6a8f..86e20ea90 100644
--- a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
+++ b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
@@ -38,6 +38,7 @@
#include <gtest/gtest.h>
+#include <private/bionic_malloc.h>
#include <private/bionic_malloc_dispatch.h>
#include <tests/utils.h>
@@ -197,19 +198,15 @@ TEST_F(MallocHooksTest, extended_functions) {
}
TEST_F(MallocHooksTest, DISABLED_extended_functions) {
- uint8_t* info = nullptr;
- size_t overall_size = 100;
- size_t info_size = 200;
- size_t total_memory = 300;
- size_t backtrace_size = 400;
- get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
- EXPECT_EQ(nullptr, info);
- EXPECT_EQ(0U, overall_size);
- EXPECT_EQ(0U, info_size);
- EXPECT_EQ(0U, total_memory);
- EXPECT_EQ(0U, backtrace_size);
-
- free_malloc_leak_info(info);
+ android_mallopt_leak_info_t leak_info;
+ ASSERT_TRUE(android_mallopt(M_GET_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info)));
+ EXPECT_EQ(nullptr, leak_info.buffer);
+ EXPECT_EQ(0U, leak_info.overall_size);
+ EXPECT_EQ(0U, leak_info.info_size);
+ EXPECT_EQ(0U, leak_info.total_memory);
+ EXPECT_EQ(0U, leak_info.backtrace_size);
+
+ ASSERT_TRUE(android_mallopt(M_FREE_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info)));
malloc_enable();
malloc_disable();
diff --git a/libc/private/bionic_malloc.h b/libc/private/bionic_malloc.h
index e8a6f6ef5..9c602eadf 100644
--- a/libc/private/bionic_malloc.h
+++ b/libc/private/bionic_malloc.h
@@ -30,6 +30,22 @@
#include <stdbool.h>
+// Structures for android_mallopt.
+
+typedef struct {
+ // Pointer to the buffer allocated by a call to M_GET_MALLOC_LEAK_INFO.
+ uint8_t* buffer;
+ // The size of the "info" buffer.
+ size_t overall_size;
+ // The size of a single entry.
+ size_t info_size;
+ // The sum of all allocations that have been tracked. Does not include
+ // any heap overhead.
+ size_t total_memory;
+ // The maximum number of backtrace entries.
+ size_t backtrace_size;
+} android_mallopt_leak_info_t;
+
// Opcodes for android_mallopt.
enum {
@@ -48,6 +64,26 @@ enum {
// Called after the zygote forks to indicate this is a child.
M_SET_ZYGOTE_CHILD = 4,
#define M_SET_ZYGOTE_CHILD M_SET_ZYGOTE_CHILD
+
+ // Options to dump backtraces of allocations. These options only
+ // work when malloc debug has been enabled.
+
+ // Writes the backtrace information of all current allocations to a file.
+ // NOTE: arg_size has to be sizeof(FILE*) because FILE is an opaque type.
+ // arg = FILE*
+ // arg_size = sizeof(FILE*)
+ M_WRITE_MALLOC_LEAK_INFO_TO_FILE = 5,
+#define M_WRITE_MALLOC_LEAK_INFO_TO_FILE M_WRITE_MALLOC_LEAK_INFO_TO_FILE
+ // Get information about the backtraces of all
+ // arg = android_mallopt_leak_info_t*
+ // arg_size = sizeof(android_mallopt_leak_info_t)
+ M_GET_MALLOC_LEAK_INFO = 6,
+#define M_GET_MALLOC_LEAK_INFO M_GET_MALLOC_LEAK_INFO
+ // Free the memory allocated and returned by M_GET_MALLOC_LEAK_INFO.
+ // arg = android_mallopt_leak_info_t*
+ // arg_size = sizeof(android_mallopt_leak_info_t)
+ M_FREE_MALLOC_LEAK_INFO = 7,
+#define M_FREE_MALLOC_LEAK_INFO M_FREE_MALLOC_LEAK_INFO
};
// Manipulates bionic-specific handling of memory allocation APIs such as
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index bd6ac3db0..692e0c0fc 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -186,7 +186,10 @@ restart:
}
_ATEXIT_UNLOCK();
- fflush(NULL);
+ /* If called via exit(), flush output of all open files. */
+ if (dso == NULL) {
+ fflush(NULL);
+ }
/* BEGIN android-changed: call __unregister_atfork if dso is not null */
if (dso != NULL) {