From 76ac1acdacc045cf1e56504e011dca68137dcd61 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 1 Apr 2015 14:45:10 -0700 Subject: [PATCH] Look into local group on dlsym with RTLD_DEFAULT Fix dlsym to look into local group when called with RTLD_DEFAULT and RTLD_NEXT. Bug: 17512583 Change-Id: I541354e89539c712af2ea4ec751e546913027084 --- linker/dlfcn.cpp | 15 +++++---------- linker/linker.cpp | 38 ++++++++++++++++++++++++++++++++++---- linker/linker.h | 2 +- tests/dlfcn_test.cpp | 23 ++++++++++++++++++++++- tests/libs/Android.mk | 9 +++++++++ tests/libs/dlsym_from_this.cpp | 30 ++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 tests/libs/dlsym_from_this.cpp diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 64df7a570..479e8317e 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -101,16 +101,11 @@ void* dlsym(void* handle, const char* symbol) { soinfo* found = nullptr; ElfW(Sym)* sym = nullptr; - if (handle == RTLD_DEFAULT) { - sym = dlsym_linear_lookup(symbol, &found, nullptr); - } else if (handle == RTLD_NEXT) { - void* caller_addr = __builtin_return_address(0); - soinfo* si = find_containing_library(caller_addr); - - sym = nullptr; - if (si && si->next) { - sym = dlsym_linear_lookup(symbol, &found, si->next); - } + void* caller_addr = __builtin_return_address(0); + soinfo* caller = find_containing_library(caller_addr); + + if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) { + sym = dlsym_linear_lookup(symbol, &found, caller, handle); } else { sym = dlsym_handle_lookup(reinterpret_cast(handle), &found, symbol); } diff --git a/linker/linker.cpp b/linker/linker.cpp index ebf125e1f..002a4f969 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -738,15 +738,21 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, 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* caller, void* handle) { SymbolName symbol_name(name); - if (start == nullptr) { - start = solist; + soinfo* start = solist; + + if (handle == RTLD_NEXT) { + if (caller == nullptr || caller->next == nullptr) { + return nullptr; + } else { + start = caller->next; + } } ElfW(Sym)* s = nullptr; - for (soinfo* si = start; (s == nullptr) && (si != nullptr); si = si->next) { + for (soinfo* si = start; si != nullptr; si = si->next) { if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) { continue; } @@ -758,6 +764,30 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) } } + // If not found - look into local_group unless + // caller is part of the global group in which + // case we already did it. + if (s == nullptr && caller != nullptr && + (caller->get_rtld_flags() & RTLD_GLOBAL) == 0) { + soinfo* local_group_root = caller->get_local_group_root(); + + if (handle == RTLD_DEFAULT) { + start = local_group_root; + } + + for (soinfo* si = start; si != nullptr; si = si->next) { + if (si->get_local_group_root() != local_group_root) { + break; + } + + s = si->find_symbol_by_name(symbol_name); + if (s != nullptr) { + *found = si; + break; + } + } + } + if (s != nullptr) { TRACE_TYPE(LOOKUP, "%s s->st_value = %p, found->base = %p", name, reinterpret_cast(s->st_value), reinterpret_cast((*found)->base)); diff --git a/linker/linker.h b/linker/linker.h index bf3e7bf5d..ec3d8f051 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -351,7 +351,7 @@ 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* caller, void* handle); soinfo* find_containing_library(const void* addr); ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 1061e84f2..a63c070cf 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -46,7 +46,7 @@ TEST(dlfcn, ctor_function_call) { ASSERT_EQ(17, g_ctor_function_called); } -TEST(dlfcn, dlsym_in_self) { +TEST(dlfcn, dlsym_in_executable) { dlerror(); // Clear any pending errors. void* self = dlopen(NULL, RTLD_NOW); ASSERT_TRUE(self != NULL); @@ -64,6 +64,27 @@ TEST(dlfcn, dlsym_in_self) { ASSERT_EQ(0, dlclose(self)); } +TEST(dlfcn, dlsym_from_sofile) { + void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_LAZY | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + // check that we cant find '_test_dlsym_symbol' via dlsym(RTLD_DEFAULT) + void* symbol = dlsym(RTLD_DEFAULT, "test_dlsym_symbol"); + ASSERT_TRUE(symbol == nullptr); + ASSERT_SUBSTR("undefined symbol: test_dlsym_symbol", dlerror()); + + typedef int* (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_DEFAULT")); + + ASSERT_TRUE(fn != nullptr) << dlerror(); + + int* ptr = fn(); + ASSERT_TRUE(ptr != nullptr) << dlerror(); + ASSERT_EQ(42, *ptr); + + dlclose(handle); +} + TEST(dlfcn, dlsym_with_dependencies) { void* handle = dlopen("libtest_with_dependency.so", RTLD_NOW); ASSERT_TRUE(handle != NULL); diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 665ce0cb7..eb0a52a15 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -371,6 +371,15 @@ module := libtest_dlsym_weak_func include $(LOCAL_PATH)/Android.build.testlib.mk # ----------------------------------------------------------------------------- +# Library to check RTLD_LOCAL with dlsym in 'this' +# ----------------------------------------------------------------------------- +libtest_dlsym_from_this_src_files := dlsym_from_this.cpp + +module := libtest_dlsym_from_this + +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- # Library with weak undefined function # ----------------------------------------------------------------------------- libtest_dlopen_weak_undefined_func_src_files := \ diff --git a/tests/libs/dlsym_from_this.cpp b/tests/libs/dlsym_from_this.cpp new file mode 100644 index 000000000..b5215c941 --- /dev/null +++ b/tests/libs/dlsym_from_this.cpp @@ -0,0 +1,30 @@ +/* + * 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 + +int test_dlsym_symbol = 42; + +extern "C" int* lookup_dlsym_symbol_using_RTLD_DEFAULT() { + dlerror(); + int* result = static_cast(dlsym(RTLD_DEFAULT, "test_dlsym_symbol")); + // TODO: remove this once b/20049306 is fixed + if (result == nullptr) { + printf("Cannot find the answer\n"); + } + return result; +} + -- 2.11.0