Group_desc * ld_get_group(Ofl_desc *ofl, Is_desc *isp) { Ifl_desc *ifl = isp->is_file; uint_t scnndx = isp->is_scnndx; Group_desc *gdp; Aliste idx; /* * Scan the GROUP sections associated with this file to find the * matching group section. */ for (ALIST_TRAVERSE(ifl->ifl_groups, idx, gdp)) { size_t ndx; Word *data; if (isp->is_shdr->sh_type == SHT_GROUP) { if (isp->is_scnndx == gdp->gd_isc->is_scnndx) return (gdp); continue; } data = gdp->gd_data; for (ndx = 1; ndx < gdp->gd_cnt; ndx++) { if (data[ndx] == scnndx) return (gdp); } } ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ELF_NOGROUPSECT), ifl->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name); return (NULL); }
/* * Clean up (free) an audit descriptor. First, gather a list of all handles, * and then close each one down. This is done rather than using the handles * directly from the auditors, as the audit list can be torn down as a result * of the dlclose. In other words, what you're pointing at can be removed * while your still pointing at it. */ void audit_desc_cleanup(Audit_desc *adp, Rt_map *clmp) { Audit_list *alp; Listnode *lnp, *olnp; Alist *ghalp = 0; if (adp == 0) return; if (adp->ad_name) free(adp->ad_name); olnp = 0; for (LIST_TRAVERSE(&(adp->ad_list), lnp, alp)) { (void) alist_append(&ghalp, &(alp->al_ghp), sizeof (Grp_hdl *), AL_CNT_GROUPS); if (olnp) free(olnp); olnp = lnp; } if (olnp) free(olnp); if (ghalp) { Grp_hdl ** ghpp; Aliste off; for (ALIST_TRAVERSE(ghalp, off, ghpp)) (void) dlclose_intn(*ghpp, clmp); free(ghalp); } free(adp); }
static Ver_index * vers_index(Ofl_desc *ofl, Ifl_desc *ifl, int avail) { size_t idx1; Ver_desc *vdp; Ver_index *vip; Sdf_desc *sdf = ifl->ifl_sdfdesc; Elf64_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)) { size_t 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; } } }
/* * Traverse all segments looking for section ordering information that hasn't * been used. If found give a warning message to the user. Also, check if * there are any SHF_ORDERED key sections, and if so set up sort key values. */ void ld_sec_validate(Ofl_desc *ofl) { Listnode *lnp1; Sg_desc *sgp; int key = 1; for (LIST_TRAVERSE(&ofl->ofl_segs, lnp1, sgp)) { Sec_order **scopp; Os_desc **ospp; Aliste off; for (ALIST_TRAVERSE(sgp->sg_secorder, off, scopp)) { Sec_order *scop = *scopp; if ((scop->sco_flags & FLG_SGO_USED) == 0) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_SECORDER), sgp->sg_name, scop->sco_secname); } } if ((sgp->sg_flags & FLG_SG_KEY) == 0) continue; for (ALIST_TRAVERSE(sgp->sg_osdescs, off, ospp)) { Listnode *lnp2; Is_desc *isp; Os_desc *osp = *ospp; if ((osp->os_flags & FLG_OS_ORDER_KEY) == 0) continue; for (LIST_TRAVERSE(&(osp->os_isdescs), lnp2, isp)) { if (isp->is_flags & FLG_IS_KEY) isp->is_key = key++; } } } }
/* * Obtain a head link-map cookie. Local auditors can provide la_preinit() and * la_activity() routines, and these routines require a cookie that represents * the object that heads the link-map of the object being audited. A list of * these cookies is maintained on the link-map list. This list allows multiple * local objects to specify the same auditor, and to obtain the same cookie * for the link-map that heads the link-map list. * * The initial cookie is created by _audit_create_head_client() which is called * from _audit_add_head(). This cookies address is then passed to the local * auditors ld_objopen() and la_activity() routines. Subsequent preinit and * activity events use _audit_get_head_client() to dynamically retrieve the * cookies address. */ static Audit_client * _audit_get_head_client(Rt_map *hlmp, Rt_map *almp) { Audit_client *acp; Aliste idx; Lm_list *hlml = LIST(hlmp); for (ALIST_TRAVERSE(hlml->lm_aud_cookies, idx, acp)) { if (acp->ac_lmp == almp) return (acp); } return (NULL); }
/* * Add a section name to the output section sort list for the given * segment. * * entry: * mf - Mapfile descriptor * sgp - Segment in question * sec_name - Name of section to be added. * * exit: * Returns true for success, false for failure. */ bool ld_map_seg_os_order_add(Mapfile *mf, Sg_desc *sgp, const char *sec_name) { size_t idx; Sec_order *scop; /* * Make sure it's not already on the list */ for (ALIST_TRAVERSE(sgp->sg_os_order, idx, scop)) if (strcmp(scop->sco_secname, sec_name) == 0) { mf_fatal(mf, (MSG_MAP_DUP_OS_ORD), sec_name); return (false); } scop = alist_append(&sgp->sg_os_order, NULL, sizeof (Sec_order), AL_CNT_SG_SECORDER); if (scop == NULL) return (false); scop->sco_secname = sec_name; //DBG_CALL(Dbg_map_seg_os_order(mf->mf_ofl->ofl_lml, sgp, sec_name, // alist_nitems(sgp->sg_os_order), mf->mf_lineno)); /* * Output section ordering is a relatively expensive operation, * and one that is generally not used. In order to avoid needless * work, the FLG_OF_OS_ORDER must be set when it will be needed. * The section we just added needs this flag to be set. However, * it is possible that a subsequent mapfile directive may come * along and clear the order list, making it unnecessary. * * Instead of setting it here, we do a final pass over the segments * in ld_map_finalize() and set it there if a segment with sorting * requirements is seen. */ return (true); }
/* * Assign move descriptors with the associated target symbol. */ static uintptr_t append_move_desc(Ofl_desc *ofl, Sym_desc *sdp, Elf64_Move *mvp, Is_desc *isp) { int i, cnt = mvp->m_repeat; for (i = 0; i < cnt; i++) { size_t idx; Mv_desc *omdp, nmd; /* LINTED */ nmd.md_len = ELF_M_SIZE(mvp->m_info); nmd.md_start = mvp->m_poffset + i * ((mvp->m_stride + 1) * nmd.md_len); nmd.md_move = mvp; /* * Verify that this move descriptor doesn't overlap any existing * move descriptors. */ for (ALIST_TRAVERSE(sdp->sd_move, idx, omdp)) { Mv_desc *smdp, *lmdp; if (nmd.md_start > omdp->md_start) { smdp = omdp; lmdp = &nmd; } else { smdp = &nmd; lmdp = omdp; } /* * If this move entry is exactly the same as that of * a symbol that has overridden this symbol (for example * should two identical COMMON definitions be associated * with the same move data), simply ignore this move * element. */ if ((nmd.md_start == omdp->md_start) && ((nmd.md_len == smdp->md_len) && sdp->sd_file != isp->is_file)) continue; if ((nmd.md_start != omdp->md_start) && ((smdp->md_start + smdp->md_len) <= lmdp->md_start)) continue; ld_eprintf(ofl, ERR_FATAL, MSG_MOVE_OVERLAP, sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, demangle(sdp->sd_name), EC_XWORD(nmd.md_start), EC_XWORD(nmd.md_len), EC_XWORD(omdp->md_start), EC_XWORD(omdp->md_len)); /* * Indicate that an error has occurred, so that * processing can be terminated once all move errors * are flushed out. */ sdp->sd_flags |= FLG_SY_OVERLAP; return (1); } if (alist_append(&sdp->sd_move, &nmd, sizeof (Mv_desc), AL_CNT_SDP_MOVE) == NULL) return (S_ERROR); } return (1); }
/* * Enter a mapfile defined symbol into the given version * * entry: * mf - Mapfile descriptor * ms - Information related to symbol being added to version * * exit: * On success, returns true. On failure that requires an immediate * halt, returns false. * * On failure that requires eventual halt, but for which it would * be OK to continue parsing in hopes of flushing out additional * problems, increments mv->mv_errcnt, and returns true. */ bool ld_map_sym_enter(Mapfile *mf, ld_map_ver_t *mv, ld_map_sym_t *ms) { Ofl_desc *ofl = mf->mf_ofl; Elf64_Word hash; avl_index_t where; Elf64_Sym *sym; Sym_desc *sdp; const char *conflict; /* * Add the new symbol. It should be noted that all * symbols added by the mapfile start out with global * scope, thus they will fall through the normal symbol * resolution process. Elf64_Symbols defined as locals will * be reduced in scope after all input file processing. */ /* LINTED */ hash = (Elf64_Word)elf_hash(ms->ms_name); //DBG_CALL(Dbg_map_version(ofl->ofl_lml, mv->mv_name, ms->ms_name, // mv->mv_scope)); /* * Make sure that any parent or external declarations fall back to * references. */ if (ms->ms_sdflags & (FLG_SY_PARENT | FLG_SY_EXTERN)) { /* * Turn it into a reference by setting the section index * to UNDEF. */ ms->ms_shndx = SHN_UNDEF; /* * It is wrong to specify size or value for an external symbol. */ if (ms->ms_value_set || (ms->ms_size != 0)) { mf_fatal0(mf, (MSG_MAP_NOEXVLSZ)); mv->mv_errcnt++; return (true); } } if ((sdp = ld_sym_find(ms->ms_name, hash, &where, ofl)) == NULL) { if ((sym = libld_calloc(sizeof (Elf64_Sym), 1)) == NULL) return (false); sym->st_shndx = (Elf64_Half)ms->ms_shndx; sym->st_value = ms->ms_value; sym->st_size = ms->ms_size; sym->st_info = ELF_ST_INFO(STB_GLOBAL, ms->ms_type); if ((sdp = ld_sym_enter(ms->ms_name, sym, hash, ld_map_ifl(mf), ofl, 0, ms->ms_shndx, ms->ms_sdflags, &where)) == (Sym_desc *)S_ERROR) return (false); sdp->sd_flags &= ~FLG_SY_CLEAN; /* * Identify any references. FLG_SY_MAPREF is * turned off once a relocatable object with * the same symbol is found, thus the existence * of FLG_SY_MAPREF at symbol validation is * used to flag undefined/misspelled entries. */ if (sym->st_shndx == SHN_UNDEF) sdp->sd_flags |= (FLG_SY_MAPREF | FLG_SY_GLOBREF); } else { conflict = NULL; sym = sdp->sd_sym; /* * If this symbol already exists, make sure this * definition doesn't conflict with the former. * Provided it doesn't, multiple definitions * from different mapfiles can augment each * other. */ if (sym->st_value) { if (ms->ms_value && (sym->st_value != ms->ms_value)) conflict = (MSG_MAP_DIFF_SYMVAL); } else { sym->st_value = ms->ms_value; } if (sym->st_size) { if (ms->ms_size && (sym->st_size != ms->ms_size)) conflict = (MSG_MAP_DIFF_SYMSZ); } else { sym->st_size = ms->ms_size; } if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE) { if ((ms->ms_type != STT_NOTYPE) && (ELF_ST_TYPE(sym->st_info) != ms->ms_type)) conflict = (MSG_MAP_DIFF_SYMTYP); } else { sym->st_info = ELF_ST_INFO(STB_GLOBAL, ms->ms_type); } if (sym->st_shndx != SHN_UNDEF) { if ((ms->ms_shndx != SHN_UNDEF) && (sym->st_shndx != ms->ms_shndx)) conflict = (MSG_MAP_DIFF_SYMNDX); } else { sym->st_shndx = sdp->sd_shndx = ms->ms_shndx; } if ((sdp->sd_flags & MSK_SY_GLOBAL) && (sdp->sd_aux->sa_overndx != VER_NDX_GLOBAL) && (mv->mv_vdp->vd_ndx != VER_NDX_GLOBAL) && (sdp->sd_aux->sa_overndx != mv->mv_vdp->vd_ndx)) { conflict = (MSG_MAP_DIFF_SYMVER); } if (conflict) { mf_fatal(mf, (MSG_MAP_SYMDEF1), demangle(ms->ms_name), sdp->sd_file->ifl_name, conflict); mv->mv_errcnt++; return (true); } /* * If this mapfile entry supplies a definition, * indicate that the symbol is now used. */ if (ms->ms_shndx != SHN_UNDEF) sdp->sd_flags |= FLG_SY_MAPUSED; } /* * A symbol declaration that defines a size but no * value is processed as a request to create an * associated backing section. The intent behind this * functionality is to provide OBJT definitions within * filters that are not ABS. ABS symbols don't allow * copy-relocations to be established to filter OBJT * definitions. */ if ((ms->ms_shndx == SHN_ABS) && ms->ms_size && !ms->ms_value_set) { /* Create backing section if not there */ if (sdp->sd_isc == NULL) { Is_desc *isp; if (ms->ms_type == STT_OBJECT) { if ((isp = ld_make_data(ofl, ms->ms_size)) == (Is_desc *)S_ERROR) return (false); } else { if ((isp = ld_make_text(ofl, ms->ms_size)) == (Is_desc *)S_ERROR) return (false); } sdp->sd_isc = isp; isp->is_file = ld_map_ifl(mf); } /* * Now that backing storage has been created, * associate the symbol descriptor. Remove the * symbols special section tag so that it will * be assigned the correct section index as part * of update symbol processing. */ sdp->sd_flags &= ~FLG_SY_SPECSEC; ms->ms_sdflags &= ~FLG_SY_SPECSEC; } /* * Indicate the new symbols scope. Although the * symbols st_other field will eventually be updated as * part of writing out the final symbol, update the * st_other field here to trigger better diagnostics * during symbol validation (for example, undefined * references that are defined symbolic in a mapfile). */ if (mv->mv_scope == FLG_SCOPE_HIDD) { /* * This symbol needs to be reduced to local. */ if (ofl->ofl_flags & FLG_OF_REDLSYM) { sdp->sd_flags |= (FLG_SY_HIDDEN | FLG_SY_ELIM); sdp->sd_sym->st_other = STV_ELIMINATE; } else { sdp->sd_flags |= FLG_SY_HIDDEN; sdp->sd_sym->st_other = STV_HIDDEN; } } else if (mv->mv_scope == FLG_SCOPE_ELIM) { /* * This symbol needs to be eliminated. Note, * the symbol is also tagged as local to trigger * any necessary relocation processing prior * to the symbol being eliminated. */ sdp->sd_flags |= (FLG_SY_HIDDEN | FLG_SY_ELIM); sdp->sd_sym->st_other = STV_ELIMINATE; } else { /* * This symbol is explicitly defined to remain * global. */ sdp->sd_flags |= ms->ms_sdflags; /* * Qualify any global scope. */ if (mv->mv_scope == FLG_SCOPE_SNGL) { sdp->sd_flags |= (FLG_SY_SINGLE | FLG_SY_NDIR); sdp->sd_sym->st_other = STV_SINGLETON; } else if (mv->mv_scope == FLG_SCOPE_PROT) { sdp->sd_flags |= FLG_SY_PROTECT; sdp->sd_sym->st_other = STV_PROTECTED; } else if (mv->mv_scope == FLG_SCOPE_EXPT) { sdp->sd_flags |= FLG_SY_EXPORT; sdp->sd_sym->st_other = STV_EXPORTED; } else sdp->sd_flags |= FLG_SY_DEFAULT; /* * Record the present version index for later * potential versioning. */ if ((sdp->sd_aux->sa_overndx == 0) || (sdp->sd_aux->sa_overndx == VER_NDX_GLOBAL)) sdp->sd_aux->sa_overndx = mv->mv_vdp->vd_ndx; mv->mv_vdp->vd_flags |= FLG_VER_REFER; } conflict = NULL; /* * Carry out some validity checks to ensure incompatible * symbol characteristics have not been defined. * These checks are carried out after symbols are added * or resolved, to catch single instance, and * multi-instance definition inconsistencies. */ if ((sdp->sd_flags & (FLG_SY_HIDDEN | FLG_SY_ELIM)) && ((mv->mv_scope != FLG_SCOPE_HIDD) && (mv->mv_scope != FLG_SCOPE_ELIM))) { conflict = (MSG_MAP_DIFF_SYMLCL); } else if ((sdp->sd_flags & (FLG_SY_SINGLE | FLG_SY_EXPORT)) && ((mv->mv_scope != FLG_SCOPE_DFLT) && (mv->mv_scope != FLG_SCOPE_EXPT) && (mv->mv_scope != FLG_SCOPE_SNGL))) { conflict = (MSG_MAP_DIFF_SYMGLOB); } else if ((sdp->sd_flags & FLG_SY_PROTECT) && ((mv->mv_scope != FLG_SCOPE_DFLT) && (mv->mv_scope != FLG_SCOPE_PROT))) { conflict = (MSG_MAP_DIFF_SYMPROT); } else if ((sdp->sd_flags & FLG_SY_NDIR) && (mv->mv_scope == FLG_SCOPE_PROT)) { conflict = (MSG_MAP_DIFF_PROTNDIR); } else if ((sdp->sd_flags & FLG_SY_DIR) && (mv->mv_scope == FLG_SCOPE_SNGL)) { conflict = (MSG_MAP_DIFF_SNGLDIR); } if (conflict) { /* * Select the conflict message from either a * single instance or multi-instance definition. */ if (sdp->sd_file->ifl_name == mf->mf_name) { mf_fatal(mf, (MSG_MAP_SYMDEF2), demangle(ms->ms_name), conflict); } else { mf_fatal(mf, (MSG_MAP_SYMDEF1), demangle(ms->ms_name), sdp->sd_file->ifl_name, conflict); } mv->mv_errcnt++; return (true); } /* * Indicate that this symbol has been explicitly * contributed from a mapfile. */ sdp->sd_flags |= (FLG_SY_MAPFILE | FLG_SY_EXPDEF); /* * If we've encountered a symbol definition simulate * that an input file has been processed - this allows * things like filters to be created purely from a * mapfile. */ if (ms->ms_type != STT_NOTYPE) ofl->ofl_objscnt++; //DBG_CALL(Dbg_map_symbol(ofl, sdp)); /* * If this symbol has an associated filtee, record the * filtee string and associate the string index with the * symbol. This is used later to associate the syminfo * information with the necessary .dynamic entry. */ if (ms->ms_filtee) { Dfltr_desc * dftp; Sfltr_desc sft; size_t idx, _idx, nitems; /* * Make sure we don't duplicate any filtee * strings, and create a new descriptor if * necessary. */ idx = nitems = alist_nitems(ofl->ofl_dtsfltrs); for (ALIST_TRAVERSE(ofl->ofl_dtsfltrs, _idx, dftp)) { if ((ms->ms_dft_flag != dftp->dft_flag) || (strcmp(dftp->dft_str, ms->ms_filtee))) continue; idx = _idx; break; } if (idx == nitems) { Dfltr_desc dft; dft.dft_str = ms->ms_filtee; dft.dft_flag = ms->ms_dft_flag; dft.dft_ndx = 0; /* * The following append puts the new * item at the offset contained in * idx, because we know idx contains * the index of the next available slot. */ if (alist_append(&ofl->ofl_dtsfltrs, &dft, sizeof (Dfltr_desc), AL_CNT_OFL_DTSFLTRS) == NULL) return (false); } /* * Create a new filter descriptor for this * symbol. */ sft.sft_sdp = sdp; sft.sft_idx = idx; if (alist_append(&ofl->ofl_symfltrs, &sft, sizeof (Sfltr_desc), AL_CNT_OFL_SYMFLTRS) == NULL) return (false); }