/* * 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) { size_t idx; Is_desc *isp; int errcnt = 0; for (APLIST_TRAVERSE(ofl->ofl_ismove, idx, isp)) { Ifl_desc *ifile = isp->is_file; Elf64_Move *mvp; Elf64_Xword i, num; mvp = (Elf64_Move *)isp->is_indata->d_buf; if (isp->is_shdr->sh_entsize == 0) { ld_eprintf(ofl, ERR_FATAL, 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++) { Elf64_Xword ndx = ELF_M_SYM(mvp->m_info); Sym_desc *sdp; Elf64_Sym *sym; if ((ndx >= (Elf64_Xword) isp->is_file->ifl_symscnt) || (ndx == 0)) { ld_eprintf(ofl, ERR_FATAL, 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) { ld_eprintf(ofl, ERR_FATAL, 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]; /* * 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: ld_eprintf(ofl, ERR_FATAL, 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(Elf64_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_flags & FLG_SY_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_IS_STATIC_EXEC(ofl)) { if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { errcnt++; ld_eprintf(ofl, ERR_FATAL, MSG_PSYM_CANNOTEXPND, sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, 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) { ld_eprintf(ofl, ERR_WARNING, MSG_PSYM_CANNOTEXPND, sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, i, MSG_STR_EMPTY); } else { sdp->sd_flags |= FLG_SY_PAREXPN; } } else if (((Elf64_Xword)((sizeof (Elf64_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); }
/* * 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); }
/* * Move data. Apply sparse initialization to data in zeroed bss. */ int move_data(Rt_map *lmp, APlist **textrel) { Lm_list *lml = LIST(lmp); Move *mv = MOVETAB(lmp); ulong_t num, mvnum = MOVESZ(lmp) / MOVEENT(lmp); int moves; /* * If these records are against the executable, and the executable was * built prior to Solaris 8, keep track of the move record symbol. See * comment in analyze.c:lookup_sym_interpose() in regards Solaris 8 * objects and DT_FLAGS. */ moves = (lmp == lml->lm_head) && ((FLAGS1(lmp) & FL1_RT_DTFLAGS) == 0); DBG_CALL(Dbg_move_data(lmp)); for (num = 0; num < mvnum; num++, mv++) { mmapobj_result_t *mpp; Addr addr, taddr; Half rep, repno, stride; Sym *sym; if ((sym = (Sym *)SYMTAB(lmp) + ELF_M_SYM(mv->m_info)) == 0) continue; stride = mv->m_stride + 1; addr = sym->st_value; /* * Determine the move data target, and verify the address is * writable. */ if ((FLAGS(lmp) & FLG_RT_FIXED) == 0) addr += ADDR(lmp); taddr = addr + mv->m_poffset; if ((mpp = find_segment((caddr_t)taddr, lmp)) == NULL) { elf_move_bad(lml, lmp, sym, num, taddr); continue; } if (((mpp->mr_prot & PROT_WRITE) == 0) && ((set_prot(lmp, mpp, 1) == 0) || (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) return (0); DBG_CALL(Dbg_move_entry2(lml, mv, sym->st_name, (const char *)(sym->st_name + STRTAB(lmp)))); for (rep = 0, repno = 0; rep < mv->m_repeat; rep++) { DBG_CALL(Dbg_move_expand(lml, mv, taddr)); switch (ELF_M_SIZE(mv->m_info)) { case 1: *((char *)taddr) = (char)mv->m_value; taddr += stride; repno++; break; case 2: /* LINTED */ *((Half *)taddr) = (Half)mv->m_value; taddr += 2 * stride; repno++; break; case 4: /* LINTED */ *((Word *)taddr) = (Word)mv->m_value; taddr += 4 * stride; repno++; break; case 8: /* LINTED */ *((unsigned long long *)taddr) = mv->m_value; taddr += 8 * stride; repno++; break; default: eprintf(lml, ERR_NONE, MSG_INTL(MSG_MOVE_ERR1)); break; } } /* * If any move records have been applied to this symbol, retain * the symbol address if required for backward compatibility * copy relocation processing. */ if (moves && repno && (aplist_append(&alp, (void *)addr, AL_CNT_MOVES) == NULL)) return (0); } /* * Binaries built in the early 1990's prior to Solaris 8, using the ild * incremental linker are known to have zero filled move sections * (presumably place holders for new, incoming move sections). If no * move records have been processed, remove the move identifier to * optimize the amount of backward compatibility copy relocation * processing that is needed. */ if (moves && (alp == NULL)) FLAGS(lmp) &= ~FLG_RT_MOVE; return (1); }