root/usr/src/cmd/sgs/libld/common/resolve.c
/*
 * 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) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * 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, sd_flag_t nsdflags)
{
}

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;

        /* Warnings are only issued when -z verbose is specified */
        if (!(ofl->ofl_flags & FLG_OF_VERBOSE) && (err != ERR_FATAL))
                return;

        ld_eprintf(ofl, err, MSG_INTL(MSG_SYM_CONFVIS), demangle(sdp->sd_name));
        ld_eprintf(ofl, 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)
                ld_eprintf(ofl, 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_flags & FLG_SY_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_flags & FLG_SY_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_flags & FLG_SY_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_flags & FLG_SY_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_flags & FLG_SY_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, sd_flag_t nsdflags)
{
        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;

        ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
            demangle(sdp->sd_name));
        ld_eprintf(ofl, 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, sd_flag_t nsdflags)
{
        /*
         * 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, sd_flag_t nsdflags)
{
        Word    shndx = nsym->st_shndx;

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

        /*
         * 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) || ((nsdflags & FLG_SY_SPECSEC) &&
                    (shndx == SHN_COMMON))) &&
                    (ELF_ST_BIND(nsym->st_info) == STB_GLOBAL))
                        sdp->sd_flags |= FLG_SY_GLOBREF;

        }
}

/*
 * Override a symbol.
 */
static void
sym_override(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
    int ndx, Word nshndx, sd_flag_t nsdflags)
{
        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, nsdflags);

        /*
         * 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 |= (nsdflags & (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, then we can only be
                         * attempting to override an existing undefined symbol.
                         * The original symbol is either:
                         *
                         *  -   a mapfile definition
                         *  -   a previous relocatable object whose visibility
                         *      or type should be overridden by this new symbol
                         *  -   a previous shared object
                         *
                         * If the original undefined symbol stems from a mapfile
                         * then don't alter the reference file name.  Should we
                         * end up with some form of 'undefined' symbol error,
                         * the characteristics of that error are most likely to
                         * have originated from a mapfile.
                         *
                         * Otherwise, update the reference file name to indicate
                         * this symbol.
                         */
                        if ((sdp->sd_flags & FLG_SY_MAPREF) == 0)
                                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_flags &
                            (FLG_SY_PROTECT | FLG_SY_DIR)) == 0))
                                sdp->sd_flags |= FLG_SY_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) ||
                    ((nsdflags & 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 (((nsdflags & FLG_SY_SPECSEC) == 0) &&
            (nsym->st_shndx != SHN_UNDEF) &&
            ((sdp->sd_isc = ifl->ifl_isdesc[nshndx]) == NULL))
                ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_SYM_NOSECDEF),
                    demangle(sdp->sd_name), ifl->ifl_name);
}

/*
 * 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, sd_flag_t nsdflags)
{
        Sym     *osym = sdp->sd_sym;
        uchar_t obind = ELF_ST_BIND(osym->st_info);
        uchar_t otype = ELF_ST_TYPE(osym->st_info);
        uchar_t nbind = ELF_ST_BIND(nsym->st_info);
        uchar_t ntype = ELF_ST_TYPE(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)) ||
            (otype == STT_NOTYPE) && (ntype != STT_NOTYPE)) {
                sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsdflags);
                return;
        }
        sym_typecheck(sdp, nsym, ifl, ofl, ndx, nshndx, nsdflags);
}

/*
 * Resolve two real definitions.
 */
static void
sym_tworeals(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
    int ndx, Word nshndx, sd_flag_t nsdflags)
{
        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))) {
                ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_SYM_MULDEF),
                    demangle(sdp->sd_name));
                ld_eprintf(ofl, 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;
        }

        /*
         * 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) {
                ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
                    demangle(sdp->sd_name));
                ld_eprintf(ofl, 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)) {
                        ld_eprintf(ofl, 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:
         *
         *  -   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
         *
         *  -   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
         *
         *  -   take the first symbol definition encountered.
         */
        if ((sdp->sd_flags & FLG_SY_SOFOUND) && (nfile == ET_DYN)) {
                if (warn)
                        ld_eprintf(ofl, 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)
                        ld_eprintf(ofl, ERR_NONE, MSG_INTL(MSG_SYM_DEFTAKEN),
                            ifl->ifl_name);
                sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsdflags);
                return;
        } else {
                if (warn)
                        ld_eprintf(ofl, ERR_NONE, MSG_INTL(MSG_SYM_DEFTAKEN),
                            sdp->sd_file->ifl_name);
                sym_promote(sdp, nsym, ifl, ofl, ndx, nshndx, nsdflags);
                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, sd_flag_t nsdflags)
{
        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.
         *
         *  -   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
         *
         *  -   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) {
                        ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
                            demangle(sdp->sd_name));
                        sym_promote(sdp, nsym, ifl, ofl, ndx,
                            nshndx, nsdflags);
                } else {
                        ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_SYM_MULDEF),
                            demangle(sdp->sd_name));
                }
                ld_eprintf(ofl, 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, nsdflags);
                                return;
                        }
                }
                if ((nfile == ET_DYN) && (ntype == STT_FUNC)) {
                        if ((ntype != STB_WEAK) && (otype == STB_WEAK)) {
                                sym_override(sdp, nsym, ifl, ofl, ndx,
                                    nshndx, nsdflags);
                                return;
                        } else
                                return;
                }
        }

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


        /*
         * Check the symbols type and size.
         */
        if (otype != ntype) {
                ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_SYM_DIFFTYPE),
                    demangle(sdp->sd_name));
                ld_eprintf(ofl, 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)))) {
                        ld_eprintf(ofl, 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));
                        ld_eprintf(ofl, ERR_NONE, MSG_INTL(MSG_SYM_TENTERR));
                } else {
                        if (!(ofl->ofl_flags & FLG_OF_NOWARN)) {
                                ld_eprintf(ofl, 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:
         *
         *  -   if the original symbol is from relocatable file and it is
         *      a protected tentative symbol, take the original one.
         *
         *  -   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.
         *
         *  -   if the original symbol is tentative, and providing the original
         *      symbol isn't strong and the new symbol weak, take the real
         *      symbol, else
         *
         *  -   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, nsdflags);
                return;
        }

        if ((sdp->sd_flags & FLG_SY_SOFOUND) && (nfile == ET_DYN)) {
                if (warn)
                        ld_eprintf(ofl, 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)
                        ld_eprintf(ofl, ERR_NONE, MSG_INTL(MSG_SYM_DEFTAKEN),
                            ifl->ifl_name);
                sym_override(sdp, nsym, ifl, ofl, ndx, nshndx, nsdflags);
                return;
        } else {
                if (warn)
                        ld_eprintf(ofl, ERR_NONE, MSG_INTL(MSG_SYM_DEFTAKEN),
                            sdp->sd_file->ifl_name);
                sym_promote(sdp, nsym, ifl, ofl, ndx, nshndx, nsdflags);
                return;
        }
}

/*
 * Resolve two tentative symbols.
 */
static void
sym_twotent(Sym_desc *sdp, Sym *nsym, Ifl_desc *ifl, Ofl_desc *ofl,
    int ndx, Word nshndx, sd_flag_t nsdflags)
{
        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,
                            nsdflags);
                        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) &&
            (nsdflags & 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) &&
            (nsdflags & 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)
                        ld_eprintf(ofl, 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.
                 *
                 *  -   if one symbol is weak and the other is non-weak
                 *      take the non-weak symbol, else
                 *
                 *  -   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)
                        ld_eprintf(ofl, 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))
                        ld_eprintf(ofl, 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))
                                ld_eprintf(ofl, ERR_NONE, emsg,
                                    sdp->sd_file->ifl_name);
                        return;
                }

                /*
                 * Having provided the necessary warning indicate what course
                 * of action we are going to take.
                 *
                 *  -   if the file types differ, take the relocatable object
                 *      and apply the largest symbol size, else
                 *  -   if one symbol is weak and the other is non-weak, take
                 *      the non-weak symbol, else
                 *  -   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, nsdflags);
                        } 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, nsdflags);
                        }
                } else if (obind != nbind) {
                        if ((obind == STB_WEAK) && (nbind != STB_WEAK)) {
                                sym_override(sdp, nsym, ifl, ofl, ndx,
                                    nshndx, nsdflags);
                                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, nsdflags);
                                file = ifl->ifl_name;
                        } else
                                file = sdp->sd_file->ifl_name;
                }
                if (!(ofl->ofl_flags & FLG_OF_NOWARN))
                        ld_eprintf(ofl, ERR_NONE, emsg, file);
                if (size)
                        sdp->sd_sym->st_size = (Xword)size;
        } else {
                /*
                 * If the sizes are the same
                 *
                 *  -   if the file types differ, take the relocatable object,
                 *      else
                 *
                 *  -   if one symbol is weak and the other is non-weak, take
                 *      the non-weak symbol, else
                 *
                 *  -   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, nsdflags);
                else
                        sym_promote(sdp, nsym, ifl, ofl, ndx,
                            nshndx, nsdflags);
        }

        /*
         * 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, sd_flag_t) = {

/*                              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, sd_flag_t nsdflags)
{
        int             row, column;            /* State table coordinates */
        Sym             *osym = sdp->sd_sym;
        sd_flag_t       osdflags = sdp->sd_flags;
        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 (osdflags & 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 ((nsdflags & FLG_SY_SPECSEC) &&
            (nsym->st_shndx == SHN_COMMON)) {
                column = SYM_TENTATIVE;
                nsdflags |= FLG_SY_TENTSYM;
#if     defined(_ELF64)
        } else if ((ld_targ.t_m.m_mach == EM_AMD64) &&
            (nsdflags & FLG_SY_SPECSEC) &&
            (nsym->st_shndx == SHN_X86_64_LCOMMON)) {
                column = SYM_TENTATIVE;
                nsdflags |= 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 (((nsdflags & FLG_SY_SPECSEC) == 0) && (nfile == ET_DYN)) {
                        isp = ifl->ifl_isdesc[nshndx];
                        if (isp && (isp->is_shdr->sh_type == SHT_NOBITS)) {
                                column = SYM_TENTATIVE;
                                nsdflags |= 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) &&
            ((nsdflags & 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, nsdflags);

        /*
         * 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_flags &= ~MSK_SY_LOCAL;

                        if (vis == STV_EXPORTED)
                                sdp->sd_flags |= FLG_SY_EXPORT;
                        else {
                                sdp->sd_flags |= (FLG_SY_NDIR | FLG_SY_SINGLE);

                                if (sdp->sd_ref == REF_REL_NEED) {
                                        ofl->ofl_flags1 |=
                                            (FLG_OF1_NDIRECT | FLG_OF1_NGLBDIR);
                                }
                        }
                } else if (vis == STV_PROTECTED) {
                        sdp->sd_flags |= FLG_SY_PROTECT;
                } else if ((vis == STV_INTERNAL) || (vis == STV_HIDDEN)) {
                        sdp->sd_flags |= FLG_SY_HIDDEN;
                } else if (vis == STV_ELIMINATE) {
                        sdp->sd_flags |= (FLG_SY_HIDDEN | FLG_SY_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;
        }

        /*
         * Make sure any special symbol requirements are carried over.
         */
        if ((osdflags & FLG_SY_CAP) || (nsdflags & FLG_SY_CAP))
                sdp->sd_flags |= FLG_SY_CAP;

        DBG_CALL(Dbg_syms_resolved(ofl, sdp));

        return (1);
}