Move dlsym and dladdr implementation to linker.cpp
Bug: http://b/25716705
Bug: http://b/22865643
Change-Id: If22fc1eda219f676b5fcc06490f7901d21d1749c
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index fde9f5c..788a7a0 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -16,12 +16,10 @@
#include "linker.h"
-#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <android/dlext.h>
#include <android/api-level.h>
#include <bionic/pthread_internal.h>
@@ -70,8 +68,7 @@
static void* dlopen_ext(const char* filename, int flags,
const android_dlextinfo* extinfo, void* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
- soinfo* caller = find_containing_library(caller_addr);
- soinfo* result = do_dlopen(filename, flags, extinfo, caller);
+ soinfo* result = do_dlopen(filename, flags, extinfo, caller_addr);
if (result == nullptr) {
__bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
return nullptr;
@@ -92,71 +89,20 @@
extern android_namespace_t* g_anonymous_namespace;
void* dlsym(void* handle, const char* symbol) {
- ScopedPthreadMutexLocker locker(&g_dl_mutex);
-
- // TODO(dimitry): move (most of) the code below to linker.cpp
-#if !defined(__LP64__)
- if (handle == nullptr) {
- __bionic_format_dlerror("dlsym library handle is null", nullptr);
- return nullptr;
- }
-#endif
-
- if (symbol == nullptr) {
- __bionic_format_dlerror("dlsym symbol name is null", nullptr);
- return nullptr;
- }
-
- soinfo* found = nullptr;
- const ElfW(Sym)* sym = nullptr;
void* caller_addr = __builtin_return_address(0);
- soinfo* caller = find_containing_library(caller_addr);
- android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
-
- if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
- sym = dlsym_linear_lookup(ns, symbol, &found, caller, handle);
- } else {
- sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, symbol);
- }
-
- if (sym != nullptr) {
- unsigned bind = ELF_ST_BIND(sym->st_info);
-
- if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
- return reinterpret_cast<void*>(found->resolve_symbol_address(sym));
- }
-
- __bionic_format_dlerror("symbol found but not global", symbol);
- return nullptr;
- } else {
- __bionic_format_dlerror("undefined symbol", symbol);
+ ScopedPthreadMutexLocker locker(&g_dl_mutex);
+ void* result;
+ if (!do_dlsym(handle, symbol, nullptr, caller_addr, &result)) {
+ __bionic_format_dlerror(linker_get_error_buffer(), nullptr);
return nullptr;
}
+
+ return result;
}
int dladdr(const void* addr, Dl_info* info) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
-
- // Determine if this address can be found in any library currently mapped.
- soinfo* si = find_containing_library(addr);
- if (si == nullptr) {
- return 0;
- }
-
- memset(info, 0, sizeof(Dl_info));
-
- info->dli_fname = si->get_realpath();
- // Address at which the shared object is loaded.
- info->dli_fbase = reinterpret_cast<void*>(si->base);
-
- // Determine if any symbol in the library contains the specified address.
- ElfW(Sym)* sym = si->find_symbol_by_address(addr);
- if (sym != nullptr) {
- info->dli_sname = si->get_string(sym->st_name);
- info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym));
- }
-
- return 1;
+ return do_dladdr(addr, info);
}
int dlclose(void* handle) {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 66955da..d30f862 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -27,7 +27,6 @@
*/
#include <android/api-level.h>
-#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -891,20 +890,23 @@
this->namespace_ = ns;
}
+static uint32_t calculate_elf_hash(const char* name) {
+ const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name);
+ uint32_t h = 0, g;
+
+ while (*name_bytes) {
+ h = (h << 4) + *name_bytes++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+
+ return h;
+}
uint32_t SymbolName::elf_hash() {
if (!has_elf_hash_) {
- const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
- uint32_t h = 0, g;
-
- while (*name) {
- h = (h << 4) + *name++;
- g = h & 0xf0000000;
- h ^= g;
- h ^= g >> 24;
- }
-
- elf_hash_ = h;
+ elf_hash_ = calculate_elf_hash(name_);
has_elf_hash_ = true;
}
@@ -1243,7 +1245,8 @@
static const ElfW(Sym)* dlsym_handle_lookup(soinfo* root, soinfo* skip_until,
- soinfo** found, SymbolName& symbol_name) {
+ soinfo** found, SymbolName& symbol_name,
+ const version_info* vi) {
const ElfW(Sym)* result = nullptr;
bool skip_lookup = skip_until != nullptr;
@@ -1253,7 +1256,7 @@
return true;
}
- if (!current_soinfo->find_symbol_by_name(symbol_name, nullptr, &result)) {
+ if (!current_soinfo->find_symbol_by_name(symbol_name, vi, &result)) {
result = nullptr;
return false;
}
@@ -1269,9 +1272,17 @@
return result;
}
+static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns,
+ const char* name,
+ const version_info* vi,
+ soinfo** found,
+ soinfo* caller,
+ void* handle);
+
// This is used by dlsym(3). It performs symbol lookup only within the
// specified soinfo object and its dependencies in breadth first order.
-const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
+static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found,
+ const char* name, const version_info* vi) {
// According to man dlopen(3) and posix docs in the case when si is handle
// of the main executable we need to search not only in the executable and its
// dependencies but also in all libraries loaded with RTLD_GLOBAL.
@@ -1280,11 +1291,11 @@
// libraries and they are loaded in breath-first (correct) order we can just execute
// dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup.
if (si == somain) {
- return dlsym_linear_lookup(&g_default_namespace, name, found, nullptr, RTLD_DEFAULT);
+ return dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT);
}
SymbolName symbol_name(name);
- return dlsym_handle_lookup(si, nullptr, found, symbol_name);
+ return dlsym_handle_lookup(si, nullptr, found, symbol_name, vi);
}
/* This is used by dlsym(3) to performs a global symbol lookup. If the
@@ -1292,11 +1303,12 @@
beginning of the global solist. Otherwise the search starts at the
specified soinfo (for RTLD_NEXT).
*/
-const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns,
- const char* name,
- soinfo** found,
- soinfo* caller,
- void* handle) {
+static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns,
+ const char* name,
+ const version_info* vi,
+ soinfo** found,
+ soinfo* caller,
+ void* handle) {
SymbolName symbol_name(name);
soinfo::soinfo_list_t& soinfo_list = ns->soinfo_list();
@@ -1322,7 +1334,7 @@
continue;
}
- if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
+ if (!si->find_symbol_by_name(symbol_name, vi, &s)) {
return nullptr;
}
@@ -1338,7 +1350,7 @@
if (s == nullptr && caller != nullptr &&
(caller->get_rtld_flags() & RTLD_GLOBAL) == 0) {
return dlsym_handle_lookup(caller->get_local_group_root(),
- (handle == RTLD_NEXT) ? caller : nullptr, found, symbol_name);
+ (handle == RTLD_NEXT) ? caller : nullptr, found, symbol_name, vi);
}
if (s != nullptr) {
@@ -2176,6 +2188,14 @@
}
}
+static std::string symbol_display_name(const char* sym_name, const char* sym_ver) {
+ if (sym_ver == nullptr) {
+ return sym_name;
+ }
+
+ return std::string(sym_name) + "@" + sym_ver;
+}
+
void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
// Use basic string manipulation calls to avoid snprintf.
// snprintf indirectly calls pthread_getspecific to get the size of a buffer.
@@ -2203,7 +2223,10 @@
parse_LD_LIBRARY_PATH(ld_library_path);
}
-soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, soinfo *caller) {
+soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
+ void* caller_addr) {
+ soinfo* const caller = find_containing_library(caller_addr);
+
if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) {
DL_ERR("invalid flags to dlopen: %x", flags);
return nullptr;
@@ -2249,6 +2272,79 @@
return si;
}
+int do_dladdr(const void* addr, Dl_info* info) {
+ // Determine if this address can be found in any library currently mapped.
+ soinfo* si = find_containing_library(addr);
+ if (si == nullptr) {
+ return 0;
+ }
+
+ memset(info, 0, sizeof(Dl_info));
+
+ info->dli_fname = si->get_realpath();
+ // Address at which the shared object is loaded.
+ info->dli_fbase = reinterpret_cast<void*>(si->base);
+
+ // Determine if any symbol in the library contains the specified address.
+ ElfW(Sym)* sym = si->find_symbol_by_address(addr);
+ if (sym != nullptr) {
+ info->dli_sname = si->get_string(sym->st_name);
+ info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym));
+ }
+
+ return 1;
+}
+
+bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver,
+ void* caller_addr, void** symbol) {
+#if !defined(__LP64__)
+ if (handle == nullptr) {
+ DL_ERR("dlsym failed: library handle is null");
+ return false;
+ }
+#endif
+
+ if (sym_name == nullptr) {
+ DL_ERR("dlsym failed: symbol name is null");
+ return false;
+ }
+
+ soinfo* found = nullptr;
+ const ElfW(Sym)* sym = nullptr;
+ soinfo* caller = find_containing_library(caller_addr);
+ android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
+
+ version_info vi_instance;
+ version_info* vi = nullptr;
+
+ if (sym_ver != nullptr) {
+ vi_instance.name = sym_name;
+ vi_instance.elf_hash = calculate_elf_hash(sym_name);
+ vi = &vi_instance;
+ }
+
+ if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
+ sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle);
+ } else {
+ sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, sym_name, vi);
+ }
+
+ if (sym != nullptr) {
+ uint32_t bind = ELF_ST_BIND(sym->st_info);
+
+ if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
+ *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
+ return true;
+ }
+
+ DL_ERR("symbol \"%s\" found but not global", symbol_display_name(sym_name, sym_ver).c_str());
+ return false;
+ }
+
+ DL_ERR("undefined symbol: %s", symbol_display_name(sym_name, sym_ver).c_str());
+ return false;
+}
+
void do_dlclose(soinfo* si) {
ProtectedDataGuard guard;
soinfo_unload(si);
diff --git a/linker/linker.h b/linker/linker.h
index 49329c7..5ec259e 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -29,6 +29,7 @@
#ifndef _LINKER_H_
#define _LINKER_H_
+#include <dlfcn.h>
#include <android/dlext.h>
#include <elf.h>
#include <inttypes.h>
@@ -135,7 +136,7 @@
};
struct version_info {
- version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {}
+ constexpr version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {}
uint32_t elf_hash;
const char* name;
@@ -422,17 +423,15 @@
void do_android_get_LD_LIBRARY_PATH(char*, size_t);
void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);
-soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, soinfo *caller);
+soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr);
void do_dlclose(soinfo* si);
int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data);
-const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, const char* name, soinfo** found,
- soinfo* caller, void* handle);
+bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver,
+ void* caller_addr, void** symbol);
-soinfo* find_containing_library(const void* addr);
-
-const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
+int do_dladdr(const void* addr, Dl_info* info);
void debuggerd_init();
extern "C" abort_msg_t* g_abort_message;