119 lines
3.3 KiB
C
119 lines
3.3 KiB
C
// POSIX compatibility support for dynamic module loading.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.h>
|
|
#include "posix.h"
|
|
#include "posix_path.h"
|
|
#include "posix_fd.h"
|
|
|
|
static char gDlError[160];
|
|
static int gDlErrorPending;
|
|
|
|
// Clears the process-local dynamic-loader error string.
|
|
static void DlClearError(void) {
|
|
gDlError[0] = '\0';
|
|
gDlErrorPending = 0;
|
|
}
|
|
|
|
// Stores one process-local dynamic-loader error string.
|
|
static void DlSetError(const char* message) {
|
|
if (!message) message = "dynamic loader error";
|
|
strncpy(gDlError, message, sizeof(gDlError) - 1);
|
|
gDlError[sizeof(gDlError) - 1] = '\0';
|
|
gDlErrorPending = 1;
|
|
}
|
|
|
|
// Converts one native loader status to errno and a dlerror message.
|
|
static void DlSetStatusError(const char* operation, uint32_t 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];
|
|
const char* path = PosixTranslatePath(filename, native, sizeof(native));
|
|
if (!path || strcmp(path, SYNTH_DEVNULL) == 0 || strcmp(path, SYNTH_DEVZERO) == 0) {
|
|
errno = EINVAL;
|
|
DlSetError("dlopen path is not a loadable module");
|
|
return 0;
|
|
}
|
|
|
|
uint32_t handle = 0;
|
|
SYS_LOAD_MODULE_REQUEST req;
|
|
memset(&req, 0, sizeof(req));
|
|
req.Size = sizeof(req);
|
|
req.Path = SysSvFromCstr(path);
|
|
req.ModuleHandleOut = &handle;
|
|
|
|
uint32_t status = SysLoadModule(&req);
|
|
if (status != NTLSTATUS_SUCCESS || handle == 0) {
|
|
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;
|
|
SYS_GET_MODULE_EXPORT_REQUEST req;
|
|
memset(&req, 0, sizeof(req));
|
|
req.Size = sizeof(req);
|
|
req.ModuleHandle = (uint32_t)(uintptr_t)handle;
|
|
req.Name = SysSvFromCstr(symbol);
|
|
req.AddressOut = &address;
|
|
|
|
uint32_t status = SysGetModuleExport(&req);
|
|
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;
|
|
}
|
|
|
|
uint32_t status = SysUnloadModule((uint32_t)(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) {
|
|
if (!gDlErrorPending || !gDlError[0]) return 0;
|
|
gDlErrorPending = 0;
|
|
return gDlError;
|
|
}
|