LibC: Implement very hacky dlopen/dlsym/dclose

If ELF loading fails, this will just crash the program :D

And there is no support for RTLD_LOCAL
This commit is contained in:
Bananymous 2025-06-26 14:21:21 +03:00
parent 83c0ef3514
commit 397219c22e
2 changed files with 125 additions and 25 deletions

View File

@ -1,23 +1,33 @@
#include <BAN/Assert.h>
#include <dlfcn.h>
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<char*>("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);
}

View File

@ -4,6 +4,7 @@
#include <LibELF/Types.h>
#include <LibELF/Values.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
@ -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<typename RelocT> requires BAN::is_same_v<RelocT, ElfNativeRelocation> || BAN::is_same_v<RelocT, ElfNativeRelocationA>
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<ElfNativeSymbol*>(elf.symtab + symbol_index * elf.syment);
const char* symbol_name = reinterpret_cast<const char*>(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<uintptr_t>(&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<const char*>(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<const char*>(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<uintptr_t>(&loaded_elf);
}
@ -1224,6 +1245,75 @@ static void call_init_funcs(LoadedElf& elf, bool is_main_elf)
reinterpret_cast<init_t*>(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<char*>(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<LoadedElf*>(handle);
// FIXME: look in ELF's dependency tree
if (auto* match = find_symbol(elf, name))
return reinterpret_cast<void*>(elf.base + match->st_value);
s_dlerror_string = "symbol not found";
return nullptr;
}
static LibELF::AuxiliaryVector* find_auxv(char** envp)
{
if (envp == nullptr)