129 lines
3.8 KiB
C
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();
|
|
}
|