/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "msg.h" #include "_libld.h" /* * Scan all partially initialized symbols to determine what output Move sections * or partially expanded data section, must be created. */ static uintptr_t make_mvsections(Ofl_desc *ofl) { Aliste idx; Sym_desc *sdp; Word mv_nums = 0; Xword align_parexpn = 0; /* for -z nopartial .data sec */ size_t size_parexpn = 0; /* size of parexpn section */ /* * Compute the size of the output move section */ for (APLIST_TRAVERSE(ofl->ofl_parsyms, idx, sdp)) { if (sdp->sd_flags & FLG_SY_PAREXPN) { Sym *sym = sdp->sd_sym; Xword align_val; if (sym->st_shndx == SHN_COMMON) align_val = sym->st_value; else align_val = 8; /* * This global symbol is redirected to the special * partial initialization .data section. */ size_parexpn = (size_t)S_ROUND(size_parexpn, sym->st_value) + sym->st_size; if (align_val > align_parexpn) align_parexpn = align_val; } else { mv_nums += alist_nitems(sdp->sd_move); } } /* * Generate a new Move section. */ if (mv_nums && (ld_make_sunwmove(ofl, mv_nums) == S_ERROR)) return (S_ERROR); /* * Add empty area for partially initialized symbols. * * A special .data section is created when the '-z nopartial' * option is in effect in order to receive the expanded data. */ if (size_parexpn) { /* LINTED */ if (ld_make_parexpn_data(ofl, size_parexpn, align_parexpn) == S_ERROR) return (S_ERROR); } return (1); } /* * Assign move descriptors with the associated target symbol. */ static uintptr_t append_move_desc(Ofl_desc *ofl, Sym_desc *sdp, Move *mvp, Is_desc *isp) { int i, cnt = mvp->m_repeat; for (i = 0; i < cnt; i++) { Aliste 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; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(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_flags1 |= FLG_SY1_OVERLAP; return (1); } if (alist_append(&sdp->sd_move, &nmd, sizeof (Mv_desc), AL_CNT_SDP_MOVE) == NULL) return (S_ERROR); } return (1); } /* * Validate a SHT_SUNW_move section. These are only processed from input * relocatable objects. The move section entries are validated and any data * structures required for later processing are created. */ uintptr_t ld_process_move(Ofl_desc *ofl) { Aliste idx; Is_desc *isp; int errcnt = 0; for (APLIST_TRAVERSE(ofl->ofl_ismove, idx, isp)) { Ifl_desc *ifile = isp->is_file; Move *mvp; Xword i, num; DBG_CALL(Dbg_move_input(ofl->ofl_lml, ifile->ifl_name)); mvp = (Move *)isp->is_indata->d_buf; if (isp->is_shdr->sh_entsize == 0) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHENTSIZE), isp->is_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, EC_XWORD(0)); return (S_ERROR); } num = isp->is_shdr->sh_size / isp->is_shdr->sh_entsize; for (i = 0; i < num; i++) { Xword ndx = ELF_M_SYM(mvp->m_info); Sym_desc *sdp; Sym *sym; if ((ndx >= (Xword) isp->is_file->ifl_symscnt) || (ndx == 0)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_PSYM_INVMINFO1), isp->is_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, EC_XWORD(mvp->m_info)); return (S_ERROR); } if (mvp->m_repeat == 0) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_PSYM_INVMREPEAT), isp->is_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, EC_XWORD(mvp->m_repeat)); return (S_ERROR); } sdp = isp->is_file->ifl_oldndx[ndx]; DBG_CALL(Dbg_move_entry1(ofl->ofl_lml, 1, mvp, sdp)); /* * Validate that this entry has a valid size. */ /* LINTED */ switch (ELF_M_SIZE(mvp->m_info)) { case 1: case 2: case 4: case 8: break; default: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_PSYM_INVMINFO2), isp->is_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, EC_XWORD(mvp->m_info)); return (S_ERROR); } /* * If this is a global symbol, adjust the visibility. */ if (sdp->sd_aux && ((sdp->sd_flags & FLG_SY_VISIBLE) == 0)) ld_sym_adjust_vis(sdp, ofl); sym = sdp->sd_sym; if (sdp->sd_move == NULL) { /* * If this is the first move entry associated * with this symbol, save the symbol on the * partial symbol list, and initialize various * state regarding this symbol. */ if (aplist_append(&ofl->ofl_parsyms, sdp, AL_CNT_OFL_PARSYMS) == NULL) return (S_ERROR); /* * Even if -zredlocsym is in effect, the local * symbol used for partial initialization is * kept. */ if ((ofl->ofl_flags & FLG_OF_REDLSYM) && (ELF_ST_BIND(sym->st_info) == STB_LOCAL) && (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)) { ofl->ofl_locscnt++; if (st_insert(ofl->ofl_strtab, sdp->sd_name) == -1) return (S_ERROR); } /* * Mark the input section associated with this * partially initialized symbol. * This is needed when the symbol * the relocation entry uses symbol information * not from the symbol entry. * * For executable, the following is * needed only for expanded symbol. However, * for shared object any partially non * expanded symbols are moved from * .bss/COMMON to .sunwbss. So the following are * needed. */ if ((sym->st_shndx != SHN_UNDEF) && (sym->st_shndx < SHN_LOPROC)) { Is_desc *isc; isc = ifile->ifl_isdesc[ sym->st_shndx]; isc->is_flags |= FLG_IS_RELUPD; if (sdp->sd_osym == NULL) { if ((sdp->sd_osym = libld_calloc(sizeof (Sym), 1)) == NULL) return (S_ERROR); *(sdp->sd_osym) = *(sdp->sd_sym); } } } if (append_move_desc(ofl, sdp, mvp, isp) == S_ERROR) return (S_ERROR); if (sdp->sd_flags1 & FLG_SY1_OVERLAP) errcnt++; /* * If this symbol is marked to be expanded, go to the * next move entry. */ if (sdp->sd_flags & FLG_SY_PAREXPN) { mvp++; continue; } /* * Decide whether this partial symbol is to be expanded * or not. * * The symbol will be expanded if: * a) '-z nopartial' is specified * b) move entries covered entire symbol * * To expand an move entry, size of the symbol to be * expanded need to be known to generate a file space. * (see make_movesections().) * * Therefore the move entry can not be expanded * if the partial symbol is a section symbol. * (The size of the symbol may be unknown.) * This may happen, for example, when a local symbol is * reduced by the -zredlocsym. * * The following two if statements checks the * if the move entry can be expanded or not. */ if (((ofl->ofl_flags & FLG_OF_STATIC) != 0) && ((ofl->ofl_flags & FLG_OF_EXEC) != 0)) { if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { errcnt++; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_PSYM_CANNOTEXPND), sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, MSG_INTL(MSG_PSYM_NOSTATIC)); } else { sdp->sd_flags |= FLG_SY_PAREXPN; } } else if ((ofl->ofl_flags1 & FLG_OF1_NOPARTI) != 0) { if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_PSYM_CANNOTEXPND), sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, MSG_ORIG(MSG_STR_EMPTY)); } else { sdp->sd_flags |= FLG_SY_PAREXPN; } } else if (((Xword)((sizeof (Move)) * alist_nitems(sdp->sd_move)) > sym->st_size) && (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)) { sdp->sd_flags |= FLG_SY_PAREXPN; } /* * If a move entry exists that references a local * symbol, and this symbol reference will eventually * be assigned to the associated section, make sure the * section symbol is available for relocating against * at runtime. */ if ((ELF_ST_BIND(sym->st_info) == STB_LOCAL) && (((ofl->ofl_flags & FLG_OF_RELOBJ) == 0) || (ofl->ofl_flags & FLG_OF_REDLSYM))) { Os_desc *osp = sdp->sd_isc->is_osdesc; if (osp && ((osp->os_flags & FLG_OS_OUTREL) == 0)) { ofl->ofl_dynshdrcnt++; osp->os_flags |= FLG_OS_OUTREL; } else if ((sdp->sd_flags & FLG_SY_PAREXPN) == 0) ofl->ofl_flags1 |= FLG_OF1_BSSOREL; } mvp++; } } if (errcnt != 0) return (S_ERROR); if (make_mvsections(ofl) == S_ERROR) return (S_ERROR); return (1); }