/* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <string.h> #include <stdio.h> #include <debug.h> #include "msg.h" #include "_libld.h" /* * Locate a version descriptor. */ Ver_desc * ld_vers_find(const char *name, Word hash, APlist *alp) { Aliste idx; Ver_desc *vdp; for (APLIST_TRAVERSE(alp, idx, vdp)) { if (vdp->vd_hash != hash) continue; if (strcmp(vdp->vd_name, name) == 0) return (vdp); } return (NULL); } /* * Add a new version descriptor to a version descriptor list. Note, users of * this are responsible for determining if the version descriptor already * exists (this can reduce the need to allocate storage for descriptor names * until it is determined a descriptor need be created (see map_symbol())). */ Ver_desc * ld_vers_desc(const char *name, Word hash, APlist **alpp) { Ver_desc *vdp; if ((vdp = libld_calloc(sizeof (Ver_desc), 1)) == NULL) return ((Ver_desc *)S_ERROR); vdp->vd_name = name; vdp->vd_hash = hash; if (aplist_append(alpp, vdp, AL_CNT_VERDESCS) == NULL) return ((Ver_desc *)S_ERROR); return (vdp); } /* * Now that all explict files have been processed validate any version * definitions. Insure that any version references are available (a version * has been defined when it's been assigned an index). Also calculate the * number of .version section entries that will be required to hold this * information. */ #define _NUM_OF_VERS_ 40 /* twice as big as the depth for libc version */ typedef struct { Ver_desc **ver_stk; int ver_sp; int ver_lmt; } Ver_Stack; static uintptr_t vers_visit_children(Ofl_desc *ofl, Ver_desc *vp, int flag) { Aliste idx; Ver_desc *vdp; static int err = 0; static Ver_Stack ver_stk = {0, 0, 0}; int tmp_sp; /* * If there was any fatal error, * just return. */ if (err == S_ERROR) return (err); /* * if this is called from, ver_check_defs(), initialize sp. */ if (flag == 0) ver_stk.ver_sp = 0; /* * Check if passed version pointer vp is already in the stack. */ for (tmp_sp = 0; tmp_sp < ver_stk.ver_sp; tmp_sp++) { Ver_desc *v; v = ver_stk.ver_stk[tmp_sp]; if (v == vp) { /* * cyclic dependency. */ if (err == 0) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_CYCLIC)); err = 1; } for (tmp_sp = 0; tmp_sp < ver_stk.ver_sp; tmp_sp++) { v = ver_stk.ver_stk[tmp_sp]; if ((v->vd_flags & FLG_VER_CYCLIC) == 0) { v->vd_flags |= FLG_VER_CYCLIC; eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_VER_ADDVER), v->vd_name); } } if ((vp->vd_flags & FLG_VER_CYCLIC) == 0) { vp->vd_flags |= FLG_VER_CYCLIC; eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_VER_ADDVER), vp->vd_name); } return (err); } } /* * Push version on the stack. */ if (ver_stk.ver_sp >= ver_stk.ver_lmt) { ver_stk.ver_lmt += _NUM_OF_VERS_; if ((ver_stk.ver_stk = (Ver_desc **) libld_realloc((void *)ver_stk.ver_stk, ver_stk.ver_lmt * sizeof (Ver_desc *))) == NULL) return (S_ERROR); } ver_stk.ver_stk[(ver_stk.ver_sp)++] = vp; /* * Now visit children. */ for (APLIST_TRAVERSE(vp->vd_deps, idx, vdp)) if (vers_visit_children(ofl, vdp, 1) == S_ERROR) return (S_ERROR); /* * Pop version from the stack. */ (ver_stk.ver_sp)--; return (err); } uintptr_t ld_vers_check_defs(Ofl_desc *ofl) { Aliste idx1; Ver_desc *vdp; uintptr_t is_cyclic = 0; DBG_CALL(Dbg_ver_def_title(ofl->ofl_lml, ofl->ofl_name)); /* * First check if there are any cyclic dependency */ for (APLIST_TRAVERSE(ofl->ofl_verdesc, idx1, vdp)) if ((is_cyclic = vers_visit_children(ofl, vdp, 0)) == S_ERROR) return (S_ERROR); if (is_cyclic) ofl->ofl_flags |= FLG_OF_FATAL; for (APLIST_TRAVERSE(ofl->ofl_verdesc, idx1, vdp)) { Byte cnt; Sym *sym; Sym_desc *sdp; const char *name = vdp->vd_name; uchar_t bind; Ver_desc *_vdp; avl_index_t where; Aliste idx2; if (vdp->vd_ndx == 0) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_UNDEF), name, vdp->vd_ref->vd_name, vdp->vd_ref->vd_file->ifl_name); ofl->ofl_flags |= FLG_OF_FATAL; continue; } DBG_CALL(Dbg_ver_desc_entry(ofl->ofl_lml, vdp)); /* * If a version definition contains no symbols this is possibly * a mapfile error. */ if ((vdp->vd_flags & (VER_FLG_BASE | VER_FLG_WEAK | FLG_VER_REFER)) == 0) DBG_CALL(Dbg_ver_nointerface(ofl->ofl_lml, vdp->vd_name)); /* * Update the version entry count to account for this new * version descriptor (the count is the size in bytes). */ ofl->ofl_verdefsz += sizeof (Verdef); /* * Traverse this versions dependency list to determine what * additional version dependencies we must account for against * this descriptor. */ cnt = 1; for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) { #if defined(__lint) /* get lint to think `_vdp' is used... */ vdp = _vdp; #endif cnt++; } ofl->ofl_verdefsz += (cnt * sizeof (Verdaux)); /* * Except for the base version descriptor, generate an absolute * symbol to reflect this version. */ if (vdp->vd_flags & VER_FLG_BASE) continue; if (vdp->vd_flags & VER_FLG_WEAK) bind = STB_WEAK; else bind = STB_GLOBAL; if (sdp = ld_sym_find(name, vdp->vd_hash, &where, ofl)) { /* * If the symbol already exists and is undefined or was * defined in a shared library, convert it to an * absolute. */ if ((sdp->sd_sym->st_shndx == SHN_UNDEF) || (sdp->sd_ref != REF_REL_NEED)) { sdp->sd_shndx = sdp->sd_sym->st_shndx = SHN_ABS; sdp->sd_sym->st_info = ELF_ST_INFO(bind, STT_OBJECT); sdp->sd_ref = REF_REL_NEED; sdp->sd_flags |= (FLG_SY_SPECSEC | FLG_SY_DEFAULT | FLG_SY_EXPDEF); sdp->sd_aux->sa_overndx = vdp->vd_ndx; /* * If the reference originated from a mapfile * insure we mark the symbol as used. */ if (sdp->sd_flags & FLG_SY_MAPREF) sdp->sd_flags |= FLG_SY_MAPUSED; } else if ((sdp->sd_flags & FLG_SY_SPECSEC) && (sdp->sd_sym->st_shndx != SHN_ABS) && (sdp->sd_ref == REF_REL_NEED)) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_VER_DEFINED), name, sdp->sd_file->ifl_name); } } else { /* * If the symbol does not exist create it. */ if ((sym = libld_calloc(sizeof (Sym), 1)) == NULL) return (S_ERROR); sym->st_shndx = SHN_ABS; sym->st_info = ELF_ST_INFO(bind, STT_OBJECT); DBG_CALL(Dbg_ver_symbol(ofl->ofl_lml, name)); if ((sdp = ld_sym_enter(name, sym, vdp->vd_hash, vdp->vd_file, ofl, 0, SHN_ABS, (FLG_SY_SPECSEC | FLG_SY_DEFAULT | FLG_SY_EXPDEF), &where)) == (Sym_desc *)S_ERROR) return (S_ERROR); sdp->sd_ref = REF_REL_NEED; sdp->sd_aux->sa_overndx = vdp->vd_ndx; } } return (1); } /* * Dereference dependencies as a part of normalizing (allows recursion). */ static void vers_derefer(Ifl_desc *ifl, Ver_desc *vdp, int weak) { Aliste idx; Ver_desc *_vdp; Ver_index *vip = &ifl->ifl_verndx[vdp->vd_ndx]; /* * Set the INFO bit on all dependencies that ld.so.1 * can skip verification for. These are the dependencies * that are inherited by others -- verifying the inheriting * version implicitily covers this one. * * If the head of the list was a weak then we only mark * weak dependencies, but if the head of the list was 'strong' * we set INFO on all dependencies. */ if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak)) vip->vi_flags |= VER_FLG_INFO; for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) vers_derefer(ifl, _vdp, weak); } /* * If we need to record the versions of any needed dependencies traverse the * shared object dependency list and calculate what version needed entries are * required. */ uintptr_t ld_vers_check_need(Ofl_desc *ofl) { Aliste idx1; Ifl_desc *ifl; Half needndx; Str_tbl *strtbl; /* * Determine which string table is appropriate. */ strtbl = (OFL_IS_STATIC_OBJ(ofl)) ? ofl->ofl_strtab : ofl->ofl_dynstrtab; /* * Versym indexes for needed versions start with the next * available version after the final definied version. * However, it can never be less than 2. 0 is always for local * scope, and 1 is always the first global definition. */ needndx = (ofl->ofl_vercnt > 0) ? (ofl->ofl_vercnt + 1) : 2; /* * Traverse the shared object list looking for dependencies. */ for (APLIST_TRAVERSE(ofl->ofl_sos, idx1, ifl)) { Aliste idx2; Ver_index *vip; Ver_desc *vdp; Byte cnt, need = 0; if (!(ifl->ifl_flags & FLG_IF_NEEDED)) continue; if (ifl->ifl_vercnt <= VER_NDX_GLOBAL) continue; /* * Scan the version index list and if any weak version * definition has been referenced by the user promote the * dependency to be non-weak. Weak version dependencies do not * cause fatal errors from the runtime linker, non-weak * dependencies do. */ for (cnt = 0; cnt <= ifl->ifl_vercnt; cnt++) { vip = &ifl->ifl_verndx[cnt]; vdp = vip->vi_desc; if ((vip->vi_flags & (FLG_VER_REFER | VER_FLG_WEAK)) == (FLG_VER_REFER | VER_FLG_WEAK)) vdp->vd_flags &= ~VER_FLG_WEAK; /* * Mark any weak reference as referred to so as to * simplify normalization and later version dependency * manipulation. */ if (vip->vi_flags & VER_FLG_WEAK) vip->vi_flags |= FLG_VER_REFER; } /* * Scan the version dependency list to normalize the referenced * dependencies. Any needed version that is inherited by * another like version is dereferenced as it is not necessary * to make this part of the version dependencies. */ for (APLIST_TRAVERSE(ifl->ifl_verdesc, idx2, vdp)) { Aliste idx3; Ver_desc *_vdp; int type; vip = &ifl->ifl_verndx[vdp->vd_ndx]; if (!(vip->vi_flags & FLG_VER_REFER)) continue; type = vdp->vd_flags & VER_FLG_WEAK; for (APLIST_TRAVERSE(vdp->vd_deps, idx3, _vdp)) vers_derefer(ifl, _vdp, type); } /* * Finally, determine how many of the version dependencies need * to be recorded. */ for (cnt = 0; cnt <= ifl->ifl_vercnt; cnt++) { vip = &ifl->ifl_verndx[cnt]; /* * If a version has been referenced then record it as a * version dependency. */ if (vip->vi_flags & FLG_VER_REFER) { /* Assign a VERSYM index for it */ vip->vi_overndx = needndx++; ofl->ofl_verneedsz += sizeof (Vernaux); if (st_insert(strtbl, vip->vi_name) == -1) return (S_ERROR); need++; } } if (need) { ifl->ifl_flags |= FLG_IF_VERNEED; ofl->ofl_verneedsz += sizeof (Verneed); if (st_insert(strtbl, ifl->ifl_soname) == -1) return (S_ERROR); } } /* * If no version needed information is required unset the output file * flag. */ if (ofl->ofl_verneedsz == 0) ofl->ofl_flags &= ~FLG_OF_VERNEED; return (1); } /* * Indicate dependency selection (allows recursion). */ static void vers_select(Ofl_desc *ofl, Ifl_desc *ifl, Ver_desc *vdp, const char *ref) { Aliste idx; Ver_desc *_vdp; Ver_index *vip = &ifl->ifl_verndx[vdp->vd_ndx]; vip->vi_flags |= FLG_VER_AVAIL; DBG_CALL(Dbg_ver_avail_entry(ofl->ofl_lml, vip, ref)); for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) vers_select(ofl, ifl, _vdp, ref); } static Ver_index * vers_index(Ofl_desc *ofl, Ifl_desc *ifl, int avail) { Aliste idx1; Ver_desc *vdp; Ver_index *vip; Sdf_desc *sdf = ifl->ifl_sdfdesc; Word count = ifl->ifl_vercnt; Sdv_desc *sdv; /* * Allocate an index array large enough to hold all of the files * version descriptors. */ if ((vip = libld_calloc(sizeof (Ver_index), (count + 1))) == NULL) return ((Ver_index *)S_ERROR); for (APLIST_TRAVERSE(ifl->ifl_verdesc, idx1, vdp)) { int ndx = vdp->vd_ndx; vip[ndx].vi_name = vdp->vd_name; vip[ndx].vi_desc = vdp; /* * Any relocatable object versions, and the `base' version are * always available. */ if (avail || (vdp->vd_flags & VER_FLG_BASE)) vip[ndx].vi_flags |= FLG_VER_AVAIL; /* * If this is a weak version mark it as such. Weak versions * are always dragged into any version dependencies created, * and if a weak version is referenced it will be promoted to * a non-weak version dependency. */ if (vdp->vd_flags & VER_FLG_WEAK) vip[ndx].vi_flags |= VER_FLG_WEAK; /* * If this version is mentioned in a mapfile using ADDVERS * syntax then check to see if it corresponds to an actual * version in the file. */ if (sdf && (sdf->sdf_flags & FLG_SDF_ADDVER)) { Aliste idx2; for (ALIST_TRAVERSE(sdf->sdf_verneed, idx2, sdv)) { if (strcmp(vip[ndx].vi_name, sdv->sdv_name)) continue; vip[ndx].vi_flags |= FLG_VER_REFER; sdv->sdv_flags |= FLG_SDV_MATCHED; break; } } } /* * if $ADDVER was specified for this object verify that * all of it's dependent upon versions were refered to. */ if (sdf && (sdf->sdf_flags & FLG_SDF_ADDVER)) { int fail = 0; for (ALIST_TRAVERSE(sdf->sdf_verneed, idx1, sdv)) { if (sdv->sdv_flags & FLG_SDV_MATCHED) continue; if (fail++ == 0) { eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_VER_ADDVERS), sdf->sdf_rfile, sdf->sdf_name); } eprintf(ofl->ofl_lml, ERR_NONE, MSG_INTL(MSG_VER_ADDVER), sdv->sdv_name); } if (fail) return ((Ver_index *)S_ERROR); } return (vip); } /* * Process a version symbol index section. */ int ld_vers_sym_process(Lm_list *lml, Is_desc *isp, Ifl_desc *ifl) { Shdr *symshdr; Shdr *vershdr = isp->is_shdr; /* * Verify that the versym is the same size as the linked symbol table. * If these two get out of sync the file is considered corrupted. */ symshdr = ifl->ifl_isdesc[vershdr->sh_link]->is_shdr; if ((symshdr->sh_size / symshdr->sh_entsize) != (vershdr->sh_size / vershdr->sh_entsize)) { Is_desc *sym_isp = ifl->ifl_isdesc[vershdr->sh_link]; eprintf(lml, ERR_WARNING, MSG_INTL(MSG_ELF_VERSYM), ifl->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, EC_WORD(vershdr->sh_size / vershdr->sh_entsize), EC_WORD(sym_isp->is_scnndx), sym_isp->is_name, EC_WORD(symshdr->sh_size / symshdr->sh_entsize)); return (1); } ifl->ifl_versym = (Versym *)isp->is_indata->d_buf; return (1); } /* * Process a version definition section from an input file. A list of version * descriptors is created and associated with the input files descriptor. If * this is a shared object these descriptors will be used to indicate the * availability of each version. If this is a relocatable object then these * descriptors will be promoted (concatenated) to the output files image. */ uintptr_t ld_vers_def_process(Is_desc *isp, Ifl_desc *ifl, Ofl_desc *ofl) { const char *str, *file = ifl->ifl_name; Sdf_desc *sdf = ifl->ifl_sdfdesc; Sdv_desc *sdv; Word num, _num; Verdef *vdf; int relobj; /* * If there is no version section then simply indicate that all version * definitions asked for do not exist. */ if (isp == NULL) { Aliste idx; for (ALIST_TRAVERSE(sdf->sdf_vers, idx, sdv)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_NOEXIST), ifl->ifl_name, sdv->sdv_name, sdv->sdv_ref); ofl->ofl_flags |= FLG_OF_FATAL; } return (0); } vdf = (Verdef *)isp->is_indata->d_buf; /* * Verify the version revision. We only check the first version * structure as it is assumed all other version structures in this * data section will be of the same revision. */ if (vdf->vd_version > VER_DEF_CURRENT) (void) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_VER_HIGHER), ifl->ifl_name, vdf->vd_version, VER_DEF_CURRENT); num = isp->is_shdr->sh_info; str = (char *)ifl->ifl_isdesc[isp->is_shdr->sh_link]->is_indata->d_buf; if (ifl->ifl_ehdr->e_type == ET_REL) relobj = 1; else relobj = 0; DBG_CALL(Dbg_ver_def_title(ofl->ofl_lml, file)); /* * Loop through the version information setting up a version descriptor * for each version definition. */ for (_num = 1; _num <= num; _num++, vdf = (Verdef *)((uintptr_t)vdf + vdf->vd_next)) { const char *name; Ver_desc *ivdp, *ovdp = NULL; Word hash; Half cnt = vdf->vd_cnt; Half ndx = vdf->vd_ndx; Verdaux *vdap = (Verdaux *)((uintptr_t)vdf + vdf->vd_aux); /* * Keep track of the largest index for use in creating a * version index array later, and create a version descriptor. */ if (ndx > ifl->ifl_vercnt) ifl->ifl_vercnt = ndx; name = (char *)(str + vdap->vda_name); /* LINTED */ hash = (Word)elf_hash(name); if (((ivdp = ld_vers_find(name, hash, ifl->ifl_verdesc)) == NULL) && ((ivdp = ld_vers_desc(name, hash, &ifl->ifl_verdesc)) == (Ver_desc *)S_ERROR)) return (S_ERROR); ivdp->vd_ndx = ndx; ivdp->vd_file = ifl; ivdp->vd_flags = vdf->vd_flags; /* * If we're processing a relocatable object then this version * definition needs to be propagated to the output file. * Generate a new output file version and associated this input * version to it. During symbol processing the version index of * the symbol will be promoted from the input file to the output * files version definition. */ if (relobj) { if (!(ofl->ofl_flags & FLG_OF_RELOBJ)) ofl->ofl_flags |= FLG_OF_PROCRED; if ((ivdp->vd_flags & VER_FLG_BASE) == 0) { /* * If no version descriptors have yet been set * up, initialize a base version to represent * the output file itself. This `base' version * catches any internally generated symbols * (_end, _etext, etc.) and * serves to initialize the output version * descriptor count. */ if (ofl->ofl_vercnt == 0) { if (ld_vers_base(ofl) == (Ver_desc *)S_ERROR) return (S_ERROR); } ofl->ofl_flags |= FLG_OF_VERDEF; if ((ovdp = ld_vers_find(name, hash, ofl->ofl_verdesc)) == NULL) { if ((ovdp = ld_vers_desc(name, hash, &ofl->ofl_verdesc)) == (Ver_desc *)S_ERROR) return (S_ERROR); /* LINTED */ ovdp->vd_ndx = (Half)++ofl->ofl_vercnt; ovdp->vd_file = ifl; ovdp->vd_flags = vdf->vd_flags; } } /* * Maintain the association between the input version * descriptor and the output version descriptor so that * an associated symbols will be assigned to the * correct version. */ ivdp->vd_ref = ovdp; } /* * Process any dependencies this version may have. */ vdap = (Verdaux *)((uintptr_t)vdap + vdap->vda_next); for (cnt--; cnt; cnt--, vdap = (Verdaux *)((uintptr_t)vdap + vdap->vda_next)) { Ver_desc *_ivdp; name = (char *)(str + vdap->vda_name); /* LINTED */ hash = (Word)elf_hash(name); if (((_ivdp = ld_vers_find(name, hash, ifl->ifl_verdesc)) == NULL) && ((_ivdp = ld_vers_desc(name, hash, &ifl->ifl_verdesc)) == (Ver_desc *)S_ERROR)) return (S_ERROR); if (aplist_append(&ivdp->vd_deps, _ivdp, AL_CNT_VERDESCS) == NULL) return (S_ERROR); } DBG_CALL(Dbg_ver_desc_entry(ofl->ofl_lml, ivdp)); } /* * Now that we know the total number of version definitions for this * file, build an index array for fast access when processing symbols. */ if ((ifl->ifl_verndx = vers_index(ofl, ifl, relobj)) == (Ver_index *)S_ERROR) return (S_ERROR); if (relobj) return (1); /* * If this object has version control definitions against it then these * must be processed so as to select those version definitions to which * symbol bindings can occur. Otherwise simply mark all versions as * available. */ DBG_CALL(Dbg_ver_avail_title(ofl->ofl_lml, file)); if (sdf && (sdf->sdf_flags & FLG_SDF_SELECT)) { Aliste idx1; for (ALIST_TRAVERSE(sdf->sdf_vers, idx1, sdv)) { Aliste idx2; Ver_desc *vdp; int found = 0; for (APLIST_TRAVERSE(ifl->ifl_verdesc, idx2, vdp)) { if (strcmp(sdv->sdv_name, vdp->vd_name) == 0) { found++; break; } } if (found) vers_select(ofl, ifl, vdp, sdv->sdv_ref); else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_NOEXIST), ifl->ifl_name, sdv->sdv_name, sdv->sdv_ref); ofl->ofl_flags |= FLG_OF_FATAL; } } } else { Ver_index *vip; int cnt; for (cnt = VER_NDX_GLOBAL; cnt <= ifl->ifl_vercnt; cnt++) { vip = &ifl->ifl_verndx[cnt]; vip->vi_flags |= FLG_VER_AVAIL; DBG_CALL(Dbg_ver_avail_entry(ofl->ofl_lml, vip, 0)); } } /* * If this is an explict dependency indicate that this file is a * candidate for requiring version needed information to be recorded in * the image we're creating. */ if (ifl->ifl_flags & FLG_IF_NEEDED) ofl->ofl_flags |= FLG_OF_VERNEED; return (1); } /* * Process a version needed section. */ uintptr_t ld_vers_need_process(Is_desc *isp, Ifl_desc *ifl, Ofl_desc *ofl) { const char *str, *file = ifl->ifl_name; Word num, _num; Verneed *vnd; vnd = (Verneed *)isp->is_indata->d_buf; /* * Verify the version revision. We only check the first version * structure as it is assumed all other version structures in this * data section will be of the same revision. */ if (vnd->vn_version > VER_DEF_CURRENT) { (void) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_VER_HIGHER), ifl->ifl_name, vnd->vn_version, VER_DEF_CURRENT); } num = isp->is_shdr->sh_info; str = (char *)ifl->ifl_isdesc[isp->is_shdr->sh_link]->is_indata->d_buf; DBG_CALL(Dbg_ver_need_title(ofl->ofl_lml, file)); /* * Loop through the version information setting up a version descriptor * for each version definition. */ for (_num = 1; _num <= num; _num++, vnd = (Verneed *)((uintptr_t)vnd + vnd->vn_next)) { Sdf_desc *sdf; const char *name; Half cnt = vnd->vn_cnt; Vernaux *vnap = (Vernaux *)((uintptr_t)vnd + vnd->vn_aux); Half _cnt; name = (char *)(str + vnd->vn_file); /* * Set up a shared object descriptor and add to it the necessary * needed versions. This information may also have been added * by a mapfile (see map_dash()). */ if ((sdf = sdf_find(name, ofl->ofl_soneed)) == NULL) { if ((sdf = sdf_add(name, &ofl->ofl_soneed)) == (Sdf_desc *)S_ERROR) return (S_ERROR); sdf->sdf_rfile = file; sdf->sdf_flags |= FLG_SDF_VERIFY; } for (_cnt = 0; cnt; _cnt++, cnt--, vnap = (Vernaux *)((uintptr_t)vnap + vnap->vna_next)) { Sdv_desc sdv; sdv.sdv_name = str + vnap->vna_name; sdv.sdv_ref = file; sdv.sdv_flags = 0; if (alist_append(&sdf->sdf_vers, &sdv, sizeof (Sdv_desc), AL_CNT_SDF_VERSIONS) == NULL) return (S_ERROR); DBG_CALL(Dbg_ver_need_entry(ofl->ofl_lml, _cnt, name, sdv.sdv_name)); } } return (1); } /* * If a symbol is obtained from a versioned relocatable object then the symbols * version association must be promoted to the version definition as it will be * represented in the output file. */ void ld_vers_promote(Sym_desc *sdp, Word ndx, Ifl_desc *ifl, Ofl_desc *ofl) { Half vndx; /* * A version symbol index of 0 implies the symbol is local. A value of * VER_NDX_GLOBAL implies the symbol is global but has not been * assigned to a specfic version definition. */ vndx = ifl->ifl_versym[ndx]; if (vndx == 0) { sdp->sd_flags |= (FLG_SY_REDUCED | FLG_SY_HIDDEN); return; } if (vndx == VER_NDX_ELIMINATE) { sdp->sd_flags |= (FLG_SY_REDUCED | FLG_SY_HIDDEN | FLG_SY_ELIM); return; } if (vndx == VER_NDX_GLOBAL) { if (!SYM_IS_HIDDEN(sdp)) sdp->sd_flags |= (FLG_SY_DEFAULT | FLG_SY_EXPDEF); if (sdp->sd_aux->sa_overndx <= VER_NDX_GLOBAL) sdp->sd_aux->sa_overndx = VER_NDX_GLOBAL; return; } /* * Any other version index requires association to the appropriate * version definition. */ if ((ifl->ifl_verndx == 0) || (vndx > ifl->ifl_vercnt)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_INVALNDX), sdp->sd_name, ifl->ifl_name, vndx); ofl->ofl_flags |= FLG_OF_FATAL; return; } if (!SYM_IS_HIDDEN(sdp)) sdp->sd_flags |= (FLG_SY_DEFAULT | FLG_SY_EXPDEF); /* * Promote the symbols version index to the appropriate output version * definition. */ if (!(sdp->sd_flags & FLG_SY_VERSPROM)) { Ver_index *vip; vip = &ifl->ifl_verndx[vndx]; sdp->sd_aux->sa_overndx = vip->vi_desc->vd_ref->vd_ndx; sdp->sd_flags |= FLG_SY_VERSPROM; } } /* * If any versioning is called for make sure an initial version descriptor is * assigned to represent the file itself. Known as the base version. */ Ver_desc * ld_vers_base(Ofl_desc *ofl) { Ver_desc *vdp; const char *name; /* * Determine the filename to associate to the version descriptor. This * is either the SONAME (if one has been supplied) or the basename of * the output file. */ if ((name = ofl->ofl_soname) == NULL) { const char *str = ofl->ofl_name; while (*str != '\0') { if (*str++ == '/') name = str; } if (name == NULL) name = ofl->ofl_name; } /* * Generate the version descriptor. */ /* LINTED */ if ((vdp = ld_vers_desc(name, (Word)elf_hash(name), &ofl->ofl_verdesc)) == (Ver_desc *)S_ERROR) return ((Ver_desc *)S_ERROR); /* * Assign the base index to this version and initialize the output file * descriptor with the number of version descriptors presently in use. */ vdp->vd_ndx = ofl->ofl_vercnt = VER_NDX_GLOBAL; vdp->vd_flags |= VER_FLG_BASE; return (vdp); } /* * Now that all input shared objects have been processed, verify that all * version requirements have been met. Any version control requirements will * have been specified by the user (and placed on the ofl_oscntl list) and are * verified at the time the object was processed (see ver_def_process()). * Here we process all version requirements established from shared objects * themselves (ie,. NEEDED dependencies). */ int ld_vers_verify(Ofl_desc *ofl) { Aliste idx1; Sdf_desc *sdf; char *nv; /* * As with the runtime environment, disable all version verification if * requested. */ #if defined(_ELF64) if ((nv = getenv(MSG_ORIG(MSG_LD_NOVERSION_64))) == NULL) #else if ((nv = getenv(MSG_ORIG(MSG_LD_NOVERSION_32))) == NULL) #endif nv = getenv(MSG_ORIG(MSG_LD_NOVERSION)); if (nv && nv[0]) return (1); for (APLIST_TRAVERSE(ofl->ofl_soneed, idx1, sdf)) { Aliste idx2; Sdv_desc *sdv; Ifl_desc *ifl = sdf->sdf_file; if (!(sdf->sdf_flags & FLG_SDF_VERIFY)) continue; /* * If this file contains no version definitions then ignore * any versioning verification. This is the same model as * carried out by ld.so.1 and is intended to allow backward * compatibility should a shared object with a version * requirement be returned to an older system on which a * non-versioned shared object exists. */ if ((ifl == NULL) || (ifl->ifl_verdesc == NULL)) continue; /* * If individual versions were specified for this file make * sure that they actually exist in the appropriate file, and * that they are available for binding. */ for (ALIST_TRAVERSE(sdf->sdf_vers, idx2, sdv)) { Aliste idx3; Ver_desc *vdp; int found = 0; for (APLIST_TRAVERSE(ifl->ifl_verdesc, idx3, vdp)) { if (strcmp(sdv->sdv_name, vdp->vd_name) == 0) { found++; break; } } if (found) { Ver_index *vip; vip = &ifl->ifl_verndx[vdp->vd_ndx]; if (!(vip->vi_flags & FLG_VER_AVAIL)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_UNAVAIL), ifl->ifl_name, sdv->sdv_name, sdv->sdv_ref); ofl->ofl_flags |= FLG_OF_FATAL; } } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_VER_NOEXIST), ifl->ifl_name, sdv->sdv_name, sdv->sdv_ref); ofl->ofl_flags |= FLG_OF_FATAL; } } } return (1); }