void Dbg_sec_unsup_strmerge(Lm_list *lml, Is_desc *isp) { dbg_isec_name_buf_t buf; char *alloc_mem; const char *str; if (DBG_NOTCLASS(DBG_C_SECTIONS)) return; /* * We can only merge string table sections with single byte * (char) characters. For any other (wide) character types, * issue a message so the user will understand why these * sections are not being picked up. */ if ((isp->is_shdr->sh_entsize > 1) || (isp->is_shdr->sh_addralign > 1)) { str = (isp->is_file != NULL) ? isp->is_file->ifl_name : MSG_INTL(MSG_STR_NULL); dbg_print(lml, MSG_INTL(MSG_SEC_STRMERGE_UNSUP), dbg_fmt_isec_name(isp, buf, &alloc_mem), str, EC_XWORD(isp->is_shdr->sh_addralign), EC_XWORD(isp->is_shdr->sh_entsize)); if (alloc_mem != NULL) free(alloc_mem); } }
void Dbg_sec_genstr_compress(Lm_list *lml, const char *os_name, Xword raw_size, Xword merge_size) { if (DBG_NOTCLASS(DBG_C_SECTIONS)) return; dbg_print(lml, MSG_INTL(MSG_SEC_GENSTR_COMP), os_name, EC_XWORD(raw_size), EC_XWORD(merge_size)); }
void Dbg_sec_strtab(Lm_list *lml, Os_desc *osp, Str_tbl *stp) { uint_t cnt; if (DBG_NOTCLASS(DBG_C_STRTAB)) return; if (!osp) return; Dbg_util_nl(lml, DBG_NL_STD); if (stp->st_flags & FLG_STTAB_COMPRESS) dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_COMP), osp->os_name, EC_XWORD(stp->st_fullstrsize), EC_XWORD(stp->st_strsize)); else dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_STND), osp->os_name, EC_XWORD(stp->st_fullstrsize)); if ((DBG_NOTDETAIL()) || ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)) return; dbg_print(lml, MSG_ORIG(MSG_STR_EMPTY)); dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_HD), osp->os_name, stp->st_hbckcnt); for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) { Str_hash *strhash = stp->st_hashbcks[cnt]; if (strhash == NULL) continue; dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_BCKT), cnt); while (strhash) { size_t stroff = strhash->hi_mstr->sm_strlen - strhash->hi_strlen; if (stroff == 0) { dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_MSTR), EC_XWORD(strhash->hi_refcnt), strhash->hi_mstr->sm_str); } else { dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_SUFSTR), EC_XWORD(strhash->hi_refcnt), &strhash->hi_mstr->sm_str[stroff], strhash->hi_mstr->sm_str); } strhash = strhash->hi_next; } } }
void Dbg_move_adjmovereloc(Lm_list *lml, Xword offset1, Xword offset2, const char *name) { if (DBG_NOTCLASS(DBG_C_MOVE | DBG_C_RELOC)) return; if (DBG_NOTDETAIL()) return; dbg_print(lml, MSG_INTL(MSG_MOVE_ADJMOVE), Dbg_demangle_name(name), EC_XWORD(offset1), EC_XWORD(offset2)); }
elfedit_atoui_t elfedit_atoui_range(const char *str, const char *item_name, elfedit_atoui_t min, elfedit_atoui_t max, const elfedit_atoui_sym_t *sym) { elfedit_atoui_t v = elfedit_atoui(str, sym); if ((v < min) || (v > max)) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_ATOUIRANGE), item_name, EC_XWORD(min), EC_XWORD(max), EC_XWORD(v)); return (v); }
void Dbg_tls_static_resv(Rt_map *lmp, ulong_t size, ulong_t resv) { Lm_list *lml = LIST(lmp); if (DBG_NOTCLASS(DBG_C_TLS)) return; Dbg_util_nl(lml, DBG_NL_STD); dbg_print(lml, MSG_INTL(MSG_TLS_STATBLOCK3), TLSMODID(lmp), NAME(lmp), EC_XWORD(size), EC_XWORD(resv)); }
void Dbg_unused_sec(Lm_list *lml, Is_desc *isp) { const char *str; if (DBG_NOTCLASS(DBG_C_UNUSED)) return; if (DBG_NOTDETAIL()) return; /* * If the file from which this section originates hasn't been referenced * at all, skip this diagnostic, as it would have been covered under * Dbg_unused_file() called from ignore_section_processing(). */ if (isp->is_file && ((isp->is_file->ifl_flags & FLG_IF_FILEREF) == 0)) return; if (isp->is_flags & FLG_IS_DISCARD) str = MSG_INTL(MSG_USD_SECDISCARD); else str = MSG_ORIG(MSG_STR_EMPTY); dbg_print(lml, MSG_INTL(MSG_USD_SEC), isp->is_basename, EC_XWORD(isp->is_shdr->sh_size), isp->is_file->ifl_name, str); }
static int conv_cap(Xword val, char *str, size_t len, Half mach, Conv_fmt_flags_t fmt_flags, elfcap_to_str_func_t *fptr) { size_t _len; int do_bkt = (fmt_flags & CONV_FMT_NOBKT) == 0; /* * Note that for the purposes of this routine, I consider * CONV_FMT_NOBKT to mean no brackets, or anything that * is placed outside of them. We also drop the hex version * of the flags that are put in front of the opening bracket. */ if (do_bkt) { _len = sprintf(str, MSG_ORIG(MSG_GBL_OSQBRKT), EC_XWORD(val)); len -= _len; str += _len; } if ((*fptr)(ELFCAP_STYLE_UC, val, str, len, ELFCAP_FMT_SNGSPACE, mach) != 0) return (0); if (do_bkt) { _len = strlen(str); if ((len - _len) >= MSG_GBL_CSQBRKT_SIZE) { str += _len; (void) strcpy(str, MSG_ORIG(MSG_GBL_CSQBRKT)); } } return (1); }
void Dbg_audit_pltenter(Lm_list *lml, int call, const char *lib, const char *name, Addr value) { if (DBG_NOTCLASS(DBG_C_AUDITING)) return; if (call == DBG_AUD_CALL) { Dbg_util_nl(lml, DBG_NL_STD); dbg_print(lml, MSG_INTL(MSG_AUD_PLTENTER), lib, name, EC_XWORD(value)); } else { dbg_print(lml, MSG_INTL(MSG_AUD_PLTENTER_R), lib, name, EC_XWORD(value)); } }
void Elf_got_entry(Lm_list *lml, Sword ndx, Addr addr, Xword value, Half mach, uchar_t ei_target_data, uchar_t ei_host_data, Word type, void *reloc, const char *name) { Rela *rela; Rel *rel; const char *str; Conv_inv_buf_t inv_buf; char index[INDEX_STR_SIZE]; (void) snprintf(index, INDEX_STR_SIZE, MSG_ORIG(MSG_GOT_INDEX), EC_SWORD(ndx)); /* * Got sections are SHT_PROGBITS, and are therefore not xlated by * libelf. If the target system has a different byte order than * the system displaying the data, swap the bytes so they are * presented properly. */ if (ei_target_data != ei_host_data) value = BSWAP_XWORD(value); if (reloc) { if (type == SHT_RELA) { rela = (Rela *)reloc; str = conv_reloc_type(mach, ELF_R_TYPE(rela->r_info, mach), 0, &inv_buf); } else { rel = (Rel *)reloc; str = conv_reloc_type(mach, ELF_R_TYPE(rel->r_info, mach), 0, &inv_buf); } if (name) name = Elf_demangle_name(name); else name = MSG_ORIG(MSG_STR_EMPTY); dbg_print(lml, MSG_INTL(MSG_GOT_ENTRY_RE), index, EC_ADDR(addr), EC_XWORD(value), str, name); } else dbg_print(lml, MSG_INTL(MSG_GOT_ENTRY_NR), index, EC_ADDR(addr), EC_XWORD(value)); }
void Dbg_audit_symbind(Lm_list *lml, int call, const char *lib, const char *name, Addr value, uint_t flags) { Conv_la_symbind_buf_t la_symbind_buf; if (DBG_NOTCLASS(DBG_C_AUDITING)) return; if (call == DBG_AUD_CALL) { Dbg_util_nl(lml, DBG_NL_STD); dbg_print(lml, MSG_INTL(MSG_AUD_SYMBIND), lib, name, EC_XWORD(value), conv_la_symbind(flags, &la_symbind_buf)); } else { dbg_print(lml, MSG_INTL(MSG_AUD_SYMBIND_R), lib, name, EC_XWORD(value), conv_la_symbind(flags, &la_symbind_buf)); } }
void Dbg_audit_symval(Lm_list *lml, const char *lib, const char *func, const char *sym, Addr pval, Addr nval) { char mesg[100]; if (DBG_NOTCLASS(DBG_C_AUDITING)) return; if (DBG_NOTDETAIL()) return; if (pval == nval) mesg[0] = '\0'; else (void) sprintf(mesg, MSG_INTL(MSG_AUD_SYMNEW), EC_XWORD(nval)); dbg_print(lml, MSG_INTL(MSG_AUD_SYM), lib, func, Dbg_demangle_name(sym), EC_XWORD(pval), mesg); }
/* * Sanity check the given capability bitmask. */ bool ld_map_cap_sanitize(Mapfile *mf, Elf64_Word type, Capmask *capmask) { elfcap_mask_t mask; switch (type) { case CA_SUNW_SF_1: /* * Unlike hardware capabilities, we do not allow setting * software capability bits that do not have known definitions. * Software capability tokens have to be validated as a unit * as the bits can affect each others meaning (see sf1_cap() * in files.c). */ if ((mask = (capmask->cm_val & ~SF1_SUNW_MASK)) != 0) { mf_warn(mf, (MSG_MAP_BADSF1), EC_XWORD(mask)); capmask->cm_val &= SF1_SUNW_MASK; } if ((capmask->cm_val & (SF1_SUNW_FPKNWN | SF1_SUNW_FPUSED)) == SF1_SUNW_FPUSED) { mf_warn(mf, (MSG_MAP_BADSF1), EC_XWORD(SF1_SUNW_FPUSED)); capmask->cm_val &= ~SF1_SUNW_FPUSED; } #if !defined(_ELF64) #if 0 /* * The SF1_SUNW_ADDR32 software capability is only meaningful * when building a 64-bit object. Warn the user, and remove the * setting, if we're building a 32-bit object. */ if (capmask->cm_val & SF1_SUNW_ADDR32) { mf_warn0(mf, (MSG_MAP_INADDR32SF1)); capmask->cm_val &= ~SF1_SUNW_ADDR32; } #endif #endif } return (true); }
void Elf_dyn_entry(Lm_list *lml, Dyn *dyn, int ndx, const char *name, uchar_t osabi, Half mach) { Conv_inv_buf_t inv_buf; char index[INDEX_STR_SIZE]; (void) snprintf(index, sizeof (index), MSG_ORIG(MSG_FMT_INDEX), ndx); dbg_print(lml, MSG_INTL(MSG_DYN_ENTRY), index, conv_dyn_tag(dyn->d_tag, osabi, mach, 0, &inv_buf), EC_XWORD(dyn->d_un.d_val), name); }
static void Dbg_tls_modent(Lm_list *lml, TLS_modinfo * tmodent) { /* * MSG_ORIG(MSG_TLS_FLAG_STATIC) */ static char flagstr[FLAGSZ]; static Val_desc vda[] = { { TM_FLG_STATICTLS, MSG_TLS_FLAG_STATIC }, { 0, 0 } }; static CONV_EXPN_FIELD_ARG conv_arg = { flagstr, sizeof (flagstr) }; ulong_t flags; if ((flags = tmodent->tm_flags) != 0) { conv_arg.oflags = conv_arg.rflags = flags; (void) conv_expn_field(&conv_arg, vda, 0); } else { flagstr[0] = '\0'; } dbg_print(lml, MSG_INTL(MSG_TLS_MODENT1), EC_XWORD((uintptr_t)tmodent->tm_tlsblock), EC_XWORD(tmodent->tm_stattlsoffset), EC_XWORD(tmodent->tm_flags), flagstr); dbg_print(lml, MSG_INTL(MSG_TLS_MODENT2), EC_XWORD(tmodent->tm_filesz), EC_XWORD(tmodent->tm_memsz), EC_XWORD(tmodent->tm_modid)); }
/* * Generate a statistics line for the auxiliary relocation descriptor cache. * * entry: * ofl - output file descriptor */ static void rel_aux_cache_statistics(Ofl_desc *ofl) { Rel_aux_cachebuf *racp; Lm_list *lml = ofl->ofl_lml; size_t desc_cnt = 0, desc_used = 0, bytes; Aliste idx; char unit_buf[CONV_INV_BUFSIZE + 10]; /* Sum the total memory allocated across all the buffers */ for (APLIST_TRAVERSE(ofl->ofl_relaux, idx, racp)) { desc_cnt += racp->rac_end - racp->rac_arr; desc_used += racp->rac_free - racp->rac_arr; } bytes = desc_cnt * sizeof (Rel_desc); dbg_print(lml, MSG_INTL(MSG_STATS_REL_ACACHE), EC_WORD(aplist_nitems(ofl->ofl_relaux)), EC_XWORD(desc_used), EC_XWORD(desc_cnt), (desc_cnt == 0) ? 100 : EC_WORD((desc_used * 100) / desc_cnt), EC_XWORD(bytes), fmt_human_units(bytes, unit_buf, sizeof (unit_buf))); }
void Dbg_tls_static_block(Lm_list *lml, void *list, ulong_t size, ulong_t resv) { if (DBG_NOTCLASS(DBG_C_TLS)) return; Dbg_util_nl(lml, DBG_NL_STD); if (list) { ulong_t ndx; TLS_modinfo **tlsmodlist; tlsmodlist = (TLS_modinfo **)list; for (ndx = 0; tlsmodlist[ndx]; ndx++) { dbg_print(lml, MSG_INTL(MSG_TLS_STATBLOCK1), ndx, tlsmodlist[ndx]->tm_modname); Dbg_tls_modent(lml, tlsmodlist[ndx]); Dbg_util_nl(lml, DBG_NL_STD); } } dbg_print(lml, MSG_INTL(MSG_TLS_STATBLOCK2), EC_XWORD(size), EC_XWORD(resv)); }
/* * -P flag specified */ static void print_with_Pflag( int ndigits, Elf *elf_file, unsigned int shstrndx, SYM *sym_data ) { #define SYM_LEN 10 char sym_name[SYM_LEN+1]; size_t len; const char * const fmt[] = { "%*llu %*llu \n", /* FMT_T_DEC */ "%*llx %*llx \n", /* FMT_T_HEX */ "%*llo %*llo \n" /* FMT_T_OCT */ }; if (is_sym_print(sym_data) != 1) return; /* * -A header */ if (A_flag != 0) (void) printf("%s", A_header); /* * Symbol name */ len = strlen(sym_data->name); if (len >= SYM_LEN) (void) printf("%s ", sym_data->name); else { (void) sprintf(sym_name, "%-10s", sym_data->name); (void) printf("%s ", sym_name); } /* * Symbol Type. */ print_brief_sym_type(elf_file, shstrndx, sym_data); /* * Symbol Value & size * (hex/octal/decimal) */ (void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value), ndigits, EC_XWORD(sym_data->size)); }
/* * Variant of Elf_dyn_entry() specifically for DT_NULL. Handles the * case of multiple adjacent DT_NULL entries by displaying them on * a single line using an index range instead of a single index. */ void Elf_dyn_null_entry(Lm_list *lml, Dyn *dyn, int start_ndx, int end_ndx) { Conv_inv_buf_t inv_buf; char index[2 * INDEX_STR_SIZE]; if (start_ndx == end_ndx) { Elf_dyn_entry(lml, dyn, start_ndx, MSG_ORIG(MSG_STR_EMPTY), ELFOSABI_NONE, 0); } else { (void) snprintf(index, sizeof (index), MSG_ORIG(MSG_FMT_INDEX_RANGE), start_ndx, end_ndx); dbg_print(lml, MSG_INTL(MSG_DYN_ENTRY), index, conv_dyn_tag(DT_NULL, ELFOSABI_NONE, 0, 0, &inv_buf), EC_XWORD(dyn->d_un.d_val), MSG_ORIG(MSG_STR_EMPTY)); } }
void Dbg_move_entry2(Lm_list *lml, Move *mv, Word st_name, const char *name) { const char *sname; if (DBG_NOTCLASS(DBG_C_MOVE)) return; if (DBG_NOTDETAIL()) return; if (st_name) sname = name; else sname = MSG_INTL(MSG_STR_UNKNOWN); dbg_print(lml, MSG_INTL(MSG_MOVE_ENTRYIN), EC_XWORD(mv->m_poffset), EC_LWORD(mv->m_value), mv->m_repeat, mv->m_stride, sname); }
void Dbg_move_entry1(Lm_list *lml, int which, Move *mv, Sym_desc *s) { const char *str; if (DBG_NOTCLASS(DBG_C_MOVE)) return; if (DBG_NOTDETAIL()) return; if (which) str = MSG_INTL(MSG_MOVE_ENTRYIN); else str = MSG_INTL(MSG_MOVE_ENTRYOUT); dbg_print(lml, str, EC_XWORD(mv->m_poffset), EC_LWORD(mv->m_value), mv->m_repeat, mv->m_stride, s->sd_name); }
static const char * fmt_human_units(size_t bytes, char *buf, size_t bufsize) { static int unit_arr[] = { 'K', 'M', 'G', 'T' }; int i, unit_ch; size_t unit_bytes = bytes; /* Convert to human readable units */ for (i = 0; i < sizeof (unit_arr) / sizeof (unit_arr[0]); i++) { if (unit_bytes < 1024) break; unit_ch = unit_arr[i]; unit_bytes /= 1024; } if (unit_bytes == bytes) buf[0] = '\0'; else (void) snprintf(buf, bufsize, MSG_ORIG(MSG_FMT_MEMUNIT), EC_XWORD(unit_bytes), unit_ch); return (buf); }
/* * Warning message for bad move target. */ void elf_move_bad(Lm_list *lml, Rt_map *lmp, Sym *sym, ulong_t num, Addr addr) { const char *name; int trace; trace = (lml->lm_flags & LML_FLG_TRC_ENABLE) && (((rtld_flags & RT_FL_SILENCERR) == 0) || (lml->lm_flags & (LML_FLG_TRC_VERBOSE | LML_FLG_TRC_WARN))); if ((trace == 0) && (DBG_ENABLED == 0)) return; if (ELF_ST_BIND(sym->st_info) != STB_LOCAL) name = (const char *)(STRTAB(lmp) + sym->st_name); else name = MSG_INTL(MSG_STR_UNKNOWN); if (trace) (void) printf(MSG_INTL(MSG_LDD_MOVE_ERR), EC_XWORD(num), name, EC_ADDR(addr)); else DBG_CALL(Dbg_move_bad(lml, num, name, addr)); }
/* * Track any static TLS use, retain the TLS header, and assign a TLS module * identifier. */ int tls_assign(Lm_list *lml, Rt_map *lmp, Phdr *phdr) { ulong_t memsz = S_ROUND(phdr->p_memsz, M_TLSSTATALIGN); ulong_t filesz = phdr->p_filesz; ulong_t resv = tls_static_resv; /* * If this object explicitly references static TLS, then there are some * limitations. */ if (FLAGS1(lmp) & FL1_RT_TLSSTAT) { /* * Static TLS is only available to objects on the primary * link-map list. */ if (((lml->lm_flags & LML_FLG_BASELM) == 0) || ((rtld_flags2 & RT_FL2_NOPLM) != 0)) { eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TLS_STATBASE), NAME(lmp)); return (0); } /* * All TLS blocks that are processed before thread * initialization, are registered with libc. This * initialization is carried out through a handshake with libc * prior to executing any user code (ie. before the first .init * sections are called). As part of this initialization, a * small backup TLS reservation is added (tls_static_resv). * Only explicit static TLS references that can be satisfied by * this TLS backup reservation can be satisfied. */ if (rtld_flags2 & RT_FL2_PLMSETUP) { /* * Initialized static TLS can not be satisfied from the * TLS backup reservation. */ if (filesz) { eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TLS_STATINIT), NAME(lmp)); return (0); } /* * Make sure the backup reservation is sufficient. */ if (memsz > tls_static_resv) { eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TLS_STATSIZE), NAME(lmp), EC_XWORD(memsz), EC_XWORD(tls_static_resv)); return (0); } tls_static_resv -= memsz; } } /* * If we haven't yet initialized threads, or this static reservation can * be satisfied from the TLS backup reservation, determine the total * static TLS size, and assign this object a static TLS offset. */ if (((rtld_flags2 & RT_FL2_PLMSETUP) == 0) || (FLAGS1(lmp) & FL1_RT_TLSSTAT)) { tls_static_size += memsz; TLSSTATOFF(lmp) = tls_static_size; } /* * Retain the PT_TLS header, obtain a new module identifier, and * indicate that this link-map list contains a new TLS object. */ PTTLS(lmp) = phdr; TLSMODID(lmp) = tls_getmodid(); /* * Now that we have a TLS module id, generate any static TLS reservation * diagnostic. */ if (resv != tls_static_resv) DBG_CALL(Dbg_tls_static_resv(lmp, memsz, tls_static_resv)); return (++lml->lm_tls); }
void Dbg_got_display(Ofl_desc *ofl, Off goff, int stage, Word m_got_xnumber, size_t m_got_entsize) { Lm_list *lml = ofl->ofl_lml; Gottable *gtp = ofl->ofl_gottable; Word gotndx; Xword *gptr; if (DBG_NOTCLASS(DBG_C_GOT)) return; if (ofl->ofl_gotcnt == m_got_xnumber) return; Dbg_util_nl(lml, DBG_NL_STD); dbg_print(lml, MSG_INTL(MSG_GOT_INFO), EC_WORD(ofl->ofl_gotcnt)); if (DBG_NOTDETAIL()) return; qsort((char *)gtp, ofl->ofl_gotcnt, sizeof (Gottable), (int(*)(const void *, const void *))Dbg_got_compare); if (stage == 0) dbg_print(lml, MSG_INTL(MSG_GOT_COLUMNS1)); else dbg_print(lml, MSG_INTL(MSG_GOT_COLUMNS2)); gptr = (Xword *)ofl->ofl_osgot->os_outdata->d_buf; for (gotndx = 0; gotndx < ofl->ofl_gotcnt; gotndx++, gtp++, gptr++) { Sym_desc *sdp = gtp->gt_sym; const char *refstr, *name; Gotndx *gnp = >p->gt_gndx; Lword gotaddval; Off off = goff + (gotndx * m_got_entsize); char index[INDEX_STR_SIZE]; (void) snprintf(index, INDEX_STR_SIZE, MSG_ORIG(MSG_GOT_INDEX), EC_SWORD(gnp->gn_gotndx)); if (sdp == 0) refstr = MSG_ORIG(MSG_STR_EMPTY); else if (sdp->sd_flags & FLG_SY_SMGOT) refstr = MSG_ORIG(MSG_GOT_SMALL_PIC); else refstr = MSG_ORIG(MSG_GOT_BIG_PIC); if (sdp == 0) name = MSG_ORIG(MSG_STR_EMPTY); else if (sdp->sd_name) name = Dbg_demangle_name(sdp->sd_name); else name = MSG_INTL(MSG_STR_UNKNOWN); if (stage == 0) gotaddval = gnp->gn_addend; else gotaddval = *gptr; if ((sdp == 0) || (sdp->sd_sym->st_shndx == SHN_UNDEF) || (sdp->sd_file == 0)) { dbg_print(lml, MSG_INTL(MSG_GOT_FORMAT1), index, refstr, EC_OFF(off), EC_XWORD(gotaddval), name); } else { dbg_print(lml, MSG_INTL(MSG_GOT_FORMAT2), index, refstr, EC_OFF(off), EC_XWORD(gotaddval), sdp->sd_file->ifl_name, name); } } }
/* * Function binding routine - invoked on the first call to a function through * the procedure linkage table; * passes first through an assembly language interface. * * Takes the offset into the relocation table of the associated * relocation entry and the address of the link map (rt_private_map struct) * for the entry. * * Returns the address of the function referenced after re-writing the PLT * entry to invoke the function directly. * * On error, causes process to terminate with a signal. */ ulong_t elf_bndr(Rt_map *lmp, ulong_t reloff, caddr_t from) { Rt_map *nlmp, *llmp; ulong_t addr, symval, rsymndx; char *name; Rel *rptr; Sym *rsym, *nsym; uint_t binfo, sb_flags = 0, dbg_class; Slookup sl; Sresult sr; int entry, lmflags; Lm_list *lml; /* * For compatibility with libthread (TI_VERSION 1) we track the entry * value. A zero value indicates we have recursed into ld.so.1 to * further process a locking request. Under this recursion we disable * tsort and cleanup activities. */ entry = enter(0); lml = LIST(lmp); if ((lmflags = lml->lm_flags) & LML_FLG_RTLDLM) { dbg_class = dbg_desc->d_class; dbg_desc->d_class = 0; } /* * Perform some basic sanity checks. If we didn't get a load map or * the relocation offset is invalid then its possible someone has walked * over the .got entries or jumped to plt0 out of the blue. */ if (!lmp || ((reloff % sizeof (Rel)) != 0)) { Conv_inv_buf_t inv_buf; eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_PLTREF), conv_reloc_386_type(R_386_JMP_SLOT, 0, &inv_buf), EC_NATPTR(lmp), EC_XWORD(reloff), EC_NATPTR(from)); rtldexit(lml, 1); } /* * Use relocation entry to get symbol table entry and symbol name. */ addr = (ulong_t)JMPREL(lmp); rptr = (Rel *)(addr + reloff); rsymndx = ELF_R_SYM(rptr->r_info); rsym = (Sym *)((ulong_t)SYMTAB(lmp) + (rsymndx * SYMENT(lmp))); name = (char *)(STRTAB(lmp) + rsym->st_name); /* * Determine the last link-map of this list, this'll be the starting * point for any tsort() processing. */ llmp = lml->lm_tail; /* * Find definition for symbol. Initialize the symbol lookup, and * symbol result, data structures. */ SLOOKUP_INIT(sl, name, lmp, lml->lm_head, ld_entry_cnt, 0, rsymndx, rsym, 0, LKUP_DEFT); SRESULT_INIT(sr, name); if (lookup_sym(&sl, &sr, &binfo, NULL) == 0) { eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_NOSYM), NAME(lmp), demangle(name)); rtldexit(lml, 1); } name = (char *)sr.sr_name; nlmp = sr.sr_dmap; nsym = sr.sr_sym; symval = nsym->st_value; if (!(FLAGS(nlmp) & FLG_RT_FIXED) && (nsym->st_shndx != SHN_ABS)) symval += ADDR(nlmp); if ((lmp != nlmp) && ((FLAGS1(nlmp) & FL1_RT_NOINIFIN) == 0)) { /* * Record that this new link map is now bound to the caller. */ if (bind_one(lmp, nlmp, BND_REFER) == 0) rtldexit(lml, 1); } if ((lml->lm_tflags | AFLAGS(lmp)) & LML_TFLG_AUD_SYMBIND) { uint_t symndx = (((uintptr_t)nsym - (uintptr_t)SYMTAB(nlmp)) / SYMENT(nlmp)); symval = audit_symbind(lmp, nlmp, nsym, symndx, symval, &sb_flags); } if (!(rtld_flags & RT_FL_NOBIND)) { addr = rptr->r_offset; if (!(FLAGS(lmp) & FLG_RT_FIXED)) addr += ADDR(lmp); if (((lml->lm_tflags | AFLAGS(lmp)) & (LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) && AUDINFO(lmp)->ai_dynplts) { int fail = 0; uint_t pltndx = reloff / sizeof (Rel); uint_t symndx = (((uintptr_t)nsym - (uintptr_t)SYMTAB(nlmp)) / SYMENT(nlmp)); symval = (ulong_t)elf_plt_trace_write(addr, lmp, nlmp, nsym, symndx, pltndx, (caddr_t)symval, sb_flags, &fail); if (fail) rtldexit(lml, 1); } else { /* * Write standard PLT entry to jump directly * to newly bound function. */ *(ulong_t *)addr = symval; } } /* * Print binding information and rebuild PLT entry. */ DBG_CALL(Dbg_bind_global(lmp, (Addr)from, (Off)(from - ADDR(lmp)), (Xword)(reloff / sizeof (Rel)), PLT_T_FULL, nlmp, (Addr)symval, nsym->st_value, name, binfo)); /* * Complete any processing for newly loaded objects. Note we don't * know exactly where any new objects are loaded (we know the object * that supplied the symbol, but others may have been loaded lazily as * we searched for the symbol), so sorting starts from the last * link-map know on entry to this routine. */ if (entry) load_completion(llmp); /* * Some operations like dldump() or dlopen()'ing a relocatable object * result in objects being loaded on rtld's link-map, make sure these * objects are initialized also. */ if ((LIST(nlmp)->lm_flags & LML_FLG_RTLDLM) && LIST(nlmp)->lm_init) load_completion(nlmp); /* * Make sure the object to which we've bound has had it's .init fired. * Cleanup before return to user code. */ if (entry) { is_dep_init(nlmp, lmp); leave(lml, 0); } if (lmflags & LML_FLG_RTLDLM) dbg_desc->d_class = dbg_class; return (symval); }
/* * 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); }
/* * 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); }
uintptr_t ld_group_process(Is_desc *gisc, Ofl_desc *ofl) { Ifl_desc *gifl = gisc->is_file; Shdr *sshdr, *gshdr = gisc->is_shdr; Is_desc *isc; Sym *sym; const char *str; Group_desc gd; size_t ndx; int gnu_stt_section; /* * Confirm that the sh_link points to a valid section. */ if ((gshdr->sh_link == SHN_UNDEF) || (gshdr->sh_link >= gifl->ifl_shnum) || ((isc = gifl->ifl_isdesc[gshdr->sh_link]) == NULL)) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHLINK), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, EC_XWORD(gshdr->sh_link)); return (0); } if (gshdr->sh_entsize == 0) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHENTSIZE), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, EC_XWORD(gshdr->sh_entsize)); return (0); } /* * Get the associated symbol table. Sanity check the sh_info field * (which points to the signature symbol table entry) against the size * of the symbol table. */ sshdr = isc->is_shdr; sym = (Sym *)isc->is_indata->d_buf; if ((sshdr->sh_info == SHN_UNDEF) || (gshdr->sh_info >= (Word)(sshdr->sh_size / sshdr->sh_entsize)) || ((isc = gifl->ifl_isdesc[sshdr->sh_link]) == NULL)) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHINFO), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, EC_XWORD(gshdr->sh_info)); return (0); } sym += gshdr->sh_info; /* * Get the symbol name from the associated string table. */ str = (char *)isc->is_indata->d_buf; str += sym->st_name; /* * The GNU assembler can use section symbols as the signature symbol * as described by this comment in the gold linker (found via google): * * It seems that some versions of gas will create a section group * associated with a section symbol, and then fail to give a name * to the section symbol. In such a case, use the name of the * section. * * In order to support such objects, we do the same. */ gnu_stt_section = ((sym->st_name == 0) || (*str == '\0')) && (ELF_ST_TYPE(sym->st_info) == STT_SECTION); if (gnu_stt_section) str = gisc->is_name; /* * Generate a group descriptor. */ gd.gd_isc = gisc; gd.gd_oisc = NULL; gd.gd_name = str; gd.gd_data = gisc->is_indata->d_buf; gd.gd_cnt = gisc->is_indata->d_size / sizeof (Word); /* * If this group is a COMDAT group, validate the signature symbol. */ if ((gd.gd_data[0] & GRP_COMDAT) && !gnu_stt_section && ((ELF_ST_BIND(sym->st_info) == STB_LOCAL) || (sym->st_shndx == SHN_UNDEF))) { /* If section symbol, construct a printable name for it */ if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { if (gisc->is_sym_name == NULL) (void) ld_stt_section_sym_name(gisc); if (gisc->is_sym_name != NULL) str = gisc->is_sym_name; } ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_GRP_INVALSYM), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, str); return (0); } /* * If the signature symbol is a name generated by the GNU compiler to * refer to a header, we need sloppy relocation. */ if (is_header_gensym(str)) { if ((ofl->ofl_flags1 & FLG_OF1_NRLXREL) == 0) ofl->ofl_flags1 |= FLG_OF1_RLXREL; DBG_CALL(Dbg_sec_gnu_comdat(ofl->ofl_lml, gisc, TRUE, (ofl->ofl_flags1 & FLG_OF1_RLXREL) != 0)); } /* * Validate the section indices within the group. If this is a COMDAT * group, mark each section as COMDAT. */ for (ndx = 1; ndx < gd.gd_cnt; ndx++) { Word gndx; if ((gndx = gd.gd_data[ndx]) >= gifl->ifl_shnum) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_GRP_INVALNDX), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, ndx, gndx); return (0); } if (gd.gd_data[0] & GRP_COMDAT) gifl->ifl_isdesc[gndx]->is_flags |= FLG_IS_COMDAT; } /* * If this is a COMDAT group, determine whether this group has already * been encountered, or whether this is the first instance of the group. */ if ((gd.gd_data[0] & GRP_COMDAT) && (gpavl_loaded(ofl, &gd) == S_ERROR)) return (S_ERROR); /* * Associate the group descriptor with this input file. */ if (alist_append(&(gifl->ifl_groups), &gd, sizeof (Group_desc), AL_CNT_IFL_GROUPS) == NULL) return (S_ERROR); return (1); }
void Dbg_statistics_ld(Ofl_desc *ofl) { Lm_list *lml = ofl->ofl_lml; if (DBG_NOTCLASS(DBG_C_STATS)) return; Dbg_util_nl(lml, DBG_NL_STD); dbg_print(lml, MSG_INTL(MSG_STATS_GENERAL)); if (ofl->ofl_objscnt || ofl->ofl_soscnt || ofl->ofl_arscnt) { dbg_print(lml, MSG_INTL(MSG_STATS_FILES), EC_XWORD(ofl->ofl_objscnt), EC_XWORD(ofl->ofl_soscnt), EC_XWORD(ofl->ofl_arscnt)); } if (ofl->ofl_locscnt || ofl->ofl_globcnt) { dbg_print(lml, MSG_INTL(MSG_STATS_SYMBOLS_OUT), EC_XWORD(ofl->ofl_globcnt), EC_XWORD(ofl->ofl_locscnt)); } if (ofl->ofl_entercnt || ofl->ofl_scopecnt || ofl->ofl_elimcnt) { dbg_print(lml, MSG_INTL(MSG_STATS_SYMBOLS_IN), EC_XWORD(ofl->ofl_entercnt), EC_XWORD(ofl->ofl_scopecnt), EC_XWORD(ofl->ofl_elimcnt)); } dbg_print(lml, MSG_INTL(MSG_STATS_REL_OUT), EC_XWORD(ofl->ofl_outrels.rc_cnt)); dbg_print(lml, MSG_INTL(MSG_STATS_REL_IN), EC_XWORD(ofl->ofl_entrelscnt), EC_XWORD(ofl->ofl_actrels.rc_cnt)); dbg_print(lml, MSG_INTL(MSG_STATS_REL_TICACHE)); rel_cache_statistics(ofl, MSG_INTL(MSG_STATS_REL_TIOUT), ofl->ofl_outrels.rc_list); rel_cache_statistics(ofl, MSG_INTL(MSG_STATS_REL_TIACT), ofl->ofl_actrels.rc_list); rel_aux_cache_statistics(ofl); }