Untitled diff

Created Diff never expires
/*
/*
* 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) {