From 9cf99cbad89c8495828788ce693a99ced434f66f Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Fri, 11 Dec 2015 14:22:24 -0800 Subject: [PATCH] linker: add dlvsym(3) This changes implements dlvsym - dlsym for versioned symbols. Bug: http://b/22865643 Change-Id: Ic90a60d512104261a1416c43f9100f0d88e3b46f --- libc/include/dlfcn.h | 11 ++++++----- libdl/libdl.arm.map | 1 + libdl/libdl.arm64.map | 1 + libdl/libdl.c | 8 ++++++++ libdl/libdl.map.txt | 1 + libdl/libdl.mips.map | 1 + libdl/libdl.mips64.map | 1 + libdl/libdl.x86.map | 1 + libdl/libdl.x86_64.map | 1 + linker/dlfcn.cpp | 30 ++++++++++++++++++++---------- linker/linker.cpp | 6 +++--- tests/dlfcn_test.cpp | 32 +++++++++++++++++++++++--------- 12 files changed, 67 insertions(+), 27 deletions(-) diff --git a/libc/include/dlfcn.h b/libc/include/dlfcn.h index afa76878f..c2e89802e 100644 --- a/libc/include/dlfcn.h +++ b/libc/include/dlfcn.h @@ -43,11 +43,12 @@ typedef struct { in dli_sname */ } Dl_info; -extern void* dlopen(const char* filename, int flag); -extern int dlclose(void* handle); -extern const char* dlerror(void); -extern void* dlsym(void* handle, const char* symbol); -extern int dladdr(const void* addr, Dl_info *info); +extern void* dlopen(const char* filename, int flag); +extern int dlclose(void* handle); +extern const char* dlerror(void); +extern void* dlsym(void* handle, const char* symbol) __nonnull((2)); +extern void* dlvsym(void* handle, const char* symbol, const char* version) __nonnull((2, 3)); +extern int dladdr(const void* addr, Dl_info *info); enum { #if defined(__LP64__) diff --git a/libdl/libdl.arm.map b/libdl/libdl.arm.map index 5ad9f9d20..b9e494a62 100644 --- a/libdl/libdl.arm.map +++ b/libdl/libdl.arm.map @@ -18,6 +18,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/libdl/libdl.arm64.map b/libdl/libdl.arm64.map index 3535774f9..a8c98daa7 100644 --- a/libdl/libdl.arm64.map +++ b/libdl/libdl.arm64.map @@ -17,6 +17,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/libdl/libdl.c b/libdl/libdl.c index af2f83e78..0604d3eee 100644 --- a/libdl/libdl.c +++ b/libdl/libdl.c @@ -24,9 +24,17 @@ // in the dynamic linker and hijacked at runtime. void* dlopen(const char* filename __unused, int flag __unused) { return 0; } + const char* dlerror(void) { return 0; } + void* dlsym(void* handle __unused, const char* symbol __unused) { return 0; } + +void* dlvsym(void* handle __unused, const char* symbol __unused, const char* version __unused) { + return 0; +} + int dladdr(const void* addr __unused, Dl_info* info __unused) { return 0; } + int dlclose(void* handle __unused) { return 0; } #if defined(__arm__) diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt index 8d123fe4b..55a03cbdf 100644 --- a/libdl/libdl.map.txt +++ b/libdl/libdl.map.txt @@ -32,6 +32,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/libdl/libdl.mips.map b/libdl/libdl.mips.map index 3535774f9..a8c98daa7 100644 --- a/libdl/libdl.mips.map +++ b/libdl/libdl.mips.map @@ -17,6 +17,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/libdl/libdl.mips64.map b/libdl/libdl.mips64.map index 3535774f9..a8c98daa7 100644 --- a/libdl/libdl.mips64.map +++ b/libdl/libdl.mips64.map @@ -17,6 +17,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/libdl/libdl.x86.map b/libdl/libdl.x86.map index 3535774f9..a8c98daa7 100644 --- a/libdl/libdl.x86.map +++ b/libdl/libdl.x86.map @@ -17,6 +17,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/libdl/libdl.x86_64.map b/libdl/libdl.x86_64.map index 3535774f9..a8c98daa7 100644 --- a/libdl/libdl.x86_64.map +++ b/libdl/libdl.x86_64.map @@ -17,6 +17,7 @@ LIBC_N { global: android_init_namespaces; android_create_namespace; + dlvsym; } LIBC; LIBC_PRIVATE { diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 788a7a036..ba54d3973 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -88,11 +88,10 @@ void* dlopen(const char* filename, int flags) { extern android_namespace_t* g_anonymous_namespace; -void* dlsym(void* handle, const char* symbol) { - void* caller_addr = __builtin_return_address(0); +void* dlsym_impl(void* handle, const char* symbol, const char* version, void* caller_addr) { ScopedPthreadMutexLocker locker(&g_dl_mutex); void* result; - if (!do_dlsym(handle, symbol, nullptr, caller_addr, &result)) { + if (!do_dlsym(handle, symbol, version, caller_addr, &result)) { __bionic_format_dlerror(linker_get_error_buffer(), nullptr); return nullptr; } @@ -100,6 +99,16 @@ void* dlsym(void* handle, const char* symbol) { return result; } +void* dlsym(void* handle, const char* symbol) { + void* caller_addr = __builtin_return_address(0); + return dlsym_impl(handle, symbol, nullptr, caller_addr); +} + +void* dlvsym(void* handle, const char* symbol, const char* version) { + void* caller_addr = __builtin_return_address(0); + return dlsym_impl(handle, symbol, version, caller_addr); +} + int dladdr(const void* addr, Dl_info* info) { ScopedPthreadMutexLocker locker(&g_dl_mutex); return do_dladdr(addr, info); @@ -179,11 +188,11 @@ static const char ANDROID_LIBDL_STRTAB[] = // 00000000001 1111111112222222222 3333333333444444444455555555556666666666777 777777788888888889999999999 // 01234567890 1234567890123456789 0123456789012345678901234567890123456789012 345678901234567890123456789 "erate_phdr\0android_dlopen_ext\0android_set_application_target_sdk_version\0android_get_application_tar" - // 0000000000111111 111122222222223333333333 4444444444555555555566666 - // 0123456789012345 678901234567890123456789 0123456789012345678901234 - "get_sdk_version\0android_init_namespaces\0android_create_namespace\0" + // 0000000000111111 111122222222223333333333 4444444444555555555566666 6666677 + // 0123456789012345 678901234567890123456789 0123456789012345678901234 5678901 + "get_sdk_version\0android_init_namespaces\0android_create_namespace\0dlvsym\0" #if defined(__arm__) - // 265 + // 272 "dl_unwind_find_exidx\0" #endif ; @@ -207,8 +216,9 @@ static ElfW(Sym) g_libdl_symtab[] = { ELFW(SYM_INITIALIZER)(173, &android_get_application_target_sdk_version, 1), ELFW(SYM_INITIALIZER)(216, &android_init_namespaces, 1), ELFW(SYM_INITIALIZER)(240, &android_create_namespace, 1), + ELFW(SYM_INITIALIZER)(265, &dlvsym, 1), #if defined(__arm__) - ELFW(SYM_INITIALIZER)(265, &dl_unwind_find_exidx, 1), + ELFW(SYM_INITIALIZER)(272, &dl_unwind_find_exidx, 1), #endif }; @@ -225,9 +235,9 @@ static ElfW(Sym) g_libdl_symtab[] = { // Note that adding any new symbols here requires stubbing them out in libdl. static unsigned g_libdl_buckets[1] = { 1 }; #if defined(__arm__) -static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0 }; +static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 }; #else -static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0 }; +static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0 }; #endif static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8))); diff --git a/linker/linker.cpp b/linker/linker.cpp index 99152ed9d..e38e2523c 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -2187,7 +2187,7 @@ static std::string symbol_display_name(const char* sym_name, const char* sym_ver return sym_name; } - return std::string(sym_name) + "@" + sym_ver; + return std::string(sym_name) + ", version " + sym_ver; } void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { @@ -2312,8 +2312,8 @@ bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver, version_info* vi = nullptr; if (sym_ver != nullptr) { - vi_instance.name = sym_name; - vi_instance.elf_hash = calculate_elf_hash(sym_name); + vi_instance.name = sym_ver; + vi_instance.elf_hash = calculate_elf_hash(sym_ver); vi = &vi_instance; } diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 30eea4ae6..81479d51b 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -624,8 +624,10 @@ TEST(dlfcn, dlopen_check_loop) { handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW | RTLD_NOLOAD); ASSERT_TRUE(handle == nullptr); #ifdef __BIONIC__ - // TODO: glibc returns nullptr on dlerror() here. Is it bug? ASSERT_STREQ("dlopen failed: library \"libtest_with_dependency_loop.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); +#else + // TODO: glibc returns nullptr on dlerror() here. Is it bug? + ASSERT_TRUE(dlerror() == nullptr); #endif handle = dlopen("libtest_with_dependency_a.so", RTLD_NOW | RTLD_NOLOAD); @@ -763,14 +765,6 @@ TEST(dlfcn, dlsym_failures) { ASSERT_STREQ("dlsym failed: library handle is null", dlerror()); #endif - // NULL symbol name. -#if defined(__BIONIC__) - // glibc marks this parameter non-null and SEGVs if you cheat. - sym = dlsym(self, nullptr); - ASSERT_TRUE(sym == nullptr); - ASSERT_STREQ("dlsym failed: symbol name is null", dlerror()); -#endif - // Symbol that doesn't exist. sym = dlsym(self, "ThisSymbolDoesNotExist"); ASSERT_TRUE(sym == nullptr); @@ -1054,6 +1048,26 @@ TEST(dlfcn, symbol_versioning_default_via_dlsym) { dlclose(handle); } +TEST(dlfcn, dlvsym_smoke) { + void* handle = dlopen("libtest_versioned_lib.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(); + + { + fn_t fn = reinterpret_cast(dlvsym(handle, "versioned_function", "nonversion")); + ASSERT_TRUE(fn == nullptr); + ASSERT_SUBSTR("undefined symbol: versioned_function, version nonversion", dlerror()); + } + + { + fn_t fn = reinterpret_cast(dlvsym(handle, "versioned_function", "TESTLIB_V2")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(2, fn()); + } + + dlclose(handle); +} + // This preempts the implementation from libtest_versioned_lib.so extern "C" int version_zero_function() { return 0;