xref: /titanic_50/usr/src/cmd/sgs/rtld/common/a.out.c (revision 5db09ef5a5ba16a4728800a9055f47b3f404a2b3)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Object file dependent support for a.out format objects.
30  */
31 #include	"_synonyms.h"
32 
33 #include	<sys/mman.h>
34 #include	<unistd.h>
35 #include	<string.h>
36 #include	<limits.h>
37 #include	<stdio.h>
38 #include	<dlfcn.h>
39 #include	<errno.h>
40 #include	"_a.out.h"
41 #include	"cache_a.out.h"
42 #include	"msg.h"
43 #include	"_rtld.h"
44 #include	"debug.h"
45 
46 /*
47  * Default and secure dependency search paths.
48  */
49 static Pnode		aout_dflt_dirs[] = {
50 	{ MSG_ORIG(MSG_PTH_USR4LIB),	0,	MSG_PTH_USR4LIB_SIZE,
51 		LA_SER_DEFAULT,		0,	&aout_dflt_dirs[1] },
52 	{ MSG_ORIG(MSG_PTH_USRLIB),	0,	MSG_PTH_USRLIB_SIZE,
53 		LA_SER_DEFAULT,		0,	&aout_dflt_dirs[2] },
54 	{ MSG_ORIG(MSG_PTH_USRLCLIB),	0,	MSG_PTH_USRLCLIB_SIZE,
55 		LA_SER_DEFAULT,		0, 0 }
56 };
57 
58 static Pnode		aout_secure_dirs[] = {
59 	{ MSG_ORIG(MSG_PTH_USR4LIB),	0,	MSG_PTH_USR4LIB_SIZE,
60 		LA_SER_SECURE,		0,	&aout_secure_dirs[1] },
61 	{ MSG_ORIG(MSG_PTH_USRLIB),	0,	MSG_PTH_USRLIB_SIZE,
62 		LA_SER_SECURE,		0,	&aout_secure_dirs[2] },
63 	{ MSG_ORIG(MSG_PTH_USRUCBLIB),	0,	MSG_PTH_USRUCBLIB_SIZE,
64 		LA_SER_SECURE,		0,	&aout_secure_dirs[3] },
65 	{ MSG_ORIG(MSG_PTH_USRLCLIB),	0,	MSG_PTH_USRLCLIB_SIZE,
66 		LA_SER_SECURE,		0, 0 }
67 };
68 
69 /*
70  * Defines for local functions.
71  */
72 static int		aout_are_u();
73 static ulong_t		aout_entry_pt();
74 static Rt_map		*aout_map_so();
75 static void		aout_unmap_so();
76 static int		aout_needed();
77 extern Sym		*aout_lookup_sym();
78 static Sym		*aout_find_sym();
79 static char		*aout_get_so();
80 static Pnode		*aout_fix_name();
81 static void		aout_dladdr();
82 static Sym		*aout_dlsym_handle();
83 static int		aout_verify_vers();
84 
85 /*
86  * Functions and data accessed through indirect pointers.
87  */
88 Fct aout_fct = {
89 	aout_are_u,
90 	aout_entry_pt,
91 	aout_map_so,
92 	aout_unmap_so,
93 	aout_needed,
94 	aout_lookup_sym,
95 	aout_reloc,
96 	aout_dflt_dirs,
97 	aout_secure_dirs,
98 	aout_fix_name,
99 	aout_get_so,
100 	aout_dladdr,
101 	aout_dlsym_handle,
102 	aout_verify_vers,
103 	aout_set_prot
104 };
105 
106 
107 /*
108  * In 4.x, a needed file or a dlopened file that was a simple file name implied
109  * that the file be found in the present working directory.  To simulate this
110  * lookup within the elf rules it is necessary to add a proceeding `./' to the
111  * filename.
112  */
113 static Pnode *
114 aout_fix_name(const char *name)
115 {
116 	size_t	len;
117 	Pnode	*pnp;
118 
119 	if ((pnp = calloc(1, sizeof (Pnode))) == 0)
120 		return (0);
121 
122 	/*
123 	 * Check for slash in name, if none, prepend "./", otherwise just
124 	 * return name given.
125 	 */
126 	if (strchr(name, '/')) {
127 		len = strlen(name) + 1;
128 		if ((pnp->p_name = malloc(len)) != 0)
129 			(void) strcpy((char *)pnp->p_name, name);
130 	} else {
131 		len = strlen(name) + 3;
132 		if ((pnp->p_name = malloc(len)) != 0)
133 			(void) snprintf((char *)pnp->p_name, len,
134 			    MSG_ORIG(MSG_FMT_4XPATH), name);
135 	}
136 
137 	if (pnp->p_name) {
138 		pnp->p_len = len;
139 		pnp->p_orig = PN_SER_NEEDED;
140 		DBG_CALL(Dbg_file_fixname(pnp->p_name, name));
141 		return (pnp);
142 	}
143 	free(pnp);
144 	return (0);
145 }
146 
147 /*
148  * Determine if we have been given an A_OUT file.  Returns 1 if true.
149  */
150 static int
151 aout_are_u()
152 {
153 	struct exec *exec;
154 
155 	/* LINTED */
156 	exec = (struct exec *)fmap->fm_maddr;
157 	if (fmap->fm_fsize < sizeof (exec) || (exec->a_machtype != M_SPARC) ||
158 	    (N_BADMAG(*exec))) {
159 		return (0);
160 	}
161 	return (1);
162 }
163 
164 /*
165  * Return the entry point the A_OUT executable. This is always zero.
166  */
167 static ulong_t
168 aout_entry_pt()
169 {
170 	return (0);
171 }
172 
173 /*
174  * Unmap a given A_OUT shared object from the address space.
175  */
176 static void
177 aout_unmap_so(Rt_map *lmp)
178 {
179 	Mmap	*immap = MMAPS(lmp);
180 
181 	(void) munmap(immap->m_vaddr, immap->m_msize);
182 }
183 
184 /*
185  * Dummy versioning interface - real functionality is only applicable to elf.
186  */
187 static int
188 aout_verify_vers()
189 {
190 	return (1);
191 }
192 
193 /*
194  * Search through the dynamic section for DT_NEEDED entries and perform one
195  * of two functions.  If only the first argument is specified then load the
196  * defined shared object, otherwise add the link map representing the
197  * defined link map the the dlopen list.
198  */
199 static int
200 aout_needed(Lm_list *lml, Aliste lmco, Rt_map *clmp)
201 {
202 	void	*need;
203 
204 	for (need = &TEXTBASE(clmp)[AOUTDYN(clmp)->v2->ld_need];
205 	    need != &TEXTBASE(clmp)[0];
206 	    need = &TEXTBASE(clmp)[((Lnk_obj *)(need))->lo_next]) {
207 		Rt_map	*nlmp;
208 		char	*name;
209 		Pnode	*pnp;
210 
211 		name = &TEXTBASE(clmp)[((Lnk_obj *)(need))->lo_name];
212 
213 		if (((Lnk_obj *)(need))->lo_library) {
214 			/*
215 			 * If lo_library field is not NULL then this needed
216 			 * library was linked in using the "-l" option.
217 			 * Thus we need to rebuild the library name before
218 			 * trying to load it.
219 			 */
220 			Pnode	*dir, *dirlist = (Pnode *)0;
221 			char	*file;
222 			size_t	len;
223 
224 			/*
225 			 * Allocate name length plus 20 for full library name.
226 			 * lib.so.. = 7 + (2 * short) + NULL = 7 + 12 + 1 = 20
227 			 */
228 			len = strlen(name) + 20;
229 			if ((file = malloc(len)) == 0)
230 				return (0);
231 			(void) snprintf(file, len, MSG_ORIG(MSG_FMT_4XLIB),
232 			    name, ((Lnk_obj *)(need))->lo_major,
233 			    ((Lnk_obj *)(need))->lo_minor);
234 
235 			DBG_CALL(Dbg_libs_find(file));
236 
237 			/*
238 			 * We need to determine what filename will match the
239 			 * the filename specified (ie, a libc.so.1.2 may match
240 			 * to a libc.so.1.3).  It's the real pathname that is
241 			 * recorded in the link maps.  If we are presently
242 			 * being traced, skip this pathname generation so
243 			 * that we fall through into load_so() to print the
244 			 * appropriate diagnostics.  I don't like this at all.
245 			 */
246 			if (lml->lm_flags & LML_FLG_TRC_ENABLE)
247 				name = file;
248 			else {
249 				char	*path = (char *)0;
250 
251 				for (dir = get_next_dir(&dirlist, clmp, 0); dir;
252 				    dir = get_next_dir(&dirlist, clmp, 0)) {
253 					if (dir->p_name == 0)
254 						continue;
255 
256 					if (path =
257 					    aout_get_so(dir->p_name, file))
258 						break;
259 				}
260 				if (!path) {
261 					eprintf(ERR_FATAL,
262 					    MSG_INTL(MSG_SYS_OPEN), file,
263 						strerror(ENOENT));
264 					return (0);
265 				}
266 				name = path;
267 			}
268 			if ((pnp = expand_paths(clmp, name,
269 			    PN_SER_NEEDED, 0)) == 0)
270 				return (0);
271 		} else {
272 			/*
273 			 * If the library is specified as a pathname, see if
274 			 * it must be fixed to specify the current working
275 			 * directory (ie. libc.so.1.2 -> ./libc.so.1.2).
276 			 */
277 			if ((pnp = aout_fix_name(name)) == 0)
278 				return (0);
279 		}
280 		DBG_CALL(Dbg_file_needed(name, NAME(clmp)));
281 
282 		nlmp = load_one(lml, lmco, pnp, clmp, MODE(clmp), 0, 0);
283 		remove_pnode(pnp);
284 		if (((nlmp == 0) || (bind_one(clmp, nlmp, BND_NEEDED) == 0)) &&
285 		    ((lml->lm_flags & LML_FLG_TRC_ENABLE) == 0))
286 			return (0);
287 	}
288 
289 	return (1);
290 }
291 
292 static Sym *
293 aout_symconvert(struct nlist *sp)
294 {
295 	static Sym	sym;
296 
297 	sym.st_value = sp->n_value;
298 	sym.st_size = 0;
299 	sym.st_info = 0;
300 	sym.st_other = 0;
301 	switch (sp->n_type) {
302 		case N_EXT + N_ABS:
303 			sym.st_shndx = SHN_ABS;
304 			break;
305 		case N_COMM:
306 			sym.st_shndx = SHN_COMMON;
307 			break;
308 		case N_EXT + N_UNDF:
309 			sym.st_shndx = SHN_UNDEF;
310 			break;
311 		default:
312 			sym.st_shndx = 0;
313 			break;
314 	}
315 	return (&sym);
316 }
317 
318 /*
319  * Process a.out format commons.
320  */
321 static struct nlist *
322 aout_find_com(struct nlist *sp, const char *name)
323 {
324 	static struct rtc_symb	*rtcp = 0;
325 	struct rtc_symb		*rs, * trs;
326 	const char		*sl;
327 	char			*cp;
328 
329 	/*
330 	 * See if common is already allocated.
331 	 */
332 	trs = rtcp;
333 	while (trs) {
334 		sl = name;
335 		cp = trs->rtc_sp->n_un.n_name;
336 		while (*sl == *cp++)
337 			if (*sl++ == '\0')
338 				return (trs->rtc_sp);
339 		trs = trs->rtc_next;
340 	}
341 
342 	/*
343 	 * If we got here, common is not already allocated so allocate it.
344 	 */
345 	if ((rs = malloc(sizeof (struct rtc_symb))) == 0)
346 		return (0);
347 	if ((rs->rtc_sp = malloc(sizeof (struct nlist))) == 0)
348 		return (0);
349 	trs = rtcp;
350 	rtcp = rs;
351 	rs->rtc_next = trs;
352 	*(rs->rtc_sp) = *sp;
353 	if ((rs->rtc_sp->n_un.n_name = malloc(strlen(name) + 1)) == 0)
354 		return (0);
355 	(void) strcpy(rs->rtc_sp->n_un.n_name, name);
356 	rs->rtc_sp->n_type = N_COMM;
357 	if ((rs->rtc_sp->n_value = (long)calloc(rs->rtc_sp->n_value, 1)) == 0)
358 		return (0);
359 	return (rs->rtc_sp);
360 }
361 
362 /*
363  * Find a.out format symbol in the specified link map.  Unlike the sister
364  * elf routine we re-calculate the symbols hash value for each link map
365  * we're looking at.
366  */
367 static struct nlist *
368 aout_findsb(const char *aname, Rt_map *lmp, int flag)
369 {
370 	const char	*name = aname;
371 	char		*cp;
372 	struct fshash	*p;
373 	int		i;
374 	struct nlist	*sp;
375 	ulong_t		hval = 0;
376 
377 #define	HASHMASK	0x7fffffff
378 #define	RTHS		126
379 
380 	/*
381 	 * The name passed to us is in ELF format, thus it is necessary to
382 	 * map this back to the A_OUT format to compute the hash value (see
383 	 * mapping rules in aout_lookup_sym()).  Basically the symbols are
384 	 * mapped according to whether a leading `.' exists.
385 	 *
386 	 *	elf symbol		a.out symbol
387 	 * i.	   .bar		->	   .bar		(LKUP_LDOT)
388 	 * ii.	   .nuts	->	    nuts
389 	 * iii.	    foo		->	   _foo
390 	 */
391 	if (*name == '.') {
392 		if (!(flag & LKUP_LDOT))
393 			name++;
394 	} else
395 		hval = '_';
396 
397 	while (*name)
398 		hval = (hval << 1) + *name++;
399 	hval = hval & HASHMASK;
400 
401 	i = hval % (AOUTDYN(lmp)->v2->ld_buckets == 0 ? RTHS :
402 		AOUTDYN(lmp)->v2->ld_buckets);
403 	p = LM2LP(lmp)->lp_hash + i;
404 
405 	if (p->fssymbno != -1)
406 		do {
407 			sp = &LM2LP(lmp)->lp_symtab[p->fssymbno];
408 			cp = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
409 			name = aname;
410 			if (*name == '.') {
411 				if (!(flag & LKUP_LDOT))
412 					name++;
413 			} else {
414 				cp++;
415 			}
416 			while (*name == *cp++) {
417 				if (*name++ == '\0')
418 					return (sp);	/* found */
419 			}
420 			if (p->next == 0)
421 				return (0);		/* not found */
422 			else
423 				continue;
424 		} while ((p = &LM2LP(lmp)->lp_hash[p->next]) != 0);
425 	return (0);
426 }
427 
428 /*
429  * The symbol name we have been asked to look up is in A_OUT format, this
430  * symbol is mapped to the appropriate ELF format which is the standard by
431  * which symbols are passed around ld.so.1.  The symbols are mapped
432  * according to whether a leading `_' or `.' exists.
433  *
434  *	a.out symbol		elf symbol
435  * i.	   _foo		->	    foo
436  * ii.	   .bar		->	   .bar		(LKUP_LDOT)
437  * iii.	    nuts	->	   .nuts
438  */
439 Sym *
440 aout_lookup_sym(Slookup *slp, Rt_map **dlmp, uint_t *binfo)
441 {
442 	char	name[PATH_MAX];
443 	Slookup	sl = *slp;
444 
445 	DBG_CALL(Dbg_syms_lookup_aout(slp->sl_name));
446 
447 	if (*sl.sl_name == '_')
448 		++sl.sl_name;
449 	else if (*sl.sl_name == '.')
450 		sl.sl_flags |= LKUP_LDOT;
451 	else {
452 		name[0] = '.';
453 		(void) strcpy(&name[1], sl.sl_name);
454 		sl.sl_name = name;
455 	}
456 
457 	/*
458 	 * Call the generic lookup routine to cycle through the specified
459 	 * link maps.
460 	 */
461 	return (lookup_sym(&sl, dlmp, binfo));
462 }
463 
464 /*
465  * Symbol lookup for an a.out format module.
466  */
467 static Sym *
468 aout_find_sym(Slookup *slp, Rt_map **dlmp, uint_t *binfo)
469 {
470 	const char	*name = slp->sl_name;
471 	Rt_map		*ilmp = slp->sl_imap;
472 	struct nlist	*sp;
473 
474 	DBG_CALL(Dbg_syms_lookup(name, NAME(ilmp), MSG_ORIG(MSG_STR_AOUT)));
475 
476 	if (sp = aout_findsb(name, ilmp, slp->sl_flags)) {
477 		if (sp->n_value != 0) {
478 			/*
479 			 * is it a common?
480 			 */
481 			if (sp->n_type == (N_EXT + N_UNDF)) {
482 				if ((sp = aout_find_com(sp, name)) == 0)
483 					return ((Sym *)0);
484 			}
485 			*dlmp = ilmp;
486 			*binfo |= DBG_BINFO_FOUND;
487 			return (aout_symconvert(sp));
488 		}
489 	}
490 	return ((Sym *)0);
491 }
492 
493 /*
494  * Map in an a.out format object.
495  * Takes an open file descriptor for the object to map and
496  * its pathname; returns a pointer to a Rt_map structure
497  * for this object, or 0 on error.
498  */
499 static Rt_map *
500 aout_map_so(Lm_list *lml, Aliste lmco, const char *pname, const char *oname,
501     int fd)
502 {
503 	struct exec	*exec;		/* working area for object headers */
504 	caddr_t		addr;		/* mmap result temporary */
505 	struct link_dynamic *ld;	/* dynamic pointer of object mapped */
506 	size_t		size;		/* size of object */
507 	Rt_map		*lmp;		/* link map created */
508 	int		err;
509 	struct nlist	*nl;
510 
511 	/*
512 	 * Map text and allocate enough address space to fit the whole
513 	 * library.  Note that we map enough to catch the first symbol
514 	 * in the symbol table and thereby avoid an "lseek" & "read"
515 	 * pair to pick it up.
516 	 */
517 	/* LINTED */
518 	exec = (struct exec *)fmap->fm_maddr;
519 	size = max(SIZE(*exec), N_SYMOFF(*exec) + sizeof (struct nlist));
520 	if ((addr = mmap(0, size, (PROT_READ | PROT_EXEC), MAP_PRIVATE,
521 	    fd, 0)) == MAP_FAILED) {
522 		err = errno;
523 		eprintf(ERR_FATAL, MSG_INTL(MSG_SYS_MMAP), pname,
524 		    strerror(err));
525 		return (0);
526 	}
527 
528 	/*
529 	 * Grab the first symbol entry while we've got it mapped aligned
530 	 * to file addresses.  We assume that this symbol describes the
531 	 * object's link_dynamic.
532 	 */
533 	/* LINTED */
534 	nl = (struct nlist *)&addr[N_SYMOFF(*exec)];
535 	/* LINTED */
536 	ld = (struct link_dynamic *)&addr[nl->n_value];
537 
538 	/*
539 	 * Map the initialized data portion of the file to the correct
540 	 * point in the range of allocated addresses.  This will leave
541 	 * some portion of the data segment "doubly mapped" on machines
542 	 * where the text/data relocation alignment is not on a page
543 	 * boundaries.  However, leaving the file mapped has the double
544 	 * advantage of both saving the munmap system call and of leaving
545 	 * us a contiguous chunk of address space devoted to the object --
546 	 * in case we need to unmap it all later.
547 	 */
548 	if (mmap((caddr_t)(addr + M_SROUND(exec->a_text)),
549 	    (int)exec->a_data, (PROT_READ | PROT_WRITE | PROT_EXEC),
550 	    (MAP_FIXED | MAP_PRIVATE), fd, (off_t)exec->a_text) == MAP_FAILED) {
551 		err = errno;
552 		eprintf(ERR_FATAL, MSG_INTL(MSG_SYS_MMAP), pname,
553 		    strerror(err));
554 		return (0);
555 	}
556 
557 	/*
558 	 * Allocate pages for the object's bss, if necessary.
559 	 */
560 	if (exec->a_bss != 0) {
561 		if (dz_map(addr + M_SROUND(exec->a_text) + exec->a_data,
562 		    (int)exec->a_bss, PROT_READ | PROT_WRITE | PROT_EXEC,
563 		    MAP_FIXED | MAP_PRIVATE) == MAP_FAILED)
564 			goto error;
565 	}
566 
567 	/*
568 	 * Create link map structure for newly mapped shared object.
569 	 */
570 	ld->v2 = (struct link_dynamic_2 *)((int)ld->v2 + (int)addr);
571 	if (!(lmp = aout_new_lm(lml, pname, oname, ld, addr, size, lmco)))
572 		goto error;
573 
574 	return (lmp);
575 
576 	/*
577 	 * Error returns: close off file and free address space.
578 	 */
579 error:
580 	(void) munmap((caddr_t)addr, size);
581 	return (0);
582 }
583 
584 /*
585  * Create a new Rt_map structure for an a.out format object and
586  * initializes all values.
587  */
588 Rt_map *
589 aout_new_lm(Lm_list *lml, const char *pname, const char *oname,
590     struct link_dynamic *ld, caddr_t addr, size_t size, Aliste lmco)
591 {
592 	Rt_map	*lmp;
593 	caddr_t offset;
594 
595 	DBG_CALL(Dbg_file_aout(pname, (ulong_t)ld, (ulong_t)addr,
596 	    (ulong_t)size));
597 
598 	/*
599 	 * Allocate space for the link-map and private a.out information.  Once
600 	 * these are allocated and initialized, we can use remove_so(0, lmp) to
601 	 * tear down the link-map should any failures occur.
602 	 */
603 	if ((lmp = calloc(sizeof (Rt_map), 1)) == 0)
604 		return (0);
605 	if ((AOUTPRV(lmp) = calloc(sizeof (Rt_aoutp), 1)) == 0) {
606 		free(lmp);
607 		return (0);
608 	}
609 	if ((((Rt_aoutp *)AOUTPRV(lmp))->lm_lpd =
610 	    calloc(sizeof (struct ld_private), 1)) == 0) {
611 		free(AOUTPRV(lmp));
612 		free(lmp);
613 		return (0);
614 	}
615 
616 	/*
617 	 * All fields not filled in were set to 0 by calloc.
618 	 */
619 	ORIGNAME(lmp) = PATHNAME(lmp) = NAME(lmp) = (char *)pname;
620 	ADDR(lmp) = (ulong_t)addr;
621 	MSIZE(lmp) = (ulong_t)size;
622 	SYMINTP(lmp) = aout_find_sym;
623 	FCT(lmp) = &aout_fct;
624 	LIST(lmp) = lml;
625 	THREADID(lmp) = rt_thr_self();
626 	OBJFLTRNDX(lmp) = FLTR_DISABLED;
627 
628 	/*
629 	 * Specific settings for a.out format.
630 	 */
631 	if (lml->lm_head == 0) {
632 		offset = (caddr_t)MAIN_BASE;
633 		FLAGS(lmp) |= FLG_RT_FIXED;
634 	} else
635 		offset = addr;
636 
637 	ETEXT(lmp) = (ulong_t)&offset[ld->v2->ld_text];
638 
639 	/*
640 	 * Create a mapping descriptor to describe the whole object as a single
641 	 * mapping.
642 	 */
643 	if ((MMAPS(lmp) = calloc(2, sizeof (Mmap))) == 0)
644 		return (0);
645 	MMAPS(lmp)->m_vaddr = offset;
646 	/* LINTED */
647 	MMAPS(lmp)->m_msize = max(SIZE(*(struct exec *)offset),
648 	    N_SYMOFF((*(struct exec *)offset)) + sizeof (struct nlist));
649 	MMAPS(lmp)->m_fsize = MMAPS(lmp)->m_msize;
650 	MMAPCNT(lmp) = 1;
651 
652 	/*
653 	 * Fill in all AOUT information.
654 	 */
655 	AOUTDYN(lmp) = ld;
656 	if ((RPATH(lmp) = (char *)&offset[ld->v2->ld_rules]) == offset)
657 		RPATH(lmp) = 0;
658 	LM2LP(lmp)->lp_symbol_base = addr;
659 	/* LINTED */
660 	LM2LP(lmp)->lp_plt = (struct jbind *)(&addr[JMPOFF(ld)]);
661 	LM2LP(lmp)->lp_rp =
662 	/* LINTED */
663 	    (struct relocation_info *)(&offset[RELOCOFF(ld)]);
664 	/* LINTED */
665 	LM2LP(lmp)->lp_hash = (struct fshash *)(&offset[HASHOFF(ld)]);
666 	/* LINTED */
667 	LM2LP(lmp)->lp_symtab = (struct nlist *)(&offset[SYMOFF(ld)]);
668 	LM2LP(lmp)->lp_symstr = &offset[STROFF(ld)];
669 	LM2LP(lmp)->lp_textbase = offset;
670 	LM2LP(lmp)->lp_refcnt++;
671 	LM2LP(lmp)->lp_dlp = NULL;
672 
673 	if (rtld_flags & RT_FL_RELATIVE)
674 		FLAGS1(lmp) |= FL1_RT_RELATIVE;
675 
676 	if ((CONDVAR(lmp) = rt_cond_create()) == 0) {
677 		remove_so(0, lmp);
678 		return (0);
679 	}
680 	if (oname && ((append_alias((lmp), oname, 0)) == 0)) {
681 		remove_so(0, lmp);
682 		return (0);
683 	}
684 
685 	/*
686 	 * Add the mapped object to the end of the link map list.
687 	 */
688 	lm_append(lml, lmco, lmp);
689 	return (lmp);
690 }
691 
692 /*
693  * Function to correct protection settings.
694  * Segments are all mapped initially with permissions as given in
695  * the segment header, but we need to turn on write permissions
696  * on a text segment if there are any relocations against that segment,
697  * and them turn write permission back off again before returning control
698  * to the program.  This function turns the permission on or off depending
699  * on the value of the argument.
700  */
701 int
702 aout_set_prot(Rt_map *lm, int permission)
703 {
704 	int		prot;		/* protection setting */
705 	caddr_t		et;		/* cached _etext of object */
706 	size_t		size;		/* size of text segment */
707 
708 	DBG_CALL(Dbg_file_prot(NAME(lm), permission));
709 
710 	et = (caddr_t)ETEXT(lm);
711 	size = M_PROUND((ulong_t)(et - TEXTBASE(lm)));
712 	prot = PROT_READ | PROT_EXEC | permission;
713 	if (mprotect((caddr_t)TEXTBASE(lm), size, prot) == -1) {
714 		int	err = errno;
715 
716 		eprintf(ERR_FATAL, MSG_INTL(MSG_SYS_MPROT), NAME(lm),
717 		    strerror(err));
718 		return (0);
719 	}
720 	return (1);
721 }
722 
723 /*
724  * Build full pathname of shared object from the given directory name and
725  * filename.
726  */
727 static char *
728 aout_get_so(const char *dir, const char *file)
729 {
730 	struct db	*dbp;
731 	char		*path = NULL;
732 
733 	if (dbp = lo_cache(dir)) {
734 		path = ask_db(dbp, file);
735 	}
736 	return (path);
737 }
738 
739 /*
740  * Determine the symbol location of an address within a link-map.  Look for
741  * the nearest symbol (whoes value is less than or equal to the required
742  * address).  This is the object specific part of dladdr().
743  */
744 static void
745 aout_dladdr(ulong_t addr, Rt_map *lmp, Dl_info *dlip, void **info,
746     int flags)
747 {
748 	ulong_t		ndx, cnt, base, _value;
749 	struct nlist	*sym, *_sym;
750 
751 	cnt = ((int)LM2LP(lmp)->lp_symstr - (int)LM2LP(lmp)->lp_symtab) /
752 		sizeof (struct nlist);
753 	sym = LM2LP(lmp)->lp_symtab;
754 
755 	if (FLAGS(lmp) & FLG_RT_FIXED)
756 		base = 0;
757 	else
758 		base = ADDR(lmp);
759 
760 	for (_sym = 0, _value = 0, ndx = 0; ndx < cnt; ndx++, sym++) {
761 		ulong_t	value;
762 
763 		if (sym->n_type == (N_EXT + N_UNDF))
764 			continue;
765 
766 		value = sym->n_value + base;
767 		if (value > addr)
768 			continue;
769 		if (value < _value)
770 			continue;
771 
772 		_sym = sym;
773 		_value = value;
774 
775 		if (value == addr)
776 			break;
777 	}
778 
779 	if (_sym) {
780 		int	_flags = flags & RTLD_DL_MASK;
781 
782 		/*
783 		 * The only way we can create a symbol entry is to use
784 		 * aout_symconvert(), however this results in us pointing to
785 		 * static data that could be overridden.  In addition the AOUT
786 		 * symbol format doesn't give us everything an ELF symbol does.
787 		 * So, unless we get convinced otherwise, don't bother returning
788 		 * a symbol entry for AOUT's.
789 		 */
790 		if (_flags == RTLD_DL_SYMENT)
791 			*info = 0;
792 		else if (_flags == RTLD_DL_LINKMAP)
793 			*info = (void *)lmp;
794 
795 		dlip->dli_sname = &LM2LP(lmp)->lp_symstr[_sym->n_un.n_strx];
796 		dlip->dli_saddr = (void *)_value;
797 	}
798 }
799 
800 /*
801  * Continue processing a dlsym request.  Lookup the required symbol in each
802  * link-map specified by the handle.  Note, that because this lookup is against
803  * individual link-maps we don't need to supply a starting link-map to the
804  * lookup routine (see lookup_sym():analyze.c).
805  */
806 Sym *
807 aout_dlsym_handle(Grp_hdl * ghp, Slookup *slp, Rt_map **_lmp, uint_t *binfo)
808 {
809 	Sym	*sym;
810 	char	buffer[PATH_MAX];
811 	Slookup	sl;
812 
813 	buffer[0] = '_';
814 	(void) strcpy(&buffer[1], slp->sl_name);
815 
816 	if ((sym = dlsym_handle(ghp, slp, _lmp, binfo)) != 0)
817 		return (sym);
818 
819 	/*
820 	 * Symbol not found as supplied.  However, most of our symbols will
821 	 * be in the "C" name space, where the implementation prepends a "_"
822 	 * to the symbol as it emits it.  Therefore, attempt to find the
823 	 * symbol with the "_" prepend.
824 	 */
825 	sl = *slp;
826 	sl.sl_name = (const char *)buffer;
827 
828 	return (dlsym_handle(ghp, &sl, _lmp, binfo));
829 }
830