os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclLoadAix.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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 
    49 typedef 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  */
    58 typedef 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 typedef 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  */
    81 static 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  */
    87 static char errbuf[BUFSIZ];
    88 static int errvalid;
    89 
    90 static void caterr(char *);
    91 static int readExports(ModulePtr);
    92 static void terminate(void);
    93 static void *findMain(void);
    94 
    95 VOID *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  */
   210 static 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 
   245 VOID *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 
   264 char *dlerror(void)
   265 {
   266 	if (errvalid) {
   267 		errvalid = 0;
   268 		return errbuf;
   269 	}
   270 	return NULL;
   271 }
   272 
   273 int 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 
   315 static void terminate(void)
   316 {
   317 	while (modList)
   318 		dlclose(modList);
   319 }
   320 
   321 /*
   322  * Build the export table from the XCOFF .loader section.
   323  */
   324 static 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  */
   508 static 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