1 /*
2  * system independant wrapper for LOAD()
3  * calls os_load_library, os_unload_library, os_find_symbol
4  */
5 
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9 
10 #include <stdio.h>
11 #include <stdlib.h>			/* free() */
12 
13 #include "h.h"
14 #include "snotypes.h"
15 #include "macros.h"
16 #include "load.h"			/* SNOLOAD_API (for module.h) */
17 #include "module.h"			/* structs */
18 #include "path.h"
19 #include "lib.h"			/* mspec2str() */
20 #include "str.h"
21 
22 struct lib {
23     struct lib *next;			/* libs list */
24     void *oslib;			/* object from os_load_library */
25     struct module *module;		/* see module.h */
26     int refcount;
27     char *name;
28     char *path;
29 };
30 
31 struct func {
32     struct func *next;			/* funcs list */
33     struct func *self;			/* for validity check */
34     loadable_func_t *entry;		/* function entry point */
35     struct lib *lib;
36     void *stash;			/* for use by os_find_symbol */
37     char name[1];			/* for unload (MUST BE LAST)! */
38 };
39 
40 /* list of loaded functions (for UNLOAD) */
41 static VAR struct func *funcs;
42 
43 /*
44  * list of loaded libraries (depends on system ref-counting)
45  * COULD keep just one, but would require a lock
46  */
47 static VAR struct lib *libs;
48 
49 #ifdef SHARED
50 static void loadx_cleanup(void);
51 #endif
52 
53 /* create refcounted lib interface */
54 
55 static struct lib *
libopen(char * name,char * path)56 libopen(char *name, char *path) {
57     void *oslib;
58     struct lib *lp;
59 
60     /* check for duplicate by ORIGINAL name/path */
61     for (lp = libs; lp; lp = lp->next)
62 	if (strcmp(name, lp->name) == 0)
63 	    break;
64 
65     if (!lp) {
66 	oslib = os_load_library(path);
67 	if (oslib == NULL)
68 	    return NULL;
69 	/*
70 	 * defend against multiple paths to same file
71 	 * Also defend against Windows, where once foo/bar.dll is loaded,
72 	 * "bar" seems to suffice
73 	 */
74 	for (lp = libs; lp; lp = lp->next) {
75 	    if (oslib == lp->oslib) {
76 		os_unload_library(oslib); /* reduce use count */
77 		goto found;
78 	    }
79 	}
80 
81 	lp = (struct lib *) malloc(sizeof(struct lib));
82 	lp->oslib = oslib;
83 	lp->refcount = 0;
84 
85 	lp->name = strdup(name);	/* save ORIGINAL name/path */
86 	lp->path = strdup(path);
87 	lp->module = os_find_symbol(oslib, "module", NULL);
88 	/* XXX error if lookup fails when SHARED (&& THREADS)? */
89 	/* XXX complain regardless? */
90 
91 	lp->next = libs;
92 	libs = lp;
93 
94 	if (lp->module)
95 	    module_instance_init(lp->module); /* XXX check return? */
96     }
97  found:
98     lp->refcount++;
99     return lp;
100 }
101 
102 static int
libclose(struct lib * lib)103 libclose(struct lib *lib) {
104     struct lib *lp, *pp;
105 
106     if (--lib->refcount > 0)
107 	return 1;
108 
109     /* find previous, if any */
110     for (lp = libs, pp = NULL; lp && lp != lib; pp = lp, lp = lp->next)
111 	;
112 
113     if (!lp)
114 	return 0;			/* not found */
115 
116     if (lp->module)
117 	module_instance_cleanup(lp->module);
118 
119     /* detach library */
120     os_unload_library(lp->oslib);
121 
122     /* unlink from list */
123     if (pp)
124 	pp->next = lp->next;
125     else
126 	libs = lp->next;
127     free(lp->name);
128     free(lp->path);
129     free(lp);
130 
131     return 1;
132 }
133 
134 /* support for SIL "LOAD" opcode -- load external function */
135 int
load(struct descr * addr,struct spec * sp1,struct spec * sp2)136 load(struct descr *addr,		/* OUT */
137      struct spec *sp1,			/* function */
138      struct spec *sp2) {		/* library */
139     char *fname = mspec2str(sp1);
140     char *lname = mspec2str(sp2);
141     loadable_func_t *entry;		/* function address */
142     struct func *fp = NULL;
143     struct lib *lp = NULL;
144     int ret = FALSE;
145     void *stash = NULL;
146 #ifdef SHARED
147     static VAR char registered;
148     if (!registered) {
149 	reg_cleanup(loadx_cleanup);
150 	registered = 1;
151     }
152 #endif
153     /* always try PML first. Only if lname is empty?? */
154     /*if (!lname || !*lname)*/
155     entry = pml_find(fname);
156 
157     if (!entry) {
158 	char *l2;
159 
160 	lp = libopen(lname, lname);
161 	if (lp)
162 	    goto found_lib;
163 
164 #ifdef DL_EXT
165 	l2 = strjoin(lname, DL_EXT, NULL);
166 	if (!l2)
167 	    return FALSE;
168 	lp = libopen(lname, l2);
169 	free(l2);
170 	if (lp)
171 	    goto found_lib;
172 
173 	if (!abspath(lname)) {
174 	    l2 = io_lib_find("shared", lname, DL_EXT);
175 	    if (l2) {
176 		lp = libopen(lname, l2);
177 		free(l2);
178 		if (lp)
179 		    goto found_lib;
180 	    }
181 
182 	    l2 = io_lib_find("dynload", lname, DL_EXT);
183 	    if (l2) {
184 		lp = libopen(lname, l2);
185 		free(l2);
186 		if (lp)
187 		    goto found_lib;
188 	    }
189 
190 	    l2 = io_lib_find(NULL, lname, DL_EXT);
191 	    if (l2) {
192 		lp = libopen(lname, l2);
193 		free(l2);
194 	    }
195 	    if (!lp)
196 		goto quit;
197 	} // !abspath
198 #endif
199     found_lib:
200 	entry = os_find_symbol(lp->oslib, fname, &stash);
201 	if (!entry) {
202 #ifdef TRY_UNDERSCORE
203 	    l2 = strjoin("_", fname, NULL);
204 	    if (!l2)
205 		goto freelib;
206 	    entry = os_find_symbol(lp->oslib, l2, &stash);
207 	    free(l2);
208 	    if (!entry)
209 #endif
210 		goto freelib;
211 	}
212     } /* not found by pml_find */
213 
214     /* here with entry */
215     fp = (struct func *) malloc( sizeof (struct func) + strlen(fname));
216     if (fp == NULL) {
217     freelib:
218 	libclose(lp);
219 	goto quit;
220     }
221     strcpy(fp->name, fname);
222     fp->lib = lp;
223     fp->entry = entry;
224     fp->stash = stash;
225     fp->self = fp;			/* make valid */
226 
227     fp->next = funcs;			/* link into list */
228     funcs = fp;
229 
230     D_A(addr) = (int_t) fp;
231     D_F(addr) = D_V(addr) = 0;		/* clear flags, type */
232     ret = TRUE;
233 
234 quit:
235     free(fname);
236     free(lname);
237     return ret;
238 }
239 
240 /* support for SIL "LINK" opcode -- call external function */
241 int
callx(struct descr * retval,struct descr * args,struct descr * nargs,struct descr * addr)242 callx(struct descr *retval, struct descr *args,
243       struct descr *nargs, struct descr *addr) {
244     struct func *fp;
245 
246     /* XXX check for zero V & F fields?? */
247     fp = (struct func *) D_A(addr);
248     if (fp == NULL)
249         return FALSE;
250 
251     if (fp->self != fp)                 /* validate, in case unloaded */
252         return FALSE;                   /* fail (fatal error??) */
253 
254     if (!fp->entry)
255 	return FALSE;			/* fail (fatal error??) */
256 
257     /* check fp->lib && fp->lib->abivers */
258     return (fp->entry)( retval, D_A(nargs), (struct descr *)D_A(args) );
259 }
260 
261 static void
funload(char * fname)262 funload(char *fname) {
263     struct func *fp, *pp;
264 
265     if (!fname)
266 	return;
267 
268     for (pp = NULL, fp = funcs; fp != NULL; pp = fp, fp = fp->next) {
269 	if (strcmp(fp->name, fname) == 0)
270 	    break;
271     }
272 
273     if (fp == NULL)			/* not found */
274 	return;
275 
276     /* unlink from list */
277     if (pp == NULL) {			/* first */
278 	funcs = fp->next;
279     }
280     else {				/* not first */
281 	pp->next = fp->next;
282     }
283 
284     os_unload_function(fp->name, fp->stash);
285 
286     libclose(fp->lib);
287 
288     fp->self = NULL;			/* invalidate self pointer!! */
289     fp->entry = NULL;			/* invalidate function pointer */
290     fp->lib = NULL;
291     free(fp);				/* free name block */
292 }
293 
294 void
unload(struct spec * sp)295 unload(struct spec *sp) {
296     char *fname = mspec2str(sp);
297     funload(fname);
298     free(fname);
299 }
300 
301 #ifdef SHARED
302 static void
loadx_cleanup(void)303 loadx_cleanup(void) {
304     while (funcs)
305 	funload(funcs->name);
306 }
307 #endif
308 
309 #include "equ.h"
310 #include "handle.h"
311 
312 pmlret_t
EXTERNAL_DATATYPE(LA_ALIST)313 EXTERNAL_DATATYPE( LA_ALIST ) {
314     struct descr *dp = LA_DESCR(0);
315     struct lib *lp;
316 
317     (void) nargs;
318     if (!dp)
319 	RETFAIL;
320 
321     for (lp = libs; lp; lp = lp->next) {
322 	struct module_instance *mip;
323 
324 	if (!lp->module)
325 	    continue;
326 	mip = (lp->module->get_module_instance)();
327 	if (mip) {
328 	    const char *type_name = handle_table_name(dp, mip);
329 	    if (type_name)
330 		RETSTR(type_name);
331 	}
332     }
333     RETFAIL;
334 }
335 
336 pmlret_t
EXTERNAL_MODULE_NAME(LA_ALIST)337 EXTERNAL_MODULE_NAME( LA_ALIST ) {
338     struct descr *dp = LA_DESCR(0);
339     struct lib *lp;
340 
341     (void) nargs;
342     if (!dp)
343 	RETFAIL;
344 
345     for (lp = libs; lp; lp = lp->next) {
346 	struct module_instance *mip;
347 
348 	if (!lp->module)
349 	    continue;
350 	mip = (lp->module->get_module_instance)();
351 	if (mip) {
352 	    if (handle_table_name(dp, mip))
353 		RETSTR(lp->module->name);
354 	}
355     }
356     RETFAIL;
357 }
358 
359 /* what was used to load; may not be full path */
360 pmlret_t
EXTERNAL_MODULE_PATH(LA_ALIST)361 EXTERNAL_MODULE_PATH( LA_ALIST ) {
362     struct descr *dp = LA_DESCR(0);
363     struct lib *lp;
364 
365     (void) nargs;
366     if (!dp)
367 	RETFAIL;
368 
369     for (lp = libs; lp; lp = lp->next) {
370 	struct module_instance *mip;
371 
372 	if (!lp->module)
373 	    continue;
374 	mip = (lp->module->get_module_instance)();
375 	if (mip) {
376 	    if (handle_table_name(dp, mip))
377 		RETSTR(lp->path);
378 	}
379     }
380     RETFAIL;
381 }
382