xref: /aosp_15_r20/external/cronet/third_party/apache-portable-runtime/src/dso/aix/dso.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  *  dso.c -- DSO system function emulation for AIX
19  *
20  *  This is *only* intended for AIX < 4.3.
21  */
22 
23 /*
24  *  Based on libdl (dlfcn.c/dlfcn.h) which is
25  *  Copyright (c) 1992,1993,1995,1996,1997,1988
26  *  Jens-Uwe Mager, Helios Software GmbH, Hannover, Germany.
27  *
28  *  Not derived from licensed software.
29  *
30  *  Permission is granted to freely use, copy, modify, and redistribute
31  *  this software, provided that the author is not construed to be liable
32  *  for any results of using the software, alterations are clearly marked
33  *  as such, and this notice is not modified.
34  *
35  *  Changes marked with `--jwe' were made on April 7 1996 by
36  *  John W. Eaton <[email protected]> to support g++
37  *
38  *  Bundled, stripped and adjusted on April 1998 as one single source file
39  *  for inclusion into the Apache HTTP server by
40  *  Ralf S. Engelschall <[email protected]>
41  *
42  *  Added to APR by David Reid April 2000
43  */
44 
45 #include <stdio.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <sys/types.h>
50 #include <sys/ldr.h>
51 #include <a.out.h>
52 #include "apr_arch_dso.h"
53 #include "apr_portable.h"
54 
55 #if APR_HAS_DSO
56 
57 #undef FREAD
58 #undef FWRITE
59 #include <ldfcn.h>
60 
61 /*
62  * AIX 4.3 does remove some useful definitions from ldfcn.h. Define
63  * these here to compensate for that lossage.
64  */
65 #ifndef BEGINNING
66 #define BEGINNING SEEK_SET
67 #endif
68 #ifndef FSEEK
69 #define FSEEK(ldptr,o,p)   fseek(IOPTR(ldptr),(p==BEGINNING)?(OFFSET(ldptr) +o):o,p)
70 #endif
71 #ifndef FREAD
72 #define FREAD(p,s,n,ldptr) fread(p,s,n,IOPTR(ldptr))
73 #endif
74 
75 /*
76  * Mode flags for the dlopen routine.
77  */
78 #undef  RTLD_LAZY
79 #define RTLD_LAZY	1	/* lazy function call binding */
80 #undef  RTLD_NOW
81 #define RTLD_NOW	2	/* immediate function call binding */
82 #undef  RTLD_GLOBAL
83 #define RTLD_GLOBAL	0x100	/* allow symbols to be global */
84 
85 /*
86  * To be able to initialize, a library may provide a dl_info structure
87  * that contains functions to be called to initialize and terminate.
88  */
89 struct dl_info {
90     void (*init) (void);
91     void (*fini) (void);
92 };
93 
94 /* APR functions...
95  *
96  * As the AIX functions have been declared in the header file we just
97  * add the basic "wrappers" here.
98  */
99 
apr_os_dso_handle_put(apr_dso_handle_t ** aprdso,apr_os_dso_handle_t osdso,apr_pool_t * pool)100 APR_DECLARE(apr_status_t) apr_os_dso_handle_put(apr_dso_handle_t **aprdso,
101                                                 apr_os_dso_handle_t osdso,
102                                                 apr_pool_t *pool)
103 {
104     *aprdso = apr_pcalloc(pool, sizeof **aprdso);
105     (*aprdso)->handle = osdso;
106     (*aprdso)->pool = pool;
107     return APR_SUCCESS;
108 }
109 
apr_os_dso_handle_get(apr_os_dso_handle_t * osdso,apr_dso_handle_t * aprdso)110 APR_DECLARE(apr_status_t) apr_os_dso_handle_get(apr_os_dso_handle_t *osdso,
111                                                 apr_dso_handle_t *aprdso)
112 {
113     *osdso = aprdso->handle;
114     return APR_SUCCESS;
115 }
116 
dso_cleanup(void * thedso)117 static apr_status_t dso_cleanup(void *thedso)
118 {
119     apr_dso_handle_t *dso = thedso;
120 
121     if (dso->handle != NULL && dlclose(dso->handle) != 0)
122         return APR_EINIT;
123     dso->handle = NULL;
124 
125     return APR_SUCCESS;
126 }
127 
apr_dso_load(apr_dso_handle_t ** res_handle,const char * path,apr_pool_t * ctx)128 APR_DECLARE(apr_status_t) apr_dso_load(apr_dso_handle_t **res_handle,
129                                        const char *path, apr_pool_t *ctx)
130 {
131     void *os_handle = dlopen((char *)path, RTLD_NOW | RTLD_GLOBAL);
132 
133     *res_handle = apr_pcalloc(ctx, sizeof(*res_handle));
134 
135     if(os_handle == NULL) {
136         (*res_handle)->errormsg = dlerror();
137         return APR_EDSOOPEN;
138     }
139 
140     (*res_handle)->handle = (void*)os_handle;
141     (*res_handle)->pool = ctx;
142     (*res_handle)->errormsg = NULL;
143 
144     apr_pool_cleanup_register(ctx, *res_handle, dso_cleanup, apr_pool_cleanup_null);
145 
146     return APR_SUCCESS;
147 }
148 
apr_dso_unload(apr_dso_handle_t * handle)149 APR_DECLARE(apr_status_t) apr_dso_unload(apr_dso_handle_t *handle)
150 {
151     return apr_pool_cleanup_run(handle->pool, handle, dso_cleanup);
152 }
153 
apr_dso_sym(apr_dso_handle_sym_t * ressym,apr_dso_handle_t * handle,const char * symname)154 APR_DECLARE(apr_status_t) apr_dso_sym(apr_dso_handle_sym_t *ressym,
155                                       apr_dso_handle_t *handle,
156                                       const char *symname)
157 {
158     void *retval = dlsym(handle->handle, symname);
159 
160     if (retval == NULL) {
161         handle->errormsg = dlerror();
162         return APR_ESYMNOTFOUND;
163     }
164 
165     *ressym = retval;
166     return APR_SUCCESS;
167 }
168 
apr_dso_error(apr_dso_handle_t * dso,char * buffer,apr_size_t buflen)169 APR_DECLARE(const char *) apr_dso_error(apr_dso_handle_t *dso, char *buffer, apr_size_t buflen)
170 {
171     if (dso->errormsg) {
172         apr_cpystrn(buffer, dso->errormsg, buflen);
173         return dso->errormsg;
174     }
175     return "No Error";
176 }
177 
178 
179 
180 /*
181  * We simulate dlopen() et al. through a call to load. Because AIX has
182  * no call to find an exported symbol we read the loader section of the
183  * loaded module and build a list of exported symbols and their virtual
184  * address.
185  */
186 
187 typedef struct {
188     char *name;			/* the symbols's name */
189     void *addr;			/* its relocated virtual address */
190 } Export, *ExportPtr;
191 
192 /*
193  * xlC uses the following structure to list its constructors and
194  * destructors. This is gleaned from the output of munch.
195  */
196 typedef struct {
197     void (*init) (void);	/* call static constructors */
198     void (*term) (void);	/* call static destructors */
199 } Cdtor, *CdtorPtr;
200 
201 typedef void (*GccCDtorPtr) (void);
202 
203 /*
204  * The void * handle returned from dlopen is actually a ModulePtr.
205  */
206 typedef struct Module {
207     struct Module *next;
208     char *name;			/* module name for refcounting */
209     int refCnt;			/* the number of references */
210     void *entry;		/* entry point from load */
211     struct dl_info *info;	/* optional init/terminate functions */
212     CdtorPtr cdtors;		/* optional C++ constructors */
213     GccCDtorPtr gcc_ctor;	/* g++ constructors  --jwe */
214     GccCDtorPtr gcc_dtor;	/* g++ destructors  --jwe */
215     int nExports;		/* the number of exports found */
216     ExportPtr exports;		/* the array of exports */
217 } Module, *ModulePtr;
218 
219 /*
220  * We keep a list of all loaded modules to be able to call the fini
221  * handlers and destructors at atexit() time.
222  */
223 static ModulePtr modList;
224 
225 /*
226  * The last error from one of the dl* routines is kept in static
227  * variables here. Each error is returned only once to the caller.
228  */
229 static char errbuf[BUFSIZ];
230 static int errvalid;
231 
232 /*
233  * The `fixed' gcc header files on AIX 3.2.5 provide a prototype for
234  * strdup().  --jwe
235  */
236 extern char *strdup(const char *);
237 static void caterr(char *);
238 static int readExports(ModulePtr);
239 static void terminate(void);
240 static void *findMain(void);
241 
dlopen(const char * path,int mode)242 void *dlopen(const char *path, int mode)
243 {
244     register ModulePtr mp;
245     static void *mainModule;
246 
247     /*
248      * Upon the first call register a terminate handler that will
249      * close all libraries. Also get a reference to the main module
250      * for use with loadbind.
251      */
252     if (!mainModule) {
253 	if ((mainModule = findMain()) == NULL)
254 	    return NULL;
255 	atexit(terminate);
256     }
257     /*
258      * Scan the list of modules if we have the module already loaded.
259      */
260     for (mp = modList; mp; mp = mp->next)
261 	if (strcmp(mp->name, path) == 0) {
262 	    mp->refCnt++;
263 	    return mp;
264 	}
265     if ((mp = (ModulePtr) calloc(1, sizeof(*mp))) == NULL) {
266 	errvalid++;
267 	strcpy(errbuf, "calloc: ");
268 	strcat(errbuf, strerror(errno));
269 	return NULL;
270     }
271     if ((mp->name = strdup(path)) == NULL) {
272 	errvalid++;
273 	strcpy(errbuf, "strdup: ");
274 	strcat(errbuf, strerror(errno));
275 	free(mp);
276 	return NULL;
277     }
278     /*
279      * load should be declared load(const char *...). Thus we
280      * cast the path to a normal char *. Ugly.
281      */
282     if ((mp->entry = (void *) loadAndInit((char *) path, L_NOAUTODEFER, NULL)) == NULL) {
283 	free(mp->name);
284 	free(mp);
285 	errvalid++;
286 	strcpy(errbuf, "dlopen: ");
287 	strcat(errbuf, path);
288 	strcat(errbuf, ": ");
289 	/*
290 	 * If AIX says the file is not executable, the error
291 	 * can be further described by querying the loader about
292 	 * the last error.
293 	 */
294 	if (errno == ENOEXEC) {
295 	    char *tmp[BUFSIZ / sizeof(char *)];
296 	    if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
297 		strcpy(errbuf, strerror(errno));
298 	    else {
299 		char **p;
300 		for (p = tmp; *p; p++)
301 		    caterr(*p);
302 	    }
303 	}
304 	else
305 	    strcat(errbuf, strerror(errno));
306 	return NULL;
307     }
308     mp->refCnt = 1;
309     mp->next = modList;
310     modList = mp;
311     if (loadbind(0, mainModule, mp->entry) == -1) {
312 	dlclose(mp);
313 	errvalid++;
314 	strcpy(errbuf, "loadbind: ");
315 	strcat(errbuf, strerror(errno));
316 	return NULL;
317     }
318     /*
319      * If the user wants global binding, loadbind against all other
320      * loaded modules.
321      */
322     if (mode & RTLD_GLOBAL) {
323 	register ModulePtr mp1;
324 	for (mp1 = mp->next; mp1; mp1 = mp1->next)
325 	    if (loadbind(0, mp1->entry, mp->entry) == -1) {
326 		dlclose(mp);
327 		errvalid++;
328 		strcpy(errbuf, "loadbind: ");
329 		strcat(errbuf, strerror(errno));
330 		return NULL;
331 	    }
332     }
333     if (readExports(mp) == -1) {
334 	dlclose(mp);
335 	return NULL;
336     }
337     /*
338      * If there is a dl_info structure, call the init function.
339      */
340     if (mp->info = (struct dl_info *) dlsym(mp, "dl_info")) {
341 	if (mp->info->init)
342 	    (*mp->info->init) ();
343     }
344     else
345 	errvalid = 0;
346     /*
347      * If the shared object was compiled using xlC we will need
348      * to call static constructors (and later on dlclose destructors).
349      */
350     if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) {
351 	CdtorPtr cp = mp->cdtors;
352 	while (cp->init || cp->term) {
353 	    if (cp->init && cp->init != (void (*)(void)) 0xffffffff)
354 		(*cp->init) ();
355 	    cp++;
356 	}
357 	/*
358 	 * If the shared object was compiled using g++, we will need
359 	 * to call global constructors using the _GLOBAL__DI function,
360 	 * and later, global destructors using the _GLOBAL_DD
361 	 * funciton.  --jwe
362 	 */
363     }
364     else if (mp->gcc_ctor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DI")) {
365 	(*mp->gcc_ctor) ();
366 	mp->gcc_dtor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DD");
367     }
368     else
369 	errvalid = 0;
370     return mp;
371 }
372 
373 /*
374  * Attempt to decipher an AIX loader error message and append it
375  * to our static error message buffer.
376  */
caterr(char * s)377 static void caterr(char *s)
378 {
379     register char *p = s;
380 
381     while (*p >= '0' && *p <= '9')
382 	p++;
383     switch (atoi(s)) {
384     case L_ERROR_TOOMANY:
385 	strcat(errbuf, "to many errors");
386 	break;
387     case L_ERROR_NOLIB:
388 	strcat(errbuf, "can't load library");
389 	strcat(errbuf, p);
390 	break;
391     case L_ERROR_UNDEF:
392 	strcat(errbuf, "can't find symbol");
393 	strcat(errbuf, p);
394 	break;
395     case L_ERROR_RLDBAD:
396 	strcat(errbuf, "bad RLD");
397 	strcat(errbuf, p);
398 	break;
399     case L_ERROR_FORMAT:
400 	strcat(errbuf, "bad exec format in");
401 	strcat(errbuf, p);
402 	break;
403     case L_ERROR_ERRNO:
404 	strcat(errbuf, strerror(atoi(++p)));
405 	break;
406     default:
407 	strcat(errbuf, s);
408 	break;
409     }
410 }
411 
dlsym(void * handle,const char * symbol)412 void *dlsym(void *handle, const char *symbol)
413 {
414     register ModulePtr mp = (ModulePtr) handle;
415     register ExportPtr ep;
416     register int i;
417 
418     /*
419      * Could speed up the search, but I assume that one assigns
420      * the result to function pointers anyways.
421      */
422     for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
423 	if (strcmp(ep->name, symbol) == 0)
424 	    return ep->addr;
425     errvalid++;
426     strcpy(errbuf, "dlsym: undefined symbol ");
427     strcat(errbuf, symbol);
428     return NULL;
429 }
430 
dlerror(void)431 const char *dlerror(void)
432 {
433     if (errvalid) {
434 	errvalid = 0;
435 	return errbuf;
436     }
437     return NULL;
438 }
439 
dlclose(void * handle)440 int dlclose(void *handle)
441 {
442     register ModulePtr mp = (ModulePtr) handle;
443     int result;
444     register ModulePtr mp1;
445 
446     if (--mp->refCnt > 0)
447 	return 0;
448     if (mp->info && mp->info->fini)
449 	(*mp->info->fini) ();
450     if (mp->cdtors) {
451 	CdtorPtr cp = mp->cdtors;
452 	while (cp->init || cp->term) {
453 	    if (cp->term && cp->init != (void (*)(void)) 0xffffffff)
454 		(*cp->term) ();
455 	    cp++;
456 	}
457 	/*
458 	 * If the function to handle global destructors for g++
459 	 * exists, call it.  --jwe
460 	 */
461     }
462     else if (mp->gcc_dtor) {
463 	(*mp->gcc_dtor) ();
464     }
465     result = unload(mp->entry);
466     if (result == -1) {
467 	errvalid++;
468 	strcpy(errbuf, strerror(errno));
469     }
470     if (mp->exports) {
471 	register ExportPtr ep;
472 	register int i;
473 	for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
474 	    if (ep->name)
475 		free(ep->name);
476 	free(mp->exports);
477     }
478     if (mp == modList)
479 	modList = mp->next;
480     else {
481 	for (mp1 = modList; mp1; mp1 = mp1->next)
482 	    if (mp1->next == mp) {
483 		mp1->next = mp->next;
484 		break;
485 	    }
486     }
487     free(mp->name);
488     free(mp);
489     return result;
490 }
491 
terminate(void)492 static void terminate(void)
493 {
494     while (modList)
495 	dlclose(modList);
496 }
497 
498 /*
499  * Build the export table from the XCOFF .loader section.
500  */
readExports(ModulePtr mp)501 static int readExports(ModulePtr mp)
502 {
503     LDFILE *ldp = NULL;
504     SCNHDR sh, shdata;
505     LDHDR *lhp;
506     char *ldbuf;
507     LDSYM *ls;
508     int i;
509     ExportPtr ep;
510     struct ld_info *lp;
511     char *buf;
512     int size = 4 * 1024;
513     void *dataorg;
514 
515     /*
516      * The module might be loaded due to the LIBPATH
517      * environment variable. Search for the loaded
518      * module using L_GETINFO.
519      */
520     if ((buf = malloc(size)) == NULL) {
521 	errvalid++;
522 	strcpy(errbuf, "readExports: ");
523 	strcat(errbuf, strerror(errno));
524 	return -1;
525     }
526     while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
527 	free(buf);
528 	size += 4 * 1024;
529 	if ((buf = malloc(size)) == NULL) {
530 	    errvalid++;
531 	    strcpy(errbuf, "readExports: ");
532 	    strcat(errbuf, strerror(errno));
533 	    return -1;
534 	}
535     }
536     if (i == -1) {
537 	errvalid++;
538 	strcpy(errbuf, "readExports: ");
539 	strcat(errbuf, strerror(errno));
540 	free(buf);
541 	return -1;
542     }
543     /*
544      * Traverse the list of loaded modules. The entry point
545      * returned by load() does actually point to the TOC
546      * entry contained in the data segment.
547      */
548     lp = (struct ld_info *) buf;
549     while (lp) {
550 	if ((unsigned long) mp->entry >= (unsigned long) lp->ldinfo_dataorg &&
551 	    (unsigned long) mp->entry < (unsigned long) lp->ldinfo_dataorg +
552 	    lp->ldinfo_datasize) {
553 	    dataorg = lp->ldinfo_dataorg;
554 	    ldp = ldopen(lp->ldinfo_filename, ldp);
555 	    break;
556 	}
557 	if (lp->ldinfo_next == 0)
558 	    lp = NULL;
559 	else
560 	    lp = (struct ld_info *) ((char *) lp + lp->ldinfo_next);
561     }
562     free(buf);
563     if (!ldp) {
564 	errvalid++;
565 	strcpy(errbuf, "readExports: ");
566 	strcat(errbuf, strerror(errno));
567 	return -1;
568     }
569     if (TYPE(ldp) != U802TOCMAGIC) {
570 	errvalid++;
571 	strcpy(errbuf, "readExports: bad magic");
572 	while (ldclose(ldp) == FAILURE);
573 	return -1;
574     }
575     /*
576      * Get the padding for the data section. This is needed for
577      * AIX 4.1 compilers. This is used when building the final
578      * function pointer to the exported symbol.
579      */
580     if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
581 	errvalid++;
582 	strcpy(errbuf, "readExports: cannot read data section header");
583 	while (ldclose(ldp) == FAILURE);
584 	return -1;
585     }
586     if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
587 	errvalid++;
588 	strcpy(errbuf, "readExports: cannot read loader section header");
589 	while (ldclose(ldp) == FAILURE);
590 	return -1;
591     }
592     /*
593      * We read the complete loader section in one chunk, this makes
594      * finding long symbol names residing in the string table easier.
595      */
596     if ((ldbuf = (char *) malloc(sh.s_size)) == NULL) {
597 	errvalid++;
598 	strcpy(errbuf, "readExports: ");
599 	strcat(errbuf, strerror(errno));
600 	while (ldclose(ldp) == FAILURE);
601 	return -1;
602     }
603     if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
604 	errvalid++;
605 	strcpy(errbuf, "readExports: cannot seek to loader section");
606 	free(ldbuf);
607 	while (ldclose(ldp) == FAILURE);
608 	return -1;
609     }
610     if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
611 	errvalid++;
612 	strcpy(errbuf, "readExports: cannot read loader section");
613 	free(ldbuf);
614 	while (ldclose(ldp) == FAILURE);
615 	return -1;
616     }
617     lhp = (LDHDR *) ldbuf;
618     ls = (LDSYM *) (ldbuf + LDHDRSZ);
619     /*
620      * Count the number of exports to include in our export table.
621      */
622     for (i = lhp->l_nsyms; i; i--, ls++) {
623 	if (!LDR_EXPORT(*ls))
624 	    continue;
625 	mp->nExports++;
626     }
627     if ((mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
628 	errvalid++;
629 	strcpy(errbuf, "readExports: ");
630 	strcat(errbuf, strerror(errno));
631 	free(ldbuf);
632 	while (ldclose(ldp) == FAILURE);
633 	return -1;
634     }
635     /*
636      * Fill in the export table. All entries are relative to
637      * the beginning of the data origin.
638      */
639     ep = mp->exports;
640     ls = (LDSYM *) (ldbuf + LDHDRSZ);
641     for (i = lhp->l_nsyms; i; i--, ls++) {
642 	char *symname;
643 	char tmpsym[SYMNMLEN + 1];
644 	if (!LDR_EXPORT(*ls))
645 	    continue;
646 	if (ls->l_zeroes == 0)
647 	    symname = ls->l_offset + lhp->l_stoff + ldbuf;
648 	else {
649 	    /*
650 	     * The l_name member is not zero terminated, we
651 	     * must copy the first SYMNMLEN chars and make
652 	     * sure we have a zero byte at the end.
653 	     */
654 	    strncpy(tmpsym, ls->l_name, SYMNMLEN);
655 	    tmpsym[SYMNMLEN] = '\0';
656 	    symname = tmpsym;
657 	}
658 	ep->name = strdup(symname);
659 	ep->addr = (void *) ((unsigned long) dataorg +
660 			     ls->l_value - shdata.s_vaddr);
661 	ep++;
662     }
663     free(ldbuf);
664     while (ldclose(ldp) == FAILURE);
665     return 0;
666 }
667 
668 /*
669  * Find the main modules data origin. This is used as export pointer
670  * for loadbind() to be able to resolve references to the main part.
671  */
findMain(void)672 static void *findMain(void)
673 {
674     struct ld_info *lp;
675     char *buf;
676     int size = 4 * 1024;
677     int i;
678     void *ret;
679 
680     if ((buf = malloc(size)) == NULL) {
681 	errvalid++;
682 	strcpy(errbuf, "findMain: ");
683 	strcat(errbuf, strerror(errno));
684 	return NULL;
685     }
686     while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
687 	free(buf);
688 	size += 4 * 1024;
689 	if ((buf = malloc(size)) == NULL) {
690 	    errvalid++;
691 	    strcpy(errbuf, "findMain: ");
692 	    strcat(errbuf, strerror(errno));
693 	    return NULL;
694 	}
695     }
696     if (i == -1) {
697 	errvalid++;
698 	strcpy(errbuf, "findMain: ");
699 	strcat(errbuf, strerror(errno));
700 	free(buf);
701 	return NULL;
702     }
703     /*
704      * The first entry is the main module. The data segment
705      * starts with the TOC entries for all exports, so the
706      * data segment origin works as argument for loadbind.
707      */
708     lp = (struct ld_info *) buf;
709     ret = lp->ldinfo_dataorg;
710     free(buf);
711     return ret;
712 }
713 
714 #endif
715