Fix bionic-unit-tests-glibc
Also add another test for dlsym(RTLD_NEXT, ..)
Bug: http://b/33106624
Test: run bionic-unit-tests-glibc and bionic-unit-tests
Change-Id: I340165d66bf2360b0e3273d3561a08cb5e7bd025
diff --git a/tests/Android.bp b/tests/Android.bp
index 3e1e13b..688d8e1 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -476,7 +476,10 @@
include_dirs: ["bionic/libc"],
- ldflags: ["-Wl,--export-dynamic"],
+ ldflags: [
+ "-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs",
+ "-Wl,--export-dynamic",
+ ],
sanitize: {
never: false,
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index ed50ea5..e629e41 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -98,7 +98,7 @@
}
TEST_F(DlExtTest, ExtInfoUseFd) {
- const std::string lib_path = g_testlib_root + "/libdlext_test_fd/libdlext_test_fd.so";
+ const std::string lib_path = get_testlib_root() + "/libdlext_test_fd/libdlext_test_fd.so";
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD;
@@ -116,7 +116,7 @@
}
TEST_F(DlExtTest, ExtInfoUseFdWithOffset) {
- const std::string lib_path = g_testlib_root + "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
+ const std::string lib_path = get_testlib_root() + "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
@@ -142,7 +142,7 @@
}
TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) {
- const std::string lib_path = g_testlib_root + "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
+ const std::string lib_path = get_testlib_root() + "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
@@ -228,7 +228,7 @@
TEST(dlfcn, dlopen_from_zip_absolute_path) {
const std::string lib_zip_path = "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
- const std::string lib_path = g_testlib_root + lib_zip_path;
+ const std::string lib_path = get_testlib_root() + lib_zip_path;
void* handle = dlopen((lib_path + "!/libdir/libatest_simple_zip.so").c_str(), RTLD_NOW);
ASSERT_TRUE(handle != nullptr) << dlerror();
@@ -242,7 +242,7 @@
TEST(dlfcn, dlopen_from_zip_with_dt_runpath) {
const std::string lib_zip_path = "/libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip";
- const std::string lib_path = g_testlib_root + lib_zip_path;
+ const std::string lib_path = get_testlib_root() + lib_zip_path;
void* handle = dlopen((lib_path + "!/libdir/libtest_dt_runpath_d_zip.so").c_str(), RTLD_NOW);
@@ -261,7 +261,7 @@
TEST(dlfcn, dlopen_from_zip_ld_library_path) {
const std::string lib_zip_path = "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
- const std::string lib_path = g_testlib_root + lib_zip_path + "!/libdir";
+ const std::string lib_path = get_testlib_root() + lib_zip_path + "!/libdir";
typedef void (*fn_t)(const char*);
fn_t android_update_LD_LIBRARY_PATH =
@@ -631,7 +631,7 @@
ASSERT_STREQ("android_init_namespaces failed: error initializing public namespace: "
"the list of public libraries is empty.", dlerror());
- const std::string lib_public_path = g_testlib_root + "/public_namespace_libs/" + g_public_lib;
+ const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
@@ -639,20 +639,20 @@
// Check that libraries added to public namespace are NODELETE
dlclose(handle_public);
- handle_public = dlopen((g_testlib_root + "/public_namespace_libs/" + g_public_lib).c_str(),
+ handle_public = dlopen((get_testlib_root() + "/public_namespace_libs/" + g_public_lib).c_str(),
RTLD_NOW | RTLD_NOLOAD);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
android_namespace_t* ns1 =
android_create_namespace("private", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
ASSERT_TRUE(ns1 != nullptr) << dlerror();
android_namespace_t* ns2 =
android_create_namespace("private_isolated", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, nullptr);
ASSERT_TRUE(ns2 != nullptr) << dlerror();
@@ -743,7 +743,7 @@
static const char* root_lib = "libnstest_root_not_isolated.so";
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
- const std::string lib_public_path = g_testlib_root + "/public_namespace_libs/" + g_public_lib;
+ const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
@@ -753,14 +753,14 @@
android_namespace_t* ns_not_isolated =
android_create_namespace("private", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
android_namespace_t* ns_isolated =
android_create_namespace("private_isolated1",
nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED,
nullptr,
nullptr);
@@ -768,10 +768,10 @@
android_namespace_t* ns_isolated2 =
android_create_namespace("private_isolated2",
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
nullptr,
ANDROID_NAMESPACE_TYPE_ISOLATED,
- g_testlib_root.c_str(),
+ get_testlib_root().c_str(),
nullptr);
ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
@@ -779,7 +779,7 @@
ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
std::string lib_private_external_path =
- g_testlib_root + "/private_namespace_libs_external/libnstest_private_external.so";
+ get_testlib_root() + "/private_namespace_libs_external/libnstest_private_external.so";
// Load lib_private_external_path to default namespace
// (it should remain invisible for the isolated namespaces after this)
@@ -808,7 +808,7 @@
extinfo.library_namespace = ns_isolated2;
- // this should work because isolation_path for private_isolated2 includes g_testlib_root
+ // this should work because isolation_path for private_isolated2 includes get_testlib_root()
handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
ASSERT_TRUE(handle2 != nullptr) << dlerror();
dlclose(handle2);
@@ -849,7 +849,7 @@
static const char* root_lib_isolated = "libnstest_root.so";
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
- const std::string lib_public_path = g_testlib_root + "/public_namespace_libs/" + g_public_lib;
+ const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
@@ -860,18 +860,18 @@
// preload this library to the default namespace to check if it
// is shared later on.
void* handle_dlopened =
- dlopen((g_testlib_root + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
+ dlopen((get_testlib_root() + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
android_namespace_t* ns_not_isolated =
android_create_namespace("private", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
android_namespace_t* ns_isolated_shared =
android_create_namespace("private_isolated_shared", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
nullptr, nullptr);
ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
@@ -880,7 +880,7 @@
ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
std::string lib_private_external_path =
- g_testlib_root + "/private_namespace_libs_external/libnstest_private_external.so";
+ get_testlib_root() + "/private_namespace_libs_external/libnstest_private_external.so";
// Load lib_private_external_path to default namespace
// (it should remain invisible for the isolated namespaces after this)
@@ -971,12 +971,12 @@
// preload this library to the default namespace to check if it
// is shared later on.
void* handle_dlopened =
- dlopen((g_testlib_root + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
+ dlopen((get_testlib_root() + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
android_namespace_t* ns_isolated_shared =
android_create_namespace("private_isolated_shared", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
nullptr, nullptr);
ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
@@ -997,7 +997,7 @@
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in shared namespace";
- handle = android_dlopen_ext((g_testlib_root + "/private_namespace_libs/libnstest_dlopened.so").c_str(),
+ handle = android_dlopen_ext((get_testlib_root() + "/private_namespace_libs/libnstest_dlopened.so").c_str(),
RTLD_NOW | RTLD_NOLOAD, &extinfo);
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in shared namespace";
@@ -1006,14 +1006,14 @@
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in default namespace";
- handle = dlopen((g_testlib_root + "/private_namespace_libs/libnstest_dlopened.so").c_str(),
+ handle = dlopen((get_testlib_root() + "/private_namespace_libs/libnstest_dlopened.so").c_str(),
RTLD_NOW | RTLD_NOLOAD);
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in default namespace";
// Now lets see if the soinfo area gets reused in the wrong way:
// load a library to default namespace.
- const std::string lib_public_path = g_testlib_root + "/public_namespace_libs/" + g_public_lib;
+ const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
@@ -1029,12 +1029,12 @@
ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr));
- const std::string lib_public_path = g_testlib_root + "/public_namespace_libs";
+ const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs";
android_namespace_t* ns1 =
android_create_namespace("isolated1",
nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED,
lib_public_path.c_str(),
nullptr);
@@ -1043,7 +1043,7 @@
android_namespace_t* ns2 =
android_create_namespace("isolated2",
nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED,
lib_public_path.c_str(),
nullptr);
@@ -1062,7 +1062,7 @@
android_namespace_t* ns1_child =
android_create_namespace("isolated1_child",
nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED,
nullptr,
ns1);
@@ -1097,22 +1097,22 @@
static const char* root_lib = "libnstest_root.so";
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
- const std::string lib_public_path = g_testlib_root + "/public_namespace_libs/" + g_public_lib;
+ const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
- ASSERT_TRUE(android_init_namespaces(path.c_str(), (g_testlib_root + "/private_namespace_libs").c_str()))
+ ASSERT_TRUE(android_init_namespaces(path.c_str(), (get_testlib_root() + "/private_namespace_libs").c_str()))
<< dlerror();
android_namespace_t* ns = android_create_namespace(
"private", nullptr,
- (g_testlib_root + "/private_namespace_libs").c_str(),
+ (get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
ASSERT_TRUE(ns != nullptr) << dlerror();
- std::string private_library_absolute_path = g_testlib_root + "/private_namespace_libs/" + root_lib;
+ std::string private_library_absolute_path = get_testlib_root() + "/private_namespace_libs/" + root_lib;
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 4d8e697..46f6ec0 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1013,6 +1013,22 @@
ASSERT_TRUE(addr != nullptr);
}
+// Check that RTLD_NEXT of a libc symbol works in dlopened library
+TEST(dlfcn, rtld_next_from_library) {
+ void* library_with_close = dlopen("libtest_check_rtld_next_from_library.so", RTLD_NOW);
+ ASSERT_TRUE(library_with_close != nullptr) << dlerror();
+ void* expected_addr = dlsym(RTLD_DEFAULT, "close");
+ ASSERT_TRUE(expected_addr != nullptr) << dlerror();
+ typedef void* (*get_libc_close_ptr_fn_t)();
+ get_libc_close_ptr_fn_t get_libc_close_ptr =
+ reinterpret_cast<get_libc_close_ptr_fn_t>(dlsym(library_with_close, "get_libc_close_ptr"));
+ ASSERT_TRUE(get_libc_close_ptr != nullptr) << dlerror();
+ ASSERT_EQ(expected_addr, get_libc_close_ptr());
+
+ dlclose(library_with_close);
+}
+
+
TEST(dlfcn, dlsym_weak_func) {
dlerror();
void* handle = dlopen("libtest_dlsym_weak_func.so", RTLD_NOW);
@@ -1159,7 +1175,7 @@
#if defined(__BIONIC__)
TEST(dlfcn, dt_runpath_absolute_path) {
- std::string libpath = g_testlib_root + "/libtest_dt_runpath_d.so";
+ std::string libpath = get_testlib_root() + "/libtest_dt_runpath_d.so";
void* handle = dlopen(libpath.c_str(), RTLD_NOW);
ASSERT_TRUE(handle != nullptr) << dlerror();
@@ -1174,7 +1190,7 @@
}
TEST(dlfcn, dlopen_invalid_rw_load_segment) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-rw_load_segment.so";
void* handle = dlopen(libpath.c_str(), RTLD_NOW);
@@ -1184,7 +1200,7 @@
}
TEST(dlfcn, dlopen_invalid_unaligned_shdr_offset) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-unaligned_shdr_offset.so";
@@ -1195,7 +1211,7 @@
}
TEST(dlfcn, dlopen_invalid_zero_shentsize) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-zero_shentsize.so";
@@ -1206,7 +1222,7 @@
}
TEST(dlfcn, dlopen_invalid_zero_shstrndx) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-zero_shstrndx.so";
@@ -1217,7 +1233,7 @@
}
TEST(dlfcn, dlopen_invalid_empty_shdr_table) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-empty_shdr_table.so";
@@ -1228,7 +1244,7 @@
}
TEST(dlfcn, dlopen_invalid_zero_shdr_table_offset) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-zero_shdr_table_offset.so";
@@ -1239,7 +1255,7 @@
}
TEST(dlfcn, dlopen_invalid_zero_shdr_table_content) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-zero_shdr_table_content.so";
@@ -1250,7 +1266,7 @@
}
TEST(dlfcn, dlopen_invalid_textrels) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-textrels.so";
@@ -1261,7 +1277,7 @@
}
TEST(dlfcn, dlopen_invalid_textrels2) {
- const std::string libpath = g_testlib_root +
+ const std::string libpath = get_testlib_root() +
"/" + kPrebuiltElfDir +
"/libtest_invalid-textrels2.so";
diff --git a/tests/gtest_globals.cpp b/tests/gtest_globals.cpp
index 4f2c82e..bb99dd6 100644
--- a/tests/gtest_globals.cpp
+++ b/tests/gtest_globals.cpp
@@ -21,11 +21,20 @@
#include <string>
-static std::string get_testlib_root() {
+static std::string init_testlib_root() {
std::string out_path;
const char* data_dir = getenv("ANDROID_DATA");
if (data_dir == nullptr) {
- out_path = "/data";
+ // Calculate ANDROID_DATA assuming the binary is in "$ANDROID_DATA/somedir/binary-dir/binary"
+ std::string path = get_executable_path();
+
+ path = get_dirname(path.c_str());
+ path += "/../..";
+
+ if (!get_realpath(path.c_str(), &out_path)) {
+ printf("Failed to get realpath for \"%s\"", path.c_str());
+ abort();
+ }
} else {
out_path = data_dir;
}
@@ -35,12 +44,18 @@
out_path += "64";
#endif
out_path += "/bionic-loader-test-libs";
+
std::string real_path;
if (!get_realpath(out_path, &real_path)) {
+ printf("\"%s\": does not exists", out_path.c_str());
abort();
}
return real_path;
}
-const std::string g_testlib_root = get_testlib_root();
+const std::string& get_testlib_root() {
+ static const std::string testlib_root = init_testlib_root();
+ return testlib_root;
+}
+
diff --git a/tests/gtest_globals.h b/tests/gtest_globals.h
index fab2a39..019849d 100644
--- a/tests/gtest_globals.h
+++ b/tests/gtest_globals.h
@@ -21,6 +21,6 @@
constexpr const char* kPrebuiltElfDir = "prebuilt-elf-files";
-extern const std::string g_testlib_root;
+const std::string& get_testlib_root();
#endif // _BIONIC_TESTS_GTEST_GLOBALS_H
diff --git a/tests/gtest_globals_cts.cpp b/tests/gtest_globals_cts.cpp
index bf891a1..2532ef1 100644
--- a/tests/gtest_globals_cts.cpp
+++ b/tests/gtest_globals_cts.cpp
@@ -18,4 +18,8 @@
#include <string>
-const std::string g_testlib_root = "/data/local/tmp/lib/bionic-loader-test-libs";
+static const std::string g_testlib_root = "/data/local/tmp/lib/bionic-loader-test-libs";
+
+const std::string& get_testlib_root() {
+ return g_testlib_root;
+}
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index aacf9ae..b12c28f 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
@@ -65,6 +66,15 @@
return true;
}
+std::string get_dirname(const char* path) {
+#if defined(__BIONIC__)
+ return dirname(path);
+#else
+ // GLIBC does not have const char* dirname
+ return dirname(const_cast<char*>(path));
+#endif
+}
+
int get_argc() {
return g_argc;
}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index fe0d6ab..4cd991a 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -411,6 +411,24 @@
}
// -----------------------------------------------------------------------------
+// Check that RTLD_NEXT of a libc symbol works in dlopened library
+// -----------------------------------------------------------------------------
+cc_test_library {
+ name: "libtest_check_rtld_next_from_library",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["check_rtld_next_from_library.cpp"],
+
+ target: {
+ android: {
+ shared_libs: ["libdl"],
+ },
+ host: {
+ host_ldlibs: ["-ldl"],
+ },
+ },
+}
+
+// -----------------------------------------------------------------------------
// Library with constructor that calls dlopen() b/7941716
// -----------------------------------------------------------------------------
cc_test_library {
diff --git a/tests/libs/check_rtld_next_from_library.cpp b/tests/libs/check_rtld_next_from_library.cpp
new file mode 100644
index 0000000..45d8eea
--- /dev/null
+++ b/tests/libs/check_rtld_next_from_library.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+static void* g_libc_close_ptr;
+
+static void __attribute__((constructor)) __libc_close_lookup() {
+ g_libc_close_ptr = dlsym(RTLD_NEXT, "close");
+}
+
+// A libc function used for RTLD_NEXT
+// This function in not supposed to be called
+extern "C" int __attribute__((weak)) close(int) {
+ abort();
+}
+
+extern "C" void* get_libc_close_ptr() {
+ return g_libc_close_ptr;
+}
+
+
diff --git a/tests/utils.h b/tests/utils.h
index c62da75..4c3aef4 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -128,6 +128,8 @@
// Get realpath
bool get_realpath(const std::string& path, std::string* realpath);
+// Get dirname
+std::string get_dirname(const char* path);
// Access to argc/argv/envp
int get_argc();