xref: /titanic_41/usr/src/cmd/sgs/rtld/common/a.out.c (revision ea394cb00fd96864e34d2841b4a22357b621c78f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Object file dependent support for a.out format objects.
29  */
30 
31 #include	<a.out.h>		/* Explicitly override M_SEGSIZE */
32 #include	<machdep.h>		/*	used in M_SROUND */
33 
34 #include	<sys/types.h>
35 #include	<sys/procfs.h>
36 #include	<sys/mman.h>
37 #include	<fcntl.h>
38 #include	<unistd.h>
39 #include	<string.h>
40 #include	<limits.h>
41 #include	<stdio.h>
42 #include	<dlfcn.h>
43 #include	<errno.h>
44 #include	<debug.h>
45 #include	"_a.out.h"
46 #include	"cache_a.out.h"
47 #include	"msg.h"
48 #include	"_rtld.h"
49 
50 /*
51  * Default and secure dependency search paths.
52  */
53 static Spath_defn _aout_def_dirs[] = {
54 	{ MSG_ORIG(MSG_PTH_USR4LIB),		MSG_PTH_USR4LIB_SIZE },
55 	{ MSG_ORIG(MSG_PTH_USRLIB),		MSG_PTH_USRLIB_SIZE },
56 	{ MSG_ORIG(MSG_PTH_USRLCLIB),		MSG_PTH_USRLCLIB_SIZE },
57 	{ 0, 0 }
58 };
59 
60 static Spath_defn _aout_sec_dirs[] = {
61 	{ MSG_ORIG(MSG_PTH_LIBSE),		MSG_PTH_LIBSE_SIZE },
62 	{ 0, 0 }
63 };
64 
65 Alist	*aout_def_dirs = NULL;
66 Alist	*aout_sec_dirs = NULL;
67 
68 /*
69  * Defines for local functions.
70  */
71 static void	aout_dladdr(ulong_t, Rt_map *, Dl_info *, void **, int);
72 static int	aout_dlsym_handle(Grp_hdl *, Slookup *, Sresult *, uint_t *,
73 		    int *);
74 static Addr	aout_entry_point(void);
75 static int	aout_find_sym(Slookup *, Sresult *, uint_t *, int *);
76 static int	aout_fix_name(const char *, Rt_map *, Alist **, Aliste, uint_t);
77 static Alist	**aout_get_def_dirs(void);
78 static Alist	**aout_get_sec_dirs(void);
79 static char	*aout_get_so(const char *, const char *, size_t, size_t);
80 static int	aout_needed(Lm_list *, Aliste, Rt_map *, int *);
81 
82 /*
83  * Functions and data accessed through indirect pointers.
84  */
85 Fct aout_fct = {
86 	aout_verify,
87 	aout_new_lmp,
88 	aout_entry_point,
89 	aout_needed,
90 	aout_lookup_sym,
91 	aout_reloc,
92 	aout_get_def_dirs,
93 	aout_get_sec_dirs,
94 	aout_fix_name,
95 	aout_get_so,
96 	aout_dladdr,
97 	aout_dlsym_handle
98 };
99 
100 /*
101  * Default and secure dependency search paths.
102  */
103 static Alist **
104 aout_get_def_dirs()
105 {
106 	if (aout_def_dirs == NULL)
107 		set_dirs(&aout_def_dirs, _aout_def_dirs, LA_SER_DEFAULT);
108 	return (&aout_def_dirs);
109 }
110 
111 static Alist **
112 aout_get_sec_dirs()
113 {
114 	if (aout_sec_dirs == NULL)
115 		set_dirs(&aout_sec_dirs, _aout_sec_dirs, LA_SER_SECURE);
116 	return (&aout_sec_dirs);
117 }
118 
119 /*
120  * In 4.x, a needed file or a dlopened file that was a simple file name implied
121  * that the file be found in the present working directory.  To simulate this
122  * lookup within the ELF rules it is necessary to add a preceding `./' to the
123  * filename.
124  */
125 /* ARGSUSED4 */
126 static int
127 aout_fix_name(const char *oname, Rt_map *clmp, Alist **alpp, Aliste alni,
128     uint_t orig)
129 {
130 	size_t		len;
131 	Pdesc		*pdp;
132 	const char	*nname;
133 
134 	/*
135 	 * Check for slash in name, if none, prepend "./", otherwise just
136 	 * return name given.
137 	 */
138 	if (strchr(oname, '/')) {
139 		len = strlen(oname) + 1;
140 		if ((nname = stravl_insert(oname, 0, len, 0)) == NULL)
141 			return (0);
142 	} else {
143 		char	buffer[PATH_MAX];
144 
145 		len = strlen(oname) + 3;
146 		(void) snprintf(buffer, len, MSG_ORIG(MSG_FMT_4XPATH), oname);
147 		if ((nname = stravl_insert(buffer, 0, len, 0)) == NULL)
148 			return (0);
149 	}
150 
151 	if ((pdp = alist_append(alpp, NULL, sizeof (Pdesc), alni)) == NULL)
152 		return (0);
153 
154 	pdp->pd_pname = nname;
155 	pdp->pd_plen = len;
156 	pdp->pd_flags = PD_FLG_PNSLASH;
157 
158 	DBG_CALL(Dbg_file_fixname(LIST(clmp), nname, oname));
159 	return (1);
160 }
161 
162 /*
163  * Determine if we have been given an A_OUT file.  Returns 1 if true.
164  */
165 Fct *
166 /* ARGSUSED1 */
167 aout_verify(caddr_t addr, size_t size, Fdesc *fdp, const char *name,
168     Rej_desc *rej)
169 {
170 	/* LINTED */
171 	struct exec *exec = (struct exec *)addr;
172 
173 	if (size < sizeof (exec) || (exec->a_machtype != M_SPARC) ||
174 	    (N_BADMAG(*exec))) {
175 		return (NULL);
176 	}
177 	return (&aout_fct);
178 }
179 
180 /*
181  * Return the entry point of the A_OUT executable.  Although the entry point
182  * within an ELF file is flexible, the entry point of an A_OUT executable is
183  * always zero.
184  */
185 static Addr
186 aout_entry_point()
187 {
188 	return (0);
189 }
190 
191 /*
192  * Search through the dynamic section for DT_NEEDED entries and perform one
193  * of two functions.  If only the first argument is specified then load the
194  * defined shared object, otherwise add the link map representing the
195  * defined link map the the dlopen list.
196  */
197 static int
198 aout_needed(Lm_list *lml, Aliste lmco, Rt_map *clmp, int *in_nfavl)
199 {
200 	Alist	*palp = NULL;
201 	void	*need;
202 
203 	for (need = &TEXTBASE(clmp)[AOUTDYN(clmp)->v2->ld_need];
204 	    need != &TEXTBASE(clmp)[0];
205 	    need = &TEXTBASE(clmp)[((Lnk_obj *)(need))->lo_next]) {
206 		Rt_map	*nlmp;
207 		char	*name;
208 
209 		name = &TEXTBASE(clmp)[((Lnk_obj *)(need))->lo_name];
210 
211 		if (((Lnk_obj *)(need))->lo_library) {
212 			/*
213 			 * If lo_library field is not NULL then this needed
214 			 * library was linked in using the "-l" option.
215 			 * Thus we need to rebuild the library name before
216 			 * trying to load it.
217 			 */
218 			char	*file;
219 			size_t	len;
220 
221 			/*
222 			 * Allocate name length plus 20 for full library name.
223 			 * lib.so.. = 7 + (2 * short) + NULL = 7 + 12 + 1 = 20
224 			 */
225 			len = strlen(name) + 20;
226 			if ((file = malloc(len)) == NULL)
227 				return (0);
228 			(void) snprintf(file, len, MSG_ORIG(MSG_FMT_4XLIB),
229 			    name, ((Lnk_obj *)(need))->lo_major,
230 			    ((Lnk_obj *)(need))->lo_minor);
231 
232 			DBG_CALL(Dbg_libs_find(lml, file));
233 
234 			/*
235 			 * We need to determine what filename will match the
236 			 * the filename specified (ie, a libc.so.1.2 may match
237 			 * to a libc.so.1.3).  It's the real pathname that is
238 			 * recorded in the link maps.  If we are presently
239 			 * being traced, skip this pathname generation so
240 			 * that we fall through into load_so() to print the
241 			 * appropriate diagnostics.  I don't like this at all.
242 			 */
243 			if (lml->lm_flags & LML_FLG_TRC_ENABLE)
244 				name = file;
245 			else {
246 				Spath_desc	sd = { search_rules, NULL, 0 };
247 				Pdesc		*pdp;
248 				char		*path = NULL;
249 
250 				for (pdp = get_next_dir(&sd, clmp, 0); pdp;
251 				    pdp = get_next_dir(&sd, clmp, 0)) {
252 					if (pdp->pd_pname == 0)
253 						continue;
254 
255 					if (path = aout_get_so(pdp->pd_pname,
256 					    file, 0, 0))
257 						break;
258 				}
259 				if (path == NULL) {
260 					eprintf(lml, ERR_FATAL,
261 					    MSG_INTL(MSG_SYS_OPEN), file,
262 					    strerror(ENOENT));
263 					return (0);
264 				}
265 				name = path;
266 			}
267 			if (expand_paths(clmp, name, &palp,
268 			    AL_CNT_NEEDED, 0, 0) == 0)
269 				return (0);
270 		} else {
271 			/*
272 			 * If the library is specified as a pathname, see if
273 			 * it must be fixed to specify the current working
274 			 * directory (ie. libc.so.1.2 -> ./libc.so.1.2).
275 			 */
276 			if (aout_fix_name(name, clmp, &palp,
277 			    AL_CNT_NEEDED, 0) == 0)
278 				return (0);
279 		}
280 
281 		DBG_CALL(Dbg_file_needed(clmp, name));
282 
283 		nlmp = load_one(lml, lmco, palp, clmp, MODE(clmp), 0, 0,
284 		    in_nfavl);
285 		remove_plist(&palp, 1);
286 		if (((nlmp == 0) || (bind_one(clmp, nlmp, BND_NEEDED) == 0)) &&
287 		    ((lml->lm_flags & LML_FLG_TRC_ENABLE) == 0))
288 			return (0);
289 	}
290 
291 	return (1);
292 }
293 
294 static Sym *
295 aout_symconvert(struct nlist *sp)
296 {
297 	static Sym	sym;
298 
299 	sym.st_value = sp->n_value;
300 	sym.st_size = 0;
301 	sym.st_info = 0;
302 	sym.st_other = 0;
303 	switch (sp->n_type) {
304 		case N_EXT + N_ABS:
305 			sym.st_shndx = SHN_ABS;
306 			break;
307 		case N_COMM:
308 			sym.st_shndx = SHN_COMMON;
309 			break;
310 		case N_EXT + N_UNDF:
311 			sym.st_shndx = SHN_UNDEF;
312 			break;
313 		default:
314 			sym.st_shndx = 0;
315 			break;
316 	}
317 	return (&sym);
318 }
319 
320 /*
321  * Process a.out format commons.
322  */
323 static struct nlist *
324 aout_find_com(struct nlist *sp, const char *name)
325 {
326 	static struct rtc_symb	*rtcp = 0;
327 	struct rtc_symb		*rs, *trs;
328 	const char		*sl;
329 	char			*cp;
330 
331 	/*
332 	 * See if common is already allocated.
333 	 */
334 	trs = rtcp;
335 	while (trs) {
336 		sl = name;
337 		cp = trs->rtc_sp->n_un.n_name;
338 		while (*sl == *cp++)
339 			if (*sl++ == '\0')
340 				return (trs->rtc_sp);
341 		trs = trs->rtc_next;
342 	}
343 
344 	/*
345 	 * If we got here, common is not already allocated so allocate it.
346 	 */
347 	if ((rs = malloc(sizeof (struct rtc_symb))) == NULL)
348 		return (NULL);
349 	if ((rs->rtc_sp = malloc(sizeof (struct nlist))) == NULL)
350 		return (NULL);
351 	trs = rtcp;
352 	rtcp = rs;
353 	rs->rtc_next = trs;
354 	*(rs->rtc_sp) = *sp;
355 	if ((rs->rtc_sp->n_un.n_name = malloc(strlen(name) + 1)) == NULL)
356 		return (NULL);
357 	(void) strcpy(rs->rtc_sp->n_un.n_name, name);
358 	rs->rtc_sp->n_type = N_COMM;
359 	if ((rs->rtc_sp->n_value =
360 	    (long)calloc(rs->rtc_sp->n_value, 1)) == NULL)
361 		return (NULL);
362 	return (rs->rtc_sp);
363 }
364 
365 /*
366  * Find a.out format symbol in the specified link map.  Unlike the sister
367  * elf routine we re-calculate the symbols hash value for each link map
368  * we're looking at.
369  */
370 static struct nlist *
371 aout_findsb(const char *aname, Rt_map *lmp, int flag)
372 {
373 	const char	*name = aname;
374 	char		*cp;
375 	struct fshash	*p;
376 	int		i;
377 	struct nlist	*sp;
378 	ulong_t		hval = 0;
379 
380 #define	HASHMASK	0x7fffffff
381 #define	RTHS		126
382 
383 	/*
384 	 * The name passed to us is in ELF format, thus it is necessary to
385 	 * map this back to the A_OUT format to compute the hash value (see
386 	 * mapping rules in aout_lookup_sym()).  Basically the symbols are
387 	 * mapped according to whether a leading `.' exists.
388 	 *
389 	 *	elf symbol		a.out symbol
390 	 * i.	   .bar		->	   .bar		(LKUP_LDOT)
391 	 * ii.	   .nuts	->	    nuts
392 	 * iii.	    foo		->	   _foo
393 	 */
394 	if (*name == '.') {
395 		if (!(flag & LKUP_LDOT))
396 			name++;
397 	} else
398 		hval = '_';
399 
400 	while (*name)
401 		hval = (hval << 1) + *name++;
402 	hval = hval & HASHMASK;
403 
404 	i = hval % (AOUTDYN(lmp)->v2->ld_buckets == 0 ? RTHS :
405 	    AOUTDYN(lmp)->v2->ld_buckets);
406 	p = LM2LP(lmp)->lp_hash + i;
407 
408 	if (p->fssymbno != -1) {
409 		do {
410 			sp = &LM2LP(lmp)->lp_symtab[p->fssymbno];
411 			cp = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
412 			name = aname;
413 			if (*name == '.') {
414 				if (!(flag & LKUP_LDOT))
415 					name++;
416 			} else {
417 				cp++;
418 			}
419 			while (*name == *cp++) {
420 				if (*name++ == '\0')
421 					return (sp);	/* found */
422 			}
423 			if (p->next == 0)
424 				return (NULL);		/* not found */
425 			else
426 				continue;
427 		} while ((p = &LM2LP(lmp)->lp_hash[p->next]) != 0);
428 	}
429 	return (NULL);
430 }
431 
432 /*
433  * The symbol name we have been asked to look up is in A_OUT format, this
434  * symbol is mapped to the appropriate ELF format which is the standard by
435  * which symbols are passed around ld.so.1.  The symbols are mapped
436  * according to whether a leading `_' or `.' exists.
437  *
438  *	a.out symbol		elf symbol
439  * i.	   _foo		->	    foo
440  * ii.	   .bar		->	   .bar		(LKUP_LDOT)
441  * iii.	    nuts	->	   .nuts
442  */
443 int
444 aout_lookup_sym(Slookup *slp, Sresult *srp, uint_t *binfo, int *in_nfavl)
445 {
446 	char	name[PATH_MAX];
447 	Slookup	sl = *slp;
448 
449 	DBG_CALL(Dbg_syms_lookup_aout(LIST(slp->sl_cmap), slp->sl_name));
450 
451 	if (*sl.sl_name == '_')
452 		++sl.sl_name;
453 	else if (*sl.sl_name == '.')
454 		sl.sl_flags |= LKUP_LDOT;
455 	else {
456 		name[0] = '.';
457 		(void) strcpy(&name[1], sl.sl_name);
458 		sl.sl_name = name;
459 	}
460 
461 	/*
462 	 * Call the generic lookup routine to cycle through the specified
463 	 * link maps.
464 	 */
465 	return (lookup_sym(&sl, srp, binfo, in_nfavl));
466 }
467 
468 /*
469  * Symbol lookup for an a.out format module.
470  */
471 /* ARGSUSED3 */
472 static int
473 aout_find_sym(Slookup *slp, Sresult *srp, uint_t *binfo, int *in_nfavl)
474 {
475 	const char	*name = slp->sl_name;
476 	Rt_map		*ilmp = slp->sl_imap;
477 	struct nlist	*sp;
478 
479 	DBG_CALL(Dbg_syms_lookup(ilmp, name, MSG_ORIG(MSG_STR_AOUT)));
480 
481 	if (sp = aout_findsb(name, ilmp, slp->sl_flags)) {
482 		if (sp->n_value != 0) {
483 			/*
484 			 * is it a common?
485 			 */
486 			if (sp->n_type == (N_EXT + N_UNDF)) {
487 				if ((sp = aout_find_com(sp, name)) == 0)
488 					return (0);
489 			}
490 			srp->sr_dmap = ilmp;
491 			srp->sr_sym = aout_symconvert(sp);
492 			*binfo |= DBG_BINFO_FOUND;
493 			return (1);
494 		}
495 	}
496 	return (0);
497 }
498 
499 /*
500  * Create a new Rt_map structure for an a.out format object and
501  * initializes all values.
502  */
503 /* ARGSUSED6 */
504 Rt_map *
505 aout_new_lmp(Lm_list *lml, Aliste lmco, Fdesc *fdp, Addr addr, size_t msize,
506     void *odyn, int *in_nfavl)
507 {
508 	const char	*name = fdp->fd_nname;
509 	Rt_map		*lmp;
510 	caddr_t		base, caddr = (caddr_t)addr;
511 	Link_dynamic	*ld = (Link_dynamic *)odyn;
512 	size_t		lmsz, rtsz, prsz;
513 
514 	DBG_CALL(Dbg_file_aout(lml, name, addr, msize, lml->lm_lmidstr, lmco));
515 
516 	/*
517 	 * Allocate space for the link-map and private a.out information.  Once
518 	 * these are allocated and initialized, we can use remove_so(0, lmp) to
519 	 * tear down the link-map should any failures occur.
520 	 */
521 	rtsz = S_DROUND(sizeof (Rt_map));
522 	prsz = S_DROUND(sizeof (Rt_aoutp));
523 	lmsz = rtsz + prsz + sizeof (struct ld_private);
524 	if ((lmp = calloc(lmsz, 1)) == NULL)
525 		return (NULL);
526 	AOUTPRV(lmp) = (void *)((uintptr_t)lmp + rtsz);
527 	((Rt_aoutp *)AOUTPRV(lmp))->lm_lpd =
528 	    (void *)((uintptr_t)lmp + rtsz + prsz);
529 	LMSIZE(lmp) = lmsz;
530 
531 	/*
532 	 * All fields not filled in were set to 0 by calloc.
533 	 */
534 	NAME(lmp) = (char *)name;
535 	ADDR(lmp) = addr;
536 	MSIZE(lmp) = msize;
537 	SYMINTP(lmp) = aout_find_sym;
538 	FCT(lmp) = &aout_fct;
539 	LIST(lmp) = lml;
540 	OBJFLTRNDX(lmp) = FLTR_DISABLED;
541 	SORTVAL(lmp) = -1;
542 
543 	/*
544 	 * Specific settings for a.out format.
545 	 */
546 	if (lml->lm_head == 0) {
547 		base = (caddr_t)MAIN_BASE;
548 		FLAGS(lmp) |= FLG_RT_FIXED;
549 	} else
550 		base = caddr;
551 
552 	/*
553 	 * Fill in all AOUT information.  Applications provide the Link_dynamic
554 	 * offset via the boot block, but if this is a shared object that
555 	 * ld.so.1 has mapped, then determine the Link_dynamic offset from the
556 	 * mapped image.
557 	 */
558 	if (ld == NULL) {
559 		/* LINTED */
560 		struct exec	*exec = (struct exec *)caddr;
561 		struct nlist	*nl;
562 
563 		/* LINTED */
564 		nl = (struct nlist *)&caddr[N_SYMOFF(*exec)];
565 		/* LINTED */
566 		ld = (Link_dynamic *)&caddr[nl->n_value];
567 
568 		ld->v2 = (struct link_dynamic_2 *)((int)ld->v2 + (int)caddr);
569 	}
570 	AOUTDYN(lmp) = ld;
571 
572 	if ((RPATH(lmp) = (char *)&base[ld->v2->ld_rules]) == base)
573 		RPATH(lmp) = 0;
574 	LM2LP(lmp)->lp_symbol_base = caddr;
575 	/* LINTED */
576 	LM2LP(lmp)->lp_plt = (struct jbind *)(&caddr[JMPOFF(ld)]);
577 	LM2LP(lmp)->lp_rp =
578 	/* LINTED */
579 	    (struct relocation_info *)(&base[RELOCOFF(ld)]);
580 	/* LINTED */
581 	LM2LP(lmp)->lp_hash = (struct fshash *)(&base[HASHOFF(ld)]);
582 	/* LINTED */
583 	LM2LP(lmp)->lp_symtab = (struct nlist *)(&base[SYMOFF(ld)]);
584 	LM2LP(lmp)->lp_symstr = &base[STROFF(ld)];
585 	LM2LP(lmp)->lp_textbase = base;
586 	LM2LP(lmp)->lp_refcnt++;
587 	LM2LP(lmp)->lp_dlp = NULL;
588 
589 	/*
590 	 * Add the mapped object to the end of the link map list.
591 	 */
592 	lm_append(lml, lmco, lmp);
593 	return (lmp);
594 }
595 
596 /*
597  * Build full pathname of shared object from the given directory name and
598  * filename.
599  */
600 static char *
601 /* ARGSUSED2 */
602 aout_get_so(const char *dir, const char *file, size_t dlen, size_t flen)
603 {
604 	struct db	*dbp;
605 	char		*path = NULL;
606 
607 	if (dbp = lo_cache(dir)) {
608 		path = ask_db(dbp, file);
609 	}
610 	return (path);
611 }
612 
613 /*
614  * Determine the symbol location of an address within a link-map.  Look for
615  * the nearest symbol (whoes value is less than or equal to the required
616  * address).  This is the object specific part of dladdr().
617  */
618 static void
619 aout_dladdr(ulong_t addr, Rt_map *lmp, Dl_info *dlip, void **info,
620     int flags)
621 {
622 	ulong_t		ndx, cnt, base, _value;
623 	struct nlist	*sym, *_sym;
624 
625 	cnt = ((int)LM2LP(lmp)->lp_symstr - (int)LM2LP(lmp)->lp_symtab) /
626 	    sizeof (struct nlist);
627 	sym = LM2LP(lmp)->lp_symtab;
628 
629 	if (FLAGS(lmp) & FLG_RT_FIXED)
630 		base = 0;
631 	else
632 		base = ADDR(lmp);
633 
634 	for (_sym = 0, _value = 0, ndx = 0; ndx < cnt; ndx++, sym++) {
635 		ulong_t	value;
636 
637 		if (sym->n_type == (N_EXT + N_UNDF))
638 			continue;
639 
640 		value = sym->n_value + base;
641 		if (value > addr)
642 			continue;
643 		if (value < _value)
644 			continue;
645 
646 		_sym = sym;
647 		_value = value;
648 
649 		if (value == addr)
650 			break;
651 	}
652 
653 	if (_sym) {
654 		int	_flags = flags & RTLD_DL_MASK;
655 
656 		/*
657 		 * The only way we can create a symbol entry is to use
658 		 * aout_symconvert(), however this results in us pointing to
659 		 * static data that could be overridden.  In addition the AOUT
660 		 * symbol format doesn't give us everything an ELF symbol does.
661 		 * So, unless we get convinced otherwise, don't bother returning
662 		 * a symbol entry for AOUT's.
663 		 */
664 		if (_flags == RTLD_DL_SYMENT)
665 			*info = 0;
666 		else if (_flags == RTLD_DL_LINKMAP)
667 			*info = (void *)lmp;
668 
669 		dlip->dli_sname = &LM2LP(lmp)->lp_symstr[_sym->n_un.n_strx];
670 		dlip->dli_saddr = (void *)_value;
671 	}
672 }
673 
674 /*
675  * Continue processing a dlsym request.  Lookup the required symbol in each
676  * link-map specified by the handle.  Note, that because this lookup is against
677  * individual link-maps we don't need to supply a starting link-map to the
678  * lookup routine (see lookup_sym():analyze.c).
679  */
680 static int
681 aout_dlsym_handle(Grp_hdl *ghp, Slookup *slp, Sresult *srp, uint_t *binfo,
682     int *in_nfavl)
683 {
684 	char	buffer[PATH_MAX];
685 	Slookup	sl;
686 
687 	if (dlsym_handle(ghp, slp, srp, binfo, in_nfavl))
688 		return (1);
689 
690 	/*
691 	 * Symbol not found as supplied.  However, most of our symbols will
692 	 * be in the "C" name space, where the implementation prepends a "_"
693 	 * to the symbol as it emits it.  Therefore, attempt to find the
694 	 * symbol with the "_" prepend.
695 	 */
696 	buffer[0] = '_';
697 	(void) strcpy(&buffer[1], slp->sl_name);
698 
699 	sl = *slp;
700 	sl.sl_name = (const char *)buffer;
701 
702 	return (dlsym_handle(ghp, &sl, srp, binfo, in_nfavl));
703 }
704 
705 /*
706  * The initial mapping of the a.out occurs through exec(2), and presently this
707  * implementation doesn't provide a mmapobj_result_t array to ld.so.1.  Thus,
708  * aout_get_mmap() is called to create the mapping information.  Unlike ELF,
709  * the information that can be gathered from a mapped AOUT file, can be limited.
710  * In some cases the AOUT header isn't available in the mapped image, and thus
711  * this can't be inspected to determine the files size (the kernel always
712  * returns a pointer to the AOUT dynamic structure, but this is only sufficient
713  * to determine the size of the text segment).
714  *
715  * Therefore, the only generic mechanism of determining the AOUT's mapping is
716  * to use /proc.  Only two mappings are required, the text (to determine any
717  * read-only region), and the data.  The two mapping validate the range in
718  * which any relocations will occur.  Should there be an additional bss segment,
719  * we don't care, as this can't be relocated, and we're never going to try
720  * unmapping the a.out.
721  */
722 int
723 aout_get_mmap(Lm_list *lml, mmapobj_result_t *mpp)
724 {
725 	prmap_t	*maps;
726 	char	proc[16];
727 	int	num, err, fd;
728 
729 	(void) snprintf(proc, 16, MSG_ORIG(MSG_FMT_PROC), (int)getpid());
730 	if ((fd = open(proc, O_RDONLY)) == -1) {
731 		err = errno;
732 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), proc,
733 		    strerror(err));
734 		return (1);
735 	}
736 
737 	if (ioctl(fd, PIOCNMAP, (void *)&num) == -1) {
738 		err = errno;
739 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_PROC), strerror(err));
740 		return (1);
741 	}
742 
743 	if ((maps = malloc((num + 1) * sizeof (prmap_t))) == NULL)
744 		return (1);
745 
746 	if (ioctl(fd, PIOCMAP, (void *)maps) == -1) {
747 		err = errno;
748 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_PROC), strerror(err));
749 		free(maps);
750 		return (1);
751 	}
752 
753 	mpp->mr_addr = maps->pr_vaddr;
754 	mpp->mr_fsize = mpp->mr_msize = maps->pr_size;
755 	mpp->mr_prot = (PROT_READ | PROT_EXEC);
756 
757 	mpp++, maps++;
758 
759 	mpp->mr_addr = maps->pr_vaddr;
760 	mpp->mr_fsize = mpp->mr_msize = maps->pr_size;
761 	mpp->mr_prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
762 
763 	maps--;
764 	free(maps);
765 	return (0);
766 }
767