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