/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Symbol table resolution
 */
#define	ELF_TARGET_AMD64

#include	<stdio.h>
#include	<debug.h>
#include	"msg.h"
#include	"_libld.h"


/*
 * Categorize the symbol types that are applicable to the resolution process.
 */
typedef	enum {
	SYM_DEFINED,		/* Defined symbol (SHN_ABS or shndx != 0) */
	SYM_UNDEFINED,		/* Undefined symbol (SHN_UNDEF) */
	SYM_TENTATIVE,		/* Tentative symbol (SHN_COMMON) */
	SYM_NUM			/* the number of symbol types */
} Symtype;

/*
 * Do nothing.
 */
/* ARGSUSED0 */
static void
sym_null(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
}

static void
sym_visibility_diag(Error err, Sym_desc *sdp, Sym *osym, Sym *nsym,
    Ifl_desc *ifl, Ofl_desc *ofl)
{
	Conv_inv_buf_t	inv_obuf, inv_nbuf;

	eprintf(ofl->ofl_lml, err, MSG_INTL(MSG_SYM_CONFVIS),
	    demangle(sdp->sd_name));
	eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_VISTYPES),
	    sdp->sd_file->ifl_name, conv_sym_other(osym->st_other, &inv_obuf),
	    ifl->ifl_name, conv_sym_other(nsym->st_other, &inv_nbuf));

	if (err == ERR_FATAL)
		ofl->ofl_flags |= FLG_OF_FATAL;
	else
		eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_DEFTAKEN),
		    ifl->ifl_name);
}

/*
 * STV_VISIBILITY rules for STV_DEFAULT/INTERNAL/HIDDEN/PROTECTED say that the
 * most restrictive visibility value should be taken.  The precedence is:
 *
 *    (most restrictive) INTERNAL -> HIDDEN -> PROTECTED -> DEFAULT  (least)
 *
 * The STV_EXPORT and STV_SINGLETON visibilities are slightly different, in that
 * the visibility must remain global and can not be reduced in any way.
 *
 * Resolution of different visibilities between two relocatable objects can
 * take the following actions:
 *
 *  i.     if applicable, the most restrictive action is silently taken.
 *  ii.    if a mapfile visibility definition competes with a more restrictive
 *         relocatable object definition, then a warning is generated, but the
 *         the more restrictive visibility is taken.
 *  iii.   in the case of conflicts with an EXPORTED or SINGLETON symbol with
 *	   any type of visibility between relocatable objects, the combination
 *	   is deemed fatal.
 *
 *                                  new visibility
 *                    D        I         H         P         X         S
 *                 ------------------------------------------------------------
 *              D |   D        I(mw)     H(mw)     P         X         S
 *   original   I |   I        I         I         I         X(mw/of)  S(mw/of)
 *  visibility  H |   H        I(mw)     H         H         X(mw/of)  S(mw/of)
 *              P |   P        I(mw)     H(mw)     P         X(mw/of)  S(mw/of)
 *              X |   X        I(mw/of)  H(mw/of)  P(mw/of)  X         S
 *              S |   S        I(mw/of)  H(mw/of)  P(mw/of)  S         S
 * where:
 *
 *  mw -  mapfile warning: if the original symbol originates from a mapfile
 *        then warn the user that their scope definition is being overridden.
 *  of -  object definitions are fatal: any combination of relocatable object
 *        visibilities that conflict with a SINGLETON and EXPORTED are fatal.
 *
 * Note, an eliminate symbol (STV_ELIMINATE) is treated as hidden (STV_HIDDEN)
 * for processing through this state table.
 */
static Half
sym_visibility(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl)
{
	Sym	*osym = sdp->sd_sym;
	uchar_t	wovis, ovis;
	uchar_t	wnvis, nvis;

	wovis = ovis = ELF_ST_VISIBILITY(osym->st_other);
	wnvis = nvis = ELF_ST_VISIBILITY(nsym->st_other);

	/*
	 * If the original visibilities are eliminate, assign them hidden for
	 * the state table processing.  The original visibility, rather than
	 * the working visibility, will be returned to the caller.
	 */
	if (wovis == STV_ELIMINATE)
		wovis = STV_HIDDEN;
	if (wnvis == STV_ELIMINATE)
		wnvis = STV_HIDDEN;

	/*
	 * The most complex visibility resolution is between two relocatable
	 * objects.  However, in the case of SINGLETONS we also want to catch
	 * any singleton definitions within shared objects.  Relocatable objects
	 * that bind to these symbols inherit the singleton visibility as this
	 * efficiently triggers ld.so.1 into carrying out the appropriate
	 * runtime symbol search.  Any other resolution between a relocatable
	 * object and a shared object will retain the relocatable objects
	 * visibility.
	 */
	if ((sdp->sd_ref == REF_REL_NEED) &&
	    (ifl->ifl_ehdr->e_type == ET_DYN)) {
		if ((sdp->sd_sym->st_shndx == SHN_UNDEF) &&
		    (nsym->st_shndx != SHN_UNDEF) && (wnvis == STV_SINGLETON))
			return (STV_SINGLETON);
		else
			return (ovis);
	}
	if ((sdp->sd_ref != REF_REL_NEED) &&
	    (ifl->ifl_ehdr->e_type == ET_REL)) {
		if ((sdp->sd_sym->st_shndx != SHN_UNDEF) &&
		    (nsym->st_shndx == SHN_UNDEF) && (wovis == STV_SINGLETON))
			return (STV_SINGLETON);
		else
			return (nvis);
	}

	/*
	 * If the visibilities are the same, we're done.  If the working
	 * visibilities differ from the original, then one must have been
	 * STV_HIDDEN and the other STV_ELIMINATE.
	 */
	if (wovis == wnvis) {
		if (ovis == nvis)
			return (nvis);
		else
			return (STV_ELIMINATE);
	}

	/*
	 * An EXPORTED symbol or SINGLETON symbol can not be demoted, any
	 * conflicting visibility from another object is fatal.  A conflicting
	 * visibility from a mapfile produces a warning, as the mapfile
	 * definition can be overridden.
	 */
	if ((wnvis == STV_EXPORTED) || (wnvis == STV_SINGLETON)) {
		if ((wovis != STV_DEFAULT) && (wovis != STV_EXPORTED) &&
		    (wovis != STV_SINGLETON)) {
			if (sdp->sd_flags1 & FLG_SY1_MAPFILE) {
				sym_visibility_diag(ERR_WARNING, sdp, osym,
				    nsym, ifl, ofl);
			} else {
				sym_visibility_diag(ERR_FATAL, sdp, osym,
				    nsym, ifl, ofl);
			}
		}
		return (nvis);
	}
	if (wovis == STV_SINGLETON) {
		if ((wnvis == STV_EXPORTED) || (wnvis == STV_DEFAULT))
			return (STV_SINGLETON);
		if (sdp->sd_flags1 & FLG_SY1_MAPFILE) {
			sym_visibility_diag(ERR_WARNING, sdp, osym,
			    nsym, ifl, ofl);
		} else {
			sym_visibility_diag(ERR_FATAL, sdp, osym,
			    nsym, ifl, ofl);
		}
		return (nvis);
	}
	if (wovis == STV_EXPORTED) {
		if (wnvis == STV_SINGLETON)
			return (STV_SINGLETON);
		if (wnvis == STV_DEFAULT)
			return (STV_EXPORTED);
		if (sdp->sd_flags1 & FLG_SY1_MAPFILE) {
			sym_visibility_diag(ERR_WARNING, sdp, osym,
			    nsym, ifl, ofl);
		} else {
			sym_visibility_diag(ERR_FATAL, sdp, osym,
			    nsym, ifl, ofl);
		}
		return (nvis);
	}

	/*
	 * Now that symbols with the same visibility, and all instances of
	 * SINGLETON's have been dealt with, we're left with visibilities that
	 * differ, but can be dealt with in the order of how restrictive the
	 * visibilities are.  When a differing visibility originates from a
	 * mapfile definition, produces a warning, as the mapfile definition
	 * can be overridden by the relocatable object.
	 */
	if ((wnvis == STV_INTERNAL) || (wovis == STV_INTERNAL)) {
		if ((wnvis == STV_INTERNAL) &&
		    (sdp->sd_flags1 & FLG_SY1_MAPFILE)) {
			sym_visibility_diag(ERR_WARNING, sdp, osym, nsym,
			    ifl, ofl);
		}
		return (STV_INTERNAL);

	} else if ((wnvis == STV_HIDDEN) || (wovis == STV_HIDDEN)) {
		if ((wnvis == STV_HIDDEN) &&
		    (sdp->sd_flags1 & FLG_SY1_MAPFILE)) {
			sym_visibility_diag(ERR_WARNING, sdp, osym, nsym,
			    ifl, ofl);
		}

		/*
		 * In the case of STV_ELIMINATE and STV_HIDDEN, the working
		 * visibility can differ from the original visibility, so make
		 * sure to return the original visibility.
		 */
		if ((ovis == STV_ELIMINATE) || (nvis == STV_ELIMINATE))
			return (STV_ELIMINATE);
		else
			return (STV_HIDDEN);

	} else if ((wnvis == STV_PROTECTED) || (wovis == STV_PROTECTED))
		return (STV_PROTECTED);

	return (STV_DEFAULT);
}

/*
 * Check if two symbols types are compatible
 */
/*ARGSUSED4*/
static void
sym_typecheck(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
	uchar_t		otype = ELF_ST_TYPE(sdp->sd_sym->st_info);
	uchar_t		ntype = ELF_ST_TYPE(nsym->st_info);
	Conv_inv_buf_t	inv_buf1, inv_buf2;

	/*
	 * Perform any machine specific type checking.
	 */
	if ((ld_targ.t_ms.ms_mach_sym_typecheck != NULL) &&
	    (*ld_targ.t_ms.ms_mach_sym_typecheck)(sdp, nsym, ifl, ofl))
		return;

	/*
	 * NOTYPE's can be combined with other types, only give an error if
	 * combining two differing types without NOTYPE.
	 */
	if ((otype == ntype) || (otype == STT_NOTYPE) || (ntype == STT_NOTYPE))
		return;

	eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
	    demangle(sdp->sd_name));
	eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_FILETYPES),
	    sdp->sd_file->ifl_name,
	    conv_sym_info_type(ofl->ofl_dehdr->e_machine, otype, 0, &inv_buf1),
	    ifl->ifl_name,
	    conv_sym_info_type(ofl->ofl_dehdr->e_machine, ntype, 0, &inv_buf2));
}

/*ARGSUSED4*/
static void
sym_mach_check(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
	/*
	 * Perform any machine specific type checking.
	 */
	if (ld_targ.t_ms.ms_mach_sym_typecheck != NULL)
		(void) (*ld_targ.t_ms.ms_mach_sym_typecheck)(sdp, nsym,
		    ifl, ofl);
}

/*
 * Promote the symbols reference.
 */
static void
/* ARGSUSED4 */
sym_promote(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
    int ndx, Word nshndx, Word nsymflags)
{
	Word	shndx = nsym->st_shndx;

	sym_typecheck(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);

	/*
	 * If the old symbol is from a shared object and the new symbol is a
	 * reference from a relocatable object, promote the old symbols
	 * reference.
	 */
	if ((sdp->sd_ref == REF_DYN_SEEN) &&
	    (ifl->ifl_ehdr->e_type == ET_REL)) {
		sdp->sd_ref = REF_DYN_NEED;

		/*
		 * If this is an undefined symbol it must be a relocatable
		 * object overriding a shared object.  In this case also
		 * override the reference name so that any undefined symbol
		 * diagnostics will refer to the relocatable object name.
		 */
		if (shndx == SHN_UNDEF)
			sdp->sd_aux->sa_rfile = ifl->ifl_name;

		/*
		 * If this symbol is an undefined, or common, determine whether
		 * it is a global or weak reference (see build_osym(), where
		 * REF_DYN_NEED definitions are returned back to undefines).
		 */
		if (((shndx == SHN_UNDEF) || ((nsymflags & FLG_SY_SPECSEC) &&
		    (shndx == SHN_COMMON))) &&
		    (ELF_ST_BIND(nsym->st_info) == STB_GLOBAL))
			sdp->sd_flags |= FLG_SY_GLOBREF;

	} else if ((shndx != SHN_UNDEF) && (ofl->ofl_dtflags_1 & DF_1_TRANS) &&
	    (sdp->sd_aux->sa_bindto == 0) && (sdp->sd_ref == REF_REL_NEED) &&
	    (ifl->ifl_ehdr->e_type == ET_DYN)) {
		/*
		 * If building a translator then record the symbol
		 * we would 'bindto' with direct bindings.
		 */
		sdp->sd_aux->sa_bindto = ifl;
	}
}

/*
 * Override a symbol.
 */
static void
sym_override(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
    int ndx, Word nshndx, Word nsymflags)
{
	Sym	*osym = sdp->sd_sym;
	Word	link;

	/*
	 * In the case of a WEAK UNDEF symbol don't let a symbol from an
	 * unavailable object override the symbol definition.  This is because
	 * this symbol *may* not be present in a future object and by promoting
	 * this symbol we are actually causing bindings (PLTS) to be formed
	 * to this symbol.  Instead let the 'generic' weak binding take place.
	 */
	if ((ELF_ST_BIND(osym->st_info) == STB_WEAK) &&
	    (sdp->sd_sym->st_shndx == SHN_UNDEF) &&
	    ((ifl->ifl_flags & FLG_IF_NEEDED) == 0))
		return;

	sym_typecheck(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);

	/*
	 * This symbol has already been compared to an SO definition,
	 * as per the runtime behavior, ignore extra definitions.
	 */
	if ((sdp->sd_flags & FLG_SY_SOFOUND) &&
	    (ifl->ifl_ehdr->e_type == ET_DYN))
		return;

	/*
	 * Mark the symbol as available and copy the new symbols contents.
	 */
	sdp->sd_flags &= ~FLG_SY_NOTAVAIL;
	*osym = *nsym;
	sdp->sd_shndx = nshndx;
	sdp->sd_flags &= ~FLG_SY_SPECSEC;
	sdp->sd_flags |= (nsymflags & (FLG_SY_SPECSEC | FLG_SY_TENTSYM));

	/*
	 * If the new symbol has PROTECTED visibility, mark it.  If a PROTECTED
	 * symbol is copy relocated, a warning message will be printed.  See
	 * reloc_exec().
	 */
	if (ELF_ST_VISIBILITY(nsym->st_other) == STV_PROTECTED)
		sdp->sd_flags |= FLG_SY_PROT;
	else
		sdp->sd_flags &= ~FLG_SY_PROT;

	/*
	 * Establish the symbols reference.  If the new symbol originates from a
	 * relocatable object then this reference becomes needed, otherwise
	 * the new symbol must be from a shared object.  In this case only
	 * promote the symbol to needed if we presently have a reference from a
	 * relocatable object.
	 */
	if (ifl->ifl_ehdr->e_type == ET_REL) {
		sdp->sd_ref = REF_REL_NEED;

		if (nsym->st_shndx == SHN_UNDEF) {
			/*
			 * If this is an undefined symbol it must be a
			 * relocatable object overriding a shared object.  In
			 * this case also override the reference name so that
			 * any undefined symbol diagnostics will refer to the
			 * relocatable object name.
			 */
			sdp->sd_aux->sa_rfile = ifl->ifl_name;
		} else {
			/*
			 * Under -Bnodirect, all exported interfaces that have
			 * not explicitly been defined protected or directly
			 * bound to, are tagged to prevent direct binding.
			 */
			if ((ofl->ofl_flags1 & FLG_OF1_ALNODIR) &&
			    ((sdp->sd_flags1 &
			    (FLG_SY1_PROTECT | FLG_SY1_DIR)) == 0))
				sdp->sd_flags1 |= FLG_SY1_NDIR;
		}

		/*
		 * If this symbol is an undefined, or common, determine whether
		 * it is a global or weak reference (see build_osym(), where
		 * REF_DYN_NEED definitions are returned back to undefines).
		 */
		if (((nsym->st_shndx == SHN_UNDEF) ||
		    ((nsymflags & FLG_SY_SPECSEC) &&
		    (nsym->st_shndx == SHN_COMMON))) &&
		    (ELF_ST_BIND(nsym->st_info) == STB_GLOBAL))
			sdp->sd_flags |= FLG_SY_GLOBREF;
		else
			sdp->sd_flags &= ~FLG_SY_GLOBREF;
	} else {
		if (sdp->sd_ref == REF_REL_NEED)
			sdp->sd_ref = REF_DYN_NEED;

		/*
		 * Determine the symbols availability.  A symbol is determined
		 * to be unavailable if it belongs to a version of a shared
		 * object that this user does not wish to use, or if it belongs
		 * to an implicit shared object.
		 */
		if (ifl->ifl_vercnt) {
			Ver_index	*vip;
			Half		vndx = ifl->ifl_versym[ndx];

			sdp->sd_aux->sa_dverndx = vndx;
			vip = &ifl->ifl_verndx[vndx];
			if (!(vip->vi_flags & FLG_VER_AVAIL)) {
				sdp->sd_flags |= FLG_SY_NOTAVAIL;
				/*
				 * If this is the first occurrence of an
				 * unavailable symbol record it for possible
				 * use in later error diagnostics
				 * (see sym_undef).
				 */
				if (!(sdp->sd_aux->sa_vfile))
					sdp->sd_aux->sa_vfile = ifl->ifl_name;
			}
		}
		if (!(ifl->ifl_flags & FLG_IF_NEEDED))
			sdp->sd_flags |= FLG_SY_NOTAVAIL;
	}

	/*
	 * Make sure any symbol association maintained by the original symbol
	 * is cleared and then update the symbols file reference.
	 */
	if ((link = sdp->sd_aux->sa_linkndx) != 0) {
		Sym_desc *	_sdp;

		_sdp = sdp->sd_file->ifl_oldndx[link];
		_sdp->sd_aux->sa_linkndx = 0;
		sdp->sd_aux->sa_linkndx = 0;
	}
	sdp->sd_file = ifl;

	/*
	 * Update the input section descriptor to that of the new input file
	 */
	if (((nsymflags & FLG_SY_SPECSEC) == 0) &&
	    (nsym->st_shndx != SHN_UNDEF)) {
		if ((sdp->sd_isc = ifl->ifl_isdesc[nshndx]) == 0) {
			eprintf(ofl->ofl_lml, ERR_FATAL,
			    MSG_INTL(MSG_SYM_NOSECDEF), demangle(sdp->sd_name),
			    ifl->ifl_name);
			ofl->ofl_flags |= FLG_OF_FATAL;
		}
	}
}

/*
 * Resolve two undefines (only called for two relocatable objects).
 */
static void
sym_twoundefs(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
	Sym	*osym = sdp->sd_sym;
	uchar_t	obind = ELF_ST_BIND(osym->st_info);
	uchar_t	nbind = ELF_ST_BIND(nsym->st_info);

	/*
	 * If two relocatable objects define a weak and non-weak undefined
	 * reference, take the non-weak definition.
	 *
	 *		-- or --
	 *
	 * If two relocatable objects define a NOTYPE & another, then
	 * take the other.
	 */
	if (((obind == STB_WEAK) && (nbind != STB_WEAK)) ||
	    (obind == STT_NOTYPE) && (nbind != STT_NOTYPE)) {
		sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
		return;
	}
	sym_typecheck(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
}

/*
 * Resolve two real definitions.
 */
static void
sym_tworeals(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
	Conv_inv_buf_t inv_buf1, inv_buf2;
	Sym	*osym = sdp->sd_sym;
	uchar_t	otype = ELF_ST_TYPE(osym->st_info);
	uchar_t	obind = ELF_ST_BIND(osym->st_info);
	uchar_t	ntype = ELF_ST_TYPE(nsym->st_info);
	uchar_t	nbind = ELF_ST_BIND(nsym->st_info);
	Half	ofile = sdp->sd_file->ifl_ehdr->e_type;
	Half	nfile = ifl->ifl_ehdr->e_type;
	int	warn = 0;

	/*
	 * If both definitions are from relocatable objects, and have non-weak
	 * binding then this is a fatal condition.
	 */
	if ((ofile == ET_REL) && (nfile == ET_REL) && (obind != STB_WEAK) &&
	    (nbind != STB_WEAK) && (!(ofl->ofl_flags & FLG_OF_MULDEFS))) {
		eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_SYM_MULDEF),
		    demangle(sdp->sd_name));
		eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_FILETYPES),
		    sdp->sd_file->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, otype,
		    0, &inv_buf1), ifl->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, ntype,
		    0, &inv_buf2));
		ofl->ofl_flags |= FLG_OF_FATAL;
		return;
	}

	/*
	 * Perform any machine specific type checking.
	 */
	if ((ld_targ.t_ms.ms_mach_sym_typecheck != NULL) &&
	    (*ld_targ.t_ms.ms_mach_sym_typecheck)(sdp, nsym, ifl, ofl))
		return;

	/*
	 * Check the symbols type and size.
	 */
	if (otype != ntype) {
		eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
		    demangle(sdp->sd_name));
		eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_FILETYPES),
		    sdp->sd_file->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, otype,
		    0, &inv_buf1), ifl->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, ntype,
		    0, &inv_buf2));
		warn++;
	} else if ((otype == STT_OBJECT) && (osym->st_size != nsym->st_size)) {
		if (!(ofl->ofl_flags & FLG_OF_NOWARN)) {
			eprintf(ofl->ofl_lml, ERR_WARNING,
			    MSG_INTL(MSG_SYM_DIFFATTR), demangle(sdp->sd_name),
			    MSG_INTL(MSG_STR_SIZES), sdp->sd_file->ifl_name,
			    EC_XWORD(osym->st_size), ifl->ifl_name,
			    EC_XWORD(nsym->st_size));
			warn++;
		}
	}

	/*
	 * Having provided the user with any necessary warnings, take the
	 * appropriate symbol:
	 *
	 *  o	if one symbol is from a shared object and the other is from a
	 *	relocatable object, take the relocatable objects symbol (the
	 *	run-time linker is always going to find the relocatable object
	 *	symbol regardless of the binding), else
	 *
	 * o	if both symbols are from relocatable objects and one symbol is
	 *	weak take the non-weak symbol (two non-weak symbols would have
	 *	generated the fatal error condition above unless -z muldefs is
	 *	in effect), else
	 *
	 *  o	take the first symbol definition encountered.
	 */
	if ((sdp->sd_flags & FLG_SY_SOFOUND) && (nfile == ET_DYN)) {
		if (warn)
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_DEFTAKEN), sdp->sd_file->ifl_name);
		return;
	} else if ((nfile == ET_REL) && ((ofile == ET_DYN) ||
	    ((obind == STB_WEAK) && (nbind != STB_WEAK)))) {
		if (warn)
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_DEFTAKEN), ifl->ifl_name);
		sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
		return;
	} else {
		if (warn)
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_DEFTAKEN), sdp->sd_file->ifl_name);
		sym_promote(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
		return;
	}
}

/*
 * Resolve a real and tentative definition.
 */
static void
sym_realtent(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
	Conv_inv_buf_t inv_buf1, inv_buf2;
	Sym	*osym = sdp->sd_sym;
	uchar_t otype = ELF_ST_TYPE(osym->st_info);
	uchar_t obind = ELF_ST_BIND(osym->st_info);
	uchar_t ntype = ELF_ST_TYPE(nsym->st_info);
	uchar_t nbind = ELF_ST_BIND(nsym->st_info);
	Boolean	otent = FALSE, ntent = FALSE;
	Half	ofile = sdp->sd_file->ifl_ehdr->e_type;
	Half	nfile = ifl->ifl_ehdr->e_type;
	int	warn = 0;
	uchar_t	ovis = ELF_ST_VISIBILITY(osym->st_other);
	uchar_t	nvis = ELF_ST_VISIBILITY(nsym->st_other);

	/*
	 * Special rules for functions.
	 *
	 *  o	If both definitions are from relocatable objects, have the same
	 *	binding (ie. two weaks or two non-weaks), and the real
	 *	definition is a function (the other must be tentative), treat
	 *	this as a multiply defined symbol error, else
	 *
	 *  o	if the real symbol definition is a function within a shared
	 *	library and the tentative symbol is a relocatable object, and
	 *	the tentative is not weak and the function real, then retain the
	 *	tentative definition.
	 */
	if ((ofile == ET_REL) && (nfile == ET_REL) && (obind == nbind) &&
	    ((otype == STT_FUNC) || (ntype == STT_FUNC))) {
		if (ofl->ofl_flags & FLG_OF_MULDEFS) {
			eprintf(ofl->ofl_lml, ERR_WARNING,
			    MSG_INTL(MSG_SYM_DIFFTYPE), demangle(sdp->sd_name));
			sym_promote(sdp, nsym, ifl, ofl, ndx,
			    nshndx, nsymflags);
		} else {
			eprintf(ofl->ofl_lml, ERR_FATAL,
			    MSG_INTL(MSG_SYM_MULDEF), demangle(sdp->sd_name));
			ofl->ofl_flags |= FLG_OF_FATAL;
		}
		eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_FILETYPES),
		    sdp->sd_file->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, otype,
		    0, &inv_buf1), ifl->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, ntype,
		    0, &inv_buf2));
		return;
	} else if (ofile != nfile) {


		if ((ofile == ET_DYN) && (otype == STT_FUNC)) {
			if ((otype != STB_WEAK) && (ntype == STB_WEAK))
				return;
			else {
				sym_override(sdp, nsym, ifl, ofl, ndx,
				    nshndx, nsymflags);
				return;
			}
		}
		if ((nfile == ET_DYN) && (ntype == STT_FUNC)) {
			if ((ntype != STB_WEAK) && (otype == STB_WEAK)) {
				sym_override(sdp, nsym, ifl, ofl, ndx,
				    nshndx, nsymflags);
				return;
			} else
				return;
		}
	}

	if (sdp->sd_flags & FLG_SY_TENTSYM)
		otent = TRUE;
	if (nsymflags & FLG_SY_TENTSYM)
		ntent = TRUE;


	/*
	 * Check the symbols type and size.
	 */
	if (otype != ntype) {
		eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
		    demangle(sdp->sd_name));
		eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_SYM_FILETYPES),
		    sdp->sd_file->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, otype,
		    0, &inv_buf1), ifl->ifl_name,
		    conv_sym_info_type(ofl->ofl_dehdr->e_machine, ntype,
		    0, &inv_buf2));
		warn++;
	} else if (osym->st_size != nsym->st_size) {
		/*
		 * If both definitions are from relocatable objects we have a
		 * potential fatal error condition.  If the tentative is larger
		 * than the real definition treat this as a multiple definition.
		 * Note that if only one symbol is weak, the non-weak will be
		 * taken.
		 */
		if (((ofile == ET_REL) && (nfile == ET_REL) &&
		    (obind == nbind)) &&
		    ((otent && (osym->st_size > nsym->st_size)) ||
		    (ntent && (osym->st_size < nsym->st_size)))) {
			eprintf(ofl->ofl_lml, ERR_FATAL,
			    MSG_INTL(MSG_SYM_DIFFATTR), demangle(sdp->sd_name),
			    MSG_INTL(MSG_STR_SIZES), sdp->sd_file->ifl_name,
			    EC_XWORD(osym->st_size), ifl->ifl_name,
			    EC_XWORD(nsym->st_size));
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_TENTERR));
			ofl->ofl_flags |= FLG_OF_FATAL;
		} else {
			if (!(ofl->ofl_flags & FLG_OF_NOWARN)) {
				eprintf(ofl->ofl_lml, ERR_WARNING,
				    MSG_INTL(MSG_SYM_DIFFATTR),
				    demangle(sdp->sd_name),
				    MSG_INTL(MSG_STR_SIZES),
				    sdp->sd_file->ifl_name,
				    EC_XWORD(osym->st_size),
				    ifl->ifl_name, EC_XWORD(nsym->st_size));
				warn++;
			}
		}
	}

	/*
	 * Having provided the user with any necessary warnings, take the
	 * appropriate symbol:
	 *
	 *  o   if the original symbol is from relocatable file and it is
	 *	a protected tentative symbol, take the original one.
	 *
	 *  o 	if the original symbol is from shared object and the new
	 *	symbol is a protected tentative symbol from a relocatable file,
	 *	take the new one.
	 *
	 *  o	if the original symbol is tentative, and providing the original
	 *	symbol isn't strong and the new symbol weak, take the real
	 *	symbol, else
	 *
	 *  o	if the original symbol is weak and the new tentative symbol is
	 *	strong take the new symbol.
	 *
	 * Refer to the System V ABI Page 4-27 for a description of the binding
	 * requirements of tentative and weak symbols.
	 */
	if ((ofile == ET_REL) && (nfile == ET_DYN) && (otent == TRUE) &&
	    (ovis == STV_PROTECTED)) {
		return;
	}

	if ((ofile == ET_DYN) && (nfile == ET_REL) && (ntent == TRUE) &&
	    (nvis == STV_PROTECTED)) {
		sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
		return;
	}

	if ((sdp->sd_flags & FLG_SY_SOFOUND) && (nfile == ET_DYN)) {
		if (warn)
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_DEFTAKEN), sdp->sd_file->ifl_name);
		return;
	}

	if (((otent) && (!((obind != STB_WEAK) && (nbind == STB_WEAK)))) ||
	    ((obind == STB_WEAK) && (nbind != STB_WEAK))) {
		if (warn)
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_DEFTAKEN), ifl->ifl_name);
		sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
		return;
	} else {
		if (warn)
			eprintf(ofl->ofl_lml, ERR_NONE,
			    MSG_INTL(MSG_SYM_DEFTAKEN), sdp->sd_file->ifl_name);
		sym_promote(sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);
		return;
	}
}

/*
 * Resolve two tentative symbols.
 */
static void
sym_twotent(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
	int ndx, Word nshndx, Word nsymflags)
{
	Sym	*osym = sdp->sd_sym;
	uchar_t	obind = ELF_ST_BIND(osym->st_info);
	uchar_t	nbind = ELF_ST_BIND(nsym->st_info);
	Half	ofile = sdp->sd_file->ifl_ehdr->e_type;
	Half	nfile = ifl->ifl_ehdr->e_type;
	size_t	size = 0;
	Xword	value = 0;

#if	defined(_ELF64)
	if (ld_targ.t_m.m_mach == EM_AMD64) {
		/*
		 * If the original and new symbols are both COMMON, but of
		 * a different size model, take the small one.
		 */
		if ((sdp->sd_sym->st_shndx == SHN_COMMON) &&
		    (nsym->st_shndx == SHN_X86_64_LCOMMON)) {
			/*
			 * Take the original symbol.
			 */
			return;

		} else if ((sdp->sd_sym->st_shndx == SHN_X86_64_LCOMMON) &&
		    (nsym->st_shndx == SHN_COMMON)) {
			/*
			 * Take the new symbol.
			 */
			sym_override(sdp, nsym, ifl, ofl, ndx, nshndx,
			    nsymflags);
			return;
		}
	}
#endif

	/*
	 * Check the alignment of the symbols.  This can only be tested for if
	 * the symbols are not real definitions to a SHT_NOBITS section (ie.
	 * they were originally tentative), as in this case the symbol would
	 * have a displacement value rather than an alignment.  In other words
	 * we can only test this for two relocatable objects.
	 */
	/* BEGIN CSTYLED */
	if ((osym->st_value != nsym->st_value) &&
	    ((sdp->sd_flags & FLG_SY_SPECSEC) &&
	    (sdp->sd_sym->st_shndx == SHN_COMMON) &&
	    (nsymflags & FLG_SY_SPECSEC) &&
#if	defined(_ELF64)
	    (nsym->st_shndx == SHN_COMMON)) ||
	    ((ld_targ.t_m.m_mach == EM_AMD64) &&
	    (sdp->sd_flags & FLG_SY_SPECSEC) &&
	    (sdp->sd_sym->st_shndx == SHN_X86_64_LCOMMON) &&
	    (nsymflags & FLG_SY_SPECSEC) &&
	    (nsym->st_shndx == SHN_X86_64_LCOMMON))) {
#else
	    (nsym->st_shndx == SHN_COMMON))) {
#endif
	/* END CSTYLED */

		const char	*emsg = MSG_INTL(MSG_SYM_DEFTAKEN);
		const char	*file;
		Xword		salign;
		Xword		balign;
		uint_t		alignscompliment;

		if (osym->st_value < nsym->st_value) {
			salign = osym->st_value;
			balign = nsym->st_value;
		} else {
			salign = nsym->st_value;
			balign = osym->st_value;
		}

		/*
		 * If the smaller alignment fits smoothly into the
		 * larger alignment - we take it with no warning.
		 */
		if (S_ALIGN(balign, salign) == balign)
			alignscompliment = 1;
		else
			alignscompliment = 0;

		if (!(ofl->ofl_flags & FLG_OF_NOWARN) && !alignscompliment)
			eprintf(ofl->ofl_lml, ERR_WARNING,
			    MSG_INTL(MSG_SYM_DIFFATTR), demangle(sdp->sd_name),
			    MSG_INTL(MSG_STR_ALIGNMENTS),
			    sdp->sd_file->ifl_name, EC_XWORD(osym->st_value),
			    ifl->ifl_name, EC_XWORD(nsym->st_value));

		/*
		 * Having provided the necessary warning indicate which
		 * relocatable object we are going to take.
		 *
		 *  o	if one symbol is weak and the other is non-weak
		 *	take the non-weak symbol, else
		 *
		 *  o	take the largest alignment (as we still have to check
		 *	the symbols size simply save the largest value for
		 *	updating later).
		 */
		if ((obind == STB_WEAK) && (nbind != STB_WEAK))
			file = ifl->ifl_name;
		else if (obind != nbind)
			file = sdp->sd_file->ifl_name;
		else {
			emsg = MSG_INTL(MSG_SYM_LARGER);
			value = balign;
		}
		if (!(ofl->ofl_flags & FLG_OF_NOWARN) && !alignscompliment)
			eprintf(ofl->ofl_lml, ERR_NONE, emsg, file);
	}

	/*
	 * Check the size of the symbols.
	 */
	if (osym->st_size != nsym->st_size) {
		const char	*emsg = MSG_INTL(MSG_SYM_DEFTAKEN);
		const char	*file;

		if (!(ofl->ofl_flags & FLG_OF_NOWARN))
			eprintf(ofl->ofl_lml, ERR_WARNING,
			    MSG_INTL(MSG_SYM_DIFFATTR), demangle(sdp->sd_name),
			    MSG_INTL(MSG_STR_SIZES), sdp->sd_file->ifl_name,
			    EC_XWORD(osym->st_size), ifl->ifl_name,
			    EC_XWORD(nsym->st_size));


		/*
		 * This symbol has already been compared to an SO definition,
		 * as per the runtime behavior, ignore extra definitions.
		 */
		if ((sdp->sd_flags & FLG_SY_SOFOUND) && (nfile == ET_DYN)) {
			if (!(ofl->ofl_flags & FLG_OF_NOWARN))
				eprintf(ofl->ofl_lml, ERR_NONE, emsg,
				    sdp->sd_file->ifl_name);
			return;
		}

		/*
		 * Having provided the necessary warning indicate what course
		 * of action we are going to take.
		 *
		 *  o	if the file types differ, take the relocatable object
		 *	and apply the largest symbol size, else
		 *  o	if one symbol is weak and the other is non-weak, take
		 *	the non-weak symbol, else
		 *  o	simply take the largest symbol reference.
		 */
		if (nfile != ofile) {
			if (nfile == ET_REL) {
				file = ifl->ifl_name;
				if (osym->st_size > nsym->st_size) {
					size = (size_t)osym->st_size;
					emsg = MSG_INTL(MSG_SYM_DEFUPDATE);
				}
				sym_override(sdp, nsym, ifl, ofl, ndx,
				    nshndx, nsymflags);
			} else {
				file = sdp->sd_file->ifl_name;
				if (osym->st_size < nsym->st_size) {
					size = (size_t)nsym->st_size;
					emsg = MSG_INTL(MSG_SYM_DEFUPDATE);
				}
				sym_promote(sdp, nsym, ifl, ofl, ndx,
				    nshndx, nsymflags);
			}
		} else if (obind != nbind) {
			if ((obind == STB_WEAK) && (nbind != STB_WEAK)) {
				sym_override(sdp, nsym, ifl, ofl, ndx,
				    nshndx, nsymflags);
				file = ifl->ifl_name;
			} else
				file = sdp->sd_file->ifl_name;
		} else {
			if (osym->st_size < nsym->st_size) {
				sym_override(sdp, nsym, ifl, ofl, ndx,
				    nshndx, nsymflags);
				file = ifl->ifl_name;
			} else
				file = sdp->sd_file->ifl_name;
		}
		if (!(ofl->ofl_flags & FLG_OF_NOWARN))
			eprintf(ofl->ofl_lml, ERR_NONE, emsg, file);
		if (size)
			sdp->sd_sym->st_size = (Xword)size;
	} else {
		/*
		 * If the sizes are the same
		 *
		 *  o	if the file types differ, take the relocatable object,
		 *	else
		 *
		 *  o	if one symbol is weak and the other is non-weak, take
		 *	the non-weak symbol, else
		 *
		 *  o	take the first reference.
		 */
		if ((sdp->sd_flags & FLG_SY_SOFOUND) && (nfile == ET_DYN))
			return;
		else if (((ofile != nfile) && (nfile == ET_REL)) ||
		    (((obind == STB_WEAK) && (nbind != STB_WEAK)) &&
		    (!((ofile != nfile) && (ofile == ET_REL)))))
			sym_override(sdp, nsym, ifl, ofl, ndx,
			    nshndx, nsymflags);
		else
			sym_promote(sdp, nsym, ifl, ofl, ndx,
			    nshndx, nsymflags);
	}

	/*
	 * Enforce the largest alignment if necessary.
	 */
	if (value)
		sdp->sd_sym->st_value = value;
}

/*
 * Symbol resolution state table.  `Action' describes the required
 * procedure to be called (if any).
 */
static void (*Action[REF_NUM * SYM_NUM * 2][SYM_NUM])(Sym_desc *,
	Sym *, Ifl_desc *, Ofl_desc *, int, Word, Word) = {

/*				defined		undef		tent	*/
/*				ET_REL		ET_REL		ET_REL	*/

/*  0 defined REF_DYN_SEEN */	sym_tworeals,	sym_promote,	sym_realtent,
/*  1   undef REF_DYN_SEEN */	sym_override,	sym_override,	sym_override,
/*  2    tent REF_DYN_SEEN */	sym_realtent,	sym_promote,	sym_twotent,
/*  3 defined REF_DYN_NEED */	sym_tworeals,	sym_typecheck,	sym_realtent,
/*  4   undef REF_DYN_NEED */	sym_override,	sym_override,	sym_override,
/*  5    tent REF_DYN_NEED */	sym_realtent,	sym_typecheck,	sym_twotent,
/*  6 defined REF_REL_NEED */	sym_tworeals,	sym_typecheck,	sym_realtent,
/*  7   undef REF_REL_NEED */	sym_override,	sym_twoundefs,	sym_override,
/*  8    tent REF_REL_NEED */	sym_realtent,	sym_null,	sym_twotent,

/*				defined		undef		tent	*/
/*				ET_DYN		ET_DYN		ET_DYN	*/

/*  9 defined REF_DYN_SEEN */	sym_tworeals,	sym_null,	sym_realtent,
/* 10   undef REF_DYN_SEEN */	sym_override,	sym_mach_check,	sym_override,
/* 11    tent REF_DYN_SEEN */	sym_realtent,	sym_null,	sym_twotent,
/* 12 defined REF_DYN_NEED */	sym_tworeals,	sym_null,	sym_realtent,
/* 13   undef REF_DYN_NEED */	sym_override,	sym_null,	sym_override,
/* 14    tent REF_DYN_NEED */	sym_realtent,	sym_null,	sym_twotent,
/* 15 defined REF_REL_NEED */	sym_tworeals,	sym_null,	sym_realtent,
/* 16   undef REF_REL_NEED */	sym_override,	sym_mach_check,	sym_override,
/* 17    tent REF_REL_NEED */	sym_realtent,	sym_null,	sym_twotent

};

uintptr_t
ld_sym_resolve(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl, int ndx,
    Word nshndx, Word nsymflags)
{
	int		row, column;		/* State table coordinates */
	Sym		*osym = sdp->sd_sym;
	Is_desc		*isp;
	Half		vis = 0, nfile = ifl->ifl_ehdr->e_type;
	Half		oref = sdp->sd_ref;

	/*
	 * Determine the original symbols definition (defines row in Action[]).
	 */
	if (sdp->sd_flags & FLG_SY_TENTSYM)
		row = SYM_TENTATIVE;
	else if ((sdp->sd_sym->st_shndx == SHN_UNDEF) ||
	    (sdp->sd_sym->st_shndx == SHN_SUNW_IGNORE))
		row = SYM_UNDEFINED;
	else
		row = SYM_DEFINED;

	/*
	 * If the input file is an implicit shared object then we don't need
	 * to bind to any symbols within it other than to verify that any
	 * undefined references will be closed (implicit shared objects are only
	 * processed when no undefined symbols are required as a result of the
	 * link-edit (see process_dynamic())).
	 */
	if ((nfile == ET_DYN) && !(ifl->ifl_flags & FLG_IF_NEEDED) &&
	    (row != SYM_UNDEFINED))
		return (1);

	/*
	 * Finish computing the Action[] row by applying the symbols reference
	 * together with the input files type.
	 */
	row = row + (REF_NUM * sdp->sd_ref);
	if (nfile == ET_DYN)
		row += (REF_NUM * SYM_NUM);

	/*
	 * If either the original or new symbol originates from a relocatable
	 * object, determine the appropriate visibility for the resolved symbol.
	 */
	if ((oref == REF_REL_NEED) || (nfile == ET_REL))
		vis = sym_visibility(sdp, nsym, ifl, ofl);

	/*
	 * Determine the new symbols definition (defines column in Action[]).
	 */
	if ((nsymflags & FLG_SY_SPECSEC) &&
	    (nsym->st_shndx == SHN_COMMON)) {
		column = SYM_TENTATIVE;
		nsymflags |= FLG_SY_TENTSYM;
#if	defined(_ELF64)
	} else if ((ld_targ.t_m.m_mach == EM_AMD64) &&
	    (nsymflags & FLG_SY_SPECSEC) &&
	    (nsym->st_shndx == SHN_X86_64_LCOMMON)) {
		column = SYM_TENTATIVE;
		nsymflags |= FLG_SY_TENTSYM;
#endif
	} else if ((nsym->st_shndx == SHN_UNDEF) ||
	    (nsym->st_shndx == SHN_SUNW_IGNORE)) {
		column = SYM_UNDEFINED;
		nshndx = SHN_UNDEF;
	} else {
		column = SYM_DEFINED;
		/*
		 * If the new symbol is from a shared library and it is
		 * associated with a SHT_NOBITS section then this symbol
		 * originated from a tentative symbol.
		 */
		if (((nsymflags & FLG_SY_SPECSEC) == 0) && (nfile == ET_DYN)) {
			isp = ifl->ifl_isdesc[nshndx];
			if (isp && (isp->is_shdr->sh_type == SHT_NOBITS)) {
				column = SYM_TENTATIVE;
				nsymflags |= FLG_SY_TENTSYM;
			}
		}
	}

	DBG_CALL(Dbg_syms_resolving(ofl, ndx, sdp->sd_name, row, column,
	    osym, nsym, sdp, ifl));

	/*
	 * Record the input filename on the defined files list for possible
	 * later diagnostics.  The `sa_dfiles' list is used to maintain the list
	 * of shared objects that define the same symbol.  This list is only
	 * generated when the -m option is in effect and is used to list
	 * multiple (interposed) definitions of a symbol (refer to ldmap_out()).
	 */
	if ((ofl->ofl_flags & FLG_OF_GENMAP) && (nsym->st_shndx != SHN_UNDEF) &&
	    ((nsymflags & FLG_SY_SPECSEC) == 0))
		if (aplist_append(&sdp->sd_aux->sa_dfiles, ifl->ifl_name,
		    AL_CNT_SDP_DFILES) == NULL)
			return (S_ERROR);

	/*
	 * Perform the required resolution.
	 */
	Action[row][column](sdp, nsym, ifl, ofl, ndx, nshndx, nsymflags);

	/*
	 * Apply any visibility requirements.  If a SINGLETON has been
	 * established, make sure no symbol reduction indicators remain
	 * associated with the symbol, and indicate that the symbol can not
	 * be directly bound to.
	 */
	if ((oref == REF_REL_NEED) || (nfile == ET_REL)) {
		if ((vis == STV_EXPORTED) || (vis == STV_SINGLETON)) {
			sdp->sd_flags1 &= ~(FLG_SY1_PROTECT | FLG_SY1_ELIM |
			    FLG_SY1_HIDDEN);

			if (vis == STV_EXPORTED)
				sdp->sd_flags1 |= FLG_SY1_EXPORT;
			else {
				sdp->sd_flags1 |=
				    (FLG_SY1_NDIR | FLG_SY1_SINGLE);

				if (sdp->sd_ref == REF_REL_NEED)
					ofl->ofl_flags1 |= FLG_OF1_NDIRECT;
			}
		} else if (vis == STV_PROTECTED) {
			sdp->sd_flags1 |= FLG_SY1_PROTECT;
		} else if ((vis == STV_INTERNAL) || (vis == STV_HIDDEN)) {
			sdp->sd_flags1 |= FLG_SY1_HIDDEN;
		} else if (vis == STV_ELIMINATE) {
			sdp->sd_flags1 |= (FLG_SY1_HIDDEN | FLG_SY1_ELIM);
		}

		sdp->sd_sym->st_other =
		    (sdp->sd_sym->st_other & ~MSK_SYM_VISIBILITY) | vis;
	}

	/*
	 * If the symbol has been resolved to the new input file, and this is
	 * a versioned relocatable object, then the version information of the
	 * new symbol must be promoted to the versioning of the output file.
	 */
	if ((sdp->sd_file == ifl) && (nfile == ET_REL) && (ifl->ifl_versym) &&
	    (nsym->st_shndx != SHN_UNDEF))
		ld_vers_promote(sdp, ndx, ifl, ofl);

	/*
	 * Determine whether a mapfile reference has been satisfied.  Mapfile
	 * symbol references augment symbols that should be contributed from
	 * the relocatable objects used to build the output image.  If a
	 * relocatable object doesn't provide one of the mapfile symbol
	 * references then somethings amiss, and will be flagged during symbol
	 * validation.
	 */
	if ((nfile == ET_REL) && ((sdp->sd_flags &
	    (FLG_SY_MAPREF | FLG_SY_MAPUSED)) == FLG_SY_MAPREF)) {
		/*
		 * Extern and parent references are satisfied by references from
		 * a relocatable object.  Note that we let *any* symbol type
		 * satisfy this reference, to be as flexible as possible with
		 * user written mapfiles.  It could be questionable, for
		 * example, if what a user expects to be an extern reference is
		 * actually found to be a definition in a relocatable object.
		 *
		 * Any other mapfile reference (typically for versioning
		 * information) simply augments a relocatables definition.
		 */
		if ((sdp->sd_flags & (FLG_SY_EXTERN | FLG_SY_PARENT)) ||
		    ((sdp->sd_sym->st_shndx != SHN_UNDEF) &&
		    (sdp->sd_ref == REF_REL_NEED)))
			sdp->sd_flags |= FLG_SY_MAPUSED;
	}

	DBG_CALL(Dbg_syms_resolved(ofl, sdp));

	return (1);
}