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