Files
nt-lite/code/user/personalities/posix/posix_dlfcn.c
T

129 lines
3.8 KiB
C

// POSIX compatibility support for dynamic module loading.
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
#include <ntlite/module_loader.h>
#include <ntlite/resource.h>
#include "posix_internal.h"
#include "posix_path.h"
#include "posix_fd.h"
// Clears the current thread's dynamic-loader error string.
static void DlClearError(void) {
PosixRuntimeClearDlError();
}
// Stores one current-thread dynamic-loader error string.
static void DlSetError(const char* message) {
PosixRuntimeSetDlError(message);
}
// Converts one native loader status to errno and a dlerror message.
static void DlSetStatusError(const char* operation, KSTATUS status) {
(void)status;
errno = PosixStatusToErrno(status);
DlSetError(operation);
}
// Loads one DLL-like user module through the native module loader.
__declspec(dllexport) void* dlopen(const char* filename, int flags) {
(void)flags;
DlClearError();
if (!filename || !filename[0]) {
errno = EINVAL;
DlSetError("dlopen requires a module path");
return 0;
}
char native[512];
KSTATUS status = PosixProcessPath(filename, native, sizeof(native));
if (status != NTLSTATUS_SUCCESS) {
errno = EINVAL;
DlSetError("dlopen path is not a loadable module");
return 0;
}
if (!strchr(native, '/')) {
size_t length = strlen(native);
if (length + sizeof("system/") > sizeof(native)) {
errno = ERANGE;
DlSetError("dlopen path is too long");
return 0;
}
memmove(native + 7, native, length + 1u);
memcpy(native, "system/", 7);
}
SYS_HANDLE root = SYS_HANDLE_INVALID;
status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root);
if (status != NTLSTATUS_SUCCESS) {
DlSetStatusError("dlopen system root failed", status);
return 0;
}
NTL_MODULE_HANDLE handle = NTL_MODULE_HANDLE_INVALID;
NTL_LOAD_MODULE_AT_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = NTL_MODULE_LOAD_FLAG_NONE;
req.RootHandle = root;
req.Path = SysSvFromCstr(native);
req.ModuleRootHandle = root;
req.ModuleDirectory = SYS_SV_LITERAL("system");
req.ResolveModuleName = PosixResolveModuleName;
req.ModuleHandleOut = &handle;
status = NtlLoadModuleAt(&req);
(void)SysCloseHandle(root);
if (status != NTLSTATUS_SUCCESS || handle == NTL_MODULE_HANDLE_INVALID) {
DlSetStatusError("dlopen failed", status);
return 0;
}
return (void*)(uintptr_t)handle;
}
// Resolves one named export from a native module handle.
__declspec(dllexport) void* dlsym(void* handle, const char* symbol) {
DlClearError();
if (!handle || !symbol || !symbol[0]) {
errno = EINVAL;
DlSetError("dlsym requires a module handle and symbol name");
return 0;
}
uintptr_t address = 0;
KSTATUS status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)handle,
SysSvFromCstr(symbol), &address);
if (status != NTLSTATUS_SUCCESS || address == 0) {
DlSetStatusError("dlsym failed", status);
return 0;
}
return (void*)address;
}
// Releases one module handle returned by dlopen.
__declspec(dllexport) int dlclose(void* handle) {
DlClearError();
if (!handle) {
errno = EINVAL;
DlSetError("dlclose requires a module handle");
return -1;
}
KSTATUS status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)handle);
if (status != NTLSTATUS_SUCCESS) {
DlSetStatusError("dlclose failed", status);
return -1;
}
return 0;
}
// Returns and clears the most recent dynamic-loader error string.
__declspec(dllexport) char* dlerror(void) {
return PosixRuntimeTakeDlError();
}