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