xref: /titanic_51/usr/src/cmd/sgs/rtld/common/a.out.c (revision fc3af78a71855c71878866a294572d00e6720533)
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 2008 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/mman.h>
35 #include	<unistd.h>
36 #include	<string.h>
37 #include	<limits.h>
38 #include	<stdio.h>
39 #include	<dlfcn.h>
40 #include	<errno.h>
41 #include	<debug.h>
42 #include	"_a.out.h"
43 #include	"cache_a.out.h"
44 #include	"msg.h"
45 #include	"_rtld.h"
46 
47 /*
48  * Default and secure dependency search paths.
49  */
50 static Pnode		aout_dflt_dirs[] = {
51 	{ MSG_ORIG(MSG_PTH_USR4LIB),	0,	MSG_PTH_USR4LIB_SIZE,
52 		LA_SER_DEFAULT,		0,	&aout_dflt_dirs[1] },
53 	{ MSG_ORIG(MSG_PTH_USRLIB),	0,	MSG_PTH_USRLIB_SIZE,
54 		LA_SER_DEFAULT,		0,	&aout_dflt_dirs[2] },
55 	{ MSG_ORIG(MSG_PTH_USRLCLIB),	0,	MSG_PTH_USRLCLIB_SIZE,
56 		LA_SER_DEFAULT,		0, 0 }
57 };
58 
59 static Pnode		aout_secure_dirs[] = {
60 #ifndef	SGS_PRE_UNIFIED_PROCESS
61 	{ MSG_ORIG(MSG_PTH_LIBSE),	0,	MSG_PTH_LIBSE_SIZE,
62 		LA_SER_SECURE,		0,	&aout_secure_dirs[1] },
63 #endif
64 	{ MSG_ORIG(MSG_PTH_USRLIBSE),	0,	MSG_PTH_USRLIBSE_SIZE,
65 		LA_SER_SECURE,		0, 0 }
66 };
67 
68 /*
69  * Defines for local functions.
70  */
71 static int		aout_are_u();
72 static ulong_t		aout_entry_pt();
73 static Rt_map		*aout_map_so();
74 static void		aout_unmap_so();
75 static int		aout_needed();
76 extern Sym		*aout_lookup_sym();
77 static Sym		*aout_find_sym();
78 static char		*aout_get_so();
79 static Pnode		*aout_fix_name();
80 static void		aout_dladdr();
81 static Sym		*aout_dlsym_handle();
82 static int		aout_verify_vers();
83 
84 /*
85  * Functions and data accessed through indirect pointers.
86  */
87 Fct aout_fct = {
88 	aout_are_u,
89 	aout_entry_pt,
90 	aout_map_so,
91 	aout_unmap_so,
92 	aout_needed,
93 	aout_lookup_sym,
94 	aout_reloc,
95 	aout_dflt_dirs,
96 	aout_secure_dirs,
97 	aout_fix_name,
98 	aout_get_so,
99 	aout_dladdr,
100 	aout_dlsym_handle,
101 	aout_verify_vers,
102 	aout_set_prot
103 };
104 
105 
106 /*
107  * In 4.x, a needed file or a dlopened file that was a simple file name implied
108  * that the file be found in the present working directory.  To simulate this
109  * lookup within the elf rules it is necessary to add a proceeding `./' to the
110  * filename.
111  */
112 static Pnode *
113 aout_fix_name(const char *name, Rt_map *clmp)
114 {
115 	size_t	len;
116 	Pnode	*pnp;
117 
118 	if ((pnp = calloc(1, sizeof (Pnode))) == 0)
119 		return (0);
120 
121 	/*
122 	 * Check for slash in name, if none, prepend "./", otherwise just
123 	 * return name given.
124 	 */
125 	if (strchr(name, '/')) {
126 		len = strlen(name) + 1;
127 		if ((pnp->p_name = malloc(len)) != 0)
128 			(void) strcpy((char *)pnp->p_name, name);
129 	} else {
130 		len = strlen(name) + 3;
131 		if ((pnp->p_name = malloc(len)) != 0)
132 			(void) snprintf((char *)pnp->p_name, len,
133 			    MSG_ORIG(MSG_FMT_4XPATH), name);
134 	}
135 
136 	if (pnp->p_name) {
137 		pnp->p_len = len;
138 		DBG_CALL(Dbg_file_fixname(LIST(clmp), pnp->p_name, name));
139 		return (pnp);
140 	}
141 	free(pnp);
142 	return (0);
143 }
144 
145 /*
146  * Determine if we have been given an A_OUT file.  Returns 1 if true.
147  */
148 static int
149 aout_are_u()
150 {
151 	struct exec *exec;
152 
153 	/* LINTED */
154 	exec = (struct exec *)fmap->fm_maddr;
155 	if (fmap->fm_fsize < sizeof (exec) || (exec->a_machtype != M_SPARC) ||
156 	    (N_BADMAG(*exec))) {
157 		return (0);
158 	}
159 	return (1);
160 }
161 
162 /*
163  * Return the entry point the A_OUT executable. This is always zero.
164  */
165 static ulong_t
166 aout_entry_pt()
167 {
168 	return (0);
169 }
170 
171 /*
172  * Unmap a given A_OUT shared object from the address space.
173  */
174 static void
175 aout_unmap_so(Rt_map *lmp)
176 {
177 	Mmap	*immap = MMAPS(lmp);
178 
179 	(void) munmap(immap->m_vaddr, immap->m_msize);
180 }
181 
182 /*
183  * Dummy versioning interface - real functionality is only applicable to elf.
184  */
185 static int
186 aout_verify_vers()
187 {
188 	return (1);
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 	void	*need;
201 
202 	for (need = &TEXTBASE(clmp)[AOUTDYN(clmp)->v2->ld_need];
203 	    need != &TEXTBASE(clmp)[0];
204 	    need = &TEXTBASE(clmp)[((Lnk_obj *)(need))->lo_next]) {
205 		Rt_map	*nlmp;
206 		char	*name;
207 		Pnode	*pnp;
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 			Pnode	*dir, *dirlist = (Pnode *)0;
219 			char	*file;
220 			size_t	len;
221 
222 			/*
223 			 * Allocate name length plus 20 for full library name.
224 			 * lib.so.. = 7 + (2 * short) + NULL = 7 + 12 + 1 = 20
225 			 */
226 			len = strlen(name) + 20;
227 			if ((file = malloc(len)) == 0)
228 				return (0);
229 			(void) snprintf(file, len, MSG_ORIG(MSG_FMT_4XLIB),
230 			    name, ((Lnk_obj *)(need))->lo_major,
231 			    ((Lnk_obj *)(need))->lo_minor);
232 
233 			DBG_CALL(Dbg_libs_find(lml, file));
234 
235 			/*
236 			 * We need to determine what filename will match the
237 			 * the filename specified (ie, a libc.so.1.2 may match
238 			 * to a libc.so.1.3).  It's the real pathname that is
239 			 * recorded in the link maps.  If we are presently
240 			 * being traced, skip this pathname generation so
241 			 * that we fall through into load_so() to print the
242 			 * appropriate diagnostics.  I don't like this at all.
243 			 */
244 			if (lml->lm_flags & LML_FLG_TRC_ENABLE)
245 				name = file;
246 			else {
247 				char	*path = (char *)0;
248 
249 				for (dir = get_next_dir(&dirlist, clmp, 0); dir;
250 				    dir = get_next_dir(&dirlist, clmp, 0)) {
251 					if (dir->p_name == 0)
252 						continue;
253 
254 					if (path =
255 					    aout_get_so(dir->p_name, file))
256 						break;
257 				}
258 				if (!path) {
259 					eprintf(lml, ERR_FATAL,
260 					    MSG_INTL(MSG_SYS_OPEN), file,
261 					    strerror(ENOENT));
262 					return (0);
263 				}
264 				name = path;
265 			}
266 			if ((pnp = expand_paths(clmp, name, 0, 0)) == 0)
267 				return (0);
268 		} else {
269 			/*
270 			 * If the library is specified as a pathname, see if
271 			 * it must be fixed to specify the current working
272 			 * directory (ie. libc.so.1.2 -> ./libc.so.1.2).
273 			 */
274 			if ((pnp = aout_fix_name(name, clmp)) == 0)
275 				return (0);
276 		}
277 
278 		DBG_CALL(Dbg_file_needed(clmp, name));
279 
280 		nlmp = load_one(lml, lmco, pnp, clmp, MODE(clmp), 0, 0,
281 		    in_nfavl);
282 		remove_pnode(pnp);
283 		if (((nlmp == 0) || (bind_one(clmp, nlmp, BND_NEEDED) == 0)) &&
284 		    ((lml->lm_flags & LML_FLG_TRC_ENABLE) == 0))
285 			return (0);
286 	}
287 
288 	return (1);
289 }
290 
291 static Sym *
292 aout_symconvert(struct nlist *sp)
293 {
294 	static Sym	sym;
295 
296 	sym.st_value = sp->n_value;
297 	sym.st_size = 0;
298 	sym.st_info = 0;
299 	sym.st_other = 0;
300 	switch (sp->n_type) {
301 		case N_EXT + N_ABS:
302 			sym.st_shndx = SHN_ABS;
303 			break;
304 		case N_COMM:
305 			sym.st_shndx = SHN_COMMON;
306 			break;
307 		case N_EXT + N_UNDF:
308 			sym.st_shndx = SHN_UNDEF;
309 			break;
310 		default:
311 			sym.st_shndx = 0;
312 			break;
313 	}
314 	return (&sym);
315 }
316 
317 /*
318  * Process a.out format commons.
319  */
320 static struct nlist *
321 aout_find_com(struct nlist *sp, const char *name)
322 {
323 	static struct rtc_symb	*rtcp = 0;
324 	struct rtc_symb		*rs, * trs;
325 	const char		*sl;
326 	char			*cp;
327 
328 	/*
329 	 * See if common is already allocated.
330 	 */
331 	trs = rtcp;
332 	while (trs) {
333 		sl = name;
334 		cp = trs->rtc_sp->n_un.n_name;
335 		while (*sl == *cp++)
336 			if (*sl++ == '\0')
337 				return (trs->rtc_sp);
338 		trs = trs->rtc_next;
339 	}
340 
341 	/*
342 	 * If we got here, common is not already allocated so allocate it.
343 	 */
344 	if ((rs = malloc(sizeof (struct rtc_symb))) == 0)
345 		return (0);
346 	if ((rs->rtc_sp = malloc(sizeof (struct nlist))) == 0)
347 		return (0);
348 	trs = rtcp;
349 	rtcp = rs;
350 	rs->rtc_next = trs;
351 	*(rs->rtc_sp) = *sp;
352 	if ((rs->rtc_sp->n_un.n_name = malloc(strlen(name) + 1)) == 0)
353 		return (0);
354 	(void) strcpy(rs->rtc_sp->n_un.n_name, name);
355 	rs->rtc_sp->n_type = N_COMM;
356 	if ((rs->rtc_sp->n_value = (long)calloc(rs->rtc_sp->n_value, 1)) == 0)
357 		return (0);
358 	return (rs->rtc_sp);
359 }
360 
361 /*
362  * Find a.out format symbol in the specified link map.  Unlike the sister
363  * elf routine we re-calculate the symbols hash value for each link map
364  * we're looking at.
365  */
366 static struct nlist *
367 aout_findsb(const char *aname, Rt_map *lmp, int flag)
368 {
369 	const char	*name = aname;
370 	char		*cp;
371 	struct fshash	*p;
372 	int		i;
373 	struct nlist	*sp;
374 	ulong_t		hval = 0;
375 
376 #define	HASHMASK	0x7fffffff
377 #define	RTHS		126
378 
379 	/*
380 	 * The name passed to us is in ELF format, thus it is necessary to
381 	 * map this back to the A_OUT format to compute the hash value (see
382 	 * mapping rules in aout_lookup_sym()).  Basically the symbols are
383 	 * mapped according to whether a leading `.' exists.
384 	 *
385 	 *	elf symbol		a.out symbol
386 	 * i.	   .bar		->	   .bar		(LKUP_LDOT)
387 	 * ii.	   .nuts	->	    nuts
388 	 * iii.	    foo		->	   _foo
389 	 */
390 	if (*name == '.') {
391 		if (!(flag & LKUP_LDOT))
392 			name++;
393 	} else
394 		hval = '_';
395 
396 	while (*name)
397 		hval = (hval << 1) + *name++;
398 	hval = hval & HASHMASK;
399 
400 	i = hval % (AOUTDYN(lmp)->v2->ld_buckets == 0 ? RTHS :
401 	    AOUTDYN(lmp)->v2->ld_buckets);
402 	p = LM2LP(lmp)->lp_hash + i;
403 
404 	if (p->fssymbno != -1)
405 		do {
406 			sp = &LM2LP(lmp)->lp_symtab[p->fssymbno];
407 			cp = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
408 			name = aname;
409 			if (*name == '.') {
410 				if (!(flag & LKUP_LDOT))
411 					name++;
412 			} else {
413 				cp++;
414 			}
415 			while (*name == *cp++) {
416 				if (*name++ == '\0')
417 					return (sp);	/* found */
418 			}
419 			if (p->next == 0)
420 				return (0);		/* not found */
421 			else
422 				continue;
423 		} while ((p = &LM2LP(lmp)->lp_hash[p->next]) != 0);
424 	return (0);
425 }
426 
427 /*
428  * The symbol name we have been asked to look up is in A_OUT format, this
429  * symbol is mapped to the appropriate ELF format which is the standard by
430  * which symbols are passed around ld.so.1.  The symbols are mapped
431  * according to whether a leading `_' or `.' exists.
432  *
433  *	a.out symbol		elf symbol
434  * i.	   _foo		->	    foo
435  * ii.	   .bar		->	   .bar		(LKUP_LDOT)
436  * iii.	    nuts	->	   .nuts
437  */
438 Sym *
439 aout_lookup_sym(Slookup *slp, Rt_map **dlmp, uint_t *binfo, int *in_nfavl)
440 {
441 	char	name[PATH_MAX];
442 	Slookup	sl = *slp;
443 
444 	DBG_CALL(Dbg_syms_lookup_aout(LIST(slp->sl_imap), slp->sl_name));
445 
446 	if (*sl.sl_name == '_')
447 		++sl.sl_name;
448 	else if (*sl.sl_name == '.')
449 		sl.sl_flags |= LKUP_LDOT;
450 	else {
451 		name[0] = '.';
452 		(void) strcpy(&name[1], sl.sl_name);
453 		sl.sl_name = name;
454 	}
455 
456 	/*
457 	 * Call the generic lookup routine to cycle through the specified
458 	 * link maps.
459 	 */
460 	return (lookup_sym(&sl, dlmp, binfo, in_nfavl));
461 }
462 
463 /*
464  * Symbol lookup for an a.out format module.
465  */
466 /* ARGSUSED3 */
467 static Sym *
468 aout_find_sym(Slookup *slp, Rt_map **dlmp, uint_t *binfo, int *in_nfavl)
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(ilmp, name, 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(lml, 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(lml, 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(lml, 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(lml, pname, (ulong_t)ld, (ulong_t)addr,
596 	    (ulong_t)size, lml->lm_lmidstr, lmco));
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 	SORTVAL(lmp) = -1;
628 
629 	/*
630 	 * Specific settings for a.out format.
631 	 */
632 	if (lml->lm_head == 0) {
633 		offset = (caddr_t)MAIN_BASE;
634 		FLAGS(lmp) |= FLG_RT_FIXED;
635 	} else
636 		offset = addr;
637 
638 	ETEXT(lmp) = (ulong_t)&offset[ld->v2->ld_text];
639 
640 	/*
641 	 * Create a mapping descriptor to describe the whole object as a single
642 	 * mapping.
643 	 */
644 	if ((MMAPS(lmp) = calloc(2, sizeof (Mmap))) == 0)
645 		return (0);
646 	MMAPS(lmp)->m_vaddr = offset;
647 	/* LINTED */
648 	MMAPS(lmp)->m_msize = max(SIZE(*(struct exec *)offset),
649 	    N_SYMOFF((*(struct exec *)offset)) + sizeof (struct nlist));
650 	MMAPS(lmp)->m_fsize = MMAPS(lmp)->m_msize;
651 	MMAPCNT(lmp) = 1;
652 
653 	/*
654 	 * Fill in all AOUT information.
655 	 */
656 	AOUTDYN(lmp) = ld;
657 	if ((RPATH(lmp) = (char *)&offset[ld->v2->ld_rules]) == offset)
658 		RPATH(lmp) = 0;
659 	LM2LP(lmp)->lp_symbol_base = addr;
660 	/* LINTED */
661 	LM2LP(lmp)->lp_plt = (struct jbind *)(&addr[JMPOFF(ld)]);
662 	LM2LP(lmp)->lp_rp =
663 	/* LINTED */
664 	    (struct relocation_info *)(&offset[RELOCOFF(ld)]);
665 	/* LINTED */
666 	LM2LP(lmp)->lp_hash = (struct fshash *)(&offset[HASHOFF(ld)]);
667 	/* LINTED */
668 	LM2LP(lmp)->lp_symtab = (struct nlist *)(&offset[SYMOFF(ld)]);
669 	LM2LP(lmp)->lp_symstr = &offset[STROFF(ld)];
670 	LM2LP(lmp)->lp_textbase = offset;
671 	LM2LP(lmp)->lp_refcnt++;
672 	LM2LP(lmp)->lp_dlp = NULL;
673 
674 	if (rtld_flags & RT_FL_RELATIVE)
675 		FLAGS1(lmp) |= FL1_RT_RELATIVE;
676 
677 	if ((CONDVAR(lmp) = rt_cond_create()) == 0) {
678 		remove_so(0, lmp);
679 		return (0);
680 	}
681 	if (oname && ((append_alias((lmp), oname, 0)) == 0)) {
682 		remove_so(0, lmp);
683 		return (0);
684 	}
685 
686 	/*
687 	 * Add the mapped object to the end of the link map list.
688 	 */
689 	lm_append(lml, lmco, lmp);
690 	return (lmp);
691 }
692 
693 /*
694  * Function to correct protection settings.
695  * Segments are all mapped initially with permissions as given in
696  * the segment header, but we need to turn on write permissions
697  * on a text segment if there are any relocations against that segment,
698  * and them turn write permission back off again before returning control
699  * to the program.  This function turns the permission on or off depending
700  * on the value of the argument.
701  */
702 int
703 aout_set_prot(Rt_map *lmp, int permission)
704 {
705 	int		prot;		/* protection setting */
706 	caddr_t		et;		/* cached _etext of object */
707 	size_t		size;		/* size of text segment */
708 
709 	DBG_CALL(Dbg_file_prot(lmp, permission));
710 
711 	et = (caddr_t)ETEXT(lmp);
712 	size = M_PROUND((ulong_t)(et - TEXTBASE(lmp)));
713 	prot = PROT_READ | PROT_EXEC | permission;
714 	if (mprotect((caddr_t)TEXTBASE(lmp), size, prot) == -1) {
715 		int	err = errno;
716 
717 		eprintf(LIST(lmp), ERR_FATAL, MSG_INTL(MSG_SYS_MPROT),
718 		    NAME(lmp), strerror(err));
719 		return (0);
720 	}
721 	return (1);
722 }
723 
724 /*
725  * Build full pathname of shared object from the given directory name and
726  * filename.
727  */
728 static char *
729 aout_get_so(const char *dir, const char *file)
730 {
731 	struct db	*dbp;
732 	char		*path = NULL;
733 
734 	if (dbp = lo_cache(dir)) {
735 		path = ask_db(dbp, file);
736 	}
737 	return (path);
738 }
739 
740 /*
741  * Determine the symbol location of an address within a link-map.  Look for
742  * the nearest symbol (whoes value is less than or equal to the required
743  * address).  This is the object specific part of dladdr().
744  */
745 static void
746 aout_dladdr(ulong_t addr, Rt_map *lmp, Dl_info *dlip, void **info,
747     int flags)
748 {
749 	ulong_t		ndx, cnt, base, _value;
750 	struct nlist	*sym, *_sym;
751 
752 	cnt = ((int)LM2LP(lmp)->lp_symstr - (int)LM2LP(lmp)->lp_symtab) /
753 	    sizeof (struct nlist);
754 	sym = LM2LP(lmp)->lp_symtab;
755 
756 	if (FLAGS(lmp) & FLG_RT_FIXED)
757 		base = 0;
758 	else
759 		base = ADDR(lmp);
760 
761 	for (_sym = 0, _value = 0, ndx = 0; ndx < cnt; ndx++, sym++) {
762 		ulong_t	value;
763 
764 		if (sym->n_type == (N_EXT + N_UNDF))
765 			continue;
766 
767 		value = sym->n_value + base;
768 		if (value > addr)
769 			continue;
770 		if (value < _value)
771 			continue;
772 
773 		_sym = sym;
774 		_value = value;
775 
776 		if (value == addr)
777 			break;
778 	}
779 
780 	if (_sym) {
781 		int	_flags = flags & RTLD_DL_MASK;
782 
783 		/*
784 		 * The only way we can create a symbol entry is to use
785 		 * aout_symconvert(), however this results in us pointing to
786 		 * static data that could be overridden.  In addition the AOUT
787 		 * symbol format doesn't give us everything an ELF symbol does.
788 		 * So, unless we get convinced otherwise, don't bother returning
789 		 * a symbol entry for AOUT's.
790 		 */
791 		if (_flags == RTLD_DL_SYMENT)
792 			*info = 0;
793 		else if (_flags == RTLD_DL_LINKMAP)
794 			*info = (void *)lmp;
795 
796 		dlip->dli_sname = &LM2LP(lmp)->lp_symstr[_sym->n_un.n_strx];
797 		dlip->dli_saddr = (void *)_value;
798 	}
799 }
800 
801 /*
802  * Continue processing a dlsym request.  Lookup the required symbol in each
803  * link-map specified by the handle.  Note, that because this lookup is against
804  * individual link-maps we don't need to supply a starting link-map to the
805  * lookup routine (see lookup_sym():analyze.c).
806  */
807 Sym *
808 aout_dlsym_handle(Grp_hdl * ghp, Slookup *slp, Rt_map **_lmp, uint_t *binfo,
809     int *in_nfavl)
810 {
811 	Sym	*sym;
812 	char	buffer[PATH_MAX];
813 	Slookup	sl;
814 
815 	buffer[0] = '_';
816 	(void) strcpy(&buffer[1], slp->sl_name);
817 
818 	if ((sym = dlsym_handle(ghp, slp, _lmp, binfo, in_nfavl)) != 0)
819 		return (sym);
820 
821 	/*
822 	 * Symbol not found as supplied.  However, most of our symbols will
823 	 * be in the "C" name space, where the implementation prepends a "_"
824 	 * to the symbol as it emits it.  Therefore, attempt to find the
825 	 * symbol with the "_" prepend.
826 	 */
827 	sl = *slp;
828 	sl.sl_name = (const char *)buffer;
829 
830 	return (dlsym_handle(ghp, &sl, _lmp, binfo, in_nfavl));
831 }
832