17 |
02111-1307 USA |
02111-1307 USA |
18 |
*/ |
*/ |
19 |
|
|
|
#define MODULES_UNINITIALISED -4444 |
|
|
|
|
20 |
#include "m4.h" |
#include "m4.h" |
21 |
#include "pathconf.h" |
#include "pathconf.h" |
22 |
#include "ltdl.h" |
#include "ltdl.h" |
26 |
#undef DEBUG_MODULES |
#undef DEBUG_MODULES |
27 |
|
|
28 |
/* |
/* |
29 |
* This file implements dynamic modules in GNU m4. A module is a |
* This file implements dynamic modules in GNU M4. A module is a |
30 |
* compiled shared object, that can be linked into GNU m4 at run |
* compiled shared object, that can be loaded into GNU M4 at run |
31 |
* time. Information about creating modules is in ../modules/README. |
* time. Information about creating modules is in ../modules/README. |
32 |
* |
* |
33 |
* The current implementation uses libltdl, which is in turn can load |
* This implementation uses libltdl, which is in turn can open modules |
34 |
* modules using either dlopen(3) (exists on GNU/Linux, OSF, Solaris, |
* using either dlopen(3) (exists on GNU/Linux, OSF, Solaris, SunOS and |
35 |
* SunOS), shl_load(3) (exists on HPUX), LoadLibrary(3) (exists on |
* others), shl_load(3) (exists on HPUX), LoadLibrary(3) (exists on |
36 |
* Windows, cygwin, OS/2), load_add_on(3) (exists on BeOS), and can |
* Windows, cygwin, OS/2), load_add_on(3) (exists on BeOS), and can |
37 |
* also fall back to dld_link(3) from libdld or lt_dlpreload(3) from |
* also fall back to dld_link(3) from GNU libdld or lt_dlpreload from |
38 |
* libtool if shared libraries are not available on the host machine. |
* libtool if shared libraries are not available on the host machine. |
39 |
* |
* |
40 |
* A m4 module need only define one external symbol, called |
* An M4 module will usually define an external symbol called |
41 |
* `m4_builtin_table'. This symbol should point to a table of `m4_builtin'. |
* `m4_builtin_table'. This symbol points to a table of `m4_builtin'. |
42 |
* This table is pushed on a list of builtin tables and each definition |
* The table is pushed on an internal stack of builtin tables, each |
43 |
* therein is added to the symbol table. |
* definition therein is added to the symbol table. |
|
* |
|
|
* The code implementing loadable modules is modest. It is divided |
|
|
* between the files builtin.c (user interface and support for multiple |
|
|
* builtin tables) and this file (OS dependant routines). |
|
44 |
* |
* |
45 |
* To load a module, use `load(modulename)'. The function |
* To load a module, call m4_module_load(), which uses the libltdl |
46 |
* `builtin_load' calls m4_module_load() in this file, which uses |
* API to find the module in the module search path. The search |
47 |
* libltdl to find the module in the module search path. This |
* path is initialised from the environment variable M4MODPATH, followed |
48 |
* path is initialised from the environment variable M4MODPATH, or if |
* by the configuration time default where the modules shipped with M4 |
49 |
* not set, initalised to a configuration time default. This |
* itself are installed. Libltdl reads the libtool .la file to |
50 |
* function reads the libtool .la file to get the real library name |
* get the real library name (which can be system dependent), returning |
51 |
* (which can be system dependent) and returns NULL on failure and a |
* NULL on failure or else a libtool module handle for the newly mapped |
52 |
* non-NULL void* on success. If successful lt_dlopen returns the |
* vm segment containing the module code. If the module is not already |
53 |
* value of this void*, which is a handle for the vm segment mapped. |
* loaded, m4_module_load() retrieves the value of the symbol |
54 |
* Module_load() checks to see if the module is already loaded, and if |
* `m4_builtin_table', which is installed using m4_builtin_table_install(). |
|
* not, retrieves the symbol `m4_builtin_table' and returns it's value to |
|
|
* m4_loadmodule(). This pointer should be an m4_builtin*, which is |
|
|
* installed using install_builtin_table(). |
|
55 |
* |
* |
56 |
* In addition to builtin functions, you can also define static macro |
* In addition to builtin functions, you can also define static macro |
57 |
* expansions in the `m4_macro_table' symbol. If you define this symbol |
* expansions in the `m4_macro_table' symbol. If you define this symbol |
58 |
* in your modules, it should be an array of `m4_macro's, mapping macro |
* in your modules, it should be an array of `m4_macro's, mapping macro |
59 |
* names to the expansion text. |
* names to the expansion text. Any macros defined in `m4_macro_table' |
60 |
* |
* are installed into the M4 symbol table with m4_macro_table_install(). |
|
* When a module is loaded, the function "void m4_init_module(struct |
|
|
* obstack *obs)" is called, if defined. Any non NULL return value of |
|
|
* this function will be the expansion of "load". Before program |
|
|
* exit, all modules are unloaded and the function "void |
|
|
* m4_finish_module(void)" is called, if defined. It is |
|
|
* safe to load the same module several times. |
|
61 |
* |
* |
62 |
* To unload a module, use `unload(modulename)'. The function |
* Each time a module is loaded, the module function |
63 |
* `m4_unload' calls m4_module_unload() in this file, which uses |
* "void m4_init_module (struct obstack *obs)" is called, if defined. |
64 |
* remove_builtin_table() to remove the builtins defined by the |
* Any value stored in IBS by this function becomes the expansion of the |
65 |
* unloaded module from the symbol table. If the module has been loaded |
* macro which called it. Before M4 exits, all modules are unloaded and |
66 |
* several times with calls to `load(modulename)', then the module |
* the function "void m4_finish_module (struct obstack *obs)" is called, |
67 |
* will not be unloaded until the same number of calls to unload |
* if defined. It is safe to load the same module several times: the |
68 |
* have been made (nor will the builtin table be purged, nor the finish |
* init and finish functions will also be called multiple times in this |
69 |
* function called). If the module is successfully unloaded, and it |
* case. |
|
* defined a function called `m4_finish_module', that function will be |
|
|
* called last of all. |
|
70 |
* |
* |
71 |
* Modules loaded from the command line (with the `-m' switch) can be |
* To unload a module, use m4_module_unload(). which uses |
72 |
* removed with unload too. |
* m4_builtin_table_uninstall() and m4_macro_table_uninstall() to remove |
73 |
* |
* the builtins defined by the unloaded module from the symbol table. If |
74 |
* When m4 exits, it will unload every remaining loaded module, calling |
* the module has been loaded several times with calls to |
75 |
* any `m4_finish_module' functions they defined before closing. |
* m4_module_load, then the module will not be unloaded until the same |
76 |
|
* number of calls to m4_module_unload() have been made (nor will the |
77 |
|
* symbol table be purged). |
78 |
**/ |
**/ |
79 |
|
|
80 |
M4_GLOBAL_DATA List *m4_modules; |
#define M4_BUILTIN_SYMBOL "m4_builtin_table" |
81 |
|
#define M4_MACRO_SYMBOL "m4_macro_table" |
82 |
|
#define M4_INIT_SYMBOL "m4_init_module" |
83 |
|
#define M4_FINISH_SYMBOL "m4_finish_module" |
84 |
|
|
85 |
|
static const char* m4_module_dlerror M4_PARAMS((void)); |
86 |
|
|
87 |
|
static lt_dlcaller_id m4_caller_id = 0; |
88 |
|
|
89 |
const char * |
const char * |
90 |
m4_module_name (module) |
m4_module_name (handle) |
91 |
const m4_module *module; |
const lt_dlhandle handle; |
92 |
{ |
{ |
93 |
return module ? module->modname : NULL; |
const lt_dlinfo *info; |
|
} |
|
94 |
|
|
95 |
const m4_builtin * |
assert (handle); |
96 |
m4_module_builtins (module) |
|
97 |
const m4_module *module; |
info = lt_dlgetinfo (handle); |
98 |
{ |
|
99 |
return module ? module->bp : NULL; |
return info ? info->name : 0; |
100 |
} |
} |
101 |
|
|
102 |
const m4_macro * |
m4_builtin * |
103 |
m4_module_macros (module) |
m4_module_builtins (handle) |
104 |
const m4_module *module; |
const lt_dlhandle handle; |
105 |
{ |
{ |
106 |
return module ? module->mp : NULL; |
m4_module_data *data; |
107 |
|
|
108 |
|
assert (handle); |
109 |
|
|
110 |
|
data = (m4_module_data *) lt_dlcaller_get_data (m4_caller_id, handle); |
111 |
|
|
112 |
|
return data ? data->bp : 0; |
113 |
} |
} |
114 |
|
|
115 |
VOID * |
m4_macro * |
116 |
m4_module_find_by_modname (elt, match) |
m4_module_macros (handle) |
117 |
List *elt; |
const lt_dlhandle handle; |
|
VOID *match; |
|
118 |
{ |
{ |
119 |
const m4_module *module = (const m4_module *) elt; |
m4_module_data *data; |
120 |
|
|
121 |
if (strcmp (module->modname, (char *) match) == 0) |
assert (handle); |
|
return (VOID *) module; |
|
122 |
|
|
123 |
return NULL; |
data = (m4_module_data *) lt_dlcaller_get_data (m4_caller_id, handle); |
124 |
|
|
125 |
|
return data ? data->mp : 0; |
126 |
} |
} |
127 |
|
|
128 |
VOID * |
lt_dlhandle |
129 |
m4_module_find_by_builtin (elt, match) |
m4_module_find_by_builtin (match) |
130 |
List *elt; |
const m4_builtin *match; |
|
VOID *match; |
|
131 |
{ |
{ |
132 |
const m4_module *module = (const m4_module *) elt; |
lt_dlhandle handle = 0; |
133 |
|
|
134 |
if (module && module->bp) |
while ((handle = lt_dlhandle_next (handle))) |
135 |
{ |
{ |
136 |
const m4_builtin *bp; |
m4_module_data *data |
137 |
|
= (m4_module_data *) lt_dlcaller_get_data (m4_caller_id, handle); |
138 |
|
|
139 |
for (bp = &module->bp[0]; bp->name; ++bp) |
if (data && data->bp) |
140 |
{ |
{ |
141 |
if (bp == (const m4_builtin *) match) |
const m4_builtin *bp; |
142 |
return (VOID *) module; |
|
143 |
|
for (bp = &data->bp[0]; bp->name; ++bp) |
144 |
|
if (bp == match) |
145 |
|
goto found; |
146 |
} |
} |
147 |
} |
} |
148 |
|
|
149 |
return NULL; |
found: |
150 |
|
return handle; |
151 |
} |
} |
152 |
|
|
153 |
/* |
|
154 |
* Initialisation. Currently the module search path in path.c is |
|
155 |
* initialised from M4MODPATH. Only absolute path names are accepted to |
const char * |
156 |
* prevent the path search of the dlopen library from finding wrong |
m4_module_dlerror () |
157 |
* files. |
{ |
158 |
*/ |
const char *dlerror = lt_dlerror (); |
159 |
|
|
160 |
|
if (!dlerror) |
161 |
|
dlerror = _("unknown error"); |
162 |
|
|
163 |
|
return dlerror; |
164 |
|
} |
165 |
|
|
166 |
|
/* Initialisation. Currently the module search path in path.c is |
167 |
|
initialised from M4MODPATH. Only absolute path names are accepted to |
168 |
|
prevent the path search of the dlopen library from finding wrong |
169 |
|
files. */ |
170 |
void |
void |
171 |
m4_module_init () |
m4_module_init () |
172 |
{ |
{ |
173 |
static int errors = MODULES_UNINITIALISED; |
int errors = 0; |
174 |
|
|
175 |
/* Do this only once! */ |
/* Do this only once! If we already have a caller_id, then the |
176 |
if (errors != MODULES_UNINITIALISED) |
module system has already been initialised. */ |
177 |
return; |
if (m4_caller_id) |
178 |
|
{ |
179 |
|
M4ERROR ((warning_status, 0, |
180 |
|
_("Warning: multiple module loader initialisations"))); |
181 |
|
return; |
182 |
|
} |
183 |
|
|
184 |
/* initialise libltdl's memory management. */ |
/* initialise libltdl's memory management. */ |
185 |
lt_dlmalloc = xmalloc; |
lt_dlmalloc = xmalloc; |
186 |
lt_dlfree = xfree; |
lt_dlfree = xfree; |
187 |
|
|
188 |
errors = lt_dlinit(); |
errors = lt_dlinit (); |
189 |
|
|
190 |
/* If the user set M4MODPATH, then use that as the start of libltdls |
/* Register with libltdl for a key to store client data against |
191 |
* module search path, else fall back on the default. |
ltdl module handles. */ |
192 |
*/ |
if (!errors) |
|
if (errors == 0) |
|
193 |
{ |
{ |
194 |
char *path = getenv("M4MODPATH"); |
m4_caller_id = lt_dlcaller_register (); |
195 |
|
|
196 |
#ifdef DEBUG_MODULES |
if (!m4_caller_id) |
197 |
M4_DEBUG_MESSAGE("Module system initialised."); |
{ |
198 |
#endif /* DEBUG_MODULES */ |
const char *error_msg = _("libltdl client registration failed"); |
199 |
|
|
200 |
if (path != NULL) |
lt_dlseterror (lt_dladderror (error_msg)); |
|
errors = lt_dladdsearchdir(path); |
|
|
} |
|
201 |
|
|
202 |
if (errors == 0) |
/* No need to check error statuses from the calls above -- If |
203 |
errors = lt_dladdsearchdir(MODULE_PATH); |
either fails for some reason, a diagnostic will be set for |
204 |
|
lt_dlerror() anyway. */ |
205 |
|
++errors; |
206 |
|
} |
207 |
|
} |
208 |
|
|
209 |
|
if (!errors) |
210 |
|
errors = lt_dlsetsearchpath (MODULE_PATH); |
211 |
|
|
212 |
if (errors != 0) |
/* If the user set M4MODPATH, then use that as the start of the module |
213 |
|
search path. */ |
214 |
|
if (!errors) |
215 |
{ |
{ |
216 |
/* Couldn't initialise the module system; diagnose and exit. */ |
char *path = getenv ("M4MODPATH"); |
217 |
const char *dlerror = lt_dlerror(); |
|
218 |
M4ERROR ((EXIT_FAILURE, 0, |
if (path) |
219 |
_("ERROR: failed to initialise modules: %s"), dlerror)); |
errors = lt_dlinsertsearchdir (lt_dlgetsearchpath (), path); |
220 |
} |
} |
221 |
|
|
222 |
|
/* Couldn't initialise the module system; diagnose and exit. */ |
223 |
|
if (errors) |
224 |
|
M4ERROR ((EXIT_FAILURE, 0, |
225 |
|
_("ERROR: failed to initialise module loader: %s"), |
226 |
|
m4_module_dlerror ())); |
227 |
|
|
228 |
|
#ifdef DEBUG_MODULES |
229 |
|
M4_DEBUG_MESSAGE ("Module loader initialised."); |
230 |
|
#endif /* DEBUG_MODULES */ |
231 |
} |
} |
232 |
|
|
233 |
/* |
|
234 |
* Load a module. MODNAME can be a absolute file name or, if relative, |
/* Load a module. MODNAME can be a absolute file name or, if relative, |
235 |
* it is searched for in the module path. The module is unloaded in |
it is searched for in the module path. The module is unloaded in |
236 |
* case of error. |
case of error. */ |
237 |
*/ |
const lt_dlhandle |
238 |
|
m4_module_open (name, obs) |
239 |
static VOID * |
const char *name; |
|
module_handle_find (elt, match) |
|
|
List *elt; |
|
|
VOID *match; |
|
|
{ |
|
|
m4_module *module = (m4_module *) elt; |
|
|
if (module->handle == match) |
|
|
return (VOID *) module; |
|
|
|
|
|
return NULL; |
|
|
} |
|
|
|
|
|
const m4_module * |
|
|
m4_module_load (modname, obs) |
|
|
const char *modname; |
|
240 |
struct obstack *obs; |
struct obstack *obs; |
241 |
{ |
{ |
242 |
lt_dlhandle handle = NULL; |
lt_dlhandle handle = 0; |
243 |
m4_module_init_t *init_func = NULL; |
m4_module_init_func *init_func = 0; |
244 |
m4_module *module = NULL; |
m4_builtin *bp = 0; |
245 |
m4_builtin *bp = NULL; |
m4_macro *mp = 0; |
|
m4_macro *mp = NULL; |
|
246 |
|
|
247 |
/* Dynamically load the named module. */ |
/* Dynamically open the named module. */ |
248 |
handle = lt_dlopenext (modname); |
handle = lt_dlopenext (name); |
249 |
|
|
250 |
if (handle) |
if (handle) |
251 |
{ |
{ |
252 |
module = (m4_module *) list_find (m4_modules, handle, |
#ifdef DEBUG_MODULES |
253 |
module_handle_find); |
const lt_dlinfo *info = lt_dlgetinfo (handle); |
254 |
if (module) |
M4_DEBUG_MESSAGE2("module %s: opening at %s", name, info->filename); |
255 |
|
#endif |
256 |
|
|
257 |
|
/* Find the builtin table in the opened module. */ |
258 |
|
bp = (m4_builtin *) lt_dlsym (handle, M4_BUILTIN_SYMBOL); |
259 |
|
|
260 |
|
/* Find the macro table in the opened module. */ |
261 |
|
mp = (m4_macro *) lt_dlsym (handle, M4_MACRO_SYMBOL); |
262 |
|
|
263 |
|
/* Find and run any initialising function in the opened module, |
264 |
|
each time the module is opened. */ |
265 |
|
init_func = (m4_module_init_func *) lt_dlsym (handle, M4_INIT_SYMBOL); |
266 |
|
if (init_func) |
267 |
{ |
{ |
268 |
lt_dlclose (handle); /* close the duplicate copy */ |
(*init_func) (handle, obs); |
|
|
|
|
/* increment the load counter */ |
|
|
module->ref_count++; |
|
269 |
|
|
270 |
#ifdef DEBUG_MODULES |
#ifdef DEBUG_MODULES |
271 |
M4_DEBUG_MESSAGE2("module %s: now has %d references.", |
M4_DEBUG_MESSAGE1("module %s: init hook called", name); |
|
modname, module->ref_count); |
|
272 |
#endif /* DEBUG_MODULES */ |
#endif /* DEBUG_MODULES */ |
|
return module; |
|
273 |
} |
} |
274 |
} |
} |
|
|
|
|
|
|
|
if (handle) |
|
|
{ |
|
|
/* Find the builtin table in the loaded module. */ |
|
|
bp = (m4_builtin *) lt_dlsym (handle, "m4_builtin_table"); |
|
|
|
|
|
/* Find the macro table in the loaded module. */ |
|
|
mp = (m4_macro *) lt_dlsym (handle, "m4_macro_table"); |
|
|
|
|
|
/* Find and run the initialising function in the loaded module |
|
|
* (if any). |
|
|
*/ |
|
|
init_func = (m4_module_init_t*) lt_dlsym (handle, "m4_init_module"); |
|
|
if (init_func != NULL) |
|
|
(*init_func) (obs); |
|
|
} |
|
275 |
else |
else |
276 |
{ |
{ |
277 |
/* Couldn't load the module; diagnose and exit. */ |
/* Couldn't open the module; diagnose and exit. */ |
278 |
const char *dlerror = lt_dlerror(); |
M4ERROR ((EXIT_FAILURE, 0, |
279 |
if (dlerror == NULL) |
_("ERROR: cannot open module `%s': %s"), |
280 |
M4ERROR ((EXIT_FAILURE, 0, |
name, m4_module_dlerror ())); |
|
_("ERROR: cannot load module: `%s'"), modname)); |
|
|
else |
|
|
M4ERROR ((EXIT_FAILURE, 0, |
|
|
_("ERROR: cannot load module: `%s': %s"), |
|
|
modname, dlerror)); |
|
281 |
} |
} |
282 |
|
|
283 |
if (!bp && !mp && !init_func) |
if (!bp && !mp && !init_func) |
284 |
{ |
{ |
285 |
M4ERROR ((EXIT_FAILURE, 0, |
/* Since we don't use it here, only check for the finish hook |
286 |
_("ERROR: module `%s' has no entry points"), modname)); |
if we were about to diagnose a module with no entry points. */ |
287 |
|
if (!lt_dlsym (handle, M4_FINISH_SYMBOL)) |
288 |
|
M4ERROR ((EXIT_FAILURE, 0, |
289 |
|
_("ERROR: module `%s' has no entry points"), |
290 |
|
name)); |
291 |
} |
} |
292 |
|
|
293 |
/* If the module was correctly loaded and has the necessary |
/* If the module was correctly opened and has the necessary |
294 |
* symbols, then update our internal tables to remember the |
symbols, then store some client data for the new module |
295 |
* new module. |
on the first open only. */ |
|
*/ |
|
296 |
if (handle) |
if (handle) |
297 |
{ |
{ |
298 |
module = XMALLOC (m4_module, 1); |
const lt_dlinfo *info = lt_dlgetinfo (handle); |
299 |
|
|
300 |
#ifdef DEBUG_MODULES |
if (info && (info->ref_count == 1)) |
301 |
M4_DEBUG_MESSAGE1("module %s: loaded ok", modname); |
{ |
302 |
#endif /* DEBUG_MODULES */ |
m4_module_data *data = XMALLOC (m4_module_data, 1); |
303 |
|
m4_module_data *stale = 0; |
304 |
|
|
305 |
module->modname = xstrdup (modname); |
data->bp = bp; |
306 |
module->handle = handle; |
data->mp = mp; |
|
module->bp = bp; |
|
|
module->mp = mp; |
|
|
module->ref_count = 1; |
|
307 |
|
|
308 |
m4_modules = list_cons ((List *) module, m4_modules); |
stale = lt_dlcaller_set_data (m4_caller_id, handle, data); |
|
} |
|
309 |
|
|
310 |
if (module) |
if (stale) |
311 |
{ |
{ |
312 |
boolean m4_resident_module |
xfree (stale); |
313 |
= (boolean) lt_dlsym (handle, "m4_resident_module"); |
|
314 |
|
M4ERROR ((warning_status, 0, |
315 |
|
_("Warning: overiding stale caller data in module `%s'"), |
316 |
|
name)); |
317 |
|
} |
318 |
|
|
319 |
if (m4_resident_module) |
#ifdef DEBUG_MODULES |
320 |
{ |
M4_DEBUG_MESSAGE1("module %s: opened", name); |
321 |
lt_dlmakeresident (handle); |
#endif /* DEBUG_MODULES */ |
322 |
} |
} |
323 |
} |
} |
324 |
|
|
325 |
return module; |
return handle; |
326 |
} |
} |
327 |
|
|
328 |
void |
void |
329 |
m4_module_install (modname) |
m4_module_close (handle, obs) |
330 |
const char *modname; |
lt_dlhandle handle; |
331 |
|
struct obstack *obs; |
332 |
{ |
{ |
333 |
const m4_module *module = m4_module_load (modname, NULL); |
m4_module_finish_func *finish_func = 0; |
334 |
|
const char *name = 0; |
335 |
|
int errors = 0; |
336 |
|
|
337 |
|
assert (handle); |
338 |
|
name = m4_module_name (handle); |
339 |
|
|
340 |
if (module) |
/* Run any finishing function for the opened module. */ |
341 |
{ |
finish_func = (m4_module_finish_func *) lt_dlsym (handle, M4_FINISH_SYMBOL); |
|
const m4_builtin *bp = m4_module_builtins (module); |
|
|
const m4_macro *mp = m4_module_macros (module); |
|
342 |
|
|
343 |
/* Install the macro functions. */ |
if (finish_func) |
344 |
if (bp) |
{ |
345 |
m4_builtin_table_install (module, bp); |
(*finish_func) (handle, obs); |
346 |
|
|
347 |
/* Install the user macros. */ |
#ifdef DEBUG_MODULES |
348 |
if (mp) |
M4_DEBUG_MESSAGE1("module %s: finish hook called", name); |
349 |
m4_macro_table_install (module, mp); |
#endif /* DEBUG_MODULES */ |
350 |
} |
} |
351 |
} |
|
352 |
|
if (!lt_dlisresident (handle)) |
353 |
|
{ |
354 |
|
{ |
355 |
|
const lt_dlinfo *info = lt_dlgetinfo (handle); |
356 |
|
|
357 |
|
/* When we are about to unload the module for the final |
358 |
|
time, be sure to release any client data memory. */ |
359 |
|
if (info && (info->ref_count == 1)) |
360 |
|
{ |
361 |
|
m4_module_data *stale |
362 |
|
= lt_dlcaller_set_data (m4_caller_id, handle, 0); |
363 |
|
xfree (stale); |
364 |
|
} |
365 |
|
} |
366 |
|
|
367 |
/* |
errors = lt_dlclose (handle); |
368 |
* Unload a module. |
if (!errors) |
|
*/ |
|
|
void |
|
|
m4_module_unload (modname, obs) |
|
|
const char *modname; |
|
|
struct obstack *obs; |
|
|
{ |
|
|
m4_module_finish_t *finish_func; |
|
|
int errors = 0; |
|
|
lt_dlhandle handle = NULL; |
|
|
m4_module *module = NULL; |
|
|
List *cur = NULL; |
|
|
List *prev = NULL; |
|
|
|
|
|
/* Scan the list for the first module with a matching modname field. */ |
|
|
for (cur = m4_modules; cur != NULL; cur = LIST_NEXT (cur)) |
|
|
{ |
|
|
module = (m4_module *) cur; |
|
|
if (modname != NULL && strcmp (modname, module->modname) == 0) |
|
369 |
{ |
{ |
370 |
handle = module->handle; |
#ifdef DEBUG_MODULES |
371 |
break; |
M4_DEBUG_MESSAGE1("module %s: closed", name); |
372 |
|
#endif /* DEBUG_MODULES */ |
373 |
} |
} |
|
prev = cur; |
|
374 |
} |
} |
375 |
|
|
376 |
if (handle == NULL) |
if (errors) |
377 |
{ |
{ |
378 |
M4ERROR ((warning_status, 0, |
M4ERROR ((EXIT_FAILURE, 0, |
379 |
_("ERROR: cannot close module: %s is not loaded."), modname)); |
_("ERROR: cannot close module `%s': %s"), |
380 |
return; |
name, m4_module_dlerror ())); |
381 |
} |
} |
382 |
|
} |
383 |
|
|
384 |
/* Only do the actual unload when the number of calls to unload this |
void |
385 |
module is equal to the number of times it was loaded. */ |
m4_module_close_all (obs) |
386 |
--module->ref_count; |
struct obstack *obs; |
387 |
if (module->ref_count > 0) |
{ |
388 |
{ |
lt_dlhandle handle = lt_dlhandle_next (0); |
|
#ifdef DEBUG_MODULES |
|
|
M4_DEBUG_MESSAGE2("module %s: now has %d references.", |
|
|
modname, module->ref_count); |
|
|
#endif /* DEBUG_MODULES */ |
|
|
return; |
|
|
} |
|
389 |
|
|
390 |
if (module->ref_count == 0) |
while (handle) |
391 |
{ |
{ |
392 |
/* Remove the table references only when ref_count is *exactly* equal |
/* Be careful not to reference each handle after it has been |
393 |
to 0. If m4_module_unload is called again on a resideent module |
closed, even to find the address of the next handle. */ |
394 |
after the references have already been removed, we needn't try to |
lt_dlhandle pending = handle; |
395 |
remove them again! */ |
handle = lt_dlhandle_next (handle); |
396 |
m4_remove_table_reference_symbols (module->bp, module->mp); |
m4_module_close (pending, obs); |
|
|
|
|
#ifdef DEBUG_MODULES |
|
|
M4_DEBUG_MESSAGE1("module %s: builtins undefined", modname); |
|
|
#endif /* DEBUG_MODULES */ |
|
397 |
} |
} |
398 |
|
|
399 |
if (lt_dlisresident (handle)) |
if (lt_dlexit() != 0) |
400 |
{ |
{ |
401 |
/* Fix up reference count for resident modules, for the next |
M4ERROR ((EXIT_FAILURE, 0, |
402 |
call to m4_module_unload(). */ |
_("ERROR: cannot close modules: %s"), |
403 |
module->ref_count = 0; |
m4_module_dlerror ())); |
404 |
} |
} |
405 |
else |
} |
|
{ |
|
|
/* Run the finishing function for the loaded module. */ |
|
|
finish_func |
|
|
= (m4_module_finish_t *) lt_dlsym (handle, "m4_finish_module"); |
|
406 |
|
|
407 |
if (finish_func != NULL) |
lt_dlhandle |
408 |
(*finish_func)(); |
m4_module_load (name, obs) |
409 |
|
const char *name; |
410 |
|
struct obstack *obs; |
411 |
|
{ |
412 |
|
const lt_dlhandle handle = m4_module_open (name, obs); |
413 |
|
|
414 |
errors = lt_dlclose (handle); |
if (handle) |
415 |
if (errors != 0) |
{ |
416 |
{ |
const lt_dlinfo *info = lt_dlgetinfo (handle); |
|
const char *dlerror = lt_dlerror(); |
|
417 |
|
|
418 |
if (dlerror == NULL) |
if (!info) |
419 |
M4ERROR ((EXIT_FAILURE, 0, |
{ |
420 |
_("ERROR: cannot close module: `%s'"), modname)); |
M4ERROR ((warning_status, 0, |
421 |
else |
_("Warning: cannot load module `%s': %s"), |
422 |
M4ERROR ((EXIT_FAILURE, 0, |
name, m4_module_dlerror ())); |
|
_("ERROR: cannot close module: `%s': %s"), |
|
|
modname, dlerror)); |
|
423 |
} |
} |
424 |
else |
else if (info->ref_count == 1) |
425 |
{ |
{ |
426 |
|
const m4_builtin *bp = m4_module_builtins (handle); |
427 |
|
const m4_macro *mp = m4_module_macros (handle); |
428 |
|
|
429 |
|
/* Install the macro functions. */ |
430 |
|
if (bp) |
431 |
|
{ |
432 |
|
m4_builtin_table_install (handle, bp); |
433 |
#ifdef DEBUG_MODULES |
#ifdef DEBUG_MODULES |
434 |
M4_DEBUG_MESSAGE1("module %s unloaded", module->modname); |
M4_DEBUG_MESSAGE1("module %s: builtins loaded", name); |
435 |
#endif /* DEBUG_MODULES */ |
#endif /* DEBUG_MODULES */ |
436 |
|
} |
437 |
|
|
438 |
if (prev) |
/* Install the user macros. */ |
439 |
prev->next = (List *) module->next; |
if (mp) |
440 |
else |
{ |
441 |
m4_modules = (List *) module->next; |
m4_macro_table_install (handle, mp); |
442 |
|
#ifdef DEBUG_MODULES |
443 |
xfree (module->modname); |
M4_DEBUG_MESSAGE1("module %s: macros loaded", name); |
444 |
xfree (module); |
#endif /* DEBUG_MODULES */ |
445 |
|
} |
446 |
} |
} |
447 |
} |
} |
448 |
|
|
449 |
|
return handle; |
450 |
} |
} |
451 |
|
|
452 |
|
/* Unload a module. */ |
453 |
void |
void |
454 |
m4_module_unload_all () |
m4_module_unload (name, obs) |
455 |
|
const char *name; |
456 |
|
struct obstack *obs; |
457 |
{ |
{ |
458 |
int errors = 0; |
lt_dlhandle handle = 0; |
459 |
m4_module *module; |
int errors = 0; |
460 |
m4_module_finish_t *finish_func; |
|
461 |
|
/* Scan the list for the first module with a matching name. */ |
462 |
|
while ((handle = lt_dlhandle_next (handle))) |
463 |
|
{ |
464 |
|
if (name && (strcmp (name, m4_module_name (handle)) == 0)) |
465 |
|
break; |
466 |
|
} |
467 |
|
|
468 |
/* Find and run the finishing function for each loaded module. */ |
if (!handle) |
|
while (m4_modules != NULL) |
|
469 |
{ |
{ |
470 |
module = (m4_module *) m4_modules; |
const char *error_msg = _("module not loaded"); |
471 |
|
|
472 |
finish_func = (m4_module_finish_t*) |
lt_dlseterror (lt_dladderror (error_msg)); |
473 |
lt_dlsym(module->handle, "m4_finish_module"); |
++errors; |
474 |
|
} |
475 |
|
else |
476 |
|
{ |
477 |
|
const lt_dlinfo *info = lt_dlgetinfo (handle); |
478 |
|
|
479 |
if (finish_func != NULL) |
/* Only do the actual close when the number of calls to close this |
480 |
|
module is equal to the number of times it was opened. */ |
481 |
|
if (info->ref_count > 1) |
482 |
{ |
{ |
|
(*finish_func)(); |
|
|
|
|
483 |
#ifdef DEBUG_MODULES |
#ifdef DEBUG_MODULES |
484 |
M4_DEBUG_MESSAGE1("module %s finish hook called", module->modname); |
M4_DEBUG_MESSAGE2("module %s: now has %d references.", |
485 |
|
name, info->ref_count -1); |
486 |
#endif /* DEBUG_MODULES */ |
#endif /* DEBUG_MODULES */ |
487 |
} |
} |
488 |
|
|
489 |
errors = lt_dlclose (module->handle); |
if (info->ref_count == 1) |
490 |
if (errors != 0) |
{ |
491 |
break; |
/* Remove the table references only when ref_count is *exactly* |
492 |
|
equal to 1. If m4_module_close is called again on a |
493 |
|
resident module after the references have already been |
494 |
|
removed, we needn't try to remove them again! */ |
495 |
|
m4_remove_table_reference_symbols (m4_module_builtins (handle), |
496 |
|
m4_module_macros (handle)); |
497 |
|
|
498 |
#ifdef DEBUG_MODULES |
#ifdef DEBUG_MODULES |
499 |
M4_DEBUG_MESSAGE1("module %s unloaded", module->modname); |
M4_DEBUG_MESSAGE1("module %s: symbols unloaded", name); |
500 |
#endif /* DEBUG_MODULES */ |
#endif /* DEBUG_MODULES */ |
|
|
|
|
m4_modules = LIST_NEXT (m4_modules); |
|
|
xfree(module->modname); |
|
|
xfree(module); |
|
|
} |
|
|
|
|
|
if (errors != 0) |
|
|
errors = lt_dlexit(); |
|
|
|
|
|
if (errors != 0) |
|
|
{ |
|
|
const char *dlerror = lt_dlerror(); |
|
|
if (m4_modules == NULL) |
|
|
{ |
|
|
if (dlerror == NULL) |
|
|
M4ERROR ((EXIT_FAILURE, 0, |
|
|
_("ERROR: cannot close modules"))); |
|
|
else |
|
|
M4ERROR ((EXIT_FAILURE, 0, |
|
|
_("ERROR: cannot close modules: %s"), |
|
|
dlerror)); |
|
|
} |
|
|
else |
|
|
{ |
|
|
if (dlerror == NULL) |
|
|
M4ERROR ((EXIT_FAILURE, 0, |
|
|
_("ERROR: cannot close module: `%s'"), |
|
|
module->modname)); |
|
|
else |
|
|
M4ERROR ((EXIT_FAILURE, 0, |
|
|
_("ERROR: cannot close module: `%s': %s"), |
|
|
module->modname, dlerror)); |
|
501 |
} |
} |
502 |
} |
} |
503 |
|
|
504 |
|
if (!errors) |
505 |
|
m4_module_close (handle, obs); |
506 |
|
|
507 |
|
if (errors) |
508 |
|
{ |
509 |
|
M4ERROR ((EXIT_FAILURE, 0, |
510 |
|
_("ERROR: cannot unload module `%s': %s"), |
511 |
|
name, m4_module_dlerror ())); |
512 |
|
} |
513 |
} |
} |