Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_module.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27
28#include <stdio.h>
29#include <string.h>
30
31#include "nilorea/n_module.h"
32
33#ifdef __windows__
34#include <windows.h>
35#else
36#include <dlfcn.h>
37#endif
38
40struct N_MODULE {
42 void* handle;
43};
44
46static __thread char _n_module_last_error[N_MODULE_ERROR_MAX] = "";
47
49static void _n_module_set_error(const char* what, const char* subject) {
50#ifdef __windows__
51 /* half the message, so the system's text leaves room for what and on what */
52 char sys[N_MODULE_ERROR_MAX / 2] = "";
53 DWORD code = GetLastError();
54 DWORD n = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, code, 0, sys, (DWORD)sizeof(sys), NULL);
55
56 /* FormatMessage ends its text with a line break */
57 while (n > 0 && (sys[n - 1] == '\r' || sys[n - 1] == '\n' || sys[n - 1] == ' ')) sys[--n] = '\0';
58 snprintf(_n_module_last_error, sizeof(_n_module_last_error), "%s %s: error %lu, %s", what, subject, (unsigned long)code, n > 0 ? sys : "no description");
59#else
60 /* dlerror() clears itself when read, and is NULL when nothing failed */
61 const char* sys = dlerror();
62
63 snprintf(_n_module_last_error, sizeof(_n_module_last_error), "%s %s: %s", what, subject, sys ? sys : "no description");
64#endif
66}
67
68N_MODULE* n_module_open(const char* path) {
69 N_MODULE* module = NULL;
70 void* handle = NULL;
71
72 __n_assert(path, return NULL);
73 _n_module_last_error[0] = '\0';
74#ifdef __windows__
75 {
76 /* The wide call, so a path outside the system code page opens; the
77 * ANSI one if the text is not valid UTF-8, which is what the caller
78 * would have got before. */
79 wchar_t wpath[MAX_PATH * 4];
80 int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, wpath, (int)(sizeof(wpath) / sizeof(wpath[0])));
81
82 if (wlen > 0)
83 handle = (void*)LoadLibraryW(wpath);
84 else
85 handle = (void*)LoadLibraryA(path);
86 }
87#else
88 /* RTLD_LAZY: a driver library pulls in far more than a caller uses.
89 * RTLD_LOCAL: a plugin must not shadow a symbol the program already has. */
90 handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
91#endif
92 if (!handle) {
93 _n_module_set_error("cannot open", path);
94 return NULL;
95 }
96 Malloc(module, N_MODULE, 1);
97 if (!module) {
98#ifdef __windows__
99 FreeLibrary((HMODULE)handle);
100#else
101 dlclose(handle);
102#endif
103 snprintf(_n_module_last_error, sizeof(_n_module_last_error), "cannot open %s: out of memory", path);
104 return NULL;
105 }
106 module->handle = handle;
107 return module;
108}
109
110void* n_module_symbol(N_MODULE* module, const char* name) {
111 void* symbol = NULL;
112
113 __n_assert(module, return NULL);
114 __n_assert(module->handle, return NULL);
115 __n_assert(name, return NULL);
116 _n_module_last_error[0] = '\0';
117#ifdef __windows__
118 {
119 /* GetProcAddress returns a function pointer and the caller wants a
120 * void pointer, which C does not convert between; memcpy is the
121 * defined way across. */
122 FARPROC proc = GetProcAddress((HMODULE)module->handle, name);
123
124 if (proc) memcpy(&symbol, &proc, sizeof(symbol));
125 }
126#else
127 /* A symbol can legitimately be NULL; clearing dlerror first is how the
128 * two cases are told apart, and nothing here wants a NULL symbol. */
129 (void)dlerror();
130 symbol = dlsym(module->handle, name);
131#endif
132 if (!symbol) _n_module_set_error("no symbol", name);
133 return symbol;
134}
135
137 int ok = TRUE;
138
139 if (!module || !*module) return TRUE;
140 if ((*module)->handle) {
141#ifdef __windows__
142 if (!FreeLibrary((HMODULE)(*module)->handle)) {
143 _n_module_set_error("cannot close", "module");
144 ok = FALSE;
145 }
146#else
147 if (dlclose((*module)->handle) != 0) {
148 _n_module_set_error("cannot close", "module");
149 ok = FALSE;
150 }
151#endif
152 }
153 Free(*module);
154 return ok;
155}
156
157const char* n_module_error(void) {
159}
160
161const char* n_module_extension(void) {
162#if defined(__windows__)
163 return ".dll";
164#elif defined(__APPLE__)
165 return ".dylib";
166#else
167 return ".so";
168#endif
169}
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
const char * n_module_extension(void)
The file extension shared libraries have on this platform.
Definition n_module.c:161
#define N_MODULE_ERROR_MAX
longest error text kept by n_module_error(), terminator included
Definition n_module.h:58
N_MODULE * n_module_open(const char *path)
Open a shared library.
Definition n_module.c:68
int n_module_close(N_MODULE **module)
Close a library and free its handle.
Definition n_module.c:136
void * n_module_symbol(N_MODULE *module, const char *name)
Find a symbol in an opened library.
Definition n_module.c:110
const char * n_module_error(void)
What the system said about this thread's last failed call.
Definition n_module.c:157
static __thread char _n_module_last_error[512]
the last failure on this thread, as the system worded it
Definition n_module.c:46
void * handle
dlopen handle, or HMODULE
Definition n_module.c:42
static void _n_module_set_error(const char *what, const char *subject)
helper: record why what failed on subject, in the system's words
Definition n_module.c:49
an opened shared library
Definition n_module.c:40
Load a shared library at runtime and find its symbols.