aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/options/rtdl/generic/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/rtdl/generic/main.cpp')
-rw-r--r--lib/mlibc/options/rtdl/generic/main.cpp844
1 files changed, 0 insertions, 844 deletions
diff --git a/lib/mlibc/options/rtdl/generic/main.cpp b/lib/mlibc/options/rtdl/generic/main.cpp
deleted file mode 100644
index 3cff1e4..0000000
--- a/lib/mlibc/options/rtdl/generic/main.cpp
+++ /dev/null
@@ -1,844 +0,0 @@
-
-#include <elf.h>
-#include <link.h>
-
-#include <frg/manual_box.hpp>
-#include <frg/small_vector.hpp>
-
-#include <abi-bits/auxv.h>
-#include <mlibc/debug.hpp>
-#include <mlibc/rtdl-sysdeps.hpp>
-#include <mlibc/rtdl-config.hpp>
-#include <mlibc/rtdl-abi.hpp>
-#include <mlibc/stack_protector.hpp>
-#include <internal-config.h>
-#include <abi-bits/auxv.h>
-
-#include "elf.hpp"
-#include "linker.hpp"
-
-#if __MLIBC_POSIX_OPTION
-#include <dlfcn.h>
-#endif
-
-#define HIDDEN __attribute__((__visibility__("hidden")))
-#define EXPORT __attribute__((__visibility__("default")))
-
-static constexpr bool logEntryExit = false;
-static constexpr bool logStartup = false;
-static constexpr bool logDlCalls = false;
-
-#ifndef MLIBC_STATIC_BUILD
-extern HIDDEN void *_GLOBAL_OFFSET_TABLE_[];
-extern HIDDEN elf_dyn _DYNAMIC[];
-#endif
-
-namespace mlibc {
- // Declared in options/internal/mlibc/tcb.hpp.
- bool tcb_available_flag = false;
-}
-
-mlibc::RtdlConfig rtdlConfig;
-
-bool ldShowAuxv = false;
-
-uintptr_t *entryStack;
-frg::manual_box<ObjectRepository> initialRepository;
-frg::manual_box<Scope> globalScope;
-
-frg::manual_box<RuntimeTlsMap> runtimeTlsMap;
-
-// We use a small vector of size 4 to avoid memory allocation for the default library paths
-frg::manual_box<frg::small_vector<frg::string_view, 4, MemoryAllocator>> libraryPaths;
-
-frg::manual_box<frg::vector<frg::string_view, MemoryAllocator>> preloads;
-
-static SharedObject *executableSO;
-extern HIDDEN char __ehdr_start[];
-
-// Global debug interface variable
-DebugInterface globalDebugInterface;
-
-#ifndef MLIBC_STATIC_BUILD
-
-// Use a PC-relative instruction sequence to find our runtime load address.
-uintptr_t getLdsoBase() {
-#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__)
- // On x86_64, the first GOT entry holds the link-time address of _DYNAMIC.
- // TODO: This isn't guaranteed on AArch64, so this might fail with some linkers.
- auto linktime_dynamic = reinterpret_cast<uintptr_t>(_GLOBAL_OFFSET_TABLE_[0]);
- auto runtime_dynamic = reinterpret_cast<uintptr_t>(_DYNAMIC);
- return runtime_dynamic - linktime_dynamic;
-#elif defined(__riscv)
- return reinterpret_cast<uintptr_t>(&__ehdr_start);
-#endif
-}
-
-// Relocates the dynamic linker (i.e. this DSO) itself.
-// Assumptions:
-// - There are no references to external symbols.
-// Note that this code is fragile in the sense that it must not contain relocations itself.
-// TODO: Use tooling to verify this at compile time.
-extern "C" void relocateSelf() {
- size_t rela_offset = 0;
- size_t rela_size = 0;
- size_t rel_offset = 0;
- size_t rel_size = 0;
- size_t relr_offset = 0;
- size_t relr_size = 0;
- for(size_t i = 0; _DYNAMIC[i].d_tag != DT_NULL; i++) {
- auto ent = &_DYNAMIC[i];
- switch(ent->d_tag) {
- case DT_REL: rel_offset = ent->d_un.d_ptr; break;
- case DT_RELSZ: rel_size = ent->d_un.d_val; break;
- case DT_RELA: rela_offset = ent->d_un.d_ptr; break;
- case DT_RELASZ: rela_size = ent->d_un.d_val; break;
- case DT_RELR: relr_offset = ent->d_un.d_ptr; break;
- case DT_RELRSZ: relr_size = ent->d_un.d_val; break;
- }
- }
-
- auto ldso_base = getLdsoBase();
-
- __ensure((rel_offset != 0) ^ (rela_offset != 0));
-
- for(size_t disp = 0; disp < rela_size; disp += sizeof(elf_rela)) {
- auto reloc = reinterpret_cast<elf_rela *>(ldso_base + rela_offset + disp);
-
- auto type = ELF_R_TYPE(reloc->r_info);
- if(ELF_R_SYM(reloc->r_info))
- __builtin_trap();
-
- auto p = reinterpret_cast<uint64_t *>(ldso_base + reloc->r_offset);
- switch(type) {
- case R_RELATIVE:
- *p = ldso_base + reloc->r_addend;
- break;
- default:
- __builtin_trap();
- }
- }
-
- for(size_t disp = 0; disp < rel_size; disp += sizeof(elf_rel)) {
- auto reloc = reinterpret_cast<elf_rel *>(ldso_base + rel_offset + disp);
-
- auto type = ELF_R_TYPE(reloc->r_info);
- if(ELF_R_SYM(reloc->r_info))
- __builtin_trap();
-
- auto p = reinterpret_cast<uint64_t *>(ldso_base + reloc->r_offset);
- switch(type) {
- case R_RELATIVE:
- *p += ldso_base;
- break;
- default:
- __builtin_trap();
- }
- }
-
- elf_addr *addr = nullptr;
- for(size_t disp = 0; disp < relr_size; disp += sizeof(elf_relr)) {
- auto entry = *(elf_relr *)(ldso_base + relr_offset + disp);
-
- // Even entry indicates the beginning address.
- if(!(entry & 1)) {
- addr = (elf_addr *)(ldso_base + entry);
- __ensure(addr);
- *addr++ += ldso_base;
- }else {
- // Odd entry indicates entry is a bitmap of the subsequent locations to be relocated.
- for(int i = 0; entry; ++i) {
- if(entry & 1) {
- addr[i] += ldso_base;
- }
- entry >>= 1;
- }
-
- // Each entry describes at max 63 (on 64bit) or 31 (on 32bit) subsequent locations.
- addr += CHAR_BIT * sizeof(elf_relr) - 1;
- }
- }
-}
-#endif
-
-extern "C" void *lazyRelocate(SharedObject *object, unsigned int rel_index) {
- __ensure(object->lazyExplicitAddend);
- auto reloc = (elf_rela *)(object->baseAddress + object->lazyRelocTableOffset
- + rel_index * sizeof(elf_rela));
- auto type = ELF_R_TYPE(reloc->r_info);
- auto symbol_index = ELF_R_SYM(reloc->r_info);
-
- __ensure(type == R_X86_64_JUMP_SLOT);
- __ensure(ELF_CLASS == ELFCLASS64);
-
- auto symbol = (elf_sym *)(object->baseAddress + object->symbolTableOffset
- + symbol_index * sizeof(elf_sym));
- ObjectSymbol r(object, symbol);
- auto p = Scope::resolveGlobalOrLocal(*globalScope, object->localScope, r.getString(), object->objectRts, 0);
- if(!p)
- mlibc::panicLogger() << "Unresolved JUMP_SLOT symbol" << frg::endlog;
-
- //mlibc::infoLogger() << "Lazy relocation to " << symbol_str
- // << " resolved to " << pointer << frg::endlog;
-
- *(uint64_t *)(object->baseAddress + reloc->r_offset) = p->virtualAddress();
- return (void *)p->virtualAddress();
-}
-
-extern "C" [[ gnu::visibility("default") ]] void *__rtdl_allocateTcb() {
- auto tcb = allocateTcb();
- initTlsObjects(tcb, globalScope->_objects, false);
- return tcb;
-}
-
-extern "C" {
- [[ gnu::visibility("hidden") ]] void dl_debug_state() {
- // This function is used to signal changes in the debugging link map,
- // GDB just sets a breakpoint on this function and we can call it
- // everytime we update the link map. We don't need to implement
- // anything besides defining and calling it.
- }
-}
-
-extern "C" [[gnu::alias("dl_debug_state"), gnu::visibility("default")]] void _dl_debug_state() noexcept;
-
-// This symbol can be used by GDB to find the global interface structure
-[[ gnu::visibility("default") ]] DebugInterface *_dl_debug_addr = &globalDebugInterface;
-
-static frg::vector<frg::string_view, MemoryAllocator> parseList(frg::string_view paths, frg::string_view separators) {
- frg::vector<frg::string_view, MemoryAllocator> list{getAllocator()};
-
- size_t p = 0;
- while(p < paths.size()) {
- size_t s; // Offset of next colon or end of string.
- if(size_t cs = paths.find_first_of(separators, p); cs != size_t(-1)) {
- s = cs;
- }else{
- s = paths.size();
- }
-
- auto path = paths.sub_string(p, s - p);
- p = s + 1;
-
- if(path.size() == 0)
- continue;
-
- if(path.ends_with("/")) {
- size_t i = path.size() - 1;
- while(i > 0 && path[i] == '/')
- i--;
- path = path.sub_string(0, i + 1);
- }
-
- if(path == "/")
- path = "";
-
- list.push_back(path);
- }
-
- return list;
-}
-
-extern "C" void *interpreterMain(uintptr_t *entry_stack) {
- if(logEntryExit)
- mlibc::infoLogger() << "Entering ld.so" << frg::endlog;
- entryStack = entry_stack;
- runtimeTlsMap.initialize();
- libraryPaths.initialize(getAllocator());
- preloads.initialize(getAllocator());
-
- void *phdr_pointer = 0;
- size_t phdr_entry_size = 0;
- size_t phdr_count = 0;
- void *entry_pointer = 0;
- void *stack_entropy = nullptr;
-
- const char *execfn = "(executable)";
-
-#ifndef MLIBC_STATIC_BUILD
- using ctor_fn = void(*)(void);
-
- ctor_fn *ldso_ctors = nullptr;
- size_t num_ldso_ctors = 0;
-
- auto ldso_base = getLdsoBase();
- if(logStartup) {
- mlibc::infoLogger() << "ldso: Own base address is: 0x"
- << frg::hex_fmt(ldso_base) << frg::endlog;
- mlibc::infoLogger() << "ldso: Own dynamic section is at: " << _DYNAMIC << frg::endlog;
- }
-
-#ifdef __x86_64__
- // These entries are reserved on x86_64.
- // TODO: Use a fake PLT stub that reports an error message?
- _GLOBAL_OFFSET_TABLE_[1] = 0;
- _GLOBAL_OFFSET_TABLE_[2] = 0;
-#endif
-
- // Validate our own dynamic section.
- // Here, we make sure that the dynamic linker does not need relocations itself.
- uintptr_t strtab_offset = 0;
- uintptr_t soname_str = 0;
- for(size_t i = 0; _DYNAMIC[i].d_tag != DT_NULL; i++) {
- auto ent = &_DYNAMIC[i];
- switch(ent->d_tag) {
- case DT_STRTAB: strtab_offset = ent->d_un.d_ptr; break;
- case DT_SONAME: soname_str = ent->d_un.d_val; break;
- case DT_INIT_ARRAY: ldso_ctors = reinterpret_cast<ctor_fn *>(ent->d_un.d_ptr + ldso_base); break;
- case DT_INIT_ARRAYSZ: num_ldso_ctors = ent->d_un.d_val / sizeof(ctor_fn); break;
- case DT_HASH:
- case DT_GNU_HASH:
- case DT_STRSZ:
- case DT_SYMTAB:
- case DT_SYMENT:
- case DT_RELA:
- case DT_RELASZ:
- case DT_RELAENT:
- case DT_RELACOUNT:
- case DT_DEBUG:
- case DT_REL:
- case DT_RELSZ:
- case DT_RELENT:
- case DT_RELCOUNT:
- case DT_RELR:
- case DT_RELRSZ:
- case DT_RELRENT:
- continue;
- default:
- __ensure(!"Unexpected dynamic entry in program interpreter");
- }
- }
- __ensure(strtab_offset);
- __ensure(soname_str);
-
- // Find the auxiliary vector by skipping args and environment.
- auto aux = entryStack;
- aux += *aux + 1; // First, we skip argc and all args.
- __ensure(!*aux);
- aux++;
- while(*aux) { // Loop through the environment.
- auto env = reinterpret_cast<char *>(*aux);
- frg::string_view view{env};
- size_t s = view.find_first('=');
-
- if(s == size_t(-1))
- mlibc::panicLogger() << "rtdl: environment '" << env << "' is missing a '='" << frg::endlog;
-
- auto name = view.sub_string(0, s);
- auto value = const_cast<char *>(view.data() + s + 1);
-
- if(name == "LD_SHOW_AUXV" && *value && *value != '0') {
- ldShowAuxv = true;
- }else if(name == "LD_LIBRARY_PATH" && *value) {
- for(auto path : parseList(value, ":;"))
- libraryPaths->push_back(path);
- }else if(name == "LD_PRELOAD" && *value) {
- *preloads = parseList(value, " :");
- }
-
- aux++;
- }
- aux++;
-
- // Add default library paths
- libraryPaths->push_back("/lib");
- libraryPaths->push_back("/lib64");
- libraryPaths->push_back("/usr/lib");
- libraryPaths->push_back("/usr/lib64");
-
- // Parse the actual vector.
- while(true) {
- auto value = aux + 1;
- if(!(*aux))
- break;
-
- if(ldShowAuxv) {
- switch(*aux) {
- case AT_PHDR: mlibc::infoLogger() << "AT_PHDR: 0x" << frg::hex_fmt{*value} << frg::endlog; break;
- case AT_PHENT: mlibc::infoLogger() << "AT_PHENT: " << *value << frg::endlog; break;
- case AT_PHNUM: mlibc::infoLogger() << "AT_PHNUM: " << *value << frg::endlog; break;
- case AT_ENTRY: mlibc::infoLogger() << "AT_ENTRY: 0x" << frg::hex_fmt{*value} << frg::endlog; break;
- case AT_PAGESZ: mlibc::infoLogger() << "AT_PAGESZ: " << *value << frg::endlog; break;
- case AT_BASE: mlibc::infoLogger() << "AT_BASE: 0x" << frg::hex_fmt{*value} << frg::endlog; break;
- case AT_FLAGS: mlibc::infoLogger() << "AT_FLAGS: 0x" << frg::hex_fmt{*value} << frg::endlog; break;
- case AT_NOTELF: mlibc::infoLogger() << "AT_NOTELF: " << frg::hex_fmt{*value} << frg::endlog; break;
- case AT_UID: mlibc::infoLogger() << "AT_UID: " << *value << frg::endlog; break;
- case AT_EUID: mlibc::infoLogger() << "AT_EUID: " << *value << frg::endlog; break;
- case AT_GID: mlibc::infoLogger() << "AT_GID: " << *value << frg::endlog; break;
- case AT_EGID: mlibc::infoLogger() << "AT_EGID: " << *value << frg::endlog; break;
-#ifdef AT_PLATFORM
- case AT_PLATFORM: mlibc::infoLogger() << "AT_PLATFORM: " << reinterpret_cast<const char *>(*value) << frg::endlog; break;
-#endif
-#ifdef AT_HWCAP
- case AT_HWCAP: mlibc::infoLogger() << "AT_HWCAP: " << frg::hex_fmt{*value} << frg::endlog; break;
-#endif
-#ifdef AT_CLKTCK
- case AT_CLKTCK: mlibc::infoLogger() << "AT_CLKTCK: " << *value << frg::endlog; break;
-#endif
-#ifdef AT_FPUCW
- case AT_FPUCW: mlibc::infoLogger() << "AT_FPUCW: " << frg::hex_fmt{*value} << frg::endlog; break;
-#endif
-#ifdef AT_SECURE
- case AT_SECURE: mlibc::infoLogger() << "AT_SECURE: " << *value << frg::endlog; break;
-#endif
-#ifdef AT_RANDOM
- case AT_RANDOM: mlibc::infoLogger() << "AT_RANDOM: 0x" << frg::hex_fmt{*value} << frg::endlog; break;
-#endif
-#ifdef AT_EXECFN
- case AT_EXECFN: mlibc::infoLogger() << "AT_EXECFN: " << reinterpret_cast<const char *>(*value) << frg::endlog; break;
-#endif
-#ifdef AT_SYSINFO_EHDR
- case AT_SYSINFO_EHDR: mlibc::infoLogger() << "AT_SYSINFO_EHDR: 0x" << frg::hex_fmt{*value} << frg::endlog; break;
-#endif
- }
- }
-
- // TODO: Whitelist auxiliary vector entries here?
- switch(*aux) {
- case AT_PHDR: phdr_pointer = reinterpret_cast<void *>(*value); break;
- case AT_PHENT: phdr_entry_size = *value; break;
- case AT_PHNUM: phdr_count = *value; break;
- case AT_ENTRY: entry_pointer = reinterpret_cast<void *>(*value); break;
- case AT_EXECFN: execfn = reinterpret_cast<const char *>(*value); break;
- case AT_RANDOM: stack_entropy = reinterpret_cast<void*>(*value); break;
- case AT_SECURE: rtdlConfig.secureRequired = reinterpret_cast<uintptr_t>(*value); break;
- }
-
- aux += 2;
- }
- globalDebugInterface.base = reinterpret_cast<void*>(ldso_base);
-
-// This is here because libgcc will add a global constructor on glibc Linux
-// (which is what it believes we are due to the aarch64-linux-gnu toolchain)
-// in order to check if LSE atomics are supported.
-//
-// This is not necessary on a custom Linux toolchain and is purely an artifact of
-// using the host toolchain.
-#if defined(__aarch64__) && defined(__gnu_linux__)
- for (size_t i = 0; i < num_ldso_ctors; i++) {
- if(logStartup)
- mlibc::infoLogger() << "ldso: Running own constructor at "
- << reinterpret_cast<void *>(ldso_ctors[i])
- << frg::endlog;
- ldso_ctors[i]();
- }
-#else
- if (num_ldso_ctors > 0) {
- mlibc::panicLogger() << "ldso: Found unexpected own global constructor(s), init_array starts at: "
- << ldso_ctors
- << frg::endlog;
- }
-#endif
-
-#else
- auto ehdr = reinterpret_cast<elf_ehdr*>(__ehdr_start);
- phdr_pointer = reinterpret_cast<void*>((uintptr_t)ehdr->e_phoff + (uintptr_t)ehdr);
- phdr_entry_size = ehdr->e_phentsize;
- phdr_count = ehdr->e_phnum;
- entry_pointer = reinterpret_cast<void*>(ehdr->e_entry);
-#endif
- __ensure(phdr_pointer);
- __ensure(entry_pointer);
-
- if(logStartup)
- mlibc::infoLogger() << "ldso: Executable PHDRs are at " << phdr_pointer
- << frg::endlog;
-
- // perform the initial dynamic linking
- initialRepository.initialize();
-
- globalScope.initialize(true);
-
- // Add the dynamic linker, as well as the exectuable to the repository.
-#ifndef MLIBC_STATIC_BUILD
- auto ldso_soname = reinterpret_cast<const char *>(ldso_base + strtab_offset + soname_str);
- auto ldso = initialRepository->injectObjectFromDts(ldso_soname,
- frg::string<MemoryAllocator> { getAllocator() },
- ldso_base, _DYNAMIC, 1);
- ldso->phdrPointer = phdr_pointer;
- ldso->phdrCount = phdr_count;
- ldso->phdrEntrySize = phdr_entry_size;
-
- // TODO: support non-zero base addresses?
- executableSO = initialRepository->injectObjectFromPhdrs(execfn,
- frg::string<MemoryAllocator> { execfn, getAllocator() },
- phdr_pointer, phdr_entry_size, phdr_count, entry_pointer, 1);
-
- // We can't initialise the ldso object after the executable SO,
- // so we have to set the ldso path after loading both.
- ldso->path = executableSO->interpreterPath;
-
-#else
- executableSO = initialRepository->injectStaticObject(execfn,
- frg::string<MemoryAllocator>{ execfn, getAllocator() },
- phdr_pointer, phdr_entry_size, phdr_count, entry_pointer, 1);
- globalDebugInterface.base = (void*)executableSO->baseAddress;
-#endif
-
- globalDebugInterface.head = &executableSO->linkMap;
- executableSO->inLinkMap = true;
- Loader linker{globalScope.get(), executableSO, true, 1};
- linker.linkObjects(executableSO);
-
- mlibc::initStackGuard(stack_entropy);
-
- auto tcb = allocateTcb();
- if(mlibc::sys_tcb_set(tcb))
- __ensure(!"sys_tcb_set() failed");
- tcb->tid = mlibc::this_tid();
- mlibc::tcb_available_flag = true;
-
- globalDebugInterface.ver = 1;
- globalDebugInterface.brk = &dl_debug_state;
- globalDebugInterface.state = 0;
- dl_debug_state();
-
- linker.initObjects();
-
- if(logEntryExit)
- mlibc::infoLogger() << "Leaving ld.so, jump to "
- << (void *)executableSO->entry << frg::endlog;
- return executableSO->entry;
-}
-
-const char *lastError;
-
-extern "C" [[ gnu::visibility("default") ]] uintptr_t *__dlapi_entrystack() {
- return entryStack;
-}
-
-extern "C" [[ gnu::visibility("default") ]]
-const char *__dlapi_error() {
- auto error = lastError;
- lastError = nullptr;
- return error;
-}
-
-extern "C" [[ gnu::visibility("default") ]]
-void *__dlapi_get_tls(struct __abi_tls_entry *entry) {
- return reinterpret_cast<char *>(accessDtv(entry->object)) + entry->offset;
-}
-
-extern "C" [[ gnu::visibility("default") ]]
-const mlibc::RtdlConfig &__dlapi_get_config() {
- return rtdlConfig;
-}
-
-#if __MLIBC_POSIX_OPTION
-
-extern "C" [[ gnu::visibility("default") ]]
-void *__dlapi_open(const char *file, int flags, void *returnAddress) {
- if (logDlCalls)
- mlibc::infoLogger() << "rtdl: __dlapi_open(" << (file ? file : "nullptr") << ")" << frg::endlog;
-
- if (flags & RTLD_DEEPBIND)
- mlibc::infoLogger() << "rtdl: dlopen(RTLD_DEEPBIND) is unsupported" << frg::endlog;
-
- if(!file)
- return executableSO;
-
- // TODO: Thread-safety!
- auto rts = rtsCounter++;
-
- SharedObject *object;
- if (flags & RTLD_NOLOAD) {
- object = initialRepository->findLoadedObject(file);
- if (object && object->globalRts == 0 && (flags & RTLD_GLOBAL)) {
- // The object was opened with RTLD_LOCAL, but we are called with RTLD_NOLOAD | RTLD_GLOBAL.
- // According to the man page, we should promote to the global scope here.
- object->globalRts = rts;
- globalScope->appendObject(object);
- }
- } else {
- bool isGlobal = flags & RTLD_GLOBAL;
- Scope *newScope = isGlobal ? globalScope.get() : nullptr;
-
- frg::expected<LinkerError, SharedObject *> objectResult;
- if (frg::string_view{file}.find_first('/') == size_t(-1)) {
- // In order to know which RUNPATH / RPATH to process, we must find the calling object.
- SharedObject *origin = initialRepository->findCaller(returnAddress);
- if (!origin) {
- mlibc::panicLogger() << "rtdl: unable to determine calling object of dlopen "
- << "(ra = " << returnAddress << ")" << frg::endlog;
- }
-
- objectResult = initialRepository->requestObjectWithName(file, origin, newScope, !isGlobal, rts);
- } else {
- objectResult = initialRepository->requestObjectAtPath(file, newScope, !isGlobal, rts);
- }
-
- if(!objectResult) {
- switch (objectResult.error()) {
- case LinkerError::success:
- __builtin_unreachable();
- case LinkerError::notFound:
- lastError = "Cannot locate requested DSO";
- break;
- case LinkerError::fileTooShort:
- lastError = "File too short";
- break;
- case LinkerError::notElf:
- lastError = "File is not an ELF file";
- break;
- case LinkerError::wrongElfType:
- lastError = "File has wrong ELF type";
- break;
- case LinkerError::outOfMemory:
- lastError = "Out of memory";
- break;
- case LinkerError::invalidProgramHeader:
- lastError = "File has invalid program header";
- break;
- }
- return nullptr;
- }
- object = objectResult.value();
-
- Loader linker{object->localScope, nullptr, false, rts};
- linker.linkObjects(object);
- linker.initObjects();
- }
-
- dl_debug_state();
-
- return object;
-}
-
-extern "C" [[ gnu::visibility("default") ]]
-void *__dlapi_resolve(void *handle, const char *string, void *returnAddress) {
- if (logDlCalls) {
- const char *name;
- bool quote = false;
- if (handle == RTLD_DEFAULT) {
- name = "RTLD_DEFAULT";
- } else if (handle == RTLD_NEXT) {
- name = "RTLD_NEXT";
- } else {
- name = ((SharedObject *)handle)->name.data();
- quote = true;
- }
-
- mlibc::infoLogger() << "rtdl: __dlapi_resolve(" << (quote ? "\"" : "") << name
- << (quote ? "\"" : "") << ", \"" << string << "\")" << frg::endlog;
- }
-
- frg::optional<ObjectSymbol> target;
-
- if (handle == RTLD_DEFAULT) {
- target = globalScope->resolveSymbol(string, 0, 0);
- } else if (handle == RTLD_NEXT) {
- SharedObject *origin = initialRepository->findCaller(returnAddress);
- if (!origin) {
- mlibc::panicLogger() << "rtdl: unable to determine calling object of dlsym "
- << "(ra = " << returnAddress << ")" << frg::endlog;
- }
-
- target = Scope::resolveGlobalOrLocalNext(*globalScope, origin->localScope, string, origin);
- } else {
- // POSIX does not unambiguously state how dlsym() is supposed to work; it just
- // states that "The symbol resolution algorithm used shall be dependency order
- // as described in dlopen()".
- //
- // Linux libc's lookup the symbol in the given DSO and all of its dependencies
- // in breadth-first order. That is also what we implement here.
- //
- // Note that this *differs* from the algorithm that is used for relocations
- // (since the algorithm used for relocations takes (i) the global scope,
- // and (ii) the local scope of the DSO into account (which can contain more objects
- // than just the dependencies of the DSO, if the DSO was loaded as a dependency
- // of a dlopen()ed DSO).
-
- frg::vector<SharedObject *, MemoryAllocator> queue{getAllocator()};
-
- struct Token { };
- frg::hash_map<
- SharedObject *, Token,
- frg::hash<SharedObject *>, MemoryAllocator
- > visited{frg::hash<SharedObject *>{}, getAllocator()};
-
- auto root = reinterpret_cast<SharedObject *>(handle);
- visited.insert(root, Token{});
- queue.push_back(root);
-
- for(size_t i = 0; i < queue.size(); i++) {
- auto current = queue[i];
-
- target = resolveInObject(current, string);
- if(target)
- break;
-
- for(auto dep : current->dependencies) {
- if(visited.get(dep))
- continue;
- visited.insert(dep, Token{});
- queue.push_back(dep);
- }
- }
- }
-
- if (!target) {
- if (logDlCalls)
- mlibc::infoLogger() << "rtdl: could not resolve \"" << string << "\"" << frg::endlog;
-
- lastError = "Cannot resolve requested symbol";
- return nullptr;
- }
- return reinterpret_cast<void *>(target->virtualAddress());
-}
-
-struct __dlapi_symbol {
- const char *file;
- void *base;
- const char *symbol;
- void *address;
-};
-
-extern "C" [[ gnu::visibility("default") ]]
-int __dlapi_reverse(const void *ptr, __dlapi_symbol *info) {
- if (logDlCalls)
- mlibc::infoLogger() << "rtdl: __dlapi_reverse(" << ptr << ")" << frg::endlog;
-
- for(size_t i = 0; i < initialRepository->loadedObjects.size(); i++) {
- auto object = initialRepository->loadedObjects[i];
-
- auto eligible = [&] (ObjectSymbol cand) {
- if(cand.symbol()->st_shndx == SHN_UNDEF)
- return false;
-
- auto bind = ELF_ST_BIND(cand.symbol()->st_info);
- if(bind != STB_GLOBAL && bind != STB_WEAK)
- return false;
-
- return true;
- };
-
- auto hash_table = (Elf64_Word *)(object->baseAddress + object->hashTableOffset);
- auto num_symbols = hash_table[1];
- for(size_t i = 0; i < num_symbols; i++) {
- ObjectSymbol cand{object, (elf_sym *)(object->baseAddress
- + object->symbolTableOffset + i * sizeof(elf_sym))};
- if(eligible(cand) && cand.virtualAddress() == reinterpret_cast<uintptr_t>(ptr)) {
- if (logDlCalls)
- mlibc::infoLogger() << "rtdl: Found symbol " << cand.getString() << " in object "
- << object->path << frg::endlog;
-
- info->file = object->path.data();
- info->base = reinterpret_cast<void *>(object->baseAddress);
- info->symbol = cand.getString();
- info->address = reinterpret_cast<void *>(cand.virtualAddress());
- return 0;
- }
- }
- }
-
- // Not found, find the DSO it should be in.
- for(size_t i = 0; i < initialRepository->loadedObjects.size(); i++) {
- auto object = initialRepository->loadedObjects[i];
-
- for(size_t j = 0; j < object->phdrCount; j++) {
- auto phdr = (elf_phdr *)((uintptr_t)object->phdrPointer + j * object->phdrEntrySize);
- if(phdr->p_type != PT_LOAD) {
- continue;
- }
- uintptr_t start = object->baseAddress + phdr->p_vaddr;
- uintptr_t end = start + phdr->p_memsz;
- if(reinterpret_cast<uintptr_t>(ptr) >= start && reinterpret_cast<uintptr_t>(ptr) < end) {
- mlibc::infoLogger() << "rtdl: Found DSO " << object->path << frg::endlog;
- info->file = object->path.data();
- info->base = reinterpret_cast<void *>(object->baseAddress);
- info->symbol = nullptr;
- info->address = 0;
- return 0;
- }
- }
- }
-
- if (logDlCalls)
- mlibc::infoLogger() << "rtdl: Could not find symbol in __dlapi_reverse()" << frg::endlog;
-
- return -1;
-}
-
-extern "C" [[ gnu::visibility("default") ]]
-int __dlapi_close(void *) {
- if (logDlCalls)
- mlibc::infoLogger() << "mlibc: dlclose() is a no-op" << frg::endlog;
- return 0;
-}
-
-#endif
-
-extern "C" [[ gnu::visibility("default") ]]
-int __dlapi_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void*), void *data) {
- int last_return = 0;
- for (auto object : initialRepository->loadedObjects) {
- struct dl_phdr_info info;
- info.dlpi_addr = object->baseAddress;
- info.dlpi_name = object->name.data();
-
- if(object->isMainObject) {
- info.dlpi_name = "";
- } else {
- info.dlpi_name = object->name.data();
- }
- info.dlpi_phdr = static_cast<ElfW(Phdr)*>(object->phdrPointer);
- info.dlpi_phnum = object->phdrCount;
- info.dlpi_adds = rtsCounter;
- info.dlpi_subs = 0; // TODO(geert): implement dlclose().
- if (object->tlsModel != TlsModel::null)
- info.dlpi_tls_modid = object->tlsIndex;
- else
- info.dlpi_tls_modid = 0;
- info.dlpi_tls_data = tryAccessDtv(object);
-
- last_return = callback(&info, sizeof(struct dl_phdr_info), data);
- if(last_return)
- return last_return;
- }
-
- return last_return;
-}
-
-extern "C" [[ gnu::visibility("default") ]]
-void __dlapi_enter(uintptr_t *entry_stack) {
-#if MLIBC_STATIC_BUILD
- interpreterMain(entry_stack);
-#else
- (void)entry_stack;
-#endif
-}
-
-// XXX(qookie):
-// This is here because libgcc will call into __getauxval on glibc Linux
-// (which is what it believes we are due to the aarch64-linux-gnu toolchain)
-// in order to find AT_HWCAP to discover if LSE atomics are supported.
-//
-// This is not necessary on a custom Linux toolchain and is purely an artifact of
-// using the host toolchain.
-
-// __gnu_linux__ is the define checked by libgcc
-#if defined(__aarch64__) && defined(__gnu_linux__) && !defined(MLIBC_STATIC_BUILD)
-
-extern "C" unsigned long __getauxval(unsigned long type) {
- // Find the auxiliary vector by skipping args and environment.
- auto aux = entryStack;
- aux += *aux + 1; // Skip argc and all arguments
- __ensure(!*aux);
- aux++;
- while(*aux) // Now, we skip the environment.
- aux++;
- aux++;
-
- // Parse the auxiliary vector.
- while(true) {
- auto value = aux + 1;
- if(*aux == AT_NULL) {
- return 0;
- }else if(*aux == type) {
- return *value;
- }
- aux += 2;
- }
-}
-
-#endif