Untitled diff

Created Diff never expires
34 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
925 lines
22 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
914 lines
/*
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* modification, are permitted provided that the following conditions
* are met:
* are met:
* * Redistributions of source code must retain the above copyright
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* the documentation and/or other materials provided with the
* distribution.
* distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* SUCH DAMAGE.
*/
*/


#include <android/api-level.h>
#include <android/api-level.h>
#include <dlfcn.h>
#include <dlfcn.h>
#include <errno.h>
#include <errno.h>
#include <fcntl.h>
#include <fcntl.h>
#include <inttypes.h>
#include <inttypes.h>
#include <pthread.h>
#include <pthread.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/param.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <unistd.h>


#include <new>
#include <new>
#include <string>
#include <string>
#include <vector>
#include <vector>


// Private C library headers.
// Private C library headers.
#include "private/bionic_tls.h"
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
#include "private/KernelArgumentBlock.h"
#include "private/ScopedPthreadMutexLocker.h"
#include "private/ScopedPthreadMutexLocker.h"
#include "private/ScopeGuard.h"
#include "private/ScopeGuard.h"
#include "private/UniquePtr.h"
#include "private/UniquePtr.h"


#include "linker.h"
#include "linker.h"
#include "linker_block_allocator.h"
#include "linker_block_allocator.h"
#include "linker_debug.h"
#include "linker_debug.h"
#include "linker_sleb128.h"
#include "linker_sleb128.h"
#include "linker_phdr.h"
#include "linker_phdr.h"
#include "linker_relocs.h"
#include "linker_relocs.h"
#include "linker_reloc_iterators.h"
#include "linker_reloc_iterators.h"
#include "ziparchive/zip_archive.h"
#include "ziparchive/zip_archive.h"


extern void __libc_init_AT_SECURE(KernelArgumentBlock&);
extern void __libc_init_AT_SECURE(KernelArgumentBlock&);


// Override macros to use C++ style casts.
// Override macros to use C++ style casts.
#undef ELF_ST_TYPE
#undef ELF_ST_TYPE
#define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)
#define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)


static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);


static LinkerTypeAllocator<soinfo> g_soinfo_allocator;
static LinkerTypeAllocator<soinfo> g_soinfo_allocator;
static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;


static soinfo* solist;
static soinfo* solist;
static soinfo* sonext;
static soinfo* sonext;
static soinfo* somain; // main process, always the one after libdl_info
static soinfo* somain; // main process, always the one after libdl_info


static const char* const kDefaultLdPaths[] = {
static const char* const kDefaultLdPaths[] = {
#if defined(__LP64__)
#if defined(__LP64__)
"/vendor/lib64",
"/vendor/lib64",
"/system/lib64",
"/system/lib64",
#else
#else
"/vendor/lib",
"/vendor/lib",
"/system/lib",
"/system/lib",
#endif
#endif
nullptr
nullptr
};
};


static const ElfW(Versym) kVersymNotNeeded = 0;
static const ElfW(Versym) kVersymNotNeeded = 0;
static const ElfW(Versym) kVersymGlobal = 1;
static const ElfW(Versym) kVersymGlobal = 1;


static std::vector<std::string> g_ld_library_paths;
static std::vector<std::string> g_ld_library_paths;
static std::vector<std::string> g_ld_preload_names;
static std::vector<std::string> g_ld_preload_names;


static std::vector<soinfo*> g_ld_preloads;
static std::vector<soinfo*> g_ld_preloads;


__LIBC_HIDDEN__ int g_ld_debug_verbosity;
__LIBC_HIDDEN__ int g_ld_debug_verbosity;


__LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.
__LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.


#if STATS
#if STATS
struct linker_stats_t {
struct linker_stats_t {
int count[kRelocMax];
int count[kRelocMax];
};
};


static linker_stats_t linker_stats;
static linker_stats_t linker_stats;


void count_relocation(RelocationKind kind) {
void count_relocation(RelocationKind kind) {
++linker_stats.count[kind];
++linker_stats.count[kind];
}
}
#else
#else
void count_relocation(RelocationKind) {
void count_relocation(RelocationKind) {
}
}
#endif
#endif


#if COUNT_PAGES
#if COUNT_PAGES
uint32_t bitmask[4096];
uint32_t bitmask[4096];
#endif
#endif


static char __linker_dl_err_buf[768];
static char __linker_dl_err_buf[768];


char* linker_get_error_buffer() {
char* linker_get_error_buffer() {
return &__linker_dl_err_buf[0];
return &__linker_dl_err_buf[0];
}
}


size_t linker_get_error_buffer_size() {
size_t linker_get_error_buffer_size() {
return sizeof(__linker_dl_err_buf);
return sizeof(__linker_dl_err_buf);
}
}


// This function is an empty stub where GDB locates a breakpoint to get notified
// This function is an empty stub where GDB locates a breakpoint to get notified
// about linker activity.
// about linker activity.
extern "C"
extern "C"
void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity();
void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity();


static pthread_mutex_t g__r_debug_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g__r_debug_mutex = PTHREAD_MUTEX_INITIALIZER;
static r_debug _r_debug =
static r_debug _r_debug =
{1, nullptr, reinterpret_cast<uintptr_t>(&rtld_db_dlactivity), r_debug::RT_CONSISTENT, 0};
{1, nullptr, reinterpret_cast<uintptr_t>(&rtld_db_dlactivity), r_debug::RT_CONSISTENT, 0};


static link_map* r_debug_tail = 0;
static link_map* r_debug_tail = 0;


static void insert_soinfo_into_debug_map(soinfo* info) {
static void insert_soinfo_into_debug_map(soinfo* info) {
// Copy the necessary fields into the debug structure.
// Copy the necessary fields into the debug structure.
link_map* map = &(info->link_map_head);
link_map* map = &(info->link_map_head);
map->l_addr = info->load_bias;
map->l_addr = info->load_bias;
// link_map l_name field is not const.
// link_map l_name field is not const.
map->l_name = const_cast<char*>(info->get_realpath());
map->l_name = const_cast<char*>(info->get_realpath());
map->l_ld = info->dynamic;
map->l_ld = info->dynamic;


// Stick the new library at the end of the list.
// Stick the new library at the end of the list.
// gdb tends to care more about libc than it does
// gdb tends to care more about libc than it does
// about leaf libraries, and ordering it this way
// about leaf libraries, and ordering it this way
// reduces the back-and-forth over the wire.
// reduces the back-and-forth over the wire.
if (r_debug_tail) {
if (r_debug_tail) {
r_debug_tail->l_next = map;
r_debug_tail->l_next = map;
map->l_prev = r_debug_tail;
map->l_prev = r_debug_tail;
map->l_next = 0;
map->l_next = 0;
} else {
} else {
_r_debug.r_map = map;
_r_debug.r_map = map;
map->l_prev = 0;
map->l_prev = 0;
map->l_next = 0;
map->l_next = 0;
}
}
r_debug_tail = map;
r_debug_tail = map;
}
}


static void remove_soinfo_from_debug_map(soinfo* info) {
static void remove_soinfo_from_debug_map(soinfo* info) {
link_map* map = &(info->link_map_head);
link_map* map = &(info->link_map_head);


if (r_debug_tail == map) {
if (r_debug_tail == map) {
r_debug_tail = map->l_prev;
r_debug_tail = map->l_prev;
}
}


if (map->l_prev) {
if (map->l_prev) {
map->l_prev->l_next = map->l_next;
map->l_prev->l_next = map->l_next;
}
}
if (map->l_next) {
if (map->l_next) {
map->l_next->l_prev = map->l_prev;
map->l_next->l_prev = map->l_prev;
}
}
}
}


static void notify_gdb_of_load(soinfo* info) {
static void notify_gdb_of_load(soinfo* info) {
if (info->is_main_executable()) {
if (info->is_main_executable()) {
// GDB already knows about the main executable
// GDB already knows about the main executable
return;
return;
}
}


ScopedPthreadMutexLocker locker(&g__r_debug_mutex);
ScopedPthreadMutexLocker locker(&g__r_debug_mutex);


_r_debug.r_state = r_debug::RT_ADD;
_r_debug.r_state = r_debug::RT_ADD;
rtld_db_dlactivity();
rtld_db_dlactivity();


insert_soinfo_into_debug_map(info);
insert_soinfo_into_debug_map(info);


_r_debug.r_state = r_debug::RT_CONSISTENT;
_r_debug.r_state = r_debug::RT_CONSISTENT;
rtld_db_dlactivity();
rtld_db_dlactivity();
}
}


static void notify_gdb_of_unload(soinfo* info) {
static void notify_gdb_of_unload(soinfo* info) {
if (info->is_main_executable()) {
if (info->is_main_executable()) {
// GDB already knows about the main executable
// GDB already knows about the main executable
return;
return;
}
}


ScopedPthreadMutexLocker locker(&g__r_debug_mutex);
ScopedPthreadMutexLocker locker(&g__r_debug_mutex);


_r_debug.r_state = r_debug::RT_DELETE;
_r_debug.r_state = r_debug::RT_DELETE;
rtld_db_dlactivity();
rtld_db_dlactivity();


remove_soinfo_from_debug_map(info);
remove_soinfo_from_debug_map(info);


_r_debug.r_state = r_debug::RT_CONSISTENT;
_r_debug.r_state = r_debug::RT_CONSISTENT;
rtld_db_dlactivity();
rtld_db_dlactivity();
}
}


void notify_gdb_of_libraries() {
void notify_gdb_of_libraries() {
_r_debug.r_state = r_debug::RT_ADD;
_r_debug.r_state = r_debug::RT_ADD;
rtld_db_dlactivity();
rtld_db_dlactivity();
_r_debug.r_state = r_debug::RT_CONSISTENT;
_r_debug.r_state = r_debug::RT_CONSISTENT;
rtld_db_dlactivity();
rtld_db_dlactivity();
}
}


LinkedListEntry<soinfo>* SoinfoListAllocator::alloc() {
LinkedListEntry<soinfo>* SoinfoListAllocator::alloc() {
return g_soinfo_links_allocator.alloc();
return g_soinfo_links_allocator.alloc();
}
}


void SoinfoListAllocator::free(LinkedListEntry<soinfo>* entry) {
void SoinfoListAllocator::free(LinkedListEntry<soinfo>* entry) {
g_soinfo_links_allocator.free(entry);
g_soinfo_links_allocator.free(entry);
}
}


static soinfo* soinfo_alloc(const char* name, struct stat* file_stat,
static soinfo* soinfo_alloc(const char* name, struct stat* file_stat,
off64_t file_offset, uint32_t rtld_flags) {
off64_t file_offset, uint32_t rtld_flags) {
if (strlen(name) >= PATH_MAX) {
if (strlen(name) >= PATH_MAX) {
DL_ERR("library name \"%s\" too long", name);
DL_ERR("library name \"%s\" too long", name);
return nullptr;
return nullptr;
}
}


soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat, file_offset, rtld_flags);
soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat, file_offset, rtld_flags);


sonext->next = si;
sonext->next = si;
sonext = si;
sonext = si;


TRACE("name %s: allocated soinfo @ %p", name, si);
TRACE("name %s: allocated soinfo @ %p", name, si);
return si;
return si;
}
}


static void soinfo_free(soinfo* si) {
static void soinfo_free(soinfo* si) {
if (si == nullptr) {
if (si == nullptr) {
return;
return;
}
}


if (si->base != 0 && si->size != 0) {
if (si->base != 0 && si->size != 0) {
munmap(reinterpret_cast<void*>(si->base), si->size);
munmap(reinterpret_cast<void*>(si->base), si->size);
}
}


soinfo *prev = nullptr, *trav;
soinfo *prev = nullptr, *trav;


TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si);
TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si);


for (trav = solist; trav != nullptr; trav = trav->next) {
for (trav = solist; trav != nullptr; trav = trav->next) {
if (trav == si) {
if (trav == si) {
break;
break;
}
}
prev = trav;
prev = trav;
}
}


if (trav == nullptr) {
if (trav == nullptr) {
// si was not in solist
// si was not in solist
DL_ERR("name \"%s\"@%p is not in solist!", si->get_realpath(), si);
DL_ERR("name \"%s\"@%p is not in solist!", si->get_realpath(), si);
return;
return;
}
}


// clear links to/from si
// clear links to/from si
si->remove_all_links();
si->remove_all_links();


// prev will never be null, because the first entry in solist is
// prev will never be null, because the first entry in solist is
// always the static libdl_info.
// always the static libdl_info.
prev->next = si->next;
prev->next = si->next;
if (si == sonext) {
if (si == sonext) {
sonext = prev;
sonext = prev;
}
}


si->~soinfo();
si->~soinfo();
g_soinfo_allocator.free(si);
g_soinfo_allocator.free(si);
}
}


static void parse_path(const char* path, const char* delimiters,
static void parse_path(const char* path, const char* delimiters,
std::vector<std::string>* paths) {
std::vector<std::string>* paths) {
if (path == nullptr) {
if (path == nullptr) {
return;
return;
}
}


paths->clear();
paths->clear();


for (const char *p = path; ; ++p) {
for (const char *p = path; ; ++p) {
size_t len = strcspn(p, delimiters);
size_t len = strcspn(p, delimiters);
// skip empty tokens
// skip empty tokens
if (len == 0) {
if (len == 0) {
continue;
continue;
}
}


paths->push_back(std::string(p, len));
paths->push_back(std::string(p, len));
p += len;
p += len;


if (*p == '\0') {
if (*p == '\0') {
break;
break;
}
}
}
}
}
}


static void parse_LD_LIBRARY_PATH(const char* path) {
static void parse_LD_LIBRARY_PATH(const char* path) {
parse_path(path, ":", &g_ld_library_paths);
parse_path(path, ":", &g_ld_library_paths);
}
}


static void parse_LD_PRELOAD(const char* path) {
static void parse_LD_PRELOAD(const char* path) {
// We have historically supported ':' as well as ' ' in LD_PRELOAD.
// We have historically supported ':' as well as ' ' in LD_PRELOAD.
parse_path(path, " :", &g_ld_preload_names);
parse_path(path, " :", &g_ld_preload_names);
}
}


static bool realpath_fd(int fd, std::string* realpath) {
static bool realpath_fd(int fd, std::string* realpath) {
std::vector<char> buf(PATH_MAX), proc_self_fd(PATH_MAX);
std::vector<char> buf(PATH_MAX), proc_self_fd(PATH_MAX);
snprintf(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd);
snprintf(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd);
// set DUMPABLE to 1 to access /proc/self/fd
int dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
auto guard = make_scope_guard([&]() {
// restore dumpable
prctl(PR_SET_DUMPABLE, dumpable, 0, 0, 0);
});
if (readlink(&proc_self_fd[0], &buf[0], buf.size()) == -1) {
if (readlink(&proc_self_fd[0], &buf[0], buf.size()) == -1) {
PRINT("readlink('%s') failed: %s [fd=%d]", &proc_self_fd[0], strerror(errno), fd);
PRINT("readlink('%s') failed: %s [fd=%d]", &proc_self_fd[0], strerror(errno), fd);
return false;
return false;
}
}


*realpath = std::string(&buf[0]);
*realpath = std::string(&buf[0]);
return true;
return true;
}
}


#if defined(__arm__)
#if defined(__arm__)


// For a given PC, find the .so that it belongs to.
// For a given PC, find the .so that it belongs to.
// Returns the base address of the .ARM.exidx section
// Returns the base address of the .ARM.exidx section
// for that .so, and the number of 8-byte entries
// for that .so, and the number of 8-byte entries
// in that section (via *pcount).
// in that section (via *pcount).
//
//
// Intended to be called by libc's __gnu_Unwind_Find_exidx().
// Intended to be called by libc's __gnu_Unwind_Find_exidx().
//
//
// This function is exposed via dlfcn.cpp and libdl.so.
// This function is exposed via dlfcn.cpp and libdl.so.
_Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) {
_Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) {
uintptr_t addr = reinterpret_cast<uintptr_t>(pc);
uintptr_t addr = reinterpret_cast<uintptr_t>(pc);


for (soinfo* si = solist; si != 0; si = si->next) {
for (soinfo* si = solist; si != 0; si = si->next) {
if ((addr >= si->base) && (addr < (si->base + si->size))) {
if ((addr >= si->base) && (addr < (si->base + si->size))) {
*pcount = si->ARM_exidx_count;
*pcount = si->ARM_exidx_count;
return reinterpret_cast<_Unwind_Ptr>(si->ARM_exidx);
return reinterpret_cast<_Unwind_Ptr>(si->ARM_exidx);
}
}
}
}
*pcount = 0;
*pcount = 0;
return nullptr;
return nullptr;
}
}


#endif
#endif


// Here, we only have to provide a callback to iterate across all the
// Here, we only have to provide a callback to iterate across all the
// loaded libraries. gcc_eh does the rest.
// loaded libraries. gcc_eh does the rest.
int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) {
int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) {
int rv = 0;
int rv = 0;
for (soinfo* si = solist; si != nullptr; si = si->next) {
for (soinfo* si = solist; si != nullptr; si = si->next) {
dl_phdr_info dl_info;
dl_phdr_info dl_info;
dl_info.dlpi_addr = si->link_map_head.l_addr;
dl_info.dlpi_addr = si->link_map_head.l_addr;
dl_info.dlpi_name = si->link_map_head.l_name;
dl_info.dlpi_name = si->link_map_head.l_name;
dl_info.dlpi_phdr = si->phdr;
dl_info.dlpi_phdr = si->phdr;
dl_info.dlpi_phnum = si->phnum;
dl_info.dlpi_phnum = si->phnum;
rv = cb(&dl_info, sizeof(dl_phdr_info), data);
rv = cb(&dl_info, sizeof(dl_phdr_info), data);
if (rv != 0) {
if (rv != 0) {
break;
break;
}
}
}
}
return rv;
return rv;
}
}


const ElfW(Versym)* soinfo::get_versym(size_t n) const {
const ElfW(Versym)* soinfo::get_versym(size_t n) const {
if (has_min_version(2) && versym_ != nullptr) {
if (has_min_version(2) && versym_ != nullptr) {
return versym_ + n;
return versym_ + n;
}
}


return nullptr;
return nullptr;
}
}


ElfW(Addr) soinfo::get_verneed_ptr() const {
ElfW(Addr) soinfo::get_verneed_ptr() const {
if (has_min_version(2)) {
if (has_min_version(2)) {
return verneed_ptr_;
return verneed_ptr_;
}
}


return 0;
return 0;
}
}


size_t soinfo::get_verneed_cnt() const {
size_t soinfo::get_verneed_cnt() const {
if (has_min_version(2)) {
if (has_min_version(2)) {
return verneed_cnt_;
return verneed_cnt_;
}
}


return 0;
return 0;
}
}


ElfW(Addr) soinfo::get_verdef_ptr() const {
ElfW(Addr) soinfo::get_verdef_ptr() const {
if (has_min_version(2)) {
if (has_min_version(2)) {
return verdef_ptr_;
return verdef_ptr_;
}
}


return 0;
return 0;
}
}


size_t soinfo::get_verdef_cnt() const {
size_t soinfo::get_verdef_cnt() const {
if (has_min_version(2)) {
if (has_min_version(2)) {
return verdef_cnt_;
return verdef_cnt_;
}
}


return 0;
return 0;
}
}


template<typename F>
template<typename F>
static bool for_each_verdef(const soinfo* si, F functor) {
static bool for_each_verdef(const soinfo* si, F functor) {
if (!si->has_min_version(2)) {
if (!si->has_min_version(2)) {
return true;
return true;
}
}


uintptr_t verdef_ptr = si->get_verdef_ptr();
uintptr_t verdef_ptr = si->get_verdef_ptr();
if (verdef_ptr == 0) {
if (verdef_ptr == 0) {
return true;
return true;
}
}


size_t offset = 0;
size_t offset = 0;


size_t verdef_cnt = si->get_verdef_cnt();
size_t verdef_cnt = si->get_verdef_cnt();
for (size_t i = 0; i<verdef_cnt; ++i) {
for (size_t i = 0; i<verdef_cnt; ++i) {
const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset);
const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset);
size_t verdaux_offset = offset + verdef->vd_aux;
size_t verdaux_offset = offset + verdef->vd_aux;
offset += verdef->vd_next;
offset += verdef->vd_next;


if (verdef->vd_version != 1) {
if (verdef->vd_version != 1) {
DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s",
DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s",
i, verdef->vd_version, si->get_realpath());
i, verdef->vd_version, si->get_realpath());
return false;
return false;
}
}


if ((verdef->vd_flags & VER_FLG_BASE) != 0) {
if ((verdef->vd_flags & VER_FLG_BASE) != 0) {
// "this is the version of the file itself. It must not be used for
// "this is the version of the file itself. It must not be used for
// matching a symbol. It can be used to match references."
// matching a symbol. It can be used to match references."
//
//
// http://www.akkadia.org/drepper/symbol-versioning
// http://www.akkadia.org/drepper/symbol-versioning
continue;
continue;
}
}


if (verdef->vd_cnt == 0) {
if (verdef->vd_cnt == 0) {
DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i);
DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i);
return false;
return false;
}
}


const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset);
const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset);


if (functor(i, verdef, verdaux) == true) {
if (functor(i, verdef, verdaux) == true) {
break;
break;
}
}
}
}


return true;
return true;
}
}


bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const {
bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const {
if (vi == nullptr) {
if (vi == nullptr) {
*versym = kVersymNotNeeded;
*versym = kVersymNotNeeded;
return true;
return true;
}
}


*versym = kVersymGlobal;
*versym = kVersymGlobal;


return for_each_verdef(this,
return for_each_verdef(this,
[&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
[&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
if (verdef->vd_hash == vi->elf_hash &&
if (verdef->vd_hash == vi->elf_hash &&
strcmp(vi->name, get_string(verdaux->vda_name)) == 0) {
strcmp(vi->name, get_string(verdaux->vda_name)) == 0) {
*versym = verdef->vd_ndx;
*versym = verdef->vd_ndx;
return true;
return true;
}
}


return false;
return false;
}
}
);
);
}
}


bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
const version_info* vi,
const version_info* vi,
const ElfW(Sym)** symbol) const {
const ElfW(Sym)** symbol) const {
uint32_t symbol_index;
uint32_t symbol_index;
bool success =
bool success =
is_gnu_hash() ?
is_gnu_hash() ?
gnu_lookup(symbol_name, vi, &symbol_index) :
gnu_lookup(symbol_name, vi, &symbol_index) :
elf_lookup(symbol_name, vi, &symbol_index);
elf_lookup(symbol_name, vi, &symbol_index);


if (success) {
if (success) {
*symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
*symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
}
}


return success;
return success;
}
}


static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
ELF_ST_BIND(s->st_info) == STB_WEAK) {
ELF_ST_BIND(s->st_info) == STB_WEAK) {
return s->st_shndx != SHN_UNDEF;
return s->st_shndx != SHN_UNDEF;
} else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) {
} else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) {
DL_WARN("unexpected ST_BIND value: %d for '%s' in '%s'",
DL_WARN("unexpected ST_BIND value: %d for '%s' in '%s'",
ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath());
ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath());
}
}


return false;
return false;
}
}


static const ElfW(Versym) kVersymHiddenBit = 0x8000;
static const ElfW(Versym) kVersymHiddenBit = 0x8000;


static inline bool is_versym_hidden(const ElfW(Versym)* versym) {
static inline bool is_versym_hidden(const ElfW(Versym)* versym) {
// the symbol is hidden if bit 15 of versym is set.
// the symbol is hidden if bit 15 of versym is set.
return versym != nullptr && (*versym & kVersymHiddenBit) != 0;
return versym != nullptr && (*versym & kVersymHiddenBit) != 0;
}
}


static inline bool check_symbol_version(const ElfW(Versym) verneed,
static inline bool check_symbol_version(const ElfW(Versym) verneed,
const ElfW(Versym)* verdef) {
const ElfW(Versym)* verdef) {
return verneed == kVersymNotNeeded ||
return verneed == kVersymNotNeeded ||
verdef == nullptr ||
verdef == nullptr ||
verneed == (*verdef & ~kVersymHiddenBit);
verneed == (*verdef & ~kVersymHiddenBit);
}
}


bool soinfo::gnu_lookup(SymbolName& symbol_name,
bool soinfo::gnu_lookup(SymbolName& symbol_name,
const version_info* vi,
const version_info* vi,
uint32_t* symbol_index) const {
uint32_t* symbol_index) const {
uint32_t hash = symbol_name.gnu_hash();
uint32_t hash = symbol_name.gnu_hash();
uint32_t h2 = hash >> gnu_shift2_;
uint32_t h2 = hash >> gnu_shift2_;


uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;
uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];


*symbol_index = 0;
*symbol_index = 0;


TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));


// test against bloom filter
// test against bloom filter
if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));


return true;
return true;
}
}


// bloom test says "probably yes"...
// bloom test says "probably yes"...
uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
uint32_t n = gnu_bucket_[hash % gnu_nbucket_];


if (n == 0) {
if (n == 0) {
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));


return true;
return true;
}
}


// lookup versym for the version definition in this library
// lookup versym for the version definition in this library
// note the difference between "version is not requested" (vi == nullptr)
// note the difference between "version is not requested" (vi == nullptr)
// and "version not found". In the first case verneed is kVersymNotNeeded
// and "version not found". In the first case verneed is kVersymNotNeeded
// which implies that the default version can be accepted; the second case results in
// which implies that the default version can be accepted; the second case results in
// verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
// verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
// for this library and consider only *global* ones.
// for this library and consider only *global* ones.
ElfW(Versym) verneed = 0;
ElfW(Versym) verneed = 0;
if (!find_verdef_version_index(vi, &verneed)) {
if (!find_verdef_version_index(vi, &verneed)) {
return false;
return false;
}
}


do {
do {
ElfW(Sym)* s = symtab_ + n;
ElfW(Sym)* s = symtab_ + n;
const ElfW(Versym)* verdef = get_versym(n);
const ElfW(Versym)* verdef = get_versym(n);
// skip hidden versions when verneed == kVersymNotNeeded (0)
// skip hidden versions when verneed == kVersymNotNeeded (0)
if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
continue;
continue;
}
}
if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
check_symbol_version(verneed, verdef) &&
check_symbol_version(verneed, verdef) &&
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
is_symbol_global_and_defined(this, s)) {
is_symbol_global_and_defined(this, s)) {
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value),
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
static_cast<size_t>(s->st_size));
*symbol_index = n;
*symbol_index = n;
return true;
return true;
}
}
} while ((gnu_chain_[n++] & 1) == 0);
} while ((gnu_chain_[n++] & 1) == 0);


TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));


return true;
return true;
}
}


bool soinfo::elf_lookup(SymbolName& symbol_name,
bool soinfo::elf_lookup(SymbolName& symbol_name,
const version_info* vi,
const version_info* vi,
uint32_t* symbol_index) const {
uint32_t* symbol_index) const {
uint32_t hash = symbol_name.elf_hash();
uint32_t hash = symbol_name.elf_hash();


TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
symbol_name.get_name(), get_realpath(),
symbol_name.get_name(), get_realpath(),
reinterpret_cast<void*>(base), hash, hash % nbucket_);
reinterpret_cast<void*>(base), hash, hash % nbucket_);


ElfW(Versym) verneed = 0;
ElfW(Versym) verneed = 0;
if (!find_verdef_version_index(vi, &verneed)) {
if (!find_verdef_version_index(vi, &verneed)) {
return false;
return false;
}
}


for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
ElfW(Sym)* s = symtab_ + n;
ElfW(Sym)* s = symtab_ + n;
const ElfW(Versym)* verdef = get_versym(n);
const ElfW(Versym)* verdef = get_versym(n);


// skip hidden versions when verneed == 0
// skip hidden versions when verneed == 0
if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
continue;
continue;
}
}


if (check_symbol_version(verneed, verdef) &&
if (check_symbol_version(verneed, verdef) &&
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
is_symbol_global_and_defined(this, s)) {
is_symbol_global_and_defined(this, s)) {
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
symbol_name.get_name(), get_realpath(),
symbol_name.get_name(), get_realpath(),
reinterpret_cast<void*>(s->st_value),
reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
static_cast<size_t>(s->st_size));
*symbol_index = n;
*symbol_index = n;
return true;
return true;
}
}
}
}


TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
symbol_name.get_name(), get_realpath(),
symbol_name.get_name(), get_realpath(),
reinterpret_cast<void*>(base), hash, hash % nbucket_);
reinterpret_cast<void*>(base), hash, hash % nbucket_);


*symbol_index = 0;
*symbol_index = 0;
return true;
return true;
}
}


soinfo::soinfo(const char* realpath, const struct stat* file_stat,
soinfo::soinfo(const char* realpath, const struct stat* file_stat,
off64_t file_offset, int rtld_flags) {
off64_t file_offset, int rtld_flags) {
memset(this, 0, sizeof(*this));
memset(this, 0, sizeof(*this));


if (realpath != nullptr) {
if (realpath != nullptr) {
realpath_ = realpath;
realpath_ = realpath;
}
}


flags_ = FLAG_NEW_SOINFO;
flags_ = FLAG_NEW_SOINFO;
version_ = SOINFO_VERSION;
version_ = SOINFO_VERSION;


if (file_stat != nullptr) {
if (file_stat != nullptr) {
this->st_dev_ = file_stat->st_dev;
this->st_dev_ = file_stat->st_dev;
this->st_ino_ = file_stat->st_ino;
this->st_ino_ = file_stat->st_ino;
this->file_offset_ = file_offset;
this->file_offset_ = file_offset;
}
}


this->rtld_flags_ = rtld_flags;
this->rtld_flags_ = rtld_flags;
}
}




uint32_t SymbolName::elf_hash() {
uint32_t SymbolName::elf_hash() {
if (!has_elf_hash_) {
if (!has_elf_hash_) {
const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
uint32_t h = 0, g;
uint32_t h = 0, g;


while (*name) {
while (*name) {
h = (h << 4) + *name++;
h = (h << 4) + *name++;
g = h & 0xf0000000;
g = h & 0xf0000000;
h ^= g;
h ^= g;
h ^= g >> 24;
h ^= g >> 24;
}
}


elf_hash_ = h;
elf_hash_ = h;
has_elf_hash_ = true;
has_elf_hash_ = true;
}
}


return elf_hash_;
return elf_hash_;
}
}


uint32_t SymbolName::gnu_hash() {
uint32_t SymbolName::gnu_hash() {
if (!has_gnu_hash_) {
if (!has_gnu_hash_) {
uint32_t h = 5381;
uint32_t h = 5381;
const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
while (*name != 0) {
while (*name != 0) {
h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c
h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c
}
}


gnu_hash_ = h;
gnu_hash_ = h;
has_gnu_hash_ = true;
has_gnu_hash_ = true;
}
}


return gnu_hash_;
return gnu_hash_;
}
}


bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
SymbolName symbol_name(name);
SymbolName symbol_name(name);
const ElfW(Sym)* s = nullptr;
const ElfW(Sym)* s = nullptr;


/* "This element's presence in a shared object library alters the dynamic linker's
/* "This element's presence in a shared object library alters the dynamic linker's
* symbol resolution algorithm for references within the library. Instead of starting
* symbol resolution algorithm for references within the library. Instead of starting
* a symbol search with the executable file, the dynamic linker starts from the shared
* a symbol search with the executable file, the dynamic linker starts from the shared
* object itself. If the shared object fails to supply the referenced symbol, the
* object itself. If the shared object fails to supply the referenced symbol, the
* dynamic linker then searches the executable file and other shared objects as usual."
* dynamic linker then searches the executable file and other shared objects as usual."
*
*
* http://www.sco.com/developers/gabi/2012-12-31/ch5.dynamic.html
* http://www.sco.com/developers/gabi/2012-12-31/ch5.dynamic.html
*
*
* Note that this is unlikely since static linker avoids generating
* Note that this is unlikely since static linker avoids generating
* relocations for -Bsymbolic linked dynamic executables.
* relocations for -Bsymbolic linked dynamic executables.
*/
*/
if (si_from->has_DT_SYMBOLIC) {
if (si_from->has_DT_SYMBOLIC) {
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_realpath(), name);
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_realpath(), name);
if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
return false;
return false;
}
}


if (s != nullptr) {
if (s != nullptr) {
*si_found_in = si_from;
*si_found_in = si_from;
}
}
}
}


// 1. Look for it in global_group
// 1. Look for it in global_group
if (s == nullptr) {
if (s == nullptr) {
bool error = false;
bool error = false;
global_group.visit([&](soinfo* global_si) {
global_group.visit([&](soinfo* global_si) {
DEBUG("%s: looking up %s in %s (from global group)",
DEBUG("%s: looking up %s in %s (from global group)",
si_from->get_realpath(), name, global_si->get_realpath());
si_from->get_realpath(), name, global_si->get_realpath());
if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) {
if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) {
error = true;
error = true;
return false;
return false;
}
}


if (s != nullptr) {
if (s != nullptr) {
*si_found_in = global_si;
*si_found_in = global_si;
return false;
return false;
}
}


return true;
return true;
});
});


if (error) {
if (error) {
return false;
return false;
}
}
}
}


// 2. Look for it in the local group
// 2. Look for it in the local group
if (s == nullptr) {
if (s == nullptr) {
bool error = false;
bool error = false;
local_group.visit([&](soinfo* local_si) {
local_group.visit([&](soinfo* local_si) {
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
// we already did this - skip
// we already did this - skip
return true;
return true;
}
}


DEBUG("%s: looking up %s in %s (from local group)",
DEBUG("%s: looking up %s in %s (from local group)",
si_from->get_realpath(), name, local_si->get_realpath());
si_from->get_realpath(), name, local_si->get_realpath());
if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) {
if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) {
error = true;
error = true;
return false;
return false;
}
}


if (s != nullptr) {
if (s != nullptr) {
*si_found_in = local_si;
*si_found_in = local_si;
return false;
return false;
}
}


return true;
return true;
});
});


if (error) {
if (error) {
return false;
return false;
}
}
}
}


if (s != nullptr) {
if (s != nullptr) {
TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, "
TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, "
"found in %s, base = %p, load bias = %p",
"found in %s, base = %p, load bias = %p",
si_from->get_realpath(), name, reinterpret_cast<void*>(s->st_value),
si_from->get_realpath(), name, reinterpret_cast<void*>(s->st_value),
(*si_found_in)->get_realpath(), reinterpret_cast<void*>((*si_found_in)->base),
(*si_found_in)->get_realpath(), reinterpret_cast<void*>((*si_found_in)->base),
reinterpret_cast<void*>((*si_found_in)->load_bias));
reinterpret_cast<void*>((*si_found_in)->load_bias));
}
}


*symbol = s;
*symbol = s;
return true;
return true;
}
}


class ProtectedDataGuard {
class ProtectedDataGuard {
public:
public:
ProtectedDataGuard() {
ProtectedDataGuard() {
if (ref_count_++ == 0) {
if (ref_count_++ == 0) {
protect_data(PROT_READ | PROT_WRITE);
protect_data(PROT_READ | PROT_WRITE);
}
}
}
}


~ProtectedDataGuard() {
~ProtectedDataGuard() {
if (ref_count_ == 0) { // overflow
if (ref_count_ == 0) { // overflow
__libc_fatal("Too many nested calls to dlopen()");
__libc_fatal("Too many nested calls to dlopen()");
}
}


if (--ref_count_ == 0) {
if (--ref_count_ == 0) {
protect_data(PROT_READ);
protect_data(PROT_READ);
}
}
}
}
private:
private:
void protect_data(int protection) {
void protect_data(int protection) {
g_soinfo_allocator.protect_all(protection);
g_soinfo_allocator.protect_all(protection);
g_soinfo_links_allocator.protect_all(protection);
g_soinfo_links_allocator.protect_all(protection);
}
}


static size_t ref_count_;
static size_t ref_count_;
};
};


size_t ProtectedDataGuard::ref_count_ = 0;
size_t ProtectedDataGuard::ref_count_ = 0;


// Each size has it's own allocator.
// Each size has it's own allocator.
template<size_t size>
template<size_t size>
class SizeBasedAllocator {
class SizeBasedAllocator {
public:
public:
static void* alloc() {
static void* alloc() {
return allocator_.alloc();
return allocator_.alloc();
}
}


static void free(void* ptr) {
static void free(void* ptr) {
allocator_.free(ptr);
allocator_.free(ptr);
}
}


private:
private:
static LinkerBlockAllocator allocator_;
static LinkerBlockAllocator allocator_;
};
};


template<size_t size>
template<size_t size>
LinkerBlockAllocator SizeBasedAllocator<size>::allocator_(size);
LinkerBlockAllocator SizeBasedAllocator<size>::allocator_(size);


template<typename T>
template<typename T>
class TypeBasedAllocator {
class TypeBasedAllocator {
public:
public:
static T* alloc() {
static T* alloc() {
return reinterpret_cast<T*>(SizeBasedAllocator<sizeof(T)>::alloc());
return reinterpret_cast<T*>(SizeBasedAllocator<sizeof(T)>::alloc());
}
}


static void free(T* ptr) {
static void free(T* ptr) {
SizeBasedAllocator<sizeof(T)>::free(ptr);
SizeBasedAllocator<sizeof(T)>::free(ptr);
}
}
};
};


class LoadTask {
class LoadTask {
public:
public:
struct deleter_t {
struct deleter_t {
void operator()(LoadTask* t) {
void operator()(LoadTask* t) {
TypeBasedAllocator<LoadTask>::free(t);
TypeBasedAllocator<LoadTask>::free(t);
}
}
};
};


typedef UniquePtr<LoadTask, deleter_t> unique_ptr;
typedef UniquePtr<LoadTask, deleter_t> unique_ptr;


static deleter_t deleter;
static deleter_t deleter;


static LoadTask* create(const char* name, soinfo* needed_by) {
static LoadTask* create(const char* name, soinfo* needed_by) {
LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
return new (ptr) LoadTask(name, needed_by);
return new (ptr) LoadTask(name, needed_by);
}
}


const char* get_name() const {
const char* get_name() const {
return name_;
return name_;
}
}


soinfo* get_needed_by() const {
soinfo* get_needed_by() const {
return needed_by_;
return needed_by_;
}
}
private:
private:
LoadTask(const char* name, soinfo* needed_by)
LoadTask(const char* name, soinfo* needed_by)
: name_(name), needed_by_(needed_by) {}
: name_(name), needed_by_(needed_by) {}


const char* name_;
const char* name_;
soinfo* needed_by_;
soinfo* needed_by_;


DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask);
DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask);
};
};


LoadTask::deleter_t LoadTask::deleter;
LoadTask::deleter_t LoadTask::deleter;


template <typename T>
template <typename T>
using linked_list_t = LinkedList<T, TypeBasedAllocator<LinkedListEntry<T>>>;
using linked_list_t = LinkedList<T, TypeBasedAllocator<LinkedListEntry<T>>>;


typedef linked_list_t<soinfo> SoinfoLinkedList;
typedef linked_list_t<soinfo> SoinfoLinkedList;
typedef linked_list_t<const char> StringLinkedList;
typedef linked_list_t<const char> StringLinkedList;
typedef linked_list_t<LoadTask> LoadTaskList;
typedef linked_list_t<LoadTask> LoadTaskList;


static soinfo* find_library(const char* name, int rtld_flags, const android_dlextinfo* extinfo);


// This function walks down the tree of soinfo dependencies
// g_ld_all_shim_libs maintains the references to memory as it used
// in breadth-first order and
// in the soinfo structures and in the g_active_shim_libs list.
// * calls action(soinfo* si) for each node, and
// * terminates walk if action returns false.
//
// walk_dependencies_tree returns false if walk was terminated
// by the action and true otherwise.
template<typename F>
static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_size, F action) {
SoinfoLinkedList visit_list;
SoinfoLinkedList visited;


for (size_t i = 0; i < root_soinfos_size; ++i) {
static std::vector<std::string> g_ld_all_shim_libs;
visit_list.push_back(root_soinfos[i]);
}


soinfo* si;
// g_active_shim_libs are all shim libs that are still eligible
while ((si = visit_list.pop_front()) != nullptr) {
// to be loaded. We must remove a shim lib from the list before
if (visited.contains(si)) {
// we load the library to avoid recursive loops (load shim libA
continue;
// for libB where libA also links against libB).
}


if (!action(si)) {
static linked_list_t<const std::string> g_active_shim_libs;
return false;
}


visited.push_back(si);
static void reset_g_active_shim_libs(void) {

g_active_shim_libs.clear();
si->get_children().for_each([&](soinfo* child) {
for (const auto& pair : g_ld_all_shim_libs) {
visit_list.push_back(child);
g_active_shim_libs.push_back(&pair);
});
}
}

return true;
}
}



static void parse_L
static const ElfW(Sym)* dlsym_handle_lookup(soinfo* root, soinfo* skip_until,
soinfo** found, SymbolName& symbol_name) {