diff --git a/userspace/libraries/LibC/dlfcn.cpp b/userspace/libraries/LibC/dlfcn.cpp index e1dc052c..114b0b49 100644 --- a/userspace/libraries/LibC/dlfcn.cpp +++ b/userspace/libraries/LibC/dlfcn.cpp @@ -1,23 +1,33 @@ -#include - #include -int dlclose(void*) +extern "C" int __dlclose(void*) __attribute__((weak)); +int dlclose(void* handle) { - ASSERT_NOT_REACHED(); + if (&__dlclose == nullptr) [[unlikely]] + return -1; + return __dlclose(handle); } +extern "C" char* __dlerror() __attribute__((weak)); char* dlerror(void) { - ASSERT_NOT_REACHED(); + if (&__dlerror == nullptr) [[unlikely]] + return const_cast("TODO: dlfcn functions with static linking"); + return __dlerror(); } -void* dlopen(const char*, int) +extern "C" void* __dlopen(const char*, int) __attribute__((weak)); +void* dlopen(const char* file, int mode) { - ASSERT_NOT_REACHED(); + if (&__dlopen == nullptr) [[unlikely]] + return nullptr; + return __dlopen(file, mode); } -void* dlsym(void* __restrict, const char* __restrict) +extern "C" void* __dlsym(void*, const char*) __attribute__((weak)); +void* dlsym(void* __restrict handle, const char* __restrict name) { - ASSERT_NOT_REACHED(); + if (&__dlsym == nullptr) [[unlikely]] + return nullptr; + return __dlsym(handle, name); } diff --git a/userspace/programs/DynamicLoader/main.cpp b/userspace/programs/DynamicLoader/main.cpp index 760c4987..1e534652 100644 --- a/userspace/programs/DynamicLoader/main.cpp +++ b/userspace/programs/DynamicLoader/main.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -421,6 +422,11 @@ static void handle_tls_relocation(const LoadedElf& elf, const RelocT& reloc) #endif } +extern "C" int __dlclose(void* handle); +extern "C" char* __dlerror(void); +extern "C" void* __dlopen(const char* file, int mode); +extern "C" void* __dlsym(void* __restrict handle, const char* __restrict name); + template requires BAN::is_same_v || BAN::is_same_v static uintptr_t handle_relocation(const LoadedElf& elf, const RelocT& reloc, bool resolve_symbols) { @@ -437,7 +443,16 @@ static uintptr_t handle_relocation(const LoadedElf& elf, const RelocT& reloc, bo const auto& symbol = *reinterpret_cast(elf.symtab + symbol_index * elf.syment); const char* symbol_name = reinterpret_cast(elf.strtab + symbol.st_name); - if (symbol.st_shndx && ELF_ST_BIND(symbol.st_info) != STB_WEAK) + if (false) {} +#define CHECK_SYM(sym) \ + else if (strcmp(symbol_name, #sym) == 0) \ + symbol_address = reinterpret_cast(&sym) + CHECK_SYM(__dlclose); + CHECK_SYM(__dlerror); + CHECK_SYM(__dlopen); + CHECK_SYM(__dlsym); +#undef CHECK_SYM + else if (symbol.st_shndx && ELF_ST_BIND(symbol.st_info) != STB_WEAK) symbol_address = elf.base + symbol.st_value; else { @@ -697,6 +712,23 @@ uintptr_t resolve_symbol(const LoadedElf& elf, uintptr_t plt_entry) static LoadedElf& load_elf(const char* path, int fd); +static bool find_library(const char* library_name, char out[PATH_MAX]) +{ + const char* library_dir = "/usr/lib/"; + + char path_buffer[PATH_MAX]; + char* path_ptr = path_buffer; + + if (library_name[0] != '/') + for (size_t i = 0; library_dir[i]; i++) + *path_ptr++ = library_dir[i]; + for (size_t i = 0; library_name[i]; i++) + *path_ptr++ = library_name[i]; + *path_ptr = '\0'; + + return syscall(SYS_REALPATH, path_buffer, out) >= 0; +} + static void handle_dynamic(LoadedElf& elf) { uintptr_t pltgot = 0; @@ -754,24 +786,13 @@ static void handle_dynamic(LoadedElf& elf) if (dynamic.d_tag != DT_NEEDED) continue; - const char* library_dir = "/usr/lib/"; + const char* library_name = reinterpret_cast(elf.strtab + dynamic.d_un.d_val); char path_buffer[PATH_MAX]; - char* path_ptr = path_buffer; + if (!find_library(library_name, path_buffer)) + print_error_and_exit("could not open shared object", 0); - const char* library_name = reinterpret_cast(elf.strtab + dynamic.d_un.d_val); - if (library_name[0] != '/') - for (size_t i = 0; library_dir[i]; i++) - *path_ptr++ = library_dir[i]; - for (size_t i = 0; library_name[i]; i++) - *path_ptr++ = library_name[i]; - *path_ptr = '\0'; - - char realpath[PATH_MAX]; - if (auto ret = syscall(SYS_REALPATH, path_buffer, realpath); ret < 0) - print_error_and_exit("realpath", ret); - - const auto& loaded_elf = load_elf(realpath, -1); + const auto& loaded_elf = load_elf(path_buffer, -1); dynamic.d_un.d_ptr = reinterpret_cast(&loaded_elf); } @@ -1224,6 +1245,75 @@ static void call_init_funcs(LoadedElf& elf, bool is_main_elf) reinterpret_cast(elf.init_array)[i](); } +int __dlclose(void* handle) +{ + // TODO: maybe actually close handles? (not required by spec) + (void)handle; + return 0; +} + +static const char* s_dlerror_string = nullptr; + +char* __dlerror(void) +{ + const char* result = s_dlerror_string; + s_dlerror_string = nullptr; + return const_cast(result); +} + +void* __dlopen(const char* file, int mode) +{ + const bool lazy = !(mode & RTLD_NOW); + + // FIXME: RTLD_{LOCAL,GLOBAL} + + char path_buffer[PATH_MAX]; + if (!find_library(file, path_buffer)) + { + s_dlerror_string = "Could not find file"; + return nullptr; + } + + init_random(); + auto& elf = load_elf(path_buffer, -1); + fini_random(); + + if (!elf.is_relocating && !elf.is_calling_init) + { + if (elf.tls_header.p_type == PT_TLS) + { + s_dlerror_string = "TODO: __dlopen with TLS"; + return nullptr; + } + + relocate_elf(elf, lazy); + call_init_funcs(elf, false); + syscall(SYS_CLOSE, elf.fd); + } + + return &elf; +} + +void* __dlsym(void* __restrict handle, const char* __restrict name) +{ + if (handle == nullptr) + { + for (size_t i = 0; i < s_loaded_file_count; i++) + if (auto* sym = __dlsym(&s_loaded_files[i], name)) + return sym; + return nullptr; + } + + auto& elf = *static_cast(handle); + + // FIXME: look in ELF's dependency tree + if (auto* match = find_symbol(elf, name)) + return reinterpret_cast(elf.base + match->st_value); + + s_dlerror_string = "symbol not found"; + return nullptr; +} + static LibELF::AuxiliaryVector* find_auxv(char** envp) { if (envp == nullptr)