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;