1/*
2 * tclLoadAix.c --
3 *
4 *	This file implements the dlopen and dlsym APIs under the
5 *	AIX operating system, to enable the Tcl "load" command to
6 *	work.  This code was provided by Jens-Uwe Mager.
7 *
8 *	This file is subject to the following copyright notice, which is
9 *	different from the notice used elsewhere in Tcl.  The file has
10 *	been modified to incorporate the file dlfcn.h in-line.
11 *
12 *	Copyright (c) 1992,1993,1995,1996, Jens-Uwe Mager, Helios Software GmbH
13 *	Not derived from licensed software.
14
15 *	Permission is granted to freely use, copy, modify, and redistribute
16 *	this software, provided that the author is not construed to be liable
17 *	for any results of using the software, alterations are clearly marked
18 *	as such, and this notice is not modified.
19 *
20 * SCCS: @(#) tclLoadAix.c 1.11 96/10/07 10:41:24
21 *
22 * Note:  this file has been altered from the original in a few
23 * ways in order to work properly with Tcl.
24 */
25
26/*
27 * @(#)dlfcn.c	1.7 revision of 95/08/14  19:08:38
28 * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH
29 * 30159 Hannover, Germany
30 */
31
32#include <stdio.h>
33#include <errno.h>
34#include <string.h>
35#include <transformInt.h>
36#include <sys/types.h>
37#include <sys/ldr.h>
38#include <a.out.h>
39#include <ldfcn.h>
40
41/*
42 * We simulate dlopen() et al. through a call to load. Because AIX has
43 * no call to find an exported symbol we read the loader section of the
44 * loaded module and build a list of exported symbols and their virtual
45 * address.
46 */
47
48typedef struct {
49	char		*name;		/* the symbols's name */
50	VOID		*addr;		/* its relocated virtual address */
51} Export, *ExportPtr;
52
53/*
54 * To be able to intialize, a library may provide a dl_info structure
55 * that contains functions to be called to initialize and terminate.
56 */
57struct dl_info {
58	void (*init) _ANSI_ARGS_((void));
59	void (*fini) _ANSI_ARGS_((void));
60};
61
62/*
63 * The VOID * handle returned from dlopen is actually a ModulePtr.
64 */
65typedef struct Module {
66	struct Module	*next;
67	char		*name;		/* module name for refcounting */
68	int		refCnt;		/* the number of references */
69	VOID		*entry;		/* entry point from load */
70	struct dl_info	*info;		/* optional init/terminate functions */
71	struct dl_info	*cdtors;	/* optional C++ constructors */
72	int		nExports;	/* the number of exports found */
73	ExportPtr	exports;	/* the array of exports */
74} Module, *ModulePtr;
75
76/*
77 * We keep a list of all loaded modules to be able to call the fini
78 * handlers and destructors at atexit() time.
79 */
80static ModulePtr modList;
81
82/*
83 * The last error from one of the dl* routines is kept in static
84 * variables here. Each error is returned only once to the caller.
85 */
86static char errbuf[BUFSIZ];
87static int errvalid;
88
89static void caterr _ANSI_ARGS_((char *));
90static int readExports _ANSI_ARGS_((ModulePtr));
91static void terminate _ANSI_ARGS_((void));
92static VOID *findMain _ANSI_ARGS_((void));
93
94VOID *
95dlopen(path, mode)
96    const char *path;
97    int mode;
98{
99	register ModulePtr mp;
100	static VOID *mainModule;
101
102	/*
103	 * Upon the first call register a terminate handler that will
104	 * close all libraries. Also get a reference to the main module
105	 * for use with loadbind.
106	 */
107	if (!mainModule) {
108		if ((mainModule = findMain()) == NULL)
109			return NULL;
110		atexit(terminate);
111	}
112	/*
113	 * Scan the list of modules if we have the module already loaded.
114	 */
115	for (mp = modList; mp; mp = mp->next)
116		if (strcmp(mp->name, path) == 0) {
117			mp->refCnt++;
118			return (VOID *) mp;
119		}
120	if ((mp = (ModulePtr)calloc(1, sizeof(*mp))) == NULL) {
121		errvalid++;
122		strcpy(errbuf, "calloc: ");
123		strcat(errbuf, strerror(errno));
124		return (VOID *) NULL;
125	}
126	mp->name = malloc((unsigned) (strlen(path) + 1));
127	strcpy(mp->name, path);
128	/*
129	 * load should be declared load(const char *...). Thus we
130	 * cast the path to a normal char *. Ugly.
131	 */
132	if ((mp->entry = (VOID *)load((char *)path, L_NOAUTODEFER, NULL)) == NULL) {
133		free(mp->name);
134		free(mp);
135		errvalid++;
136		strcpy(errbuf, "dlopen: ");
137		strcat(errbuf, path);
138		strcat(errbuf, ": ");
139		/*
140		 * If AIX says the file is not executable, the error
141		 * can be further described by querying the loader about
142		 * the last error.
143		 */
144		if (errno == ENOEXEC) {
145			char *tmp[BUFSIZ/sizeof(char *)];
146			if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
147				strcpy(errbuf, strerror(errno));
148			else {
149				char **p;
150				for (p = tmp; *p; p++)
151					caterr(*p);
152			}
153		} else
154			strcat(errbuf, strerror(errno));
155		return (VOID *) NULL;
156	}
157	mp->refCnt = 1;
158	mp->next = modList;
159	modList = mp;
160	if (loadbind(0, mainModule, mp->entry) == -1) {
161		dlclose(mp);
162		errvalid++;
163		strcpy(errbuf, "loadbind: ");
164		strcat(errbuf, strerror(errno));
165		return (VOID *) NULL;
166	}
167	/*
168	 * If the user wants global binding, loadbind against all other
169	 * loaded modules.
170	 */
171	if (mode & RTLD_GLOBAL) {
172		register ModulePtr mp1;
173		for (mp1 = mp->next; mp1; mp1 = mp1->next)
174			if (loadbind(0, mp1->entry, mp->entry) == -1) {
175				dlclose(mp);
176				errvalid++;
177				strcpy(errbuf, "loadbind: ");
178				strcat(errbuf, strerror(errno));
179				return (VOID *) NULL;
180			}
181	}
182	if (readExports(mp) == -1) {
183		dlclose(mp);
184		return (VOID *) NULL;
185	}
186	/*
187	 * If there is a dl_info structure, call the init function.
188	 */
189	if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) {
190		if (mp->info->init)
191			(*mp->info->init)();
192	} else
193		errvalid = 0;
194	/*
195	 * If the shared object was compiled using xlC we will need
196	 * to call static constructors (and later on dlclose destructors).
197	 */
198	if (mp->cdtors = (struct dl_info *) dlsym(mp, "__cdtors")) {
199		while (mp->cdtors->init) {
200			(*mp->cdtors->init)();
201			mp->cdtors++;
202		}
203	} else
204		errvalid = 0;
205	return (VOID *) mp;
206}
207
208/*
209 * Attempt to decipher an AIX loader error message and append it
210 * to our static error message buffer.
211 */
212static void
213caterr(s)
214    char *s;
215{
216	register char *p = s;
217
218	while (*p >= '0' && *p <= '9')
219		p++;
220	switch(atoi(s)) {
221	case L_ERROR_TOOMANY:
222		strcat(errbuf, "to many errors");
223		break;
224	case L_ERROR_NOLIB:
225		strcat(errbuf, "can't load library");
226		strcat(errbuf, p);
227		break;
228	case L_ERROR_UNDEF:
229		strcat(errbuf, "can't find symbol");
230		strcat(errbuf, p);
231		break;
232	case L_ERROR_RLDBAD:
233		strcat(errbuf, "bad RLD");
234		strcat(errbuf, p);
235		break;
236	case L_ERROR_FORMAT:
237		strcat(errbuf, "bad exec format in");
238		strcat(errbuf, p);
239		break;
240	case L_ERROR_ERRNO:
241		strcat(errbuf, strerror(atoi(++p)));
242		break;
243	default:
244		strcat(errbuf, s);
245		break;
246	}
247}
248
249VOID *
250dlsym(handle, symbol)
251    VOID *handle;
252    CONST char *symbol;
253{
254	register ModulePtr mp = (ModulePtr)handle;
255	register ExportPtr ep;
256	register int i;
257
258	/*
259	 * Could speed up the search, but I assume that one assigns
260	 * the result to function pointers anyways.
261	 */
262	for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
263		if (strcmp(ep->name, symbol) == 0)
264			return ep->addr;
265	errvalid++;
266	strcpy(errbuf, "dlsym: undefined symbol ");
267	strcat(errbuf, symbol);
268	return NULL;
269}
270
271char *
272dlerror()
273{
274	if (errvalid) {
275		errvalid = 0;
276		return errbuf;
277	}
278	return NULL;
279}
280
281int
282dlclose(handle)
283    VOID *handle;
284{
285	register ModulePtr mp = (ModulePtr)handle;
286	int result;
287	register ModulePtr mp1;
288
289	if (--mp->refCnt > 0)
290		return 0;
291	if (mp->info && mp->info->fini)
292		(*mp->info->fini)();
293	if (mp->cdtors)
294		while (mp->cdtors->fini) {
295			(*mp->cdtors->fini)();
296			mp->cdtors++;
297		}
298	result = unload(mp->entry);
299	if (result == -1) {
300		errvalid++;
301		strcpy(errbuf, strerror(errno));
302	}
303	if (mp->exports) {
304		register ExportPtr ep;
305		register int i;
306		for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
307			if (ep->name)
308				free(ep->name);
309		free(mp->exports);
310	}
311	if (mp == modList)
312		modList = mp->next;
313	else {
314		for (mp1 = modList; mp1; mp1 = mp1->next)
315			if (mp1->next == mp) {
316				mp1->next = mp->next;
317				break;
318			}
319	}
320	free(mp->name);
321	free(mp);
322	return result;
323}
324
325static void
326terminate()
327{
328	while (modList)
329		dlclose(modList);
330}
331
332/*
333 * Build the export table from the XCOFF .loader section.
334 */
335static int
336readExports(mp)
337    ModulePtr mp;
338{
339	LDFILE *ldp = NULL;
340	SCNHDR sh, shdata;
341	LDHDR *lhp;
342	char *ldbuf;
343	LDSYM *ls;
344	int i;
345	ExportPtr ep;
346
347	if ((ldp = ldopen(mp->name, ldp)) == NULL) {
348		struct ld_info *lp;
349		char *buf;
350		int size = 4*1024;
351		if (errno != ENOENT) {
352			errvalid++;
353			strcpy(errbuf, "readExports: ");
354			strcat(errbuf, strerror(errno));
355			return -1;
356		}
357		/*
358		 * The module might be loaded due to the LIBPATH
359		 * environment variable. Search for the loaded
360		 * module using L_GETINFO.
361		 */
362		if ((buf = malloc(size)) == NULL) {
363			errvalid++;
364			strcpy(errbuf, "readExports: ");
365			strcat(errbuf, strerror(errno));
366			return -1;
367		}
368		while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
369			free(buf);
370			size += 4*1024;
371			if ((buf = malloc(size)) == NULL) {
372				errvalid++;
373				strcpy(errbuf, "readExports: ");
374				strcat(errbuf, strerror(errno));
375				return -1;
376			}
377		}
378		if (i == -1) {
379			errvalid++;
380			strcpy(errbuf, "readExports: ");
381			strcat(errbuf, strerror(errno));
382			free(buf);
383			return -1;
384		}
385		/*
386		 * Traverse the list of loaded modules. The entry point
387		 * returned by load() does actually point to the data
388		 * segment origin.
389		 */
390		lp = (struct ld_info *)buf;
391		while (lp) {
392			if (lp->ldinfo_dataorg == mp->entry) {
393				ldp = ldopen(lp->ldinfo_filename, ldp);
394				break;
395			}
396			if (lp->ldinfo_next == 0)
397				lp = NULL;
398			else
399				lp = (struct ld_info *)((char *)lp + lp->ldinfo_next);
400		}
401		free(buf);
402		if (!ldp) {
403			errvalid++;
404			strcpy(errbuf, "readExports: ");
405			strcat(errbuf, strerror(errno));
406			return -1;
407		}
408	}
409	if (TYPE(ldp) != U802TOCMAGIC) {
410		errvalid++;
411		strcpy(errbuf, "readExports: bad magic");
412		while(ldclose(ldp) == FAILURE)
413			;
414		return -1;
415	}
416	/*
417	 * Get the padding for the data section. This is needed for
418	 * AIX 4.1 compilers. This is used when building the final
419	 * function pointer to the exported symbol.
420	 */
421	if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
422		errvalid++;
423		strcpy(errbuf, "readExports: cannot read data section header");
424		while(ldclose(ldp) == FAILURE)
425			;
426		return -1;
427	}
428	if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
429		errvalid++;
430		strcpy(errbuf, "readExports: cannot read loader section header");
431		while(ldclose(ldp) == FAILURE)
432			;
433		return -1;
434	}
435	/*
436	 * We read the complete loader section in one chunk, this makes
437	 * finding long symbol names residing in the string table easier.
438	 */
439	if ((ldbuf = (char *)malloc(sh.s_size)) == NULL) {
440		errvalid++;
441		strcpy(errbuf, "readExports: ");
442		strcat(errbuf, strerror(errno));
443		while(ldclose(ldp) == FAILURE)
444			;
445		return -1;
446	}
447	if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
448		errvalid++;
449		strcpy(errbuf, "readExports: cannot seek to loader section");
450		free(ldbuf);
451		while(ldclose(ldp) == FAILURE)
452			;
453		return -1;
454	}
455	if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
456		errvalid++;
457		strcpy(errbuf, "readExports: cannot read loader section");
458		free(ldbuf);
459		while(ldclose(ldp) == FAILURE)
460			;
461		return -1;
462	}
463	lhp = (LDHDR *)ldbuf;
464	ls = (LDSYM *)(ldbuf+LDHDRSZ);
465	/*
466	 * Count the number of exports to include in our export table.
467	 */
468	for (i = lhp->l_nsyms; i; i--, ls++) {
469		if (!LDR_EXPORT(*ls))
470			continue;
471		mp->nExports++;
472	}
473	if ((mp->exports = (ExportPtr)calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
474		errvalid++;
475		strcpy(errbuf, "readExports: ");
476		strcat(errbuf, strerror(errno));
477		free(ldbuf);
478		while(ldclose(ldp) == FAILURE)
479			;
480		return -1;
481	}
482	/*
483	 * Fill in the export table. All entries are relative to
484	 * the entry point we got from load.
485	 */
486	ep = mp->exports;
487	ls = (LDSYM *)(ldbuf+LDHDRSZ);
488	for (i = lhp->l_nsyms; i; i--, ls++) {
489		char *symname;
490		char tmpsym[SYMNMLEN+1];
491		if (!LDR_EXPORT(*ls))
492			continue;
493		if (ls->l_zeroes == 0)
494			symname = ls->l_offset+lhp->l_stoff+ldbuf;
495		else {
496			/*
497			 * The l_name member is not zero terminated, we
498			 * must copy the first SYMNMLEN chars and make
499			 * sure we have a zero byte at the end.
500			 */
501			strncpy(tmpsym, ls->l_name, SYMNMLEN);
502			tmpsym[SYMNMLEN] = '\0';
503			symname = tmpsym;
504		}
505		ep->name = malloc((unsigned) (strlen(symname) + 1));
506		strcpy(ep->name, symname);
507		ep->addr = (VOID *)((unsigned long)mp->entry +
508					ls->l_value - shdata.s_vaddr);
509		ep++;
510	}
511	free(ldbuf);
512	while(ldclose(ldp) == FAILURE)
513		;
514	return 0;
515}
516
517/*
518 * Find the main modules entry point. This is used as export pointer
519 * for loadbind() to be able to resolve references to the main part.
520 */
521static VOID *
522findMain()
523{
524	struct ld_info *lp;
525	char *buf;
526	int size = 4*1024;
527	int i;
528	VOID *ret;
529
530	if ((buf = malloc(size)) == NULL) {
531		errvalid++;
532		strcpy(errbuf, "findMain: ");
533		strcat(errbuf, strerror(errno));
534		return NULL;
535	}
536	while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
537		free(buf);
538		size += 4*1024;
539		if ((buf = malloc(size)) == NULL) {
540			errvalid++;
541			strcpy(errbuf, "findMain: ");
542			strcat(errbuf, strerror(errno));
543			return NULL;
544		}
545	}
546	if (i == -1) {
547		errvalid++;
548		strcpy(errbuf, "findMain: ");
549		strcat(errbuf, strerror(errno));
550		free(buf);
551		return NULL;
552	}
553	/*
554	 * The first entry is the main module. The entry point
555	 * returned by load() does actually point to the data
556	 * segment origin.
557	 */
558	lp = (struct ld_info *)buf;
559	ret = lp->ldinfo_dataorg;
560	free(buf);
561	return ret;
562}
563
564