usr/src/cmd/sgs/rtld/sparc/sparc_a.out.c
author Rod Evans <Rod.Evans@Sun.COM>
Wed, 19 May 2010 22:33:49 -0700
changeset 12449 a87750d92895
parent 11827 d7ef53deac3f
permissions -rw-r--r--
6943772 Testing for a symbols existence with RTLD_PROBE is compromised by RTLD_BIND_NOW PSARC/2010/175 Deferred symbol references 6943432 dlsym(RTLD_PROBE) should only bind to symbol definitions 6668759 an external method for determining whether an ELF dependency is optional

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 *	Copyright (c) 1988 AT&T
 *	All Rights Reserved
 *
 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * SPARC machine dependent and a.out format file class dependent functions.
 * Contains routines for performing function binding and symbol relocations.
 */

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/mman.h>
#include	<synch.h>
#include	<dlfcn.h>
#include	<debug.h>
#include	"_a.out.h"
#include	"_rtld.h"
#include	"_audit.h"
#include	"_inline_gen.h"
#include	"msg.h"

extern void	iflush_range(caddr_t, size_t);

/*
 * Function binding routine - invoked on the first call to a function through
 * the procedure linkage table;
 * passes first through an assembly language interface.
 *
 * Takes the address of the PLT entry where the call originated,
 * the offset into the relocation table of the associated
 * relocation entry and the address of the link map (rt_private_map struct)
 * for the entry.
 *
 * Returns the address of the function referenced after re-writing the PLT
 * entry to invoke the function directly.
 *
 * On error, causes process to terminate with a signal.
 */
ulong_t
aout_bndr(caddr_t pc)
{
	Rt_map		*lmp, *nlmp, *llmp;
	struct relocation_info *rp;
	struct nlist	*sp;
	Sym		*sym;
	char		*name;
	int 		rndx, entry;
	ulong_t		symval;
	Slookup		sl;
	Sresult		sr;
	uint_t		binfo;
	Lm_list		*lml;

	/*
	 * For compatibility with libthread (TI_VERSION 1) we track the entry
	 * value.  A zero value indicates we have recursed into ld.so.1 to
	 * further process a locking request (see comments in completion()).
	 * Under this recursion we disable tsort and cleanup activities.
	 */
	entry = enter(0);

	for (lmp = lml_main.lm_head; lmp; lmp = NEXT_RT_MAP(lmp)) {
		if (THIS_IS_AOUT(lmp)) {
			if (pc > (caddr_t)(LM2LP(lmp)->lp_plt) &&
			    pc < (caddr_t)((int)LM2LP(lmp)->lp_plt +
			    AOUTDYN(lmp)->v2->ld_plt_sz))  {
				break;
			}
		}
	}

#define	LAST22BITS	0x3fffff

	/* LINTED */
	rndx = *(int *)(pc + (sizeof (ulong_t *) * 2)) & LAST22BITS;
	rp = &LM2LP(lmp)->lp_rp[rndx];
	sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
	name = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];

	/*
	 * Determine the last link-map of this list, this'll be the starting
	 * point for any tsort() processing.
	 */
	lml = LIST(lmp);
	llmp = lml->lm_tail;

	/*
	 * Find definition for symbol.  Initialize the symbol lookup data
	 * structure.
	 */
	SLOOKUP_INIT(sl, name, lmp, lml->lm_head, ld_entry_cnt, 0, 0, 0, 0,
	    LKUP_DEFT);
	SRESULT_INIT(sr, name);

	if (aout_lookup_sym(&sl, &sr, &binfo, NULL) == 0) {
		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
		    demangle(name));
		rtldexit(lml, 1);
	}

	name = (char *)sr.sr_name;
	nlmp = sr.sr_dmap;
	sym = sr.sr_sym;

	symval = sym->st_value;

	if (!(FLAGS(nlmp) & FLG_RT_FIXED) &&
	    (sym->st_shndx != SHN_ABS))
		symval += (int)(ADDR(nlmp));
	if ((lmp != nlmp) && ((FLAGS1(nlmp) & FL1_RT_NOINIFIN) == 0)) {
		/*
		 * Record that this new link map is now bound to the caller.
		 */
		if (bind_one(lmp, nlmp, BND_REFER) == 0)
			rtldexit(lml, 1);
	}

	/*
	 * Print binding information and rebuild PLT entry.
	 */
	DBG_CALL(Dbg_bind_global(lmp, (Addr)(ADDR(lmp) + rp->r_address),
	    (Off)rp->r_address, (Xword)(-1), PLT_T_NONE, nlmp,
	    (Addr)symval, sym->st_value, name, binfo));

	if (!(rtld_flags & RT_FL_NOBIND))
		aout_plt_write((caddr_t)(ADDR(lmp) + rp->r_address), symval);

	/*
	 * Complete any processing for newly loaded objects.  Note we don't
	 * know exactly where any new objects are loaded (we know the object
	 * that supplied the symbol, but others may have been loaded lazily as
	 * we searched for the symbol), so sorting starts from the last
	 * link-map know on entry to this routine.
	 */
	if (entry)
		load_completion(llmp);

	/*
	 * Make sure the object to which we've bound has had it's .init fired.
	 * Cleanup before return to user code.
	 */
	if (entry) {
		is_dep_init(nlmp, lmp);
		leave(lml, 0);
	}

	return (symval);
}


#define	IS_PC_RELATIVE(X) (pc_rel_type[(X)] == 1)

static const uchar_t pc_rel_type[] = {
	0,				/* RELOC_8 */
	0,				/* RELOC_16 */
	0,				/* RELOC_32 */
	1,				/* RELOC_DISP8 */
	1,				/* RELOC_DISP16 */
	1,				/* RELOC_DISP32 */
	1,				/* RELOC_WDISP30 */
	1,				/* RELOC_WDISP22 */
	0,				/* RELOC_HI22 */
	0,				/* RELOC_22 */
	0,				/* RELOC_13 */
	0,				/* RELOC_LO10 */
	0,				/* RELOC_SFA_BASE */
	0,				/* RELOC_SFA_OFF13 */
	0,				/* RELOC_BASE10 */
	0,				/* RELOC_BASE13 */
	0,				/* RELOC_BASE22 */
	0,				/* RELOC_PC10 */
	0,				/* RELOC_PC22 */
	0,				/* RELOC_JMP_TBL */
	0,				/* RELOC_SEGOFF16 */
	0,				/* RELOC_GLOB_DAT */
	0,				/* RELOC_JMP_SLOT */
	0				/* RELOC_RELATIVE */
};

int
aout_reloc(Rt_map *lmp, uint_t plt, int *in_nfavl, APlist **textrel)
{
	int		k;		/* loop temporary */
	int		nr;		/* number of relocations */
	char		*name;		/* symbol being searched for */
	long		value;		/* relocation temporary */
	long		*ra;		/* cached relocation address */
	struct relocation_info *rp;	/* current relocation */
	struct nlist	*sp;		/* symbol table of "symbol" */
	Rt_map *	_lmp;		/* lm which holds symbol definition */
	Sym *		sym;		/* symbol definition */
	int		ret = 1;
	APlist		*bound = NULL;
	Lm_list		*lml = LIST(lmp);

	DBG_CALL(Dbg_reloc_run(lmp, SHT_RELA, plt, DBG_REL_START));

	/*
	 * If we've been called upon to promote an RTLD_LAZY object to an
	 * RTLD_NOW don't bother to do anything - a.out's are bound as if
	 * RTLD_NOW regardless.
	 */
	if (plt)
		return (1);

	rp = LM2LP(lmp)->lp_rp;
	nr = GETRELSZ(AOUTDYN(lmp)) / sizeof (struct relocation_info);

	/*
	 * Initialize _PLT_, if any.
	 */
	if (AOUTDYN(lmp)->v2->ld_plt_sz)
		aout_plt_write((caddr_t)LM2LP(lmp)->lp_plt->jb_inst,
		    (ulong_t)aout_rtbndr);

	/*
	 * Loop through relocations.
	 */
	for (k = 0; k < nr; k++, rp++) {
		mmapobj_result_t	*mpp;

		/* LINTED */
		ra = (long *)&((char *)ADDR(lmp))[rp->r_address];

		/*
		 * Make sure the segment is writable.
		 */
		if (((mpp = find_segment((caddr_t)ra, lmp)) != NULL) &&
		    ((mpp->mr_prot & PROT_WRITE) == 0)) {
			if ((set_prot(lmp, mpp, 1) == 0) ||
			    (aplist_append(textrel, mpp,
			    AL_CNT_TEXTREL) == NULL)) {
				ret = 0;
				break;
			}
		}

		/*
		 * Perform the relocation.
		 */
		if (rp->r_extern == 0) {
			name = NULL;
			value = ADDR(lmp);
		} else {
			Slookup		sl;
			Sresult		sr;
			uint_t		binfo;

			if (rp->r_type == RELOC_JMP_SLOT)
				continue;
			sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
			name = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];

			/*
			 * Locate symbol.  Initialize the symbol lookup data
			 * structure.
			 */
			SLOOKUP_INIT(sl, name, lmp, 0, ld_entry_cnt,
			    0, 0, 0, 0, LKUP_STDRELOC);
			SRESULT_INIT(sr, name);

			if (aout_lookup_sym(&sl, &sr, &binfo, in_nfavl) == 0) {
				if (lml->lm_flags & LML_FLG_TRC_WARN) {
					(void)
					    printf(MSG_INTL(MSG_LDD_SYM_NFOUND),
					    demangle(name), NAME(lmp));
					continue;
				} else {
					eprintf(lml, ERR_FATAL,
					    MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
					    demangle(name));
					ret = 0;
					break;
				}
			}

			/*
			 * If symbol was found in an object other than the
			 * referencing object then record the binding.
			 */
			name = (char *)sr.sr_name;
			_lmp = sr.sr_dmap;
			sym = sr.sr_sym;

			if ((lmp != _lmp) &&
			    ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) {
				if (aplist_test(&bound, _lmp,
				    AL_CNT_RELBIND) == 0) {
					ret = 0;
					break;
				}
			}

			value = sym->st_value + rp->r_addend;
			if (!(FLAGS(_lmp) & FLG_RT_FIXED) &&
			    (sym->st_shndx != SHN_COMMON) &&
			    (sym->st_shndx != SHN_ABS))
				value += ADDR(_lmp);

			if (IS_PC_RELATIVE(rp->r_type))
				value -= (long)ADDR(lmp);

			DBG_CALL(Dbg_bind_global(lmp, (Addr)ra,
			    (Off)(ra - ADDR(lmp)), (Xword)(-1), PLT_T_NONE,
			    _lmp, (Addr)value, sym->st_value, name, binfo));
		}

		/*
		 * Perform a specific relocation operation.
		 */
		switch (rp->r_type) {
		case RELOC_RELATIVE:
			value += *ra << (32-22);
			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
			    ((value >> (32 - 22)) & S_MASK(22));
			ra++;
			value += (*ra & S_MASK(10));
			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
			    (value & S_MASK(10));
			break;
		case RELOC_8:
		case RELOC_DISP8:
			value += *ra & S_MASK(8);
			if (!S_INRANGE(value, 8)) {
				eprintf(lml, ERR_FATAL,
				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
				    (name ? demangle(name) :
				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 8,
				    (uint_t)ra);
			}
			*ra = value;
			break;
		case RELOC_LO10:
		case RELOC_BASE10:
			value += *ra & S_MASK(10);
			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
			    (value & S_MASK(10));
			break;
		case RELOC_BASE13:
		case RELOC_13:
			value += *ra & S_MASK(13);
			*(long *)ra = (*(long *)ra & ~S_MASK(13)) |
			    (value & S_MASK(13));
			break;
		case RELOC_16:
		case RELOC_DISP16:
			value += *ra & S_MASK(16);
			if (!S_INRANGE(value, 16)) {
				eprintf(lml, ERR_FATAL,
				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
				    (name ? demangle(name) :
				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 16,
				    (uint_t)ra);
			}
			*(short *)ra = value;
			break;
		case RELOC_22:
		case RELOC_BASE22:
			value += *ra & S_MASK(22);
			if (!S_INRANGE(value, 22)) {
				eprintf(lml, ERR_FATAL,
				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
				    (name ? demangle(name) :
				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
				    (uint_t)ra);
			}
			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
			    (value & S_MASK(22));
			break;
		case RELOC_HI22:
			value += (*ra & S_MASK(22)) << (32 - 22);
			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
			    ((value >> (32 - 22)) & S_MASK(22));
			break;
		case RELOC_WDISP22:
			value += *ra & S_MASK(22);
			value >>= 2;
			if (!S_INRANGE(value, 22)) {
				eprintf(lml, ERR_FATAL,
				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
				    (name ? demangle(name) :
				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
				    (uint_t)ra);
			}
			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
			    (value & S_MASK(22));
			break;
		case RELOC_WDISP30:
			value += *ra & S_MASK(30);
			value >>= 2;
			*(long *)ra = (*(long *)ra & ~S_MASK(30)) |
			    (value & S_MASK(30));
			break;
		case RELOC_32:
		case RELOC_GLOB_DAT:
		case RELOC_DISP32:
			value += *ra;
			*(long *)ra = value;
			break;
		default:
			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_UNIMPL),
			    NAME(lmp), (name ? demangle(name) :
			    MSG_INTL(MSG_STR_UNKNOWN)), rp->r_type);
			ret = 0;
			break;
		}

		/*
		 * If this relocation is against a text segment we must make
		 * sure that the instruction cache is flushed.
		 */
		if (textrel) {
			if (rp->r_type == RELOC_RELATIVE)
				iflush_range((caddr_t)(ra - 1), 0x8);
			else
				iflush_range((caddr_t)ra, 0x4);
		}
	}

	return (relocate_finish(lmp, bound, ret));
}