This patch includes a module called dl_tests which clones the bionic
and compile the needed library files.
Signed-off-by: Zhiting Zhu <
zhit...@cs.utexas.edu>
---
.gitignore | 2 +
modules/dl_tests/Makefile | 112 ++++++++++++++++++
modules/tests/Makefile | 2 +
modules/tests/module.py | 1 +
tests/tst-dlfcn.cc | 243 ++++++++++++++++++++++++++++++++++++++
5 files changed, 360 insertions(+)
create mode 100644 modules/dl_tests/Makefile
diff --git a/.gitignore b/.gitignore
index e0a76dc2..2dcb3225 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,6 @@ modules/httpserver-jvm-plugin/obj/
modules/libtools/*.so
modules/libtools/*.o
modules/libyaml/usr.manifest
+modules/dl_tests/bionic/
+modules/dl_tests/usr.manifest
.idea
diff --git a/modules/dl_tests/Makefile b/modules/dl_tests/Makefile
new file mode 100644
index 00000000..da48e15c
--- /dev/null
+++ b/modules/dl_tests/Makefile
@@ -0,0 +1,112 @@
+module: usr.manifest build_all
+
+src := $(OSV_BASE)
+out := $(OSV_BUILD_PATH)
+arch := $(ARCH)
+fs_type := $(fs_type)
+bionic_test_libs := $(src)/modules/dl_tests/bionic/tests/libs
+
+quiet = $(if $V, $1, @echo " $2"; $1)
+very-quiet = $(if $V, $1, @$1)
+makedir = $(call very-quiet, mkdir -p $(dir $@))
+
+autodepend = -MD -MT $@ -MP
+
+INCLUDES = -I$(src)/arch/$(ARCH) -I$(src) -I$(src)/include \
+ -I$(src)/arch/common -isystem $(src)/include/glibc-compat \
+ $(shell $(CXX) -E -xc++ - -v </dev/null 2>&1 | awk '/^End/ {exit} /^ .*c\+\+/ {print "-isystem" $$0}') \
+ -isystem $(src)/include/api -isystem $(src)/include/api/$(ARCH) \
+ -isystem $(out)/gen/include
+
+COMMON = $(autodepend) $(INCLUDES) -g -O2 -fPIC -DBOOST_TEST_DYN_LINK \
+ -U _FORTIFY_SOURCE -D_KERNEL -D__OSV__ -DCONF_debug_memory=0 \
+ -Wall -Wno-pointer-arith -Wformat=0 -Wno-format-security
+
+LIBS =
+
+CXXFLAGS = -std=gnu++11 $(COMMON)
+CFLAGS = -std=gnu99 $(COMMON)
+
+tests := libtest_simple.so libtest_empty.so libtest_dlsym_from_this_grandchild.so \
+ libtest_dlsym_from_this_child.so libtest_dlsym_from_this.so libdlext_test.so \
+ libtest_with_dependency.so libtest_check_rtld_next_from_library.so
+
+.PHONY: get_file
+get_file:
+ if cd $(src)/modules/dl_tests/bionic; then \
+ git pull; \
+ else \
+ git clone --depth=1
https://android.googlesource.com/platform/bionic $(src)/modules/dl_tests/bionic; \
+ fi
+
+all_tests := $(tests:%=dl_tests/%)
+
+build_all: get_file $(all_tests:%=$(out)/%)
+.PHONY: build_all
+
+$(out)/dl_tests/libtest_simple.so: $(bionic_test_libs)/dlopen_testlib_simple.cpp
+ $(makedir)
+ $(call quiet, cd $(out); $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_simple.so)
+
+$(out)/dl_tests/libtest_empty.so: $(bionic_test_libs)/empty.cpp
+ $(makedir)
+ $(call quiet, cd $(out); $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_empty.so)
+
+$(out)/dl_tests/libtest_check_rtld_next_from_library.so: $(bionic_test_libs)/check_rtld_next_from_library.cpp
+ $(makedir)
+ $(call quiet, cd $(out); $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_check_rtld_next_from_library.so)
+
+$(out)/dl_tests/libtest_dlsym_from_this_grandchild.so: $(bionic_test_libs)/dlsym_from_this_symbol2.cpp
+ $(makedir)
+ $(call quiet, cd $(out); $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_dlsym_from_this_grandchild.so)
+
+$(out)/dl_tests/libtest_dlsym_from_this_child.so: COMMON += -Wl,--no-as-needed -ltest_dlsym_from_this_grandchild -L=$(out)/dl_tests -L=/usr/lib -Wl,-rpath . -Wl,-rpath / -Wl,-rpath /usr/lib
+$(out)/dl_tests/libtest_dlsym_from_this_child.so: \
+ $(bionic_test_libs)/dlsym_from_this_functions.cpp \
+ $(out)/dl_tests/libtest_dlsym_from_this_grandchild.so
+ $(makedir)
+ $(call quiet, cd $(out)/tests; $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_dlsym_from_this_child.so)
+
+$(out)/dl_tests/libtest_dlsym_from_this.so: COMMON += -Wl,--no-as-needed -ltest_dlsym_from_this_child -L=$(out)/dl_tests -L=/usr/lib -Wl,-rpath . -Wl,-rpath / -Wl,-rpath /usr/lib
+$(out)/dl_tests/libtest_dlsym_from_this.so: \
+ $(bionic_test_libs)/dlsym_from_this_symbol.cpp \
+ $(out)/dl_tests/libtest_dlsym_from_this_child.so
+ $(makedir)
+ $(call quiet, cd $(out)/tests; $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_dlsym_from_this.so)
+
+$(out)/dl_tests/libdlext_test.so: COMMON += -Wl,-z,relro -Wl,--no-as-needed -ltest_simple -L=$(out)/dl_tests -L=/usr/lib -Wl,-rpath . -Wl,-rpath / -Wl,-rpath /usr/lib
+$(out)/dl_tests/libdlext_test.so: $(bionic_test_libs)/dlext_test_library.cpp
+ $(makedir)
+ $(call quiet, cd $(out)/tests; $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libdlext_test.so)
+
+$(out)/dl_tests/libtest_with_dependency.so: COMMON += -Wl,--no-as-needed -ldlext_test -L=$(out)/dl_tests -L=/usr/lib -Wl,-rpath . -Wl,-rpath / -Wl,-rpath /usr/lib
+$(out)/dl_tests/libtest_with_dependency.so: $(bionic_test_libs)/dlopen_testlib_simple.cpp
+ $(makedir)
+ $(call quiet, cd $(out)/tests; $(CXX) $(CXXFLAGS) -D__SHARED_OBJECT__=1 -shared -o $@ $<, CXX dl_tests/libtest_with_dependency.so)
+
+usr.manifest: build_all FORCE
+ @echo "/usr/lib/libtest_simple.so: ./dl_tests/libtest_simple.so" > $@
+ @echo "/usr/lib/libtest_empty.so: ./dl_tests/libtest_empty.so" >> $@
+ @echo "/usr/lib/libtest_dlsym_from_this_grandchild.so: ./dl_tests/libtest_dlsym_from_this_grandchild.so" >> $@
+ @echo "/usr/lib/libtest_dlsym_from_this_child.so: ./dl_tests/libtest_dlsym_from_this_child.so" >> $@
+ @echo "/usr/lib/libtest_dlsym_from_this.so: ./dl_tests/libtest_dlsym_from_this.so" >> $@
+ @echo "/usr/lib/libdlext_test.so: ./dl_tests/libdlext_test.so" >> $@
+ @echo "/usr/lib/libtest_with_dependency.so: ./dl_tests/libtest_with_dependency.so" >> $@
+ @echo "/usr/lib/libtest_check_rtld_next_from_library.so: ./dl_tests/libtest_check_rtld_next_from_library.so" >> $@
+ @echo "/tests/libtest_simple.so: ./dl_tests/libtest_simple.so" >> $@
+ @echo "/tests/libtest_empty.so: ./dl_tests/libtest_empty.so" >> $@
+ @echo "/tests/libtest_dlsym_from_this_grandchild.so: ./dl_tests/libtest_dlsym_from_this_grandchild.so" >> $@
+ @echo "/tests/libtest_dlsym_from_this_child.so: ./dl_tests/libtest_dlsym_from_this_child.so" >> $@
+ @echo "/tests/libtest_dlsym_from_this.so: ./dl_tests/libtest_dlsym_from_this.so" >> $@
+ @echo "/tests/libdlext_test.so: ./dl_tests/libdlext_test.so" >> $@
+ @echo "/tests/libtest_with_dependency.so: ./dl_tests/libtest_with_dependency.so" >> $@
+ @echo "/tests/libtest_check_rtld_next_from_library.so: ./dl_tests/libtest_check_rtld_next_from_library.so" >> $@
+.PHONY: FORCE
+FORCE:
+
+clean:
+ -rm -f usr.manifest
+
+ifneq ($(MAKECMDGOALS),clean)
+include $(shell test -d $(out)/dl_tests && find $(out)/dl_tests -name '*.d')
+endif
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index aac12d3d..ce004339 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -147,6 +147,8 @@ $(out)/tests/tst-mmap.so: COMMON += -fuse-ld=bfd
$(out)/tests/tst-elf-permissions.so: COMMON += -fuse-ld=bfd
$(out)/tests/tst-tls.so: COMMON += -fuse-ld=bfd
+$(out)/tests/tst-dlfcn.so: COMMON += -rdynamic -ldl
+
$(out)/tests/tst-tls.so: \
$(src)/tests/tst-tls.cc \
$(out)/tests/libtls.so
diff --git a/modules/tests/module.py b/modules/tests/module.py
index be6315c5..f795280d 100644
--- a/modules/tests/module.py
+++ b/modules/tests/module.py
@@ -1,3 +1,4 @@
from osv.modules import api
api.require('java-tests')
+api.require('dl_tests')
diff --git a/tests/tst-dlfcn.cc b/tests/tst-dlfcn.cc
index 3913c6bf..d2c9f288 100644
--- a/tests/tst-dlfcn.cc
+++ b/tests/tst-dlfcn.cc
@@ -10,6 +10,16 @@
#include <dlfcn.h>
#include <boost/test/unit_test.hpp>
+namespace utf = boost::unit_test;
+
+const bool rtld_next = false;
+const bool deep_lookup = false;
+
+static bool called = false;
+extern "C" void DlSymTestFunction()
+{
+ called = true;
+}
BOOST_AUTO_TEST_CASE(test_dlopen_with_null_as_filename)
{
@@ -53,3 +63,236 @@ BOOST_AUTO_TEST_CASE(test_dladdr)
BOOST_CHECK_EQUAL("vfprintf", info.dli_sname);
BOOST_CHECK_EQUAL(vfprintf, info.dli_saddr);
}
+
+BOOST_AUTO_TEST_CASE(test_dlsym_in_executable)
+{
+ dlerror();
+ void* self = dlopen(NULL, RTLD_NOW);
+ BOOST_REQUIRE_NE(self, nullptr);
+ BOOST_REQUIRE_EQUAL(dlerror(), nullptr);
+
+ void* sym = dlsym(self, "DlSymTestFunction");
+ BOOST_REQUIRE_NE(sym, nullptr);
+
+ void (*function)() = reinterpret_cast<void(*)()>(sym);
+
+ called = false;
+ function();
+ BOOST_REQUIRE_EQUAL(called, true);
+ BOOST_CHECK_EQUAL(0, dlclose(self));
+}
+
+BOOST_AUTO_TEST_CASE(test_dlsym_from_sofile,
+ *utf::enable_if<rtld_next>())
+{
+ dlerror();
+ void* handle = dlopen("/tests/libtest_dlsym_from_this.so", RTLD_LAZY | RTLD_LOCAL);
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE_NE(handle, nullptr);
+
+ // check that we can't find '_test_dlsym_symbol' via dlsym(RTLD_DEFAULT)
+ void* symbol = dlsym(RTLD_DEFAULT, "test_dlsym_symbol");
+ // TODO: dlopen ignores all the flags; the current implementation assumes the flag to be RTLD_GLOBAL but the default should be RTLD_LOCAL
+ // Should reenable the test after flags are implemented
+ // BOOST_REQUIRE_EQUAL(symbol, nullptr);
+ // auto err_msg = std::string(dlerror());
+ // BOOST_TEST_CONTEXT(err_msg)
+ // BOOST_REQUIRE_NE(err_msg.find("dlsym: symbol test_dlsym_symbol not found"),
+ // std::string::npos);
+ // BOOST_REQUIRE_NE(err_msg.find("undefined symbol: test_dlsym_symbol"),
+ // std::string::npos);
+
+ typedef int* (*fn_t)();
+ fn_t lookup_dlsym_symbol_using_RTLD_DEFAULT =
+ reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_DEFAULT"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE_NE(lookup_dlsym_symbol_using_RTLD_DEFAULT, nullptr);
+
+ int* ptr = lookup_dlsym_symbol_using_RTLD_DEFAULT();
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE_NE(ptr, nullptr);
+ BOOST_REQUIRE_EQUAL(42, *ptr);
+
+ fn_t lookup_dlsym_symbol2_using_RTLD_DEFAULT =
+ reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol2_using_RTLD_DEFAULT"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(lookup_dlsym_symbol2_using_RTLD_DEFAULT != nullptr);
+
+ ptr = lookup_dlsym_symbol2_using_RTLD_DEFAULT();
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(ptr != nullptr);
+ BOOST_REQUIRE_EQUAL(44, *ptr);
+
+ fn_t lookup_dlsym_symbol_using_RTLD_NEXT =
+ reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_NEXT"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(lookup_dlsym_symbol_using_RTLD_NEXT != nullptr);
+
+ ptr = lookup_dlsym_symbol_using_RTLD_NEXT();
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(ptr != nullptr);
+ BOOST_REQUIRE_EQUAL(43, *ptr);
+
+ dlclose(handle);
+}
+
+BOOST_AUTO_TEST_CASE(test_dlsym_from_sofile_with_preload,
+ *utf::enable_if<deep_lookup>())
+{
+ void* preload = dlopen("/tests/libtest_dlsym_from_this_grandchild.so", RTLD_NOW | RTLD_LOCAL);
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(preload != nullptr);
+
+ void* handle = dlopen("/tests/libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL);
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(handle != nullptr);
+
+ // check that we can't find '_test_dlsym_symbol' via dlsym(RTLD_DEFAULT)
+ void* symbol = dlsym(RTLD_DEFAULT, "test_dlsym_symbol");
+ // TODO: dlopen ignores all the flags; the current implementation assumes the flag to be RTLD_GLOBAL but the default should be RTLD_LOCAL
+ // Should reenable the test after flags are implemented
+ // BOOST_REQUIRE_EQUAL(symbol, nullptr);
+ // auto err_msg = std::string(dlerror());
+ // BOOST_TEST_CONTEXT(err_msg)
+ // BOOST_REQUIRE_NE(err_msg.find("dlsym: symbol test_dlsym_symbol not found"),
+ // std::string::npos);
+ // BOOST_REQUIRE_NE(err_msg.find("undefined symbol: test_dlsym_symbol"),
+ // std::string::npos);
+
+ typedef int* (*fn_t)();
+ fn_t lookup_dlsym_symbol_using_RTLD_DEFAULT =
+ reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_DEFAULT"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(lookup_dlsym_symbol_using_RTLD_DEFAULT != nullptr);
+
+ int* ptr = lookup_dlsym_symbol_using_RTLD_DEFAULT();
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(ptr != nullptr);
+ BOOST_REQUIRE_EQUAL(42, *ptr);
+
+ fn_t lookup_dlsym_symbol2_using_RTLD_DEFAULT =
+ reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol2_using_RTLD_DEFAULT"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(lookup_dlsym_symbol2_using_RTLD_DEFAULT != nullptr);
+
+ ptr = lookup_dlsym_symbol2_using_RTLD_DEFAULT();
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE_NE(ptr, nullptr);
+ BOOST_REQUIRE_EQUAL(44, *ptr);
+
+ fn_t lookup_dlsym_symbol_using_RTLD_NEXT =
+ reinterpret_cast<fn_t>(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_NEXT"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(lookup_dlsym_symbol_using_RTLD_NEXT != nullptr);
+
+ ptr = lookup_dlsym_symbol_using_RTLD_NEXT();
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(ptr != nullptr);
+ BOOST_REQUIRE_EQUAL(43, *ptr);
+
+ dlclose(handle);
+ dlclose(preload);
+}
+
+BOOST_AUTO_TEST_CASE(dlsym_handle_global_sym)
+{
+ // check that we do not look into global group
+ // when looking up symbol by handle
+ void* handle = dlopen("/tests/libtest_empty.so", RTLD_NOW);
+ dlopen("libtest_with_dependency.so", RTLD_NOW | RTLD_GLOBAL);
+ void* sym = dlsym(handle, "getRandomNumber");
+ BOOST_REQUIRE(sym == nullptr);
+ auto err_msg = std::string(dlerror());
+ BOOST_TEST_CONTEXT(err_msg)
+ BOOST_REQUIRE_NE(err_msg.find("dlsym: symbol getRandomNumber not found"),
+ std::string::npos);
+ // BOOST_REQUIRE_NE(err_msg.find("undefined symbol: getRandomNumber"),
+ // std::string::npos);
+
+ sym = dlsym(handle, "DlSymTestFunction");
+ BOOST_REQUIRE(sym == nullptr);
+ err_msg = std::string(dlerror());
+ BOOST_TEST_CONTEXT(err_msg)
+ BOOST_REQUIRE_NE(err_msg.find("dlsym: symbol DlSymTestFunction not found"),
+ std::string::npos);
+ // BOOST_REQUIRE_NE(err_msg.find("undefined symbol: DlSymTestFunction"),
+ // std::string::npos);
+ dlclose(handle);
+}
+
+BOOST_AUTO_TEST_CASE(dlsym_handle_empty_symbol)
+{
+ // check that dlsym of an empty symbol fails (see
http://b/33530622)
+ void* handle = dlopen("/tests/libtest_dlsym_from_this.so", RTLD_NOW);
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(handle != nullptr);
+ void* sym = dlsym(handle, "");
+ BOOST_REQUIRE(sym == nullptr);
+ auto err_msg = std::string(dlerror());
+ BOOST_TEST_CONTEXT(err_msg)
+ BOOST_REQUIRE_NE(err_msg.find("dlsym: symbol not found"),
+ std::string::npos);
+ // BOOST_REQUIRE_NE(err_msg.find("undefined symbol: "),
+ // std::string::npos);
+ dlclose(handle);
+}
+
+BOOST_AUTO_TEST_CASE(dlsym_with_dependencies,
+ *utf::enable_if<deep_lookup>())
+{
+ void* handle = dlopen("/tests/libtest_with_dependency.so", RTLD_NOW);
+ BOOST_REQUIRE(handle != nullptr);
+ dlerror();
+ // This symbol is in DT_NEEDED library.
+ void* sym = dlsym(handle, "getRandomNumber");
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE_NE(sym, nullptr);
+ int (*fn)(void);
+ fn = reinterpret_cast<int (*)(void)>(sym);
+ BOOST_CHECK_EQUAL(4, fn());
+ dlclose(handle);
+}
+
+BOOST_AUTO_TEST_CASE(rtld_default_unknown_symbol)
+{
+ void* addr = dlsym(RTLD_DEFAULT, "UNKNOWN");
+ BOOST_REQUIRE_EQUAL(addr, nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(rtld_default_fclose)
+{
+ void* addr = dlsym(RTLD_DEFAULT, "fclose");
+ BOOST_REQUIRE_NE(addr, nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(rtld_next_unknown_symbol,
+ *utf::enable_if<rtld_next>())
+{
+ void* addr = dlsym(RTLD_NEXT, "UNKNOWN");
+ BOOST_REQUIRE_EQUAL(addr, nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(rtld_next_fclose,
+ *utf::enable_if<rtld_next>())
+{
+ void* addr = dlsym(RTLD_NEXT, "fclose");
+ BOOST_REQUIRE_NE(addr, nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(rtld_next_from_lib, *utf::enable_if<rtld_next>())
+{
+ void* library_with_fclose = dlopen("/tests/libtest_check_rtld_next_from_library.so", RTLD_NOW | RTLD_GLOBAL);
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(library_with_fclose != nullptr);
+ void* expected_addr = dlsym(RTLD_DEFAULT, "fclose");
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(expected_addr != nullptr);
+ typedef void* (*get_libc_fclose_ptr_fn_t)();
+ get_libc_fclose_ptr_fn_t get_libc_fclose_ptr =
+ reinterpret_cast<get_libc_fclose_ptr_fn_t>(dlsym(library_with_fclose, "get_libc_fclose_ptr"));
+ BOOST_TEST_CONTEXT(dlerror())
+ BOOST_REQUIRE(get_libc_fclose_ptr != nullptr);
+ BOOST_REQUIRE_EQUAL(expected_addr, get_libc_fclose_ptr());
+
+ dlclose(library_with_fclose);
+}
--
2.17.1