Support symbol versioning
Bug: http://b/20139821
Change-Id: I64122a0fb0960c20b2ce614161b7ab048456b681
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 708e2cd..1023644 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -925,3 +925,63 @@
GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
#endif
}
+
+TEST(dlfcn, symbol_versioning_use_v1) {
+ void* handle = dlopen("libtest_versioned_uselibv1.so", RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+ typedef int (*fn_t)();
+ fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
+ ASSERT_TRUE(fn != nullptr) << dlerror();
+ ASSERT_EQ(1, fn());
+ dlclose(handle);
+}
+
+TEST(dlfcn, symbol_versioning_use_v2) {
+ void* handle = dlopen("libtest_versioned_uselibv2.so", RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+ typedef int (*fn_t)();
+ fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
+ ASSERT_TRUE(fn != nullptr) << dlerror();
+ ASSERT_EQ(2, fn());
+ dlclose(handle);
+}
+
+TEST(dlfcn, symbol_versioning_use_other_v2) {
+ void* handle = dlopen("libtest_versioned_uselibv2_other.so", RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+ typedef int (*fn_t)();
+ fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
+ ASSERT_TRUE(fn != nullptr) << dlerror();
+ ASSERT_EQ(20, fn());
+ dlclose(handle);
+}
+
+TEST(dlfcn, symbol_versioning_use_other_v3) {
+ void* handle = dlopen("libtest_versioned_uselibv3_other.so", RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+ typedef int (*fn_t)();
+ fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
+ ASSERT_TRUE(fn != nullptr) << dlerror();
+ ASSERT_EQ(3, fn());
+ dlclose(handle);
+}
+
+TEST(dlfcn, symbol_versioning_default_via_dlsym) {
+ void* handle = dlopen("libtest_versioned_lib.so", RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+ typedef int (*fn_t)();
+ fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "versioned_function"));
+ ASSERT_TRUE(fn != nullptr) << dlerror();
+ ASSERT_EQ(3, fn()); // the default version is 3
+ dlclose(handle);
+}
+
+// This preempts the implementation from libtest_versioned_lib.so
+extern "C" int version_zero_function() {
+ return 0;
+}
+
+// This preempts the implementation from libtest_versioned_uselibv*.so
+extern "C" int version_zero_function2() {
+ return 0;
+}
diff --git a/tests/libs/Android.build.versioned_lib.mk b/tests/libs/Android.build.versioned_lib.mk
new file mode 100644
index 0000000..f3a6374
--- /dev/null
+++ b/tests/libs/Android.build.versioned_lib.mk
@@ -0,0 +1,120 @@
+#
+# Copyright (C) 2015 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.
+#
+
+# -----------------------------------------------------------------------------
+# Libraries used to test versioned symbols
+# -----------------------------------------------------------------------------
+libtest_versioned_uselibv1_src_files := versioned_uselib.cpp
+
+libtest_versioned_uselibv1_shared_libraries := \
+ libtest_versioned_libv1
+
+module := libtest_versioned_uselibv1
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+libtest_versioned_uselibv2_src_files := \
+ versioned_uselib.cpp
+
+libtest_versioned_uselibv2_shared_libraries := \
+ libtest_versioned_libv2
+
+libtest_versioned_uselibv2_ldflags := \
+ -Wl,--version-script,$(LOCAL_PATH)/versioned_uselib.map
+
+module := libtest_versioned_uselibv2
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+libtest_versioned_uselibv2_other_src_files := \
+ versioned_uselib.cpp
+
+libtest_versioned_uselibv2_other_shared_libraries := \
+ libtest_versioned_otherlib_empty libtest_versioned_libv2
+
+module := libtest_versioned_uselibv2_other
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+libtest_versioned_uselibv3_other_src_files := \
+ versioned_uselib.cpp
+
+libtest_versioned_uselibv3_other_shared_libraries := \
+ libtest_versioned_otherlib_empty libtest_versioned_lib
+
+module := libtest_versioned_uselibv3_other
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# lib v1 - this one used during static linking but never used at runtime
+# which forces libtest_versioned_uselibv1 to use function v1 from
+# libtest_versioned_lib.so
+# -----------------------------------------------------------------------------
+libtest_versioned_libv1_src_files := \
+ versioned_lib_v1.cpp
+
+libtest_versioned_libv1_ldflags := \
+ -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v1.map \
+ -Wl,-soname,libtest_versioned_lib.so
+
+module := libtest_versioned_libv1
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# lib v2 - to make libtest_versioned_uselibv2.so use version 2 of versioned_function()
+# -----------------------------------------------------------------------------
+libtest_versioned_libv2_src_files := \
+ versioned_lib_v2.cpp
+
+libtest_versioned_libv2_ldflags := \
+ -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v2.map \
+ -Wl,-soname,libtest_versioned_lib.so
+
+module := libtest_versioned_libv2
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+
+# -----------------------------------------------------------------------------
+# last version - this one is used at the runtime and exports 3 versions
+# of versioned_symbol().
+# -----------------------------------------------------------------------------
+libtest_versioned_lib_src_files := \
+ versioned_lib_v3.cpp
+
+libtest_versioned_lib_ldflags := \
+ -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v3.map
+
+module := libtest_versioned_lib
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# This library is empty, the actual implementation will provide an unversioned
+# symbol for versioned_function().
+# -----------------------------------------------------------------------------
+libtest_versioned_otherlib_empty_src_files := empty.cpp
+
+libtest_versioned_otherlib_empty_ldflags := -Wl,-soname,libtest_versioned_otherlib.so
+module := libtest_versioned_otherlib_empty
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+libtest_versioned_otherlib_src_files := versioned_lib_other.cpp
+
+libtest_versioned_otherlib_ldflags := \
+ -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_other.map
+
+module := libtest_versioned_otherlib
+include $(LOCAL_PATH)/Android.build.testlib.mk
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index da3fb1e..3d5b060 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -26,6 +26,7 @@
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
$(LOCAL_PATH)/Android.build.testlib.mk \
+ $(LOCAL_PATH)/Android.build.versioned_lib.mk \
$(TEST_PATH)/Android.build.mk
# -----------------------------------------------------------------------------
@@ -198,6 +199,11 @@
include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk
# -----------------------------------------------------------------------------
+# Build libtest_versioned_lib.so with its dependencies.
+# -----------------------------------------------------------------------------
+include $(LOCAL_PATH)/Android.build.versioned_lib.mk
+
+# -----------------------------------------------------------------------------
# Library with dependency loop used by dlfcn tests
#
# libtest_with_dependency_loop -> a -> b -> c -> a
diff --git a/tests/libs/versioned_lib_other.cpp b/tests/libs/versioned_lib_other.cpp
new file mode 100644
index 0000000..60fa99a
--- /dev/null
+++ b/tests/libs/versioned_lib_other.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+extern "C" int versioned_function_v2() {
+ return 20;
+}
+
+__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
diff --git a/tests/libs/versioned_lib_other.map b/tests/libs/versioned_lib_other.map
new file mode 100644
index 0000000..752686d
--- /dev/null
+++ b/tests/libs/versioned_lib_other.map
@@ -0,0 +1,9 @@
+TESTLIB_V0 {
+ local:
+ versioned_function_v*;
+};
+
+TESTLIB_V2 {
+ global:
+ versioned_function;
+} TESTLIB_V0;
diff --git a/tests/libs/versioned_lib_v1.cpp b/tests/libs/versioned_lib_v1.cpp
new file mode 100644
index 0000000..c81cbf1
--- /dev/null
+++ b/tests/libs/versioned_lib_v1.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+extern "C" {
+ int versioned_function_v1(); // __attribute__((visibility("hidden")));
+ int version_zero_function();
+}
+
+int versioned_function_v1() {
+ return 1;
+}
+
+int version_zero_function() {
+ return 100;
+}
+
+__asm__(".symver versioned_function_v1,versioned_function@@TESTLIB_V1");
diff --git a/tests/libs/versioned_lib_v1.map b/tests/libs/versioned_lib_v1.map
new file mode 100644
index 0000000..dbda327
--- /dev/null
+++ b/tests/libs/versioned_lib_v1.map
@@ -0,0 +1,12 @@
+TESTLIB_V0 {
+ global:
+ version_zero_function;
+ local:
+ versioned_function_v*;
+};
+
+TESTLIB_V1 {
+ global:
+ versioned_function;
+} TESTLIB_V0;
+
diff --git a/tests/libs/versioned_lib_v2.cpp b/tests/libs/versioned_lib_v2.cpp
new file mode 100644
index 0000000..d7d413f
--- /dev/null
+++ b/tests/libs/versioned_lib_v2.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+extern "C" {
+ int versioned_function_v1(); // __attribute__((visibility("hidden")));
+ int versioned_function_v2(); // __attribute__((visibility("hidden")));
+ int version_zero_function();
+}
+
+int versioned_function_v1() {
+ return 1;
+}
+
+int versioned_function_v2() {
+ return 2;
+}
+
+int version_zero_function() {
+ return 200;
+}
+__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
+__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
diff --git a/tests/libs/versioned_lib_v2.map b/tests/libs/versioned_lib_v2.map
new file mode 100644
index 0000000..bb38102
--- /dev/null
+++ b/tests/libs/versioned_lib_v2.map
@@ -0,0 +1,16 @@
+TESTLIB_V0 {
+ global:
+ version_zero_function;
+ local:
+ versioned_function_v*;
+};
+
+TESTLIB_V1 {
+ global:
+ versioned_function;
+} TESTLIB_V0;
+
+TESTLIB_V2 {
+ global:
+ versioned_function;
+} TESTLIB_V1;
diff --git a/tests/libs/versioned_lib_v3.cpp b/tests/libs/versioned_lib_v3.cpp
new file mode 100644
index 0000000..f4740a4
--- /dev/null
+++ b/tests/libs/versioned_lib_v3.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+extern "C" {
+ int versioned_function_v1(); // __attribute__((visibility("hidden")));
+ int versioned_function_v2(); // __attribute__((visibility("hidden")));
+ int versioned_function_v3(); // __attribute__((visibility("hidden")));
+ int version_zero_function();
+}
+
+int versioned_function_v1() {
+ return 1;
+}
+
+int versioned_function_v2() {
+ return 2;
+}
+
+int versioned_function_v3() {
+ return 3;
+}
+
+int version_zero_function() {
+ return 1000;
+}
+
+__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
+__asm__(".symver versioned_function_v2,versioned_function@TESTLIB_V2");
+__asm__(".symver versioned_function_v3,versioned_function@@TESTLIB_V3");
diff --git a/tests/libs/versioned_lib_v3.map b/tests/libs/versioned_lib_v3.map
new file mode 100644
index 0000000..5b1ce59
--- /dev/null
+++ b/tests/libs/versioned_lib_v3.map
@@ -0,0 +1,21 @@
+TESTLIB_V0 {
+ global:
+ version_zero_function;
+ local:
+ versioned_function_v*;
+};
+
+TESTLIB_V1 {
+ global:
+ versioned_function;
+} TESTLIB_V0;
+
+TESTLIB_V2 {
+ global:
+ versioned_function;
+} TESTLIB_V1;
+
+TESTLIB_V3 {
+ global:
+ versioned_function;
+} TESTLIB_V2;
diff --git a/tests/libs/versioned_uselib.cpp b/tests/libs/versioned_uselib.cpp
new file mode 100644
index 0000000..96eb7c3
--- /dev/null
+++ b/tests/libs/versioned_uselib.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+extern "C" {
+ int versioned_function();
+
+ int get_function_version();
+ int version_zero_function();
+ int version_zero_function2() __attribute__((weak));
+}
+
+int get_function_version() {
+ return version_zero_function2() + version_zero_function() + versioned_function();
+}
+
+// we expect this function to be preempted by main executable.
+int version_zero_function2() {
+ return 40000;
+}
diff --git a/tests/libs/versioned_uselib.map b/tests/libs/versioned_uselib.map
new file mode 100644
index 0000000..10bc9ce
--- /dev/null
+++ b/tests/libs/versioned_uselib.map
@@ -0,0 +1,9 @@
+TESTLIB_NONE {
+ global:
+ get_function_version;
+};
+
+TESTLIB_ZERO {
+ global:
+ version_zero_function2;
+} TESTLIB_NONE;