From d97e9f546ea195686a78e539315b273393609b9e Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Sun, 29 Jun 2014 12:28:37 -0700 Subject: [PATCH] Add support for protected local symbol lookup. Bug: http://code.google.com/p/android/issues/detail?id=66048 Change-Id: Ib334223df27adad9477fb241ab099c5e26df4a7d --- linker/dlfcn.cpp | 27 +++++------- linker/linker.cpp | 51 ++++++++++++++++------- linker/linker.h | 4 +- tests/dlfcn_test.cpp | 14 +++++++ tests/libs/Android.mk | 14 +++++++ tests/libs/dlsym_local_symbol.map | 22 ++++++++++ tests/libs/dlsym_local_symbol_private.cpp | 24 +++++++++++ tests/libs/dlsym_local_symbol_public.cpp | 47 +++++++++++++++++++++ 8 files changed, 168 insertions(+), 35 deletions(-) create mode 100644 tests/libs/dlsym_local_symbol.map create mode 100644 tests/libs/dlsym_local_symbol_private.cpp create mode 100644 tests/libs/dlsym_local_symbol_public.cpp diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 529e20fd6..efb829e7a 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -100,30 +100,23 @@ void* dlsym(void* handle, const char* symbol) { soinfo* found = NULL; ElfW(Sym)* sym = NULL; - if (handle == RTLD_DEFAULT) { - sym = dlsym_linear_lookup(symbol, &found, NULL); - } else if (handle == RTLD_NEXT) { - void* caller_addr = __builtin_return_address(0); - soinfo* si = find_containing_library(caller_addr); + void* caller_addr = __builtin_return_address(0); + soinfo* caller_si = find_containing_library(caller_addr); + if (handle == RTLD_DEFAULT) { + sym = dlsym_linear_lookup(symbol, &found, NULL, caller_si); + } else if (handle == RTLD_NEXT) { sym = NULL; - if (si && si->next) { - sym = dlsym_linear_lookup(symbol, &found, si->next); + if (caller_si && caller_si->next) { + sym = dlsym_linear_lookup(symbol, &found, caller_si->next, caller_si); } } else { found = reinterpret_cast(handle); - sym = dlsym_handle_lookup(found, symbol); + sym = dlsym_handle_lookup(found, symbol, caller_si); } - if (sym != NULL) { - unsigned bind = ELF_ST_BIND(sym->st_info); - - if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) { - return reinterpret_cast(sym->st_value + found->load_bias); - } - - __bionic_format_dlerror("symbol found but not global", symbol); - return NULL; + if (sym != NULL && sym->st_shndx != 0) { + return reinterpret_cast(sym->st_value + found->load_bias); } else { __bionic_format_dlerror("undefined symbol", symbol); return NULL; diff --git a/linker/linker.cpp b/linker/linker.cpp index 45889485f..442f7cede 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -125,6 +125,11 @@ enum RelocationKind { kRelocMax }; +enum class SymbolLookupScope { + kAllowLocal, + kExcludeLocal, +}; + #if STATS struct linker_stats_t { int count[kRelocMax]; @@ -431,7 +436,7 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void return rv; } -static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { +static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name, const SymbolLookupScope& lookup_scope) { ElfW(Sym)* symtab = si->symtab; const char* strtab = si->strtab; @@ -442,18 +447,30 @@ static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) ElfW(Sym)* s = symtab + n; if (strcmp(strtab + s->st_name, name)) continue; - /* only concern ourselves with global and weak symbol definitions */ switch (ELF_ST_BIND(s->st_info)) { case STB_GLOBAL: case STB_WEAK: if (s->st_shndx == SHN_UNDEF) { - continue; - } + continue; + } - TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", + TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", name, si->name, reinterpret_cast(s->st_value), static_cast(s->st_size)); - return s; + return s; + case STB_LOCAL: + if (lookup_scope != SymbolLookupScope::kAllowLocal) { + continue; + } + TRACE_TYPE(LOOKUP, "FOUND LOCAL %s in %s (%p) %zd", + name, si->name, reinterpret_cast(s->st_value), + static_cast(s->st_size)); + return s; + default: + const char* msg = "FATAL: Unexpected ST_BIND\n"; + __libc_format_log(ANDROID_LOG_FATAL, "linker", "%s", msg); + write(2, msg, strlen(msg)); + abort(); } } @@ -484,7 +501,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, s */ if (si == somain) { - s = soinfo_elf_lookup(si, elf_hash, name); + s = soinfo_elf_lookup(si, elf_hash, name, SymbolLookupScope::kAllowLocal); if (s != NULL) { *lsi = si; goto done; @@ -501,7 +518,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, s if (!si->has_DT_SYMBOLIC) { DEBUG("%s: looking up %s in executable %s", si->name, name, somain->name); - s = soinfo_elf_lookup(somain, elf_hash, name); + s = soinfo_elf_lookup(somain, elf_hash, name, SymbolLookupScope::kExcludeLocal); if (s != NULL) { *lsi = somain; goto done; @@ -518,7 +535,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, s * and some the first non-weak definition. This is system dependent. * Here we return the first definition found for simplicity. */ - s = soinfo_elf_lookup(si, elf_hash, name); + s = soinfo_elf_lookup(si, elf_hash, name, SymbolLookupScope::kAllowLocal); if (s != NULL) { *lsi = si; goto done; @@ -532,7 +549,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, s if (si->has_DT_SYMBOLIC) { DEBUG("%s: looking up %s in executable %s after local scope", si->name, name, somain->name); - s = soinfo_elf_lookup(somain, elf_hash, name); + s = soinfo_elf_lookup(somain, elf_hash, name, SymbolLookupScope::kExcludeLocal); if (s != NULL) { *lsi = somain; goto done; @@ -543,7 +560,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, s /* Next, look for it in the preloads list */ for (int i = 0; g_ld_preloads[i] != NULL; i++) { - s = soinfo_elf_lookup(g_ld_preloads[i], elf_hash, name); + s = soinfo_elf_lookup(g_ld_preloads[i], elf_hash, name, SymbolLookupScope::kExcludeLocal); if (s != NULL) { *lsi = g_ld_preloads[i]; goto done; @@ -553,7 +570,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, s for (int i = 0; needed[i] != NULL; i++) { DEBUG("%s: looking up %s in %s", si->name, name, needed[i]->name); - s = soinfo_elf_lookup(needed[i], elf_hash, name); + s = soinfo_elf_lookup(needed[i], elf_hash, name, SymbolLookupScope::kExcludeLocal); if (s != NULL) { *lsi = needed[i]; goto done; @@ -582,8 +599,9 @@ done: Binary Interface) where in Chapter 5 it discuss resolving "Shared Object Dependencies" in breadth first search order. */ -ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name) { - return soinfo_elf_lookup(si, elfhash(name), name); +ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name, soinfo* caller) { + return soinfo_elf_lookup(si, elfhash(name), name, + caller == si ? SymbolLookupScope::kAllowLocal : SymbolLookupScope::kExcludeLocal); } /* This is used by dlsym(3) to performs a global symbol lookup. If the @@ -591,7 +609,7 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name) { beginning of the global solist. Otherwise the search starts at the specified soinfo (for RTLD_NEXT). */ -ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) { +ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start, soinfo* caller) { unsigned elf_hash = elfhash(name); if (start == NULL) { @@ -600,7 +618,8 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) ElfW(Sym)* s = NULL; for (soinfo* si = start; (s == NULL) && (si != NULL); si = si->next) { - s = soinfo_elf_lookup(si, elf_hash, name); + s = soinfo_elf_lookup(si, elf_hash, name, + caller == si ? SymbolLookupScope::kAllowLocal : SymbolLookupScope::kExcludeLocal); if (s != NULL) { *found = si; break; diff --git a/linker/linker.h b/linker/linker.h index 0a72d9256..e1112e6e6 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -234,11 +234,11 @@ void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo); void do_dlclose(soinfo* si); -ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start); +ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start, soinfo* caller_si); soinfo* find_containing_library(const void* addr); ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr); -ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name); +ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name, soinfo* caller_si); void debuggerd_init(); extern "C" abort_msg_t* g_abort_message; diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 8b8918326..18963cf3f 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -50,6 +50,20 @@ TEST(dlfcn, dlsym_in_self) { ASSERT_EQ(0, dlclose(self)); } +TEST(dlfcn, dlsym_local_symbol) { + void* handle = dlopen("libtest_local_symbol.so", RTLD_NOW); + ASSERT_TRUE(handle != NULL); + dlerror(); + void* sym = dlsym(handle, "private_taxicab_number"); + ASSERT_TRUE(sym == NULL); + ASSERT_STREQ("undefined symbol: private_taxicab_number", dlerror()); + + uint32_t (*f)(void); + f = reinterpret_cast(dlsym(handle, "dlsym_local_symbol_get_taxicab_number_using_dlsym")); + ASSERT_TRUE(f != NULL); + ASSERT_EQ(1729, f()); +} + TEST(dlfcn, dlopen_noload) { void* handle = dlopen("libtest_simple.so", RTLD_NOW | RTLD_NOLOAD); ASSERT_TRUE(handle == NULL); diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 67ea562b3..efce5b53a 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -88,6 +88,20 @@ build_type := target build_target := SHARED_LIBRARY include $(TEST_PATH)/Android.build.mk +# ----------------------------------------------------------------------------- +# Library used to test local symbol lookup +# ----------------------------------------------------------------------------- +libtest_local_symbol_src_files := \ + dlsym_local_symbol_private.cpp \ + dlsym_local_symbol_public.cpp + +module := libtest_local_symbol +build_target := SHARED_LIBRARY +libtest_local_symbol_ldflags := -Wl,--version-script=$(LOCAL_PATH)/dlsym_local_symbol.map +libtest_local_symbol_cppflags := -std=gnu++11 +libtest_local_symbol_shared_libraries_target := libdl +build_type := target +include $(TEST_PATH)/Android.build.mk # ----------------------------------------------------------------------------- # Library used by atexit tests diff --git a/tests/libs/dlsym_local_symbol.map b/tests/libs/dlsym_local_symbol.map new file mode 100644 index 000000000..58a2299a8 --- /dev/null +++ b/tests/libs/dlsym_local_symbol.map @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 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. + */ +LIBTEST_LOCAL_SYMBOL_1.0 { + global: + dlsym_local_symbol_get_taxicab_number; + dlsym_local_symbol_get_taxicab_number_using_dlsym; + local: + *; +}; diff --git a/tests/libs/dlsym_local_symbol_private.cpp b/tests/libs/dlsym_local_symbol_private.cpp new file mode 100644 index 000000000..2587508e9 --- /dev/null +++ b/tests/libs/dlsym_local_symbol_private.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 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 +#include +#include + +// This symbol is declared local in +// the linker version map: libdlsym_local_symbol.map. +// It should not be visible from the outside. +extern "C" const uint32_t __attribute__ ((visibility ("protected"))) private_taxicab_number = 1729; diff --git a/tests/libs/dlsym_local_symbol_public.cpp b/tests/libs/dlsym_local_symbol_public.cpp new file mode 100644 index 000000000..d9da32a59 --- /dev/null +++ b/tests/libs/dlsym_local_symbol_public.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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 +#include +#include + +extern const uint32_t private_taxicab_number; + +extern "C" { +uint32_t dlsym_local_symbol_get_taxicab_number(); +uint32_t dlsym_local_symbol_get_taxicab_number_using_dlsym(); +} + +uint32_t dlsym_local_symbol_get_taxicab_number() { + return private_taxicab_number; +} + +// Let's make sure that dlsym works correctly for local symbol +uint32_t dlsym_local_symbol_get_taxicab_number_using_dlsym() { + dlerror(); + uint32_t* ptr = reinterpret_cast(dlsym(RTLD_DEFAULT, "private_taxicab_number")); + if (ptr == nullptr) { + const char* dlerr = dlerror(); + if (dlerr != nullptr) { + fprintf(stderr, "dlsym error: %s\n", dlerr); + } else { + fprintf(stderr, "dlsym returned NULL with no dlerror.\n"); + } + return 0; + } + + return *ptr; +}