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.
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.
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]
23 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Object file dependent support for a.out format objects.
30 #include <a.out.h> /* Explicitly override M_SEGSIZE */
31 #include <machdep.h> /* used in M_SROUND */
33 #include <sys/types.h>
34 #include <sys/procfs.h>
45 #include "cache_a.out.h"
50 * Default and secure dependency search paths.
52 static Spath_defn _aout_def_dirs
[] = {
53 { MSG_ORIG(MSG_PTH_USR4LIB
), MSG_PTH_USR4LIB_SIZE
},
54 { MSG_ORIG(MSG_PTH_USRLIB
), MSG_PTH_USRLIB_SIZE
},
55 { MSG_ORIG(MSG_PTH_USRLCLIB
), MSG_PTH_USRLCLIB_SIZE
},
59 static Spath_defn _aout_sec_dirs
[] = {
60 { MSG_ORIG(MSG_PTH_LIBSE
), MSG_PTH_LIBSE_SIZE
},
64 Alist
*aout_def_dirs
= NULL
;
65 Alist
*aout_sec_dirs
= NULL
;
68 * Defines for local functions.
70 static void aout_dladdr(ulong_t
, Rt_map
*, Dl_info
*, void **, int);
71 static int aout_dlsym_handle(Grp_hdl
*, Slookup
*, Sresult
*, uint_t
*,
73 static Addr
aout_entry_point(void);
74 static int aout_find_sym(Slookup
*, Sresult
*, uint_t
*, int *);
75 static int aout_fix_name(const char *, Rt_map
*, Alist
**, Aliste
, uint_t
);
76 static Alist
**aout_get_def_dirs(void);
77 static Alist
**aout_get_sec_dirs(void);
78 static char *aout_get_so(const char *, const char *, size_t, size_t);
79 static int aout_needed(Lm_list
*, Aliste
, Rt_map
*, int *);
82 * Functions and data accessed through indirect pointers.
100 * Default and secure dependency search paths.
105 if (aout_def_dirs
== NULL
)
106 set_dirs(&aout_def_dirs
, _aout_def_dirs
, LA_SER_DEFAULT
);
107 return (&aout_def_dirs
);
113 if (aout_sec_dirs
== NULL
)
114 set_dirs(&aout_sec_dirs
, _aout_sec_dirs
, LA_SER_SECURE
);
115 return (&aout_sec_dirs
);
119 * In 4.x, a needed file or a dlopened file that was a simple file name implied
120 * that the file be found in the present working directory. To simulate this
121 * lookup within the ELF rules it is necessary to add a preceding `./' to the
126 aout_fix_name(const char *oname
, Rt_map
*clmp
, Alist
**alpp
, Aliste alni
,
134 * Check for slash in name, if none, prepend "./", otherwise just
137 if (strchr(oname
, '/')) {
138 len
= strlen(oname
) + 1;
139 if ((nname
= stravl_insert(oname
, 0, len
, 0)) == NULL
)
142 char buffer
[PATH_MAX
];
144 len
= strlen(oname
) + 3;
145 (void) snprintf(buffer
, len
, MSG_ORIG(MSG_FMT_4XPATH
), oname
);
146 if ((nname
= stravl_insert(buffer
, 0, len
, 0)) == NULL
)
150 if ((pdp
= alist_append(alpp
, NULL
, sizeof (Pdesc
), alni
)) == NULL
)
153 pdp
->pd_pname
= nname
;
155 pdp
->pd_flags
= PD_FLG_PNSLASH
;
157 DBG_CALL(Dbg_file_fixname(LIST(clmp
), nname
, oname
));
162 * Determine if we have been given an A_OUT file. Returns 1 if true.
166 aout_verify(caddr_t addr
, size_t size
, Fdesc
*fdp
, const char *name
,
170 struct exec
*exec
= (struct exec
*)addr
;
172 if (size
< sizeof (exec
) || (exec
->a_machtype
!= M_SPARC
) ||
180 * Return the entry point of the A_OUT executable. Although the entry point
181 * within an ELF file is flexible, the entry point of an A_OUT executable is
191 * Search through the dynamic section for DT_NEEDED entries and perform one
192 * of two functions. If only the first argument is specified then load the
193 * defined shared object, otherwise add the link map representing the
194 * defined link map the the dlopen list.
197 aout_needed(Lm_list
*lml
, Aliste lmco
, Rt_map
*clmp
, int *in_nfavl
)
202 for (need
= &TEXTBASE(clmp
)[AOUTDYN(clmp
)->v2
->ld_need
];
203 need
!= &TEXTBASE(clmp
)[0];
204 need
= &TEXTBASE(clmp
)[((Lnk_obj
*)(need
))->lo_next
]) {
208 name
= &TEXTBASE(clmp
)[((Lnk_obj
*)(need
))->lo_name
];
210 if (((Lnk_obj
*)(need
))->lo_library
) {
212 * If lo_library field is not NULL then this needed
213 * library was linked in using the "-l" option.
214 * Thus we need to rebuild the library name before
221 * Allocate name length plus 20 for full library name.
222 * lib.so.. = 7 + (2 * short) + NULL = 7 + 12 + 1 = 20
224 len
= strlen(name
) + 20;
225 if ((file
= malloc(len
)) == NULL
)
227 (void) snprintf(file
, len
, MSG_ORIG(MSG_FMT_4XLIB
),
228 name
, ((Lnk_obj
*)(need
))->lo_major
,
229 ((Lnk_obj
*)(need
))->lo_minor
);
231 DBG_CALL(Dbg_libs_find(lml
, file
));
234 * We need to determine what filename will match the
235 * the filename specified (ie, a libc.so.1.2 may match
236 * to a libc.so.1.3). It's the real pathname that is
237 * recorded in the link maps. If we are presently
238 * being traced, skip this pathname generation so
239 * that we fall through into load_so() to print the
240 * appropriate diagnostics. I don't like this at all.
242 if (lml
->lm_flags
& LML_FLG_TRC_ENABLE
)
245 Spath_desc sd
= { search_rules
, NULL
, 0 };
249 for (pdp
= get_next_dir(&sd
, clmp
, 0); pdp
;
250 pdp
= get_next_dir(&sd
, clmp
, 0)) {
251 if (pdp
->pd_pname
== NULL
)
254 if (path
= aout_get_so(pdp
->pd_pname
,
259 eprintf(lml
, ERR_FATAL
,
260 MSG_INTL(MSG_SYS_OPEN
), file
,
266 if (expand_paths(clmp
, name
, &palp
,
267 AL_CNT_NEEDED
, 0, 0) == 0)
271 * If the library is specified as a pathname, see if
272 * it must be fixed to specify the current working
273 * directory (ie. libc.so.1.2 -> ./libc.so.1.2).
275 if (aout_fix_name(name
, clmp
, &palp
,
276 AL_CNT_NEEDED
, 0) == 0)
280 DBG_CALL(Dbg_file_needed(clmp
, name
));
282 nlmp
= load_one(lml
, lmco
, palp
, clmp
, MODE(clmp
), 0, 0,
284 remove_alist(&palp
, 1);
285 if (((nlmp
== NULL
) ||
286 (bind_one(clmp
, nlmp
, BND_NEEDED
) == 0)) &&
287 ((lml
->lm_flags
& LML_FLG_TRC_ENABLE
) == 0))
295 aout_symconvert(struct nlist
*sp
)
299 sym
.st_value
= sp
->n_value
;
303 switch (sp
->n_type
) {
305 sym
.st_shndx
= SHN_ABS
;
308 sym
.st_shndx
= SHN_COMMON
;
311 sym
.st_shndx
= SHN_UNDEF
;
321 * Process a.out format commons.
323 static struct nlist
*
324 aout_find_com(struct nlist
*sp
, const char *name
)
326 static struct rtc_symb
*rtcp
= NULL
;
327 struct rtc_symb
*rs
, *trs
;
332 * See if common is already allocated.
337 cp
= trs
->rtc_sp
->n_un
.n_name
;
340 return (trs
->rtc_sp
);
345 * If we got here, common is not already allocated so allocate it.
347 if ((rs
= malloc(sizeof (struct rtc_symb
))) == NULL
)
349 if ((rs
->rtc_sp
= malloc(sizeof (struct nlist
))) == NULL
)
355 if ((rs
->rtc_sp
->n_un
.n_name
= malloc(strlen(name
) + 1)) == NULL
)
357 (void) strcpy(rs
->rtc_sp
->n_un
.n_name
, name
);
358 rs
->rtc_sp
->n_type
= N_COMM
;
359 if ((rs
->rtc_sp
->n_value
=
360 (long)calloc(rs
->rtc_sp
->n_value
, 1)) == NULL
)
366 * Find a.out format symbol in the specified link map. Unlike the sister
367 * elf routine we re-calculate the symbols hash value for each link map
370 static struct nlist
*
371 aout_findsb(const char *aname
, Rt_map
*lmp
, int flag
)
373 const char *name
= aname
;
380 #define HASHMASK 0x7fffffff
384 * The name passed to us is in ELF format, thus it is necessary to
385 * map this back to the A_OUT format to compute the hash value (see
386 * mapping rules in aout_lookup_sym()). Basically the symbols are
387 * mapped according to whether a leading `.' exists.
389 * elf symbol a.out symbol
390 * i. .bar -> .bar (LKUP_LDOT)
395 if (!(flag
& LKUP_LDOT
))
401 hval
= (hval
<< 1) + *name
++;
402 hval
= hval
& HASHMASK
;
404 i
= hval
% (AOUTDYN(lmp
)->v2
->ld_buckets
== 0 ? RTHS
:
405 AOUTDYN(lmp
)->v2
->ld_buckets
);
406 p
= LM2LP(lmp
)->lp_hash
+ i
;
408 if (p
->fssymbno
!= -1) {
410 sp
= &LM2LP(lmp
)->lp_symtab
[p
->fssymbno
];
411 cp
= &LM2LP(lmp
)->lp_symstr
[sp
->n_un
.n_strx
];
414 if (!(flag
& LKUP_LDOT
))
419 while (*name
== *cp
++) {
421 return (sp
); /* found */
424 return (NULL
); /* not found */
427 } while ((p
= &LM2LP(lmp
)->lp_hash
[p
->next
]) != NULL
);
433 * The symbol name we have been asked to look up is in A_OUT format, this
434 * symbol is mapped to the appropriate ELF format which is the standard by
435 * which symbols are passed around ld.so.1. The symbols are mapped
436 * according to whether a leading `_' or `.' exists.
438 * a.out symbol elf symbol
440 * ii. .bar -> .bar (LKUP_LDOT)
444 aout_lookup_sym(Slookup
*slp
, Sresult
*srp
, uint_t
*binfo
, int *in_nfavl
)
449 DBG_CALL(Dbg_syms_lookup_aout(LIST(slp
->sl_cmap
), slp
->sl_name
));
451 if (*sl
.sl_name
== '_')
453 else if (*sl
.sl_name
== '.')
454 sl
.sl_flags
|= LKUP_LDOT
;
457 (void) strcpy(&name
[1], sl
.sl_name
);
462 * Call the generic lookup routine to cycle through the specified
465 return (lookup_sym(&sl
, srp
, binfo
, in_nfavl
));
469 * Symbol lookup for an a.out format module.
473 aout_find_sym(Slookup
*slp
, Sresult
*srp
, uint_t
*binfo
, int *in_nfavl
)
475 const char *name
= slp
->sl_name
;
476 Rt_map
*ilmp
= slp
->sl_imap
;
479 DBG_CALL(Dbg_syms_lookup(ilmp
, name
, MSG_ORIG(MSG_STR_AOUT
)));
481 if (sp
= aout_findsb(name
, ilmp
, slp
->sl_flags
)) {
482 if (sp
->n_value
!= 0) {
486 if (sp
->n_type
== (N_EXT
+ N_UNDF
)) {
487 if ((sp
= aout_find_com(sp
, name
)) == NULL
)
491 srp
->sr_sym
= aout_symconvert(sp
);
492 *binfo
|= DBG_BINFO_FOUND
;
500 * Create a new Rt_map structure for an a.out format object and
501 * initializes all values.
505 aout_new_lmp(Lm_list
*lml
, Aliste lmco
, Fdesc
*fdp
, Addr addr
, size_t msize
,
506 void *odyn
, Rt_map
*clmp
, int *in_nfavl
)
508 const char *name
= fdp
->fd_nname
;
510 caddr_t base
, caddr
= (caddr_t
)addr
;
511 Link_dynamic
*ld
= (Link_dynamic
*)odyn
;
512 size_t lmsz
, rtsz
, prsz
;
514 DBG_CALL(Dbg_file_aout(lml
, name
, addr
, msize
, lml
->lm_lmidstr
, lmco
));
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.
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
)
526 AOUTPRV(lmp
) = (void *)((uintptr_t)lmp
+ rtsz
);
527 ((Rt_aoutp
*)AOUTPRV(lmp
))->lm_lpd
=
528 (void *)((uintptr_t)lmp
+ rtsz
+ prsz
);
532 * All fields not filled in were set to 0 by calloc.
534 NAME(lmp
) = (char *)name
;
537 SYMINTP(lmp
) = aout_find_sym
;
538 FCT(lmp
) = &aout_fct
;
540 OBJFLTRNDX(lmp
) = FLTR_DISABLED
;
544 * Specific settings for a.out format.
546 if (lml
->lm_head
== NULL
) {
547 base
= (caddr_t
)MAIN_BASE
;
548 FLAGS(lmp
) |= FLG_RT_FIXED
;
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
560 struct exec
*exec
= (struct exec
*)caddr
;
564 nl
= (struct nlist
*)&caddr
[N_SYMOFF(*exec
)];
566 ld
= (Link_dynamic
*)&caddr
[nl
->n_value
];
568 ld
->v2
= (struct link_dynamic_2
*)((int)ld
->v2
+ (int)caddr
);
572 if ((RPATH(lmp
) = (char *)&base
[ld
->v2
->ld_rules
]) == base
)
574 LM2LP(lmp
)->lp_symbol_base
= caddr
;
576 LM2LP(lmp
)->lp_plt
= (struct jbind
*)(&caddr
[JMPOFF(ld
)]);
579 (struct relocation_info
*)(&base
[RELOCOFF(ld
)]);
581 LM2LP(lmp
)->lp_hash
= (struct fshash
*)(&base
[HASHOFF(ld
)]);
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
;
590 * Add the mapped object to the end of the link map list.
592 lm_append(lml
, lmco
, lmp
);
597 * Build full pathname of shared object from the given directory name and
602 aout_get_so(const char *dir
, const char *file
, size_t dlen
, size_t flen
)
607 if (dbp
= lo_cache(dir
)) {
608 path
= ask_db(dbp
, file
);
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().
619 aout_dladdr(ulong_t addr
, Rt_map
*lmp
, Dl_info
*dlip
, void **info
,
622 ulong_t ndx
, cnt
, base
, _value
;
623 struct nlist
*sym
, *_sym
;
625 cnt
= ((int)LM2LP(lmp
)->lp_symstr
- (int)LM2LP(lmp
)->lp_symtab
) /
626 sizeof (struct nlist
);
627 sym
= LM2LP(lmp
)->lp_symtab
;
629 if (FLAGS(lmp
) & FLG_RT_FIXED
)
634 for (_sym
= NULL
, _value
= 0, ndx
= 0; ndx
< cnt
; ndx
++, sym
++) {
637 if (sym
->n_type
== (N_EXT
+ N_UNDF
))
640 value
= sym
->n_value
+ base
;
654 int _flags
= flags
& RTLD_DL_MASK
;
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.
664 if (_flags
== RTLD_DL_SYMENT
)
666 else if (_flags
== RTLD_DL_LINKMAP
)
669 dlip
->dli_sname
= &LM2LP(lmp
)->lp_symstr
[_sym
->n_un
.n_strx
];
670 dlip
->dli_saddr
= (void *)_value
;
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).
681 aout_dlsym_handle(Grp_hdl
*ghp
, Slookup
*slp
, Sresult
*srp
, uint_t
*binfo
,
684 char buffer
[PATH_MAX
];
687 if (dlsym_handle(ghp
, slp
, srp
, binfo
, in_nfavl
))
691 * Symbol not found as supplied. However, most of our symbols will
692 * be in the "C" name space, where the implementation prepends a "_"
693 * to the symbol as it emits it. Therefore, attempt to find the
694 * symbol with the "_" prepend.
697 (void) strcpy(&buffer
[1], slp
->sl_name
);
700 sl
.sl_name
= (const char *)buffer
;
702 return (dlsym_handle(ghp
, &sl
, srp
, binfo
, in_nfavl
));
706 * The initial mapping of the a.out occurs through exec(2), and presently this
707 * implementation doesn't provide a mmapobj_result_t array to ld.so.1. Thus,
708 * aout_get_mmap() is called to create the mapping information. Unlike ELF,
709 * the information that can be gathered from a mapped AOUT file, can be limited.
710 * In some cases the AOUT header isn't available in the mapped image, and thus
711 * this can't be inspected to determine the files size (the kernel always
712 * returns a pointer to the AOUT dynamic structure, but this is only sufficient
713 * to determine the size of the text segment).
715 * Therefore, the only generic mechanism of determining the AOUT's mapping is
716 * to use /proc. Only two mappings are required, the text (to determine any
717 * read-only region), and the data. The two mapping validate the range in
718 * which any relocations will occur. Should there be an additional bss segment,
719 * we don't care, as this can't be relocated, and we're never going to try
720 * unmapping the a.out.
725 aout_get_mmap(Lm_list
*lml
, mmapobj_result_t
*mpp
)
731 (void) snprintf(proc
, PROCSIZE
, MSG_ORIG(MSG_FMT_PROC
),
733 if ((fd
= open(proc
, O_RDONLY
)) == -1) {
735 eprintf(lml
, ERR_FATAL
, MSG_INTL(MSG_SYS_OPEN
), proc
,
740 if (ioctl(fd
, PIOCNMAP
, (void *)&num
) == -1) {
742 eprintf(lml
, ERR_FATAL
, MSG_INTL(MSG_SYS_PROC
), strerror(err
));
746 if ((maps
= malloc((num
+ 1) * sizeof (prmap_t
))) == NULL
)
749 if (ioctl(fd
, PIOCMAP
, (void *)maps
) == -1) {
751 eprintf(lml
, ERR_FATAL
, MSG_INTL(MSG_SYS_PROC
), strerror(err
));
756 mpp
->mr_addr
= maps
->pr_vaddr
;
757 mpp
->mr_fsize
= mpp
->mr_msize
= maps
->pr_size
;
758 mpp
->mr_prot
= (PROT_READ
| PROT_EXEC
);
762 mpp
->mr_addr
= maps
->pr_vaddr
;
763 mpp
->mr_fsize
= mpp
->mr_msize
= maps
->pr_size
;
764 mpp
->mr_prot
= (PROT_READ
| PROT_WRITE
| PROT_EXEC
);