Home | History | Annotate | Download | only in common
      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 2009 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 Sym	*aout_dlsym_handle(Grp_hdl *, Slookup *, Rt_map **, uint_t *,
     73 		    int *l);
     74 static Addr	aout_entry_point(void);
     75 static Sym	*aout_find_sym(Slookup *, Rt_map **, 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 extern Sym	*aout_lookup_sym(Slookup *, Rt_map **, uint_t *, int *);
     81 static int	aout_needed(Lm_list *, Aliste, Rt_map *, int *);
     82 
     83 /*
     84  * Functions and data accessed through indirect pointers.
     85  */
     86 Fct aout_fct = {
     87 	aout_verify,
     88 	aout_new_lmp,
     89 	aout_entry_point,
     90 	aout_needed,
     91 	aout_lookup_sym,
     92 	aout_reloc,
     93 	aout_get_def_dirs,
     94 	aout_get_sec_dirs,
     95 	aout_fix_name,
     96 	aout_get_so,
     97 	aout_dladdr,
     98 	aout_dlsym_handle
     99 };
    100 
    101 /*
    102  * Default and secure dependency search paths.
    103  */
    104 static Alist **
    105 aout_get_def_dirs()
    106 {
    107 	if (aout_def_dirs == NULL)
    108 		set_dirs(&aout_def_dirs, _aout_def_dirs, LA_SER_DEFAULT);
    109 	return (&aout_def_dirs);
    110 }
    111 
    112 static Alist **
    113 aout_get_sec_dirs()
    114 {
    115 	if (aout_sec_dirs == NULL)
    116 		set_dirs(&aout_sec_dirs, _aout_sec_dirs, LA_SER_SECURE);
    117 	return (&aout_sec_dirs);
    118 }
    119 
    120 /*
    121  * In 4.x, a needed file or a dlopened file that was a simple file name implied
    122  * that the file be found in the present working directory.  To simulate this
    123  * lookup within the ELF rules it is necessary to add a preceding `./' to the
    124  * filename.
    125  */
    126 /* ARGSUSED4 */
    127 static int
    128 aout_fix_name(const char *oname, Rt_map *clmp, Alist **alpp, Aliste alni,
    129     uint_t orig)
    130 {
    131 	size_t		len;
    132 	Pdesc		*pdp;
    133 	const char	*nname;
    134 
    135 	/*
    136 	 * Check for slash in name, if none, prepend "./", otherwise just
    137 	 * return name given.
    138 	 */
    139 	if (strchr(oname, '/')) {
    140 		len = strlen(oname) + 1;
    141 		if ((nname = stravl_insert(oname, 0, len, 0)) == NULL)
    142 			return (0);
    143 	} else {
    144 		char	buffer[PATH_MAX];
    145 
    146 		len = strlen(oname) + 3;
    147 		(void) snprintf(buffer, len, MSG_ORIG(MSG_FMT_4XPATH), oname);
    148 		if ((nname = stravl_insert(buffer, 0, len, 0)) == NULL)
    149 			return (0);
    150 	}
    151 
    152 	if ((pdp = alist_append(alpp, NULL, sizeof (Pdesc), alni)) == NULL)
    153 		return (0);
    154 
    155 	pdp->pd_pname = nname;
    156 	pdp->pd_plen = len;
    157 	pdp->pd_flags = PD_FLG_PNSLASH;
    158 
    159 	DBG_CALL(Dbg_file_fixname(LIST(clmp), nname, oname));
    160 	return (1);
    161 }
    162 
    163 /*
    164  * Determine if we have been given an A_OUT file.  Returns 1 if true.
    165  */
    166 Fct *
    167 /* ARGSUSED1 */
    168 aout_verify(caddr_t addr, size_t size, Fdesc *fdp, const char *name,
    169     Rej_desc *rej)
    170 {
    171 	/* LINTED */
    172 	struct exec *exec = (struct exec *)addr;
    173 
    174 	if (size < sizeof (exec) || (exec->a_machtype != M_SPARC) ||
    175 	    (N_BADMAG(*exec))) {
    176 		return (NULL);
    177 	}
    178 	return (&aout_fct);
    179 }
    180 
    181 /*
    182  * Return the entry point of the A_OUT executable.  Although the entry point
    183  * within an ELF file is flexible, the entry point of an A_OUT executable is
    184  * always zero.
    185  */
    186 static Addr
    187 aout_entry_point()
    188 {
    189 	return (0);
    190 }
    191 
    192 /*
    193  * Search through the dynamic section for DT_NEEDED entries and perform one
    194  * of two functions.  If only the first argument is specified then load the
    195  * defined shared object, otherwise add the link map representing the
    196  * defined link map the the dlopen list.
    197  */
    198 static int
    199 aout_needed(Lm_list *lml, Aliste lmco, Rt_map *clmp, int *in_nfavl)
    200 {
    201 	Alist	*palp = NULL;
    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 
    210 		name = &TEXTBASE(clmp)[((Lnk_obj *)(need))->lo_name];
    211 
    212 		if (((Lnk_obj *)(need))->lo_library) {
    213 			/*
    214 			 * If lo_library field is not NULL then this needed
    215 			 * library was linked in using the "-l" option.
    216 			 * Thus we need to rebuild the library name before
    217 			 * trying to load it.
    218 			 */
    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)) == NULL)
    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 				Spath_desc	sd = { search_rules, NULL, 0 };
    248 				Pdesc		*pdp;
    249 				char		*path = NULL;
    250 
    251 				for (pdp = get_next_dir(&sd, clmp, 0); pdp;
    252 				    pdp = get_next_dir(&sd, clmp, 0)) {
    253 					if (pdp->pd_pname == 0)
    254 						continue;
    255 
    256 					if (path = aout_get_so(pdp->pd_pname,
    257 					    file, 0, 0))
    258 						break;
    259 				}
    260 				if (path == NULL) {
    261 					eprintf(lml, ERR_FATAL,
    262 					    MSG_INTL(MSG_SYS_OPEN), file,
    263 					    strerror(ENOENT));
    264 					return (0);
    265 				}
    266 				name = path;
    267 			}
    268 			if (expand_paths(clmp, name, &palp,
    269 			    AL_CNT_NEEDED, 0, 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 (aout_fix_name(name, clmp, &palp,
    278 			    AL_CNT_NEEDED, 0) == 0)
    279 				return (0);
    280 		}
    281 
    282 		DBG_CALL(Dbg_file_needed(clmp, name));
    283 
    284 		nlmp = load_one(lml, lmco, palp, clmp, MODE(clmp), 0, 0,
    285 		    in_nfavl);
    286 		remove_plist(&palp, 1);
    287 		if (((nlmp == 0) || (bind_one(clmp, nlmp, BND_NEEDED) == 0)) &&
    288 		    ((lml->lm_flags & LML_FLG_TRC_ENABLE) == 0))
    289 			return (0);
    290 	}
    291 
    292 	return (1);
    293 }
    294 
    295 static Sym *
    296 aout_symconvert(struct nlist *sp)
    297 {
    298 	static Sym	sym;
    299 
    300 	sym.st_value = sp->n_value;
    301 	sym.st_size = 0;
    302 	sym.st_info = 0;
    303 	sym.st_other = 0;
    304 	switch (sp->n_type) {
    305 		case N_EXT + N_ABS:
    306 			sym.st_shndx = SHN_ABS;
    307 			break;
    308 		case N_COMM:
    309 			sym.st_shndx = SHN_COMMON;
    310 			break;
    311 		case N_EXT + N_UNDF:
    312 			sym.st_shndx = SHN_UNDEF;
    313 			break;
    314 		default:
    315 			sym.st_shndx = 0;
    316 			break;
    317 	}
    318 	return (&sym);
    319 }
    320 
    321 /*
    322  * Process a.out format commons.
    323  */
    324 static struct nlist *
    325 aout_find_com(struct nlist *sp, const char *name)
    326 {
    327 	static struct rtc_symb	*rtcp = 0;
    328 	struct rtc_symb		*rs, *trs;
    329 	const char		*sl;
    330 	char			*cp;
    331 
    332 	/*
    333 	 * See if common is already allocated.
    334 	 */
    335 	trs = rtcp;
    336 	while (trs) {
    337 		sl = name;
    338 		cp = trs->rtc_sp->n_un.n_name;
    339 		while (*sl == *cp++)
    340 			if (*sl++ == '\0')
    341 				return (trs->rtc_sp);
    342 		trs = trs->rtc_next;
    343 	}
    344 
    345 	/*
    346 	 * If we got here, common is not already allocated so allocate it.
    347 	 */
    348 	if ((rs = malloc(sizeof (struct rtc_symb))) == NULL)
    349 		return (NULL);
    350 	if ((rs->rtc_sp = malloc(sizeof (struct nlist))) == NULL)
    351 		return (NULL);
    352 	trs = rtcp;
    353 	rtcp = rs;
    354 	rs->rtc_next = trs;
    355 	*(rs->rtc_sp) = *sp;
    356 	if ((rs->rtc_sp->n_un.n_name = malloc(strlen(name) + 1)) == NULL)
    357 		return (NULL);
    358 	(void) strcpy(rs->rtc_sp->n_un.n_name, name);
    359 	rs->rtc_sp->n_type = N_COMM;
    360 	if ((rs->rtc_sp->n_value =
    361 	    (long)calloc(rs->rtc_sp->n_value, 1)) == NULL)
    362 		return (NULL);
    363 	return (rs->rtc_sp);
    364 }
    365 
    366 /*
    367  * Find a.out format symbol in the specified link map.  Unlike the sister
    368  * elf routine we re-calculate the symbols hash value for each link map
    369  * we're looking at.
    370  */
    371 static struct nlist *
    372 aout_findsb(const char *aname, Rt_map *lmp, int flag)
    373 {
    374 	const char	*name = aname;
    375 	char		*cp;
    376 	struct fshash	*p;
    377 	int		i;
    378 	struct nlist	*sp;
    379 	ulong_t		hval = 0;
    380 
    381 #define	HASHMASK	0x7fffffff
    382 #define	RTHS		126
    383 
    384 	/*
    385 	 * The name passed to us is in ELF format, thus it is necessary to
    386 	 * map this back to the A_OUT format to compute the hash value (see
    387 	 * mapping rules in aout_lookup_sym()).  Basically the symbols are
    388 	 * mapped according to whether a leading `.' exists.
    389 	 *
    390 	 *	elf symbol		a.out symbol
    391 	 * i.	   .bar		->	   .bar		(LKUP_LDOT)
    392 	 * ii.	   .nuts	->	    nuts
    393 	 * iii.	    foo		->	   _foo
    394 	 */
    395 	if (*name == '.') {
    396 		if (!(flag & LKUP_LDOT))
    397 			name++;
    398 	} else
    399 		hval = '_';
    400 
    401 	while (*name)
    402 		hval = (hval << 1) + *name++;
    403 	hval = hval & HASHMASK;
    404 
    405 	i = hval % (AOUTDYN(lmp)->v2->ld_buckets == 0 ? RTHS :
    406 	    AOUTDYN(lmp)->v2->ld_buckets);
    407 	p = LM2LP(lmp)->lp_hash + i;
    408 
    409 	if (p->fssymbno != -1) {
    410 		do {
    411 			sp = &LM2LP(lmp)->lp_symtab[p->fssymbno];
    412 			cp = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
    413 			name = aname;
    414 			if (*name == '.') {
    415 				if (!(flag & LKUP_LDOT))
    416 					name++;
    417 			} else {
    418 				cp++;
    419 			}
    420 			while (*name == *cp++) {
    421 				if (*name++ == '\0')
    422 					return (sp);	/* found */
    423 			}
    424 			if (p->next == 0)
    425 				return (NULL);		/* not found */
    426 			else
    427 				continue;
    428 		} while ((p = &LM2LP(lmp)->lp_hash[p->next]) != 0);
    429 	}
    430 	return (NULL);
    431 }
    432 
    433 /*
    434  * The symbol name we have been asked to look up is in A_OUT format, this
    435  * symbol is mapped to the appropriate ELF format which is the standard by
    436  * which symbols are passed around ld.so.1.  The symbols are mapped
    437  * according to whether a leading `_' or `.' exists.
    438  *
    439  *	a.out symbol		elf symbol
    440  * i.	   _foo		->	    foo
    441  * ii.	   .bar		->	   .bar		(LKUP_LDOT)
    442  * iii.	    nuts	->	   .nuts
    443  */
    444 Sym *
    445 aout_lookup_sym(Slookup *slp, Rt_map **dlmp, uint_t *binfo, int *in_nfavl)
    446 {
    447 	char	name[PATH_MAX];
    448 	Slookup	sl = *slp;
    449 
    450 	DBG_CALL(Dbg_syms_lookup_aout(LIST(slp->sl_cmap), slp->sl_name));
    451 
    452 	if (*sl.sl_name == '_')
    453 		++sl.sl_name;
    454 	else if (*sl.sl_name == '.')
    455 		sl.sl_flags |= LKUP_LDOT;
    456 	else {
    457 		name[0] = '.';
    458 		(void) strcpy(&name[1], sl.sl_name);
    459 		sl.sl_name = name;
    460 	}
    461 
    462 	/*
    463 	 * Call the generic lookup routine to cycle through the specified
    464 	 * link maps.
    465 	 */
    466 	return (lookup_sym(&sl, dlmp, binfo, in_nfavl));
    467 }
    468 
    469 /*
    470  * Symbol lookup for an a.out format module.
    471  */
    472 /* ARGSUSED3 */
    473 static Sym *
    474 aout_find_sym(Slookup *slp, Rt_map **dlmp, uint_t *binfo, int *in_nfavl)
    475 {
    476 	const char	*name = slp->sl_name;
    477 	Rt_map		*ilmp = slp->sl_imap;
    478 	struct nlist	*sp;
    479 
    480 	DBG_CALL(Dbg_syms_lookup(ilmp, name, MSG_ORIG(MSG_STR_AOUT)));
    481 
    482 	if (sp = aout_findsb(name, ilmp, slp->sl_flags)) {
    483 		if (sp->n_value != 0) {
    484 			/*
    485 			 * is it a common?
    486 			 */
    487 			if (sp->n_type == (N_EXT + N_UNDF)) {
    488 				if ((sp = aout_find_com(sp, name)) == 0)
    489 					return (NULL);
    490 			}
    491 			*dlmp = ilmp;
    492 			*binfo |= DBG_BINFO_FOUND;
    493 			return (aout_symconvert(sp));
    494 		}
    495 	}
    496 	return (NULL);
    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 Sym *
    681 aout_dlsym_handle(Grp_hdl *ghp, Slookup *slp, Rt_map **_lmp, uint_t *binfo,
    682     int *in_nfavl)
    683 {
    684 	Sym	*sym;
    685 	char	buffer[PATH_MAX];
    686 	Slookup	sl;
    687 
    688 	buffer[0] = '_';
    689 	(void) strcpy(&buffer[1], slp->sl_name);
    690 
    691 	if ((sym = dlsym_handle(ghp, slp, _lmp, binfo, in_nfavl)) != 0)
    692 		return (sym);
    693 
    694 	/*
    695 	 * Symbol not found as supplied.  However, most of our symbols will
    696 	 * be in the "C" name space, where the implementation prepends a "_"
    697 	 * to the symbol as it emits it.  Therefore, attempt to find the
    698 	 * symbol with the "_" prepend.
    699 	 */
    700 	sl = *slp;
    701 	sl.sl_name = (const char *)buffer;
    702 
    703 	return (dlsym_handle(ghp, &sl, _lmp, binfo, in_nfavl));
    704 }
    705 
    706 /*
    707  * The initial mapping of the a.out occurs through exec(2), and presently this
    708  * implementation doesn't provide a mmapobj_result_t array to ld.so.1.  Thus,
    709  * aout_get_mmap() is called to create the mapping information.  Unlike ELF,
    710  * the information that can be gathered from a mapped AOUT file, can be limited.
    711  * In some cases the AOUT header isn't available in the mapped image, and thus
    712  * this can't be inspected to determine the files size (the kernel always
    713  * returns a pointer to the AOUT dynamic structure, but this is only sufficient
    714  * to determine the size of the text segment).
    715  *
    716  * Therefore, the only generic mechanism of determining the AOUT's mapping is
    717  * to use /proc.  Only two mappings are required, the text (to determine any
    718  * read-only region), and the data.  The two mapping validate the range in
    719  * which any relocations will occur.  Should there be an additional bss segment,
    720  * we don't care, as this can't be relocated, and we're never going to try
    721  * unmapping the a.out.
    722  */
    723 int
    724 aout_get_mmap(Lm_list *lml, mmapobj_result_t *mpp)
    725 {
    726 	prmap_t	*maps;
    727 	char	proc[16];
    728 	int	num, err, fd;
    729 
    730 	(void) snprintf(proc, 16, MSG_ORIG(MSG_FMT_PROC), (int)getpid());
    731 	if ((fd = open(proc, O_RDONLY)) == -1) {
    732 		err = errno;
    733 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), proc,
    734 		    strerror(err));
    735 		return (1);
    736 	}
    737 
    738 	if (ioctl(fd, PIOCNMAP, (void *)&num) == -1) {
    739 		err = errno;
    740 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_PROC), strerror(err));
    741 		return (1);
    742 	}
    743 
    744 	if ((maps = malloc((num + 1) * sizeof (prmap_t))) == NULL)
    745 		return (1);
    746 
    747 	if (ioctl(fd, PIOCMAP, (void *)maps) == -1) {
    748 		err = errno;
    749 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_PROC), strerror(err));
    750 		free(maps);
    751 		return (1);
    752 	}
    753 
    754 	mpp->mr_addr = maps->pr_vaddr;
    755 	mpp->mr_fsize = mpp->mr_msize = maps->pr_size;
    756 	mpp->mr_prot = (PROT_READ | PROT_EXEC);
    757 
    758 	mpp++, maps++;
    759 
    760 	mpp->mr_addr = maps->pr_vaddr;
    761 	mpp->mr_fsize = mpp->mr_msize = maps->pr_size;
    762 	mpp->mr_prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
    763 
    764 	maps--;
    765 	free(maps);
    766 	return (0);
    767 }
    768