/* * 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 (c) 1988 AT&T * All Rights Reserved * * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2022 Oxide Computer Company */ /* * Module sections. Initialize special sections */ #define ELF_TARGET_AMD64 #include #include #include #include #include #include #include "msg.h" #include "_libld.h" inline static void remove_local(Ofl_desc *ofl, Sym_desc *sdp, int allow_ldynsym) { Sym *sym = sdp->sd_sym; uchar_t type = ELF_ST_TYPE(sym->st_info); /* LINTED - only used for assert() */ int err; if ((ofl->ofl_flags & FLG_OF_REDLSYM) == 0) { ofl->ofl_locscnt--; err = st_delstring(ofl->ofl_strtab, sdp->sd_name); assert(err != -1); if (allow_ldynsym && ldynsym_symtype[type]) { ofl->ofl_dynlocscnt--; err = st_delstring(ofl->ofl_dynstrtab, sdp->sd_name); assert(err != -1); /* Remove from sort section? */ DYNSORT_COUNT(sdp, sym, type, --); } } sdp->sd_flags |= FLG_SY_ISDISC; } inline static void remove_scoped(Ofl_desc *ofl, Sym_desc *sdp, int allow_ldynsym) { Sym *sym = sdp->sd_sym; uchar_t type = ELF_ST_TYPE(sym->st_info); /* LINTED - only used for assert() */ int err; ofl->ofl_scopecnt--; ofl->ofl_elimcnt++; err = st_delstring(ofl->ofl_strtab, sdp->sd_name); assert(err != -1); if (allow_ldynsym && ldynsym_symtype[type]) { ofl->ofl_dynscopecnt--; err = st_delstring(ofl->ofl_dynstrtab, sdp->sd_name); assert(err != -1); /* Remove from sort section? */ DYNSORT_COUNT(sdp, sym, type, --); } sdp->sd_flags |= FLG_SY_ELIM; } inline static void ignore_sym(Ofl_desc *ofl, Ifl_desc *ifl, Sym_desc *sdp, int allow_ldynsym) { Os_desc *osp; Is_desc *isp = sdp->sd_isc; uchar_t bind = ELF_ST_BIND(sdp->sd_sym->st_info); if (bind == STB_LOCAL) { uchar_t type = ELF_ST_TYPE(sdp->sd_sym->st_info); /* * Skip section symbols, these were never collected in the * first place. */ if (type == STT_SECTION) return; /* * Determine if the whole file is being removed. Remove any * file symbol, and any symbol that is not associated with a * section, provided the symbol has not been identified as * (update) required. */ if (((ifl->ifl_flags & FLG_IF_FILEREF) == 0) && ((type == STT_FILE) || ((isp == NULL) && ((sdp->sd_flags & FLG_SY_UPREQD) == 0)))) { DBG_CALL(Dbg_syms_discarded(ofl->ofl_lml, sdp)); if (ifl->ifl_flags & FLG_IF_IGNORE) remove_local(ofl, sdp, allow_ldynsym); return; } } else { /* * Global symbols can only be eliminated when the interfaces of * an object have been defined via versioning/scoping. */ if (!SYM_IS_HIDDEN(sdp)) return; /* * Remove any unreferenced symbols that are not associated with * a section. */ if ((isp == NULL) && ((sdp->sd_flags & FLG_SY_UPREQD) == 0)) { DBG_CALL(Dbg_syms_discarded(ofl->ofl_lml, sdp)); if (ifl->ifl_flags & FLG_IF_IGNORE) remove_scoped(ofl, sdp, allow_ldynsym); return; } } /* * Do not discard any symbols that are associated with non-allocable * segments. */ if (isp && ((isp->is_flags & FLG_IS_SECTREF) == 0) && ((osp = isp->is_osdesc) != 0) && (osp->os_sgdesc->sg_phdr.p_type == PT_LOAD)) { DBG_CALL(Dbg_syms_discarded(ofl->ofl_lml, sdp)); if (ifl->ifl_flags & FLG_IF_IGNORE) { if (bind == STB_LOCAL) remove_local(ofl, sdp, allow_ldynsym); else remove_scoped(ofl, sdp, allow_ldynsym); } } } static Boolean isdesc_discarded(Is_desc *isp) { Ifl_desc *ifl = isp->is_file; Os_desc *osp = isp->is_osdesc; Word ptype = osp->os_sgdesc->sg_phdr.p_type; if (isp->is_flags & FLG_IS_DISCARD) return (TRUE); /* * If the file is discarded, it will take * the section with it. */ if (ifl && (((ifl->ifl_flags & FLG_IF_FILEREF) == 0) || ((ptype == PT_LOAD) && ((isp->is_flags & FLG_IS_SECTREF) == 0) && (isp->is_shdr->sh_size > 0))) && (ifl->ifl_flags & FLG_IF_IGNORE)) return (TRUE); return (FALSE); } /* * There are situations where we may count output sections (ofl_shdrcnt) * that are subsequently eliminated from the output object. Whether or * not this happens cannot be known until all input has been seen and * section elimination code has run. However, the situations where this * outcome is possible are known, and are flagged by setting FLG_OF_ADJOSCNT. * * If FLG_OF_ADJOSCNT is set, this routine makes a pass over the output * sections. If an unused output section is encountered, we decrement * ofl->ofl_shdrcnt and remove the section name from the .shstrtab string * table (ofl->ofl_shdrsttab). * * This code must be kept in sync with the similar code * found in outfile.c:ld_create_outfile(). */ static void adjust_os_count(Ofl_desc *ofl) { Sg_desc *sgp; Is_desc *isp; Os_desc *osp; Aliste idx1; if ((ofl->ofl_flags & FLG_OF_ADJOSCNT) == 0) return; /* * For each output section, look at the input sections to find at least * one input section that has not been eliminated. If none are found, * the -z ignore processing above has eliminated that output section. */ for (APLIST_TRAVERSE(ofl->ofl_segs, idx1, sgp)) { Aliste idx2; for (APLIST_TRAVERSE(sgp->sg_osdescs, idx2, osp)) { Aliste idx3; int keep = 0, os_isdescs_idx; OS_ISDESCS_TRAVERSE(os_isdescs_idx, osp, idx3, isp) { /* * We have found a kept input section, * so the output section will be created. */ if (!isdesc_discarded(isp)) { keep = 1; break; } } /* * If no section of this name was kept, decrement * the count and remove the name from .shstrtab. */ if (keep == 0) { /* LINTED - only used for assert() */ int err; ofl->ofl_shdrcnt--; err = st_delstring(ofl->ofl_shdrsttab, osp->os_name); assert(err != -1); } } } } /* * If -zignore has been in effect, scan all input files to determine if the * file, or sections from the file, have been referenced. If not, the file or * some of the files sections can be discarded. If sections are to be * discarded, rescan the output relocations and the symbol table and remove * the relocations and symbol entries that are no longer required. * * Note: It's possible that a section which is being discarded has contributed * to the GOT table or the PLT table. However, we can't at this point * eliminate the corresponding entries. This is because there could well * be other sections referencing those same entries, but we don't have * the infrastructure to determine this. So, keep the PLT and GOT * entries in the table in case someone wants them. * Note: The section to be affected needs to be allocatable. * So even if -zignore is in effect, if the section is not allocatable, * we do not eliminate it. */ static uintptr_t ignore_section_processing(Ofl_desc *ofl) { Sg_desc *sgp; Is_desc *isp; Os_desc *osp; Ifl_desc *ifl; Rel_cachebuf *rcbp; Rel_desc *rsp; int allow_ldynsym = OFL_ALLOW_LDYNSYM(ofl); Aliste idx1; for (APLIST_TRAVERSE(ofl->ofl_objs, idx1, ifl)) { uint_t num, discard; /* * Diagnose (-D unused) a completely unreferenced file. */ if ((ifl->ifl_flags & FLG_IF_FILEREF) == 0) DBG_CALL(Dbg_unused_file(ofl->ofl_lml, ifl->ifl_name, 0, 0)); if (((ofl->ofl_flags1 & FLG_OF1_IGNPRC) == 0) || ((ifl->ifl_flags & FLG_IF_IGNORE) == 0)) continue; /* * Before scanning the whole symbol table to determine if * symbols should be discard - quickly (relatively) scan the * sections to determine if any are to be discarded. */ discard = 0; if (ifl->ifl_flags & FLG_IF_FILEREF) { for (num = 1; num < ifl->ifl_shnum; num++) { if (((isp = ifl->ifl_isdesc[num]) != NULL) && ((isp->is_flags & FLG_IS_SECTREF) == 0) && ((osp = isp->is_osdesc) != NULL) && ((sgp = osp->os_sgdesc) != NULL) && (sgp->sg_phdr.p_type == PT_LOAD)) { discard++; break; } } } /* * No sections are to be 'ignored' */ if ((discard == 0) && (ifl->ifl_flags & FLG_IF_FILEREF)) continue; /* * We know that we have discarded sections. Scan the symbol * table for this file to determine if symbols need to be * discarded that are associated with the 'ignored' sections. */ for (num = 1; num < ifl->ifl_symscnt; num++) { Sym_desc *sdp; /* * If the symbol definition has been resolved to another * file, or the symbol has already been discarded or * eliminated, skip it. */ sdp = ifl->ifl_oldndx[num]; if ((sdp->sd_file != ifl) || (sdp->sd_flags & (FLG_SY_ISDISC | FLG_SY_INVALID | FLG_SY_ELIM))) continue; /* * Complete the investigation of the symbol. */ ignore_sym(ofl, ifl, sdp, allow_ldynsym); } } /* * If we were only here to solicit debugging diagnostics, we're done. */ if ((ofl->ofl_flags1 & FLG_OF1_IGNPRC) == 0) return (1); /* * Scan all output relocations searching for those against discarded or * ignored sections. If one is found, decrement the total outrel count. */ REL_CACHE_TRAVERSE(&ofl->ofl_outrels, idx1, rcbp, rsp) { Is_desc *isc = rsp->rel_isdesc; uint_t flags, entsize; Shdr *shdr; if ((isc == NULL) || ((isc->is_flags & (FLG_IS_SECTREF))) || ((ifl = isc->is_file) == NULL) || ((ifl->ifl_flags & FLG_IF_IGNORE) == 0) || ((shdr = isc->is_shdr) == NULL) || ((shdr->sh_flags & SHF_ALLOC) == 0)) continue; flags = rsp->rel_flags; if (flags & (FLG_REL_GOT | FLG_REL_BSS | FLG_REL_NOINFO | FLG_REL_PLT)) continue; osp = RELAUX_GET_OSDESC(rsp); if (rsp->rel_flags & FLG_REL_RELA) entsize = sizeof (Rela); else entsize = sizeof (Rel); assert(osp->os_szoutrels > 0); osp->os_szoutrels -= entsize; if (!(flags & FLG_REL_PLT)) ofl->ofl_reloccntsub++; if (rsp->rel_rtype == ld_targ.t_m.m_r_relative) ofl->ofl_relocrelcnt--; } /* * As a result of our work here, the number of output sections may * have decreased. Trigger a call to adjust_os_count(). */ ofl->ofl_flags |= FLG_OF_ADJOSCNT; return (1); } /* * Allocate Elf_Data, Shdr, and Is_desc structures for a new * section. * * entry: * ofl - Output file descriptor * shtype - SHT_ type code for section. * shname - String giving the name for the new section. * entcnt - # of items contained in the data part of the new section. * This value is multiplied against the known element size * for the section type to determine the size of the data * area for the section. It is only meaningful in cases where * the section type has a non-zero element size. In other cases, * the caller must set the size fields in the *ret_data and * *ret_shdr structs manually. * ret_isec, ret_shdr, ret_data - Address of pointers to * receive address of newly allocated structs. * * exit: * On error, returns S_ERROR. On success, returns (1), and the * ret_ pointers have been updated to point at the new structures, * which have been filled in. To finish the task, the caller must * update any fields within the supplied descriptors that differ * from its needs, and then call ld_place_section(). */ static uintptr_t new_section(Ofl_desc *ofl, Word shtype, const char *shname, Xword entcnt, Is_desc **ret_isec, Shdr **ret_shdr, Elf_Data **ret_data) { typedef struct sec_info { Word d_type; Word align; /* Used in both data and section header */ Word sh_flags; Word sh_entsize; } SEC_INFO_T; const SEC_INFO_T *sec_info; Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size; /* * For each type of section, we have a distinct set of * SEC_INFO_T values. This macro defines a static structure * containing those values and generates code to set the sec_info * pointer to refer to it. The pointer in sec_info remains valid * outside of the declaration scope because the info_s struct is static. * * We can't determine the value of M_WORD_ALIGN at compile time, so * a different variant is used for those cases. */ #define SET_SEC_INFO(d_type, d_align, sh_flags, sh_entsize) \ { \ static const SEC_INFO_T info_s = { d_type, d_align, sh_flags, \ sh_entsize}; \ sec_info = &info_s; \ } #define SET_SEC_INFO_WORD_ALIGN(d_type, sh_flags, sh_entsize) \ { \ static SEC_INFO_T info_s = { d_type, 0, sh_flags, \ sh_entsize}; \ info_s.align = ld_targ.t_m.m_word_align; \ sec_info = &info_s; \ } switch (shtype) { case SHT_PROGBITS: /* * SHT_PROGBITS sections contain are used for many * different sections. Alignments and flags differ. * Some have a standard entsize, and others don't. * We set some defaults here, but there is no expectation * that they are correct or complete for any specific * purpose. The caller must provide the correct values. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_BYTE, SHF_ALLOC, 0) break; case SHT_SYMTAB: SET_SEC_INFO_WORD_ALIGN(ELF_T_SYM, 0, sizeof (Sym)) break; case SHT_DYNSYM: SET_SEC_INFO_WORD_ALIGN(ELF_T_SYM, SHF_ALLOC, sizeof (Sym)) break; case SHT_SUNW_LDYNSYM: ofl->ofl_flags |= FLG_OF_OSABI; SET_SEC_INFO_WORD_ALIGN(ELF_T_SYM, SHF_ALLOC, sizeof (Sym)) break; case SHT_STRTAB: /* * A string table may or may not be allocable, depending * on context, so we leave that flag unset and leave it to * the caller to add it if necessary. * * String tables do not have a standard entsize, so * we set it to 0. */ SET_SEC_INFO(ELF_T_BYTE, 1, SHF_STRINGS, 0) break; case SHT_RELA: /* * Relocations with an addend (Everything except 32-bit X86). * The caller is expected to set all section header flags. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_RELA, 0, sizeof (Rela)) break; case SHT_REL: /* * Relocations without an addend (32-bit X86 only). * The caller is expected to set all section header flags. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_REL, 0, sizeof (Rel)) break; case SHT_HASH: SET_SEC_INFO_WORD_ALIGN(ELF_T_WORD, SHF_ALLOC, sizeof (Word)) break; case SHT_SUNW_symsort: case SHT_SUNW_tlssort: ofl->ofl_flags |= FLG_OF_OSABI; SET_SEC_INFO_WORD_ALIGN(ELF_T_WORD, SHF_ALLOC, sizeof (Word)) break; case SHT_DYNAMIC: /* * A dynamic section may or may not be allocable, and may or * may not be writable, depending on context, so we leave the * flags unset and leave it to the caller to add them if * necessary. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_DYN, 0, sizeof (Dyn)) break; case SHT_NOBITS: /* * SHT_NOBITS is used for BSS-type sections. The size and * alignment depend on the specific use and must be adjusted * by the caller. */ SET_SEC_INFO(ELF_T_BYTE, 0, SHF_ALLOC | SHF_WRITE, 0) break; case SHT_INIT_ARRAY: case SHT_FINI_ARRAY: case SHT_PREINIT_ARRAY: SET_SEC_INFO(ELF_T_ADDR, sizeof (Addr), SHF_ALLOC | SHF_WRITE, sizeof (Addr)) break; case SHT_SYMTAB_SHNDX: /* * Note that these sections are created to be associated * with both symtab and dynsym symbol tables. However, they * are non-allocable in all cases, because the runtime * linker has no need for this information. It is purely * informational, used by elfdump(1), debuggers, etc. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_WORD, 0, sizeof (Word)); break; case SHT_SUNW_cap: ofl->ofl_flags |= FLG_OF_OSABI; SET_SEC_INFO_WORD_ALIGN(ELF_T_CAP, SHF_ALLOC, sizeof (Cap)); break; case SHT_SUNW_capchain: ofl->ofl_flags |= FLG_OF_OSABI; SET_SEC_INFO_WORD_ALIGN(ELF_T_WORD, SHF_ALLOC, sizeof (Capchain)); break; case SHT_SUNW_capinfo: ofl->ofl_flags |= FLG_OF_OSABI; #if _ELF64 SET_SEC_INFO(ELF_T_XWORD, sizeof (Xword), SHF_ALLOC, sizeof (Capinfo)); #else SET_SEC_INFO(ELF_T_WORD, sizeof (Word), SHF_ALLOC, sizeof (Capinfo)); #endif break; case SHT_SUNW_move: ofl->ofl_flags |= FLG_OF_OSABI; SET_SEC_INFO(ELF_T_BYTE, sizeof (Lword), SHF_ALLOC | SHF_WRITE, sizeof (Move)); break; case SHT_SUNW_syminfo: ofl->ofl_flags |= FLG_OF_OSABI; /* * The sh_info field of the SHT_*_syminfo section points * to the header index of the associated .dynamic section, * so we also set SHF_INFO_LINK. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_BYTE, SHF_ALLOC | SHF_INFO_LINK, sizeof (Syminfo)); break; case SHT_SUNW_verneed: case SHT_SUNW_verdef: ofl->ofl_flags |= FLG_OF_OSABI; /* * The info for verneed and versym happen to be the same. * The entries in these sections are not of uniform size, * so we set the entsize to 0. */ SET_SEC_INFO_WORD_ALIGN(ELF_T_BYTE, SHF_ALLOC, 0); break; case SHT_SUNW_versym: ofl->ofl_flags |= FLG_OF_OSABI; SET_SEC_INFO_WORD_ALIGN(ELF_T_BYTE, SHF_ALLOC, sizeof (Versym)); break; default: /* Should not happen: fcn called with unknown section type */ assert(0); return (S_ERROR); } #undef SET_SEC_INFO #undef SET_SEC_INFO_WORD_ALIGN size = entcnt * sec_info->sh_entsize; /* * Allocate and initialize the Elf_Data structure. */ if ((data = libld_calloc(1, sizeof (Elf_Data))) == NULL) return (S_ERROR); data->d_type = sec_info->d_type; data->d_size = size; data->d_align = sec_info->align; data->d_version = ofl->ofl_dehdr->e_version; /* * Allocate and initialize the Shdr structure. */ if ((shdr = libld_calloc(1, sizeof (Shdr))) == NULL) return (S_ERROR); shdr->sh_type = shtype; shdr->sh_size = size; shdr->sh_flags = sec_info->sh_flags; shdr->sh_addralign = sec_info->align; shdr->sh_entsize = sec_info->sh_entsize; /* * Allocate and initialize the Is_desc structure. */ if ((isec = libld_calloc(1, sizeof (Is_desc))) == NULL) return (S_ERROR); isec->is_name = shname; isec->is_shdr = shdr; isec->is_indata = data; *ret_isec = isec; *ret_shdr = shdr; *ret_data = data; return (1); } /* * Use an existing input section as a template to create a new * input section with the same values as the original, other than * the size of the data area which is supplied by the caller. * * entry: * ofl - Output file descriptor * ifl - Input file section to use as a template * size - Size of data area for new section * ret_isec, ret_shdr, ret_data - Address of pointers to * receive address of newly allocated structs. * * exit: * On error, returns S_ERROR. On success, returns (1), and the * ret_ pointers have been updated to point at the new structures, * which have been filled in. To finish the task, the caller must * update any fields within the supplied descriptors that differ * from its needs, and then call ld_place_section(). */ static uintptr_t new_section_from_template(Ofl_desc *ofl, Is_desc *tmpl_isp, size_t size, Is_desc **ret_isec, Shdr **ret_shdr, Elf_Data **ret_data) { Shdr *shdr; Elf_Data *data; Is_desc *isec; /* * Allocate and initialize the Elf_Data structure. */ if ((data = libld_calloc(1, sizeof (Elf_Data))) == NULL) return (S_ERROR); data->d_type = tmpl_isp->is_indata->d_type; data->d_size = size; data->d_align = tmpl_isp->is_shdr->sh_addralign; data->d_version = ofl->ofl_dehdr->e_version; /* * Allocate and initialize the Shdr structure. */ if ((shdr = libld_malloc(sizeof (Shdr))) == NULL) return (S_ERROR); *shdr = *tmpl_isp->is_shdr; shdr->sh_addr = 0; shdr->sh_offset = 0; shdr->sh_size = size; /* * Allocate and initialize the Is_desc structure. */ if ((isec = libld_calloc(1, sizeof (Is_desc))) == NULL) return (S_ERROR); isec->is_name = tmpl_isp->is_name; isec->is_shdr = shdr; isec->is_indata = data; *ret_isec = isec; *ret_shdr = shdr; *ret_data = data; return (1); } /* * Build a .bss section for allocation of tentative definitions. Any `static' * .bss definitions would have been associated to their own .bss sections and * thus collected from the input files. `global' .bss definitions are tagged * as COMMON and do not cause any associated .bss section elements to be * generated. Here we add up all these COMMON symbols and generate the .bss * section required to represent them. */ uintptr_t ld_make_bss(Ofl_desc *ofl, Xword size, Xword align, uint_t ident) { Shdr *shdr; Elf_Data *data; Is_desc *isec; Os_desc *osp; Xword rsize = (Xword)ofl->ofl_relocbsssz; /* * Allocate header structs. We will set the name ourselves below, * and there is no entcnt for a BSS. So, the shname and entcnt * arguments are 0. */ if (new_section(ofl, SHT_NOBITS, NULL, 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); data->d_size = (size_t)size; data->d_align = (size_t)align; shdr->sh_size = size; shdr->sh_addralign = align; if (ident == ld_targ.t_id.id_tlsbss) { isec->is_name = MSG_ORIG(MSG_SCN_TBSS); ofl->ofl_istlsbss = isec; shdr->sh_flags |= SHF_TLS; } else if (ident == ld_targ.t_id.id_bss) { isec->is_name = MSG_ORIG(MSG_SCN_BSS); ofl->ofl_isbss = isec; #if defined(_ELF64) } else if ((ld_targ.t_m.m_mach == EM_AMD64) && (ident == ld_targ.t_id.id_lbss)) { isec->is_name = MSG_ORIG(MSG_SCN_LBSS); ofl->ofl_islbss = isec; shdr->sh_flags |= SHF_AMD64_LARGE; #endif } /* * Retain this .*bss input section as this will be where global symbol * references are added. */ if ((osp = ld_place_section(ofl, isec, NULL, ident, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); /* * If relocations exist against a .*bss section, a section symbol must * be created for the section in the .dynsym symbol table. */ if (!(osp->os_flags & FLG_OS_OUTREL)) { ofl_flag_t flagtotest; if (ident == ld_targ.t_id.id_tlsbss) flagtotest = FLG_OF1_TLSOREL; else flagtotest = FLG_OF1_BSSOREL; if (ofl->ofl_flags1 & flagtotest) { ofl->ofl_dynshdrcnt++; osp->os_flags |= FLG_OS_OUTREL; } } osp->os_szoutrels = rsize; return (1); } /* * Build a SHT_{INIT|FINI|PREINIT}ARRAY section (specified via * ld -z *array=name). */ static uintptr_t make_array(Ofl_desc *ofl, Word shtype, const char *sectname, APlist *alp) { uint_t entcount; Aliste idx; Elf_Data *data; Is_desc *isec; Shdr *shdr; Sym_desc *sdp; Rel_desc reld; Rela reloc; Os_desc *osp; uintptr_t ret = 1; if (alp == NULL) return (1); entcount = 0; for (APLIST_TRAVERSE(alp, idx, sdp)) entcount++; if (new_section(ofl, shtype, sectname, entcount, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); if ((data->d_buf = libld_calloc(entcount, sizeof (Addr))) == NULL) return (S_ERROR); if (ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_array, NULL) == (Os_desc *)S_ERROR) return (S_ERROR); osp = isec->is_osdesc; if ((ofl->ofl_osinitarray == NULL) && (shtype == SHT_INIT_ARRAY)) ofl->ofl_osinitarray = osp; if ((ofl->ofl_ospreinitarray == NULL) && (shtype == SHT_PREINIT_ARRAY)) ofl->ofl_ospreinitarray = osp; else if ((ofl->ofl_osfiniarray == NULL) && (shtype == SHT_FINI_ARRAY)) ofl->ofl_osfiniarray = osp; /* * Create relocations against this section to initialize it to the * function addresses. */ reld.rel_isdesc = isec; reld.rel_aux = NULL; reld.rel_flags = FLG_REL_LOAD; /* * Fabricate the relocation information (as if a relocation record had * been input - see init_rel()). */ reld.rel_rtype = ld_targ.t_m.m_r_arrayaddr; reld.rel_roffset = 0; reld.rel_raddend = 0; /* * Create a minimal relocation record to satisfy process_sym_reloc() * debugging requirements. */ reloc.r_offset = 0; reloc.r_info = ELF_R_INFO(0, ld_targ.t_m.m_r_arrayaddr); reloc.r_addend = 0; DBG_CALL(Dbg_reloc_generate(ofl->ofl_lml, osp, ld_targ.t_m.m_rel_sht_type)); for (APLIST_TRAVERSE(alp, idx, sdp)) { reld.rel_sym = sdp; if (ld_process_sym_reloc(ofl, &reld, (Rel *)&reloc, isec, MSG_INTL(MSG_STR_COMMAND), 0) == S_ERROR) { ret = S_ERROR; continue; } reld.rel_roffset += (Xword)sizeof (Addr); reloc.r_offset = reld.rel_roffset; } return (ret); } /* * Build a comment section (-Qy option). */ static uintptr_t make_comment(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_COMMENT), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); data->d_buf = (void *)ofl->ofl_sgsid; data->d_size = strlen(ofl->ofl_sgsid) + 1; data->d_align = 1; shdr->sh_size = (Xword)data->d_size; shdr->sh_flags = 0; shdr->sh_addralign = 1; return ((uintptr_t)ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_note, NULL)); } /* * Make the dynamic section. Calculate the size of any strings referenced * within this structure, they will be added to the global string table * (.dynstr). This routine should be called before make_dynstr(). * * This routine must be maintained in parallel with update_odynamic() * in update.c */ static uintptr_t make_dynamic(Ofl_desc *ofl) { Shdr *shdr; Os_desc *osp; Elf_Data *data; Is_desc *isec; size_t cnt = 0; Aliste idx; Ifl_desc *ifl; Sym_desc *sdp; size_t size; Str_tbl *strtbl; ofl_flag_t flags = ofl->ofl_flags; int not_relobj = !(flags & FLG_OF_RELOBJ); int unused = 0; /* * Select the required string table. */ if (OFL_IS_STATIC_OBJ(ofl)) strtbl = ofl->ofl_strtab; else strtbl = ofl->ofl_dynstrtab; /* * Only a limited subset of DT_ entries apply to relocatable * objects. See the comment at the head of update_odynamic() in * update.c for details. */ if (new_section(ofl, SHT_DYNAMIC, MSG_ORIG(MSG_SCN_DYNAMIC), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* * new_section() does not set SHF_ALLOC. If we're building anything * besides a relocatable object, then the .dynamic section should * reside in allocatable memory. */ if (not_relobj) shdr->sh_flags |= SHF_ALLOC; /* * new_section() does not set SHF_WRITE. If we're building an object * that specifies an interpretor, then a DT_DEBUG entry is created, * which is initialized to the applications link-map list at runtime. */ if (ofl->ofl_osinterp) shdr->sh_flags |= SHF_WRITE; osp = ofl->ofl_osdynamic = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_dynamic, NULL); /* * Reserve entries for any needed dependencies. */ for (APLIST_TRAVERSE(ofl->ofl_sos, idx, ifl)) { if (!(ifl->ifl_flags & (FLG_IF_NEEDED | FLG_IF_NEEDSTR))) continue; /* * If this dependency didn't satisfy any symbol references, * generate a debugging diagnostic (ld(1) -Dunused can be used * to display these). If this is a standard needed dependency, * and -z ignore is in effect, drop the dependency. Explicitly * defined dependencies (i.e., -N dep) don't get dropped, and * are flagged as being required to simplify update_odynamic() * processing. */ if ((ifl->ifl_flags & FLG_IF_NEEDSTR) || ((ifl->ifl_flags & FLG_IF_DEPREQD) == 0)) { if (unused++ == 0) DBG_CALL(Dbg_util_nl(ofl->ofl_lml, DBG_NL_STD)); DBG_CALL(Dbg_unused_file(ofl->ofl_lml, ifl->ifl_soname, (ifl->ifl_flags & FLG_IF_NEEDSTR), 0)); /* * Guidance: Remove unused dependency. * * If -z ignore is in effect, this warning is not * needed because we will quietly remove the unused * dependency. */ if (OFL_GUIDANCE(ofl, FLG_OFG_NO_UNUSED) && ((ifl->ifl_flags & FLG_IF_IGNORE) == 0)) ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_UNUSED), ifl->ifl_soname); if (ifl->ifl_flags & FLG_IF_NEEDSTR) ifl->ifl_flags |= FLG_IF_DEPREQD; else if (ifl->ifl_flags & FLG_IF_IGNORE) continue; } /* * If this object requires a DT_POSFLAG_1 entry, reserve it. */ if ((ifl->ifl_flags & MSK_IF_POSFLAG1) && not_relobj) cnt++; if (st_insert(strtbl, ifl->ifl_soname) == -1) return (S_ERROR); cnt++; /* * If the needed entry contains the $ORIGIN token make sure * the associated DT_1_FLAGS entry is created. */ if (strstr(ifl->ifl_soname, MSG_ORIG(MSG_STR_ORIGIN))) { ofl->ofl_dtflags_1 |= DF_1_ORIGIN; ofl->ofl_dtflags |= DF_ORIGIN; } } if (unused) DBG_CALL(Dbg_util_nl(ofl->ofl_lml, DBG_NL_STD)); if (not_relobj) { /* * Reserve entries for any per-symbol auxiliary/filter strings. */ cnt += alist_nitems(ofl->ofl_dtsfltrs); /* * Reserve entries for _init() and _fini() section addresses. */ if (((sdp = ld_sym_find(MSG_ORIG(MSG_SYM_INIT_U), SYM_NOHASH, NULL, ofl)) != NULL) && (sdp->sd_ref == REF_REL_NEED) && (sdp->sd_sym->st_shndx != SHN_UNDEF)) { sdp->sd_flags |= FLG_SY_UPREQD; cnt++; } if (((sdp = ld_sym_find(MSG_ORIG(MSG_SYM_FINI_U), SYM_NOHASH, NULL, ofl)) != NULL) && (sdp->sd_ref == REF_REL_NEED) && (sdp->sd_sym->st_shndx != SHN_UNDEF)) { sdp->sd_flags |= FLG_SY_UPREQD; cnt++; } /* * Reserve entries for any soname, filter name (shared libs * only), run-path pointers, cache names and audit requirements. */ if (ofl->ofl_soname) { cnt++; if (st_insert(strtbl, ofl->ofl_soname) == -1) return (S_ERROR); } if (ofl->ofl_filtees) { cnt++; if (st_insert(strtbl, ofl->ofl_filtees) == -1) return (S_ERROR); /* * If the filtees entry contains the $ORIGIN token * make sure the associated DT_1_FLAGS entry is created. */ if (strstr(ofl->ofl_filtees, MSG_ORIG(MSG_STR_ORIGIN))) { ofl->ofl_dtflags_1 |= DF_1_ORIGIN; ofl->ofl_dtflags |= DF_ORIGIN; } } } if (ofl->ofl_rpath) { cnt += 2; /* DT_RPATH & DT_RUNPATH */ if (st_insert(strtbl, ofl->ofl_rpath) == -1) return (S_ERROR); /* * If the rpath entry contains the $ORIGIN token make sure * the associated DT_1_FLAGS entry is created. */ if (strstr(ofl->ofl_rpath, MSG_ORIG(MSG_STR_ORIGIN))) { ofl->ofl_dtflags_1 |= DF_1_ORIGIN; ofl->ofl_dtflags |= DF_ORIGIN; } } if (not_relobj) { Aliste idx; Sg_desc *sgp; if (ofl->ofl_config) { cnt++; if (st_insert(strtbl, ofl->ofl_config) == -1) return (S_ERROR); /* * If the config entry contains the $ORIGIN token * make sure the associated DT_1_FLAGS entry is created. */ if (strstr(ofl->ofl_config, MSG_ORIG(MSG_STR_ORIGIN))) { ofl->ofl_dtflags_1 |= DF_1_ORIGIN; ofl->ofl_dtflags |= DF_ORIGIN; } } if (ofl->ofl_depaudit) { cnt++; if (st_insert(strtbl, ofl->ofl_depaudit) == -1) return (S_ERROR); } if (ofl->ofl_audit) { cnt++; if (st_insert(strtbl, ofl->ofl_audit) == -1) return (S_ERROR); } /* * Reserve entries for the DT_HASH, DT_STRTAB, DT_STRSZ, * DT_SYMTAB, DT_SYMENT, and DT_CHECKSUM. */ cnt += 6; /* * If we are including local functions at the head of * the dynsym, then also reserve entries for DT_SUNW_SYMTAB * and DT_SUNW_SYMSZ. */ if (OFL_ALLOW_LDYNSYM(ofl)) cnt += 2; if ((ofl->ofl_dynsymsortcnt > 0) || (ofl->ofl_dyntlssortcnt > 0)) cnt++; /* DT_SUNW_SORTENT */ if (ofl->ofl_dynsymsortcnt > 0) cnt += 2; /* DT_SUNW_[SYMSORT|SYMSORTSZ] */ if (ofl->ofl_dyntlssortcnt > 0) cnt += 2; /* DT_SUNW_[TLSSORT|TLSSORTSZ] */ if ((flags & (FLG_OF_VERDEF | FLG_OF_NOVERSEC)) == FLG_OF_VERDEF) cnt += 2; /* DT_VERDEF & DT_VERDEFNUM */ if ((flags & (FLG_OF_VERNEED | FLG_OF_NOVERSEC)) == FLG_OF_VERNEED) cnt += 2; /* DT_VERNEED & DT_VERNEEDNUM */ if ((flags & FLG_OF_COMREL) && ofl->ofl_relocrelcnt) cnt++; /* DT_RELACOUNT */ if (flags & FLG_OF_TEXTREL) /* DT_TEXTREL */ cnt++; if (ofl->ofl_osfiniarray) /* DT_FINI_ARRAY */ cnt += 2; /* DT_FINI_ARRAYSZ */ if (ofl->ofl_osinitarray) /* DT_INIT_ARRAY */ cnt += 2; /* DT_INIT_ARRAYSZ */ if (ofl->ofl_ospreinitarray) /* DT_PREINIT_ARRAY & */ cnt += 2; /* DT_PREINIT_ARRAYSZ */ /* * If we have plt's reserve a DT_PLTRELSZ, DT_PLTREL and * DT_JMPREL. */ if (ofl->ofl_pltcnt) cnt += 3; /* * If plt padding is needed (Sparcv9). */ if (ofl->ofl_pltpad) cnt += 2; /* DT_PLTPAD & DT_PLTPADSZ */ /* * If we have any relocations reserve a DT_REL, DT_RELSZ and * DT_RELENT entry. */ if (ofl->ofl_relocsz) cnt += 3; /* * If a syminfo section is required create DT_SYMINFO, * DT_SYMINSZ, and DT_SYMINENT entries. */ if (flags & FLG_OF_SYMINFO) cnt += 3; /* * If there are any partially initialized sections allocate * DT_MOVETAB, DT_MOVESZ and DT_MOVEENT. */ if (ofl->ofl_osmove) cnt += 3; /* * Allocate one DT_REGISTER entry for every register symbol. */ cnt += ofl->ofl_regsymcnt; /* * Reserve a entry for each '-zrtldinfo=...' specified * on the command line. */ for (APLIST_TRAVERSE(ofl->ofl_rtldinfo, idx, sdp)) cnt++; /* * The following entry should only be placed in a segment that * is writable. */ if (((sgp = osp->os_sgdesc) != NULL) && (sgp->sg_phdr.p_flags & PF_W) && ofl->ofl_osinterp) cnt++; /* DT_DEBUG */ /* * Capabilities require a .dynamic entry for the .SUNW_cap * section. */ if (ofl->ofl_oscap) cnt++; /* DT_SUNW_CAP */ /* * Symbol capabilities require a .dynamic entry for the * .SUNW_capinfo section. */ if (ofl->ofl_oscapinfo) cnt++; /* DT_SUNW_CAPINFO */ /* * Capabilities chain information requires a .SUNW_capchain * entry (DT_SUNW_CAPCHAIN), entry size (DT_SUNW_CAPCHAINENT), * and total size (DT_SUNW_CAPCHAINSZ). */ if (ofl->ofl_oscapchain) cnt += 3; if (flags & FLG_OF_SYMBOLIC) cnt++; /* DT_SYMBOLIC */ if (ofl->ofl_aslr != 0) /* DT_SUNW_ASLR */ cnt++; } /* DT_SUNW_KMOD */ if (ofl->ofl_flags & FLG_OF_KMOD) cnt++; /* * Account for Architecture dependent .dynamic entries, and defaults. */ (*ld_targ.t_mr.mr_mach_make_dynamic)(ofl, &cnt); /* * DT_FLAGS, DT_FLAGS_1, DT_SUNW_STRPAD, and DT_NULL. Also, * allow room for the unused extra DT_NULLs. These are included * to allow an ELF editor room to add items later. */ cnt += 4 + DYNAMIC_EXTRA_ELTS; /* * DT_SUNW_LDMACH. Used to hold the ELF machine code of the * linker that produced the output object. This information * allows us to determine whether a given object was linked * natively, or by a linker running on a different type of * system. This information can be valuable if one suspects * that a problem might be due to alignment or byte order issues. */ cnt++; /* * Determine the size of the section from the number of entries. */ size = cnt * (size_t)shdr->sh_entsize; shdr->sh_size = (Xword)size; data->d_size = size; /* * There are several tags that are specific to the Solaris osabi * range which we unconditionally put into any dynamic section * we create (e.g. DT_SUNW_STRPAD or DT_SUNW_LDMACH). As such, * any Solaris object with a dynamic section should be tagged as * ELFOSABI_SOLARIS. */ ofl->ofl_flags |= FLG_OF_OSABI; return ((uintptr_t)ofl->ofl_osdynamic); } /* * Build the GOT section and its associated relocation entries. */ uintptr_t ld_make_got(Ofl_desc *ofl) { Elf_Data *data; Shdr *shdr; Is_desc *isec; size_t size = (size_t)ofl->ofl_gotcnt * ld_targ.t_m.m_got_entsize; size_t rsize = (size_t)ofl->ofl_relocgotsz; if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_GOT), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); data->d_size = size; shdr->sh_flags |= SHF_WRITE; shdr->sh_size = (Xword)size; shdr->sh_entsize = ld_targ.t_m.m_got_entsize; ofl->ofl_osgot = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_got, NULL); if (ofl->ofl_osgot == (Os_desc *)S_ERROR) return (S_ERROR); ofl->ofl_osgot->os_szoutrels = (Xword)rsize; return (1); } /* * Build an interpreter section. */ static uintptr_t make_interp(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; const char *iname = ofl->ofl_interp; size_t size; /* * If -z nointerp is in effect, don't create an interpreter section. */ if (ofl->ofl_flags1 & FLG_OF1_NOINTRP) return (1); /* * An .interp section is always created for a dynamic executable. * A user can define the interpreter to use. This definition overrides * the default that would be recorded in an executable, and triggers * the creation of an .interp section in any other object. Presumably * the user knows what they are doing. Refer to the generic ELF ABI * section 5-4, and the ld(1) -I option. */ if (((ofl->ofl_flags & (FLG_OF_DYNAMIC | FLG_OF_EXEC | FLG_OF_RELOBJ)) != (FLG_OF_DYNAMIC | FLG_OF_EXEC)) && !iname) return (1); /* * In the case of a dynamic executable, supply a default interpreter * if the user has not specified their own. */ if (iname == NULL) iname = ofl->ofl_interp = ld_targ.t_m.m_def_interp; size = strlen(iname) + 1; if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_INTERP), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); data->d_size = size; shdr->sh_size = (Xword)size; data->d_align = shdr->sh_addralign = 1; ofl->ofl_osinterp = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_interp, NULL); return ((uintptr_t)ofl->ofl_osinterp); } /* * Common function used to build the SHT_SUNW_versym section, SHT_SUNW_syminfo * section, and SHT_SUNW_capinfo section. Each of these sections provide * additional symbol information, and their size parallels the associated * symbol table. */ static Os_desc * make_sym_sec(Ofl_desc *ofl, const char *sectname, Word stype, int ident) { Shdr *shdr; Elf_Data *data; Is_desc *isec; /* * We don't know the size of this section yet, so set it to 0. The * size gets filled in after the associated symbol table is sized. */ if (new_section(ofl, stype, sectname, 0, &isec, &shdr, &data) == S_ERROR) return ((Os_desc *)S_ERROR); return (ld_place_section(ofl, isec, NULL, ident, NULL)); } /* * Determine whether a symbol capability is redundant because the object * capabilities are more restrictive. */ inline static int is_cap_redundant(Objcapset *ocapset, Objcapset *scapset) { Alist *oalp, *salp; elfcap_mask_t omsk, smsk; /* * Inspect any platform capabilities. If the object defines platform * capabilities, then the object will only be loaded for those * platforms. A symbol capability set that doesn't define the same * platforms is redundant, and a symbol capability that does not provide * at least one platform name that matches a platform name in the object * capabilities will never execute (as the object wouldn't have been * loaded). */ oalp = ocapset->oc_plat.cl_val; salp = scapset->oc_plat.cl_val; if (oalp && ((salp == NULL) || cap_names_match(oalp, salp))) return (1); /* * If the symbol capability set defines platforms, and the object * doesn't, then the symbol set is more restrictive. */ if (salp && (oalp == NULL)) return (0); /* * Next, inspect any machine name capabilities. If the object defines * machine name capabilities, then the object will only be loaded for * those machines. A symbol capability set that doesn't define the same * machine names is redundant, and a symbol capability that does not * provide at least one machine name that matches a machine name in the * object capabilities will never execute (as the object wouldn't have * been loaded). */ oalp = ocapset->oc_plat.cl_val; salp = scapset->oc_plat.cl_val; if (oalp && ((salp == NULL) || cap_names_match(oalp, salp))) return (1); /* * If the symbol capability set defines machine names, and the object * doesn't, then the symbol set is more restrictive. */ if (salp && (oalp == NULL)) return (0); /* * Next, inspect any hardware capabilities. If the objects hardware * capabilities are greater than or equal to that of the symbols * capabilities, then the symbol capability set is redundant. If the * symbols hardware capabilities are greater that the objects, then the * symbol set is more restrictive. * * Note that this is a somewhat arbitrary definition, as each capability * bit is independent of the others, and some of the higher order bits * could be considered to be less important than lower ones. However, * this is the only reasonable non-subjective definition. */ omsk = ocapset->oc_hw_3.cm_val; smsk = scapset->oc_hw_3.cm_val; if ((omsk > smsk) || (omsk && (omsk == smsk))) return (1); if (omsk < smsk) return (0); omsk = ocapset->oc_hw_2.cm_val; smsk = scapset->oc_hw_2.cm_val; if ((omsk > smsk) || (omsk && (omsk == smsk))) return (1); if (omsk < smsk) return (0); /* * Finally, inspect the remaining hardware capabilities. */ omsk = ocapset->oc_hw_1.cm_val; smsk = scapset->oc_hw_1.cm_val; if ((omsk > smsk) || (omsk && (omsk == smsk))) return (1); return (0); } /* * Capabilities values might have been assigned excluded values. These * excluded values should be removed before calculating any capabilities * sections size. */ static void capmask_value(Lm_list *lml, Word type, Capmask *capmask, int *title) { /* * First determine whether any bits should be excluded. */ if ((capmask->cm_val & capmask->cm_exc) == 0) return; DBG_CALL(Dbg_cap_post_title(lml, title)); DBG_CALL(Dbg_cap_val_entry(lml, DBG_STATE_CURRENT, type, capmask->cm_val, ld_targ.t_m.m_mach)); DBG_CALL(Dbg_cap_val_entry(lml, DBG_STATE_EXCLUDE, type, capmask->cm_exc, ld_targ.t_m.m_mach)); capmask->cm_val &= ~capmask->cm_exc; DBG_CALL(Dbg_cap_val_entry(lml, DBG_STATE_RESOLVED, type, capmask->cm_val, ld_targ.t_m.m_mach)); } static void capstr_value(Lm_list *lml, Word type, Caplist *caplist, int *title) { Aliste idx1, idx2; char *estr; Capstr *capstr; Boolean found = FALSE; /* * First determine whether any strings should be excluded. */ for (APLIST_TRAVERSE(caplist->cl_exc, idx1, estr)) { for (ALIST_TRAVERSE(caplist->cl_val, idx2, capstr)) { if (strcmp(estr, capstr->cs_str) == 0) { found = TRUE; break; } } } if (found == FALSE) return; /* * Traverse the current strings, then delete the excluded strings, * and finally display the resolved strings. */ if (DBG_ENABLED) { Dbg_cap_post_title(lml, title); for (ALIST_TRAVERSE(caplist->cl_val, idx2, capstr)) { Dbg_cap_ptr_entry(lml, DBG_STATE_CURRENT, type, capstr->cs_str); } } for (APLIST_TRAVERSE(caplist->cl_exc, idx1, estr)) { for (ALIST_TRAVERSE(caplist->cl_val, idx2, capstr)) { if (strcmp(estr, capstr->cs_str) == 0) { DBG_CALL(Dbg_cap_ptr_entry(lml, DBG_STATE_EXCLUDE, type, capstr->cs_str)); alist_delete(caplist->cl_val, &idx2); break; } } } if (DBG_ENABLED) { for (ALIST_TRAVERSE(caplist->cl_val, idx2, capstr)) { Dbg_cap_ptr_entry(lml, DBG_STATE_RESOLVED, type, capstr->cs_str); } } } /* * Build a capabilities section. */ #define CAP_UPDATE(cap, capndx, tag, val) \ cap->c_tag = tag; \ cap->c_un.c_val = val; \ cap++, capndx++; static uintptr_t make_cap(Ofl_desc *ofl, Word shtype, const char *shname, int ident) { Shdr *shdr; Elf_Data *data; Is_desc *isec; Cap *cap; size_t size = 0; Word capndx = 0; Str_tbl *strtbl; Objcapset *ocapset = &ofl->ofl_ocapset; Aliste idx1; Capstr *capstr; int title = 0; /* * Determine which string table to use for any CA_SUNW_MACH, * CA_SUNW_PLAT, or CA_SUNW_ID strings. */ if (OFL_IS_STATIC_OBJ(ofl)) strtbl = ofl->ofl_strtab; else strtbl = ofl->ofl_dynstrtab; /* * If symbol capabilities have been requested, but none have been * created, warn the user. This scenario can occur if none of the * input relocatable objects defined any object capabilities. */ if ((ofl->ofl_flags & FLG_OF_OTOSCAP) && (ofl->ofl_capsymcnt == 0)) ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_CAP_NOSYMSFOUND)); /* * If symbol capabilities have been collected, but no symbols are left * referencing these capabilities, promote the capability groups back * to an object capability definition. */ if ((ofl->ofl_flags & FLG_OF_OTOSCAP) && ofl->ofl_capsymcnt && (ofl->ofl_capfamilies == NULL)) { ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_CAP_NOSYMSFOUND)); ld_cap_move_symtoobj(ofl); ofl->ofl_capsymcnt = 0; ofl->ofl_capgroups = NULL; ofl->ofl_flags &= ~FLG_OF_OTOSCAP; } /* * Remove any excluded capabilities. */ capstr_value(ofl->ofl_lml, CA_SUNW_PLAT, &ocapset->oc_plat, &title); capstr_value(ofl->ofl_lml, CA_SUNW_MACH, &ocapset->oc_mach, &title); capmask_value(ofl->ofl_lml, CA_SUNW_HW_3, &ocapset->oc_hw_3, &title); capmask_value(ofl->ofl_lml, CA_SUNW_HW_2, &ocapset->oc_hw_2, &title); capmask_value(ofl->ofl_lml, CA_SUNW_HW_1, &ocapset->oc_hw_1, &title); capmask_value(ofl->ofl_lml, CA_SUNW_SF_1, &ocapset->oc_sf_1, &title); /* * Determine how many entries are required for any object capabilities. */ size += alist_nitems(ocapset->oc_plat.cl_val); size += alist_nitems(ocapset->oc_mach.cl_val); if (ocapset->oc_hw_3.cm_val) size++; if (ocapset->oc_hw_2.cm_val) size++; if (ocapset->oc_hw_1.cm_val) size++; if (ocapset->oc_sf_1.cm_val) size++; /* * Only identify a capabilities group if the group has content. If a * capabilities identifier exists, and no other capabilities have been * supplied, remove the identifier. This scenario could exist if a * user mistakenly defined a lone identifier, or if an identified group * was overridden so as to clear the existing capabilities and the * identifier was not also cleared. */ if (ocapset->oc_id.cs_str) { if (size) size++; else ocapset->oc_id.cs_str = NULL; } if (size) size++; /* Add CA_SUNW_NULL */ /* * Determine how many entries are required for any symbol capabilities. */ if (ofl->ofl_capsymcnt) { /* * If there are no object capabilities, a CA_SUNW_NULL entry * is required before any symbol capabilities. */ if (size == 0) size++; size += ofl->ofl_capsymcnt; } if (size == 0) return (0); if (new_section(ofl, shtype, shname, size, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); if ((data->d_buf = libld_malloc(shdr->sh_size)) == NULL) return (S_ERROR); cap = (Cap *)data->d_buf; /* * Fill in any object capabilities. If there is an identifier, then the * identifier comes first. The remaining items follow in precedence * order, although the order isn't important for runtime verification. */ if (ocapset->oc_id.cs_str) { ofl->ofl_flags |= FLG_OF_CAPSTRS; if (st_insert(strtbl, ocapset->oc_id.cs_str) == -1) return (S_ERROR); ocapset->oc_id.cs_ndx = capndx; CAP_UPDATE(cap, capndx, CA_SUNW_ID, 0); } if (ocapset->oc_plat.cl_val) { ofl->ofl_flags |= (FLG_OF_PTCAP | FLG_OF_CAPSTRS); /* * Insert any platform name strings in the appropriate string * table. The capability value can't be filled in yet, as the * final offset of the strings isn't known until later. */ for (ALIST_TRAVERSE(ocapset->oc_plat.cl_val, idx1, capstr)) { if (st_insert(strtbl, capstr->cs_str) == -1) return (S_ERROR); capstr->cs_ndx = capndx; CAP_UPDATE(cap, capndx, CA_SUNW_PLAT, 0); } } if (ocapset->oc_mach.cl_val) { ofl->ofl_flags |= (FLG_OF_PTCAP | FLG_OF_CAPSTRS); /* * Insert the machine name strings in the appropriate string * table. The capability value can't be filled in yet, as the * final offset of the strings isn't known until later. */ for (ALIST_TRAVERSE(ocapset->oc_mach.cl_val, idx1, capstr)) { if (st_insert(strtbl, capstr->cs_str) == -1) return (S_ERROR); capstr->cs_ndx = capndx; CAP_UPDATE(cap, capndx, CA_SUNW_MACH, 0); } } if (ocapset->oc_hw_3.cm_val) { ofl->ofl_flags |= FLG_OF_PTCAP; CAP_UPDATE(cap, capndx, CA_SUNW_HW_3, ocapset->oc_hw_3.cm_val); } if (ocapset->oc_hw_2.cm_val) { ofl->ofl_flags |= FLG_OF_PTCAP; CAP_UPDATE(cap, capndx, CA_SUNW_HW_2, ocapset->oc_hw_2.cm_val); } if (ocapset->oc_hw_1.cm_val) { ofl->ofl_flags |= FLG_OF_PTCAP; CAP_UPDATE(cap, capndx, CA_SUNW_HW_1, ocapset->oc_hw_1.cm_val); } if (ocapset->oc_sf_1.cm_val) { ofl->ofl_flags |= FLG_OF_PTCAP; CAP_UPDATE(cap, capndx, CA_SUNW_SF_1, ocapset->oc_sf_1.cm_val); } CAP_UPDATE(cap, capndx, CA_SUNW_NULL, 0); /* * Fill in any symbol capabilities. */ if (ofl->ofl_capgroups) { Cap_group *cgp; for (APLIST_TRAVERSE(ofl->ofl_capgroups, idx1, cgp)) { Objcapset *scapset = &cgp->cg_set; Aliste idx2; Is_desc *isp; cgp->cg_ndx = capndx; if (scapset->oc_id.cs_str) { ofl->ofl_flags |= FLG_OF_CAPSTRS; /* * Insert the identifier string in the * appropriate string table. The capability * value can't be filled in yet, as the final * offset of the string isn't known until later. */ if (st_insert(strtbl, scapset->oc_id.cs_str) == -1) return (S_ERROR); scapset->oc_id.cs_ndx = capndx; CAP_UPDATE(cap, capndx, CA_SUNW_ID, 0); } if (scapset->oc_plat.cl_val) { ofl->ofl_flags |= FLG_OF_CAPSTRS; /* * Insert the platform name string in the * appropriate string table. The capability * value can't be filled in yet, as the final * offset of the string isn't known until later. */ for (ALIST_TRAVERSE(scapset->oc_plat.cl_val, idx2, capstr)) { if (st_insert(strtbl, capstr->cs_str) == -1) return (S_ERROR); capstr->cs_ndx = capndx; CAP_UPDATE(cap, capndx, CA_SUNW_PLAT, 0); } } if (scapset->oc_mach.cl_val) { ofl->ofl_flags |= FLG_OF_CAPSTRS; /* * Insert the machine name string in the * appropriate string table. The capability * value can't be filled in yet, as the final * offset of the string isn't known until later. */ for (ALIST_TRAVERSE(scapset->oc_mach.cl_val, idx2, capstr)) { if (st_insert(strtbl, capstr->cs_str) == -1) return (S_ERROR); capstr->cs_ndx = capndx; CAP_UPDATE(cap, capndx, CA_SUNW_MACH, 0); } } if (scapset->oc_hw_3.cm_val) { CAP_UPDATE(cap, capndx, CA_SUNW_HW_3, scapset->oc_hw_3.cm_val); } if (scapset->oc_hw_2.cm_val) { CAP_UPDATE(cap, capndx, CA_SUNW_HW_2, scapset->oc_hw_2.cm_val); } if (scapset->oc_hw_1.cm_val) { CAP_UPDATE(cap, capndx, CA_SUNW_HW_1, scapset->oc_hw_1.cm_val); } if (scapset->oc_sf_1.cm_val) { CAP_UPDATE(cap, capndx, CA_SUNW_SF_1, scapset->oc_sf_1.cm_val); } CAP_UPDATE(cap, capndx, CA_SUNW_NULL, 0); /* * If any object capabilities are available, determine * whether these symbol capabilities are less * restrictive, and hence redundant. */ if (((ofl->ofl_flags & FLG_OF_PTCAP) == 0) || (is_cap_redundant(ocapset, scapset) == 0)) continue; /* * Indicate any files that provide redundant symbol * capabilities. */ for (APLIST_TRAVERSE(cgp->cg_secs, idx2, isp)) { ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_CAP_REDUNDANT), isp->is_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name); } } } /* * If capabilities strings are required, the sh_info field of the * section header will be set to the associated string table. */ if (ofl->ofl_flags & FLG_OF_CAPSTRS) shdr->sh_flags |= SHF_INFO_LINK; /* * Place these capabilities in the output file. */ if ((ofl->ofl_oscap = ld_place_section(ofl, isec, NULL, ident, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); /* * If symbol capabilities are required, then a .SUNW_capinfo section is * also created. This table will eventually be sized to match the * associated symbol table. */ if (ofl->ofl_capfamilies) { if ((ofl->ofl_oscapinfo = make_sym_sec(ofl, MSG_ORIG(MSG_SCN_SUNWCAPINFO), SHT_SUNW_capinfo, ld_targ.t_id.id_capinfo)) == (Os_desc *)S_ERROR) return (S_ERROR); /* * If we're generating a dynamic object, capabilities family * members are maintained in a .SUNW_capchain section. */ if (ofl->ofl_capchaincnt && ((ofl->ofl_flags & FLG_OF_RELOBJ) == 0)) { if (new_section(ofl, SHT_SUNW_capchain, MSG_ORIG(MSG_SCN_SUNWCAPCHAIN), ofl->ofl_capchaincnt, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); ofl->ofl_oscapchain = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_capchain, NULL); if (ofl->ofl_oscapchain == (Os_desc *)S_ERROR) return (S_ERROR); } } return (1); } #undef CAP_UPDATE /* * Build the PLT section and its associated relocation entries. */ static uintptr_t make_plt(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size = ld_targ.t_m.m_plt_reservsz + (((size_t)ofl->ofl_pltcnt + (size_t)ofl->ofl_pltpad) * ld_targ.t_m.m_plt_entsize); size_t rsize = (size_t)ofl->ofl_relocpltsz; /* * On sparc, account for the NOP at the end of the plt. */ if (ld_targ.t_m.m_mach == LD_TARG_BYCLASS(EM_SPARC, EM_SPARCV9)) size += sizeof (Word); if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_PLT), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); data->d_size = size; data->d_align = ld_targ.t_m.m_plt_align; shdr->sh_flags = ld_targ.t_m.m_plt_shf_flags; shdr->sh_size = (Xword)size; shdr->sh_addralign = ld_targ.t_m.m_plt_align; shdr->sh_entsize = ld_targ.t_m.m_plt_entsize; ofl->ofl_osplt = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_plt, NULL); if (ofl->ofl_osplt == (Os_desc *)S_ERROR) return (S_ERROR); ofl->ofl_osplt->os_szoutrels = (Xword)rsize; return (1); } /* * Make the hash table. Only built for dynamic executables and shared * libraries, and provides hashed lookup into the global symbol table * (.dynsym) for the run-time linker to resolve symbol lookups. */ static uintptr_t make_hash(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size; Word nsyms = ofl->ofl_globcnt; size_t cnt; /* * Allocate section header structures. We set entcnt to 0 * because it's going to change after we place this section. */ if (new_section(ofl, SHT_HASH, MSG_ORIG(MSG_SCN_HASH), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* * Place the section first since it will affect the local symbol * count. */ ofl->ofl_oshash = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_hash, NULL); if (ofl->ofl_oshash == (Os_desc *)S_ERROR) return (S_ERROR); /* * Calculate the number of output hash buckets. */ ofl->ofl_hashbkts = findprime(nsyms); /* * The size of the hash table is determined by * * i. the initial nbucket and nchain entries (2) * ii. the number of buckets (calculated above) * iii. the number of chains (this is based on the number of * symbols in the .dynsym array). */ cnt = 2 + ofl->ofl_hashbkts + DYNSYM_ALL_CNT(ofl); size = cnt * shdr->sh_entsize; /* * Finalize the section header and data buffer initialization. */ if ((data->d_buf = libld_calloc(size, 1)) == NULL) return (S_ERROR); data->d_size = size; shdr->sh_size = (Xword)size; return (1); } /* * Generate the standard symbol table. Contains all locals and globals, * and resides in a non-allocatable section (ie. it can be stripped). */ static uintptr_t make_symtab(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; Is_desc *xisec = 0; size_t size; Word symcnt; /* * Create the section headers. Note that we supply an ent_cnt * of 0. We won't know the count until the section has been placed. */ if (new_section(ofl, SHT_SYMTAB, MSG_ORIG(MSG_SCN_SYMTAB), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* * Place the section first since it will affect the local symbol * count. */ if ((ofl->ofl_ossymtab = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_symtab, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); /* * At this point we've created all but the 'shstrtab' section. * Determine if we have to use 'Extended Sections'. If so - then * also create a SHT_SYMTAB_SHNDX section. */ if ((ofl->ofl_shdrcnt + 1) >= SHN_LORESERVE) { Shdr *xshdr; Elf_Data *xdata; if (new_section(ofl, SHT_SYMTAB_SHNDX, MSG_ORIG(MSG_SCN_SYMTAB_SHNDX), 0, &xisec, &xshdr, &xdata) == S_ERROR) return (S_ERROR); if ((ofl->ofl_ossymshndx = ld_place_section(ofl, xisec, NULL, ld_targ.t_id.id_symtab_ndx, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); } /* * Calculated number of symbols, which need to be augmented by * the (yet to be created) .shstrtab entry. */ symcnt = (size_t)(1 + SYMTAB_ALL_CNT(ofl)); size = symcnt * shdr->sh_entsize; /* * Finalize the section header and data buffer initialization. */ data->d_size = size; shdr->sh_size = (Xword)size; /* * If we created a SHT_SYMTAB_SHNDX - then set it's sizes too. */ if (xisec) { size_t xsize = symcnt * sizeof (Word); xisec->is_indata->d_size = xsize; xisec->is_shdr->sh_size = (Xword)xsize; } return (1); } /* * Build a dynamic symbol table. These tables reside in the text * segment of a dynamic executable or shared library. * * .SUNW_ldynsym contains local function symbols * .dynsym contains only globals symbols * * The two tables are created adjacent to each other, with .SUNW_ldynsym * coming first. */ static uintptr_t make_dynsym(Ofl_desc *ofl) { Shdr *shdr, *lshdr; Elf_Data *data, *ldata; Is_desc *isec, *lisec; size_t size; Xword cnt; int allow_ldynsym; /* * Unless explicitly disabled, always produce a .SUNW_ldynsym section * when it is allowed by the file type, even if the resulting * table only ends up with a single STT_FILE in it. There are * two reasons: (1) It causes the generation of the DT_SUNW_SYMTAB * entry in the .dynamic section, which is something we would * like to encourage, and (2) Without it, we cannot generate * the associated .SUNW_dyn[sym|tls]sort sections, which are of * value to DTrace. * * In practice, it is extremely rare for an object not to have * local symbols for .SUNW_ldynsym, so 99% of the time, we'd be * doing it anyway. */ allow_ldynsym = OFL_ALLOW_LDYNSYM(ofl); /* * Create the section headers. Note that we supply an ent_cnt * of 0. We won't know the count until the section has been placed. */ if (allow_ldynsym && new_section(ofl, SHT_SUNW_LDYNSYM, MSG_ORIG(MSG_SCN_LDYNSYM), 0, &lisec, &lshdr, &ldata) == S_ERROR) return (S_ERROR); if (new_section(ofl, SHT_DYNSYM, MSG_ORIG(MSG_SCN_DYNSYM), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* * Place the section(s) first since it will affect the local symbol * count. */ if (allow_ldynsym && ((ofl->ofl_osldynsym = ld_place_section(ofl, lisec, NULL, ld_targ.t_id.id_ldynsym, NULL)) == (Os_desc *)S_ERROR)) return (S_ERROR); ofl->ofl_osdynsym = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_dynsym, NULL); if (ofl->ofl_osdynsym == (Os_desc *)S_ERROR) return (S_ERROR); cnt = DYNSYM_ALL_CNT(ofl); size = (size_t)cnt * shdr->sh_entsize; /* * Finalize the section header and data buffer initialization. */ data->d_size = size; shdr->sh_size = (Xword)size; /* * An ldynsym contains local function symbols. It is not * used for linking, but if present, serves to allow better * stack traces to be generated in contexts where the symtab * is not available. (dladdr(), or stripped executable/library files). */ if (allow_ldynsym) { cnt = 1 + ofl->ofl_dynlocscnt + ofl->ofl_dynscopecnt; size = (size_t)cnt * shdr->sh_entsize; ldata->d_size = size; lshdr->sh_size = (Xword)size; } return (1); } /* * Build .SUNW_dynsymsort and/or .SUNW_dyntlssort sections. These are * index sections for the .SUNW_ldynsym/.dynsym pair that present data * and function symbols sorted by address. */ static uintptr_t make_dynsort(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; /* Only do it if the .SUNW_ldynsym section is present */ if (!OFL_ALLOW_LDYNSYM(ofl)) return (1); /* .SUNW_dynsymsort */ if (ofl->ofl_dynsymsortcnt > 0) { if (new_section(ofl, SHT_SUNW_symsort, MSG_ORIG(MSG_SCN_DYNSYMSORT), ofl->ofl_dynsymsortcnt, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); if ((ofl->ofl_osdynsymsort = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_dynsort, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); } /* .SUNW_dyntlssort */ if (ofl->ofl_dyntlssortcnt > 0) { if (new_section(ofl, SHT_SUNW_tlssort, MSG_ORIG(MSG_SCN_DYNTLSSORT), ofl->ofl_dyntlssortcnt, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); if ((ofl->ofl_osdyntlssort = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_dynsort, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); } return (1); } /* * Helper routine for make_dynsym_shndx. Builds a * a SHT_SYMTAB_SHNDX for .dynsym or .SUNW_ldynsym, without knowing * which one it is. */ static uintptr_t make_dyn_shndx(Ofl_desc *ofl, const char *shname, Os_desc *symtab, Os_desc **ret_os) { Is_desc *isec; Is_desc *dynsymisp; Shdr *shdr, *dynshdr; Elf_Data *data; dynsymisp = ld_os_first_isdesc(symtab); dynshdr = dynsymisp->is_shdr; if (new_section(ofl, SHT_SYMTAB_SHNDX, shname, (dynshdr->sh_size / dynshdr->sh_entsize), &isec, &shdr, &data) == S_ERROR) return (S_ERROR); if ((*ret_os = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_dynsym_ndx, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); assert(*ret_os); return (1); } /* * Build a SHT_SYMTAB_SHNDX for the .dynsym, and .SUNW_ldynsym */ static uintptr_t make_dynsym_shndx(Ofl_desc *ofl) { /* * If there is a .SUNW_ldynsym, generate a section for its extended * index section as well. */ if (OFL_ALLOW_LDYNSYM(ofl)) { if (make_dyn_shndx(ofl, MSG_ORIG(MSG_SCN_LDYNSYM_SHNDX), ofl->ofl_osldynsym, &ofl->ofl_osldynshndx) == S_ERROR) return (S_ERROR); } /* The Generate a section for the dynsym */ if (make_dyn_shndx(ofl, MSG_ORIG(MSG_SCN_DYNSYM_SHNDX), ofl->ofl_osdynsym, &ofl->ofl_osdynshndx) == S_ERROR) return (S_ERROR); return (1); } /* * Build a string table for the section headers. */ static uintptr_t make_shstrtab(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size; if (new_section(ofl, SHT_STRTAB, MSG_ORIG(MSG_SCN_SHSTRTAB), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* * Place the section first, as it may effect the number of section * headers to account for. */ ofl->ofl_osshstrtab = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_note, NULL); if (ofl->ofl_osshstrtab == (Os_desc *)S_ERROR) return (S_ERROR); size = st_getstrtab_sz(ofl->ofl_shdrsttab); assert(size > 0); data->d_size = size; shdr->sh_size = (Xword)size; return (1); } /* * Build a string section for the standard symbol table. */ static uintptr_t make_strtab(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size; /* * This string table consists of all the global and local symbols. * Account for null bytes at end of the file name and the beginning * of section. */ if (st_insert(ofl->ofl_strtab, ofl->ofl_name) == -1) return (S_ERROR); size = st_getstrtab_sz(ofl->ofl_strtab); assert(size > 0); if (new_section(ofl, SHT_STRTAB, MSG_ORIG(MSG_SCN_STRTAB), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* Set the size of the data area */ data->d_size = size; shdr->sh_size = (Xword)size; ofl->ofl_osstrtab = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_strtab, NULL); return ((uintptr_t)ofl->ofl_osstrtab); } /* * Build a string table for the dynamic symbol table. */ static uintptr_t make_dynstr(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size; /* * If producing a .SUNW_ldynsym, account for the initial STT_FILE * symbol that precedes the scope reduced global symbols. */ if (OFL_ALLOW_LDYNSYM(ofl)) { if (st_insert(ofl->ofl_dynstrtab, ofl->ofl_name) == -1) return (S_ERROR); ofl->ofl_dynscopecnt++; } /* * Account for any local, named register symbols. These locals are * required for reference from DT_REGISTER .dynamic entries. */ if (ofl->ofl_regsyms) { int ndx; for (ndx = 0; ndx < ofl->ofl_regsymsno; ndx++) { Sym_desc *sdp; if ((sdp = ofl->ofl_regsyms[ndx]) == NULL) continue; if (!SYM_IS_HIDDEN(sdp) && (ELF_ST_BIND(sdp->sd_sym->st_info) != STB_LOCAL)) continue; if (sdp->sd_sym->st_name == 0) continue; if (st_insert(ofl->ofl_dynstrtab, sdp->sd_name) == -1) return (S_ERROR); } } /* * Reserve entries for any per-symbol auxiliary/filter strings. */ if (ofl->ofl_dtsfltrs != NULL) { Dfltr_desc *dftp; Aliste idx; for (ALIST_TRAVERSE(ofl->ofl_dtsfltrs, idx, dftp)) if (st_insert(ofl->ofl_dynstrtab, dftp->dft_str) == -1) return (S_ERROR); } size = st_getstrtab_sz(ofl->ofl_dynstrtab); assert(size > 0); if (new_section(ofl, SHT_STRTAB, MSG_ORIG(MSG_SCN_DYNSTR), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* Make it allocable if necessary */ if (!(ofl->ofl_flags & FLG_OF_RELOBJ)) shdr->sh_flags |= SHF_ALLOC; /* Set the size of the data area */ data->d_size = size + DYNSTR_EXTRA_PAD; shdr->sh_size = (Xword)size; ofl->ofl_osdynstr = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_dynstr, NULL); return ((uintptr_t)ofl->ofl_osdynstr); } /* * Generate an output relocation section which will contain the relocation * information to be applied to the `osp' section. * * If (osp == NULL) then we are creating the coalesced relocation section * for an executable and/or a shared object. */ static uintptr_t make_reloc(Ofl_desc *ofl, Os_desc *osp) { Shdr *shdr; Elf_Data *data; Is_desc *isec; size_t size; Xword sh_flags; char *sectname; Os_desc *rosp; Word relsize; const char *rel_prefix; /* LINTED */ if (ld_targ.t_m.m_rel_sht_type == SHT_REL) { /* REL */ relsize = sizeof (Rel); rel_prefix = MSG_ORIG(MSG_SCN_REL); } else { /* RELA */ relsize = sizeof (Rela); rel_prefix = MSG_ORIG(MSG_SCN_RELA); } if (osp) { size = osp->os_szoutrels; sh_flags = osp->os_shdr->sh_flags; if ((sectname = libld_malloc(strlen(rel_prefix) + strlen(osp->os_name) + 1)) == 0) return (S_ERROR); (void) strcpy(sectname, rel_prefix); (void) strcat(sectname, osp->os_name); } else if (ofl->ofl_flags & FLG_OF_COMREL) { size = (ofl->ofl_reloccnt - ofl->ofl_reloccntsub) * relsize; sh_flags = SHF_ALLOC; sectname = (char *)MSG_ORIG(MSG_SCN_SUNWRELOC); } else { size = ofl->ofl_relocrelsz; sh_flags = SHF_ALLOC; sectname = (char *)rel_prefix; } /* * Keep track of total size of 'output relocations' (to be stored * in .dynamic) */ /* LINTED */ ofl->ofl_relocsz += (Xword)size; if (new_section(ofl, ld_targ.t_m.m_rel_sht_type, sectname, 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); data->d_size = size; shdr->sh_size = (Xword)size; if (OFL_ALLOW_DYNSYM(ofl) && (sh_flags & SHF_ALLOC)) shdr->sh_flags = SHF_ALLOC; if (osp) { /* * The sh_info field of the SHT_REL* sections points to the * section the relocations are to be applied to. */ shdr->sh_flags |= SHF_INFO_LINK; } rosp = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_rel, NULL); if (rosp == (Os_desc *)S_ERROR) return (S_ERROR); /* * Associate this relocation section to the section its going to * relocate. */ if (osp) { Aliste idx; Is_desc *risp; /* * This is used primarily so that we can update * SHT_GROUP[sect_no] entries to point to the * created output relocation sections. */ for (APLIST_TRAVERSE(osp->os_relisdescs, idx, risp)) { risp->is_osdesc = rosp; /* * If the input relocation section had the SHF_GROUP * flag set - propagate it to the output relocation * section. */ if (risp->is_shdr->sh_flags & SHF_GROUP) { rosp->os_shdr->sh_flags |= SHF_GROUP; break; } } osp->os_relosdesc = rosp; } else ofl->ofl_osrel = rosp; /* * If this is the first relocation section we've encountered save it * so that the .dynamic entry can be initialized accordingly. */ if (ofl->ofl_osrelhead == (Os_desc *)0) ofl->ofl_osrelhead = rosp; return (1); } /* * Generate version needed section. */ static uintptr_t make_verneed(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; /* * verneed sections do not have a constant element size, so the * value of ent_cnt specified here (0) is meaningless. */ if (new_section(ofl, SHT_SUNW_verneed, MSG_ORIG(MSG_SCN_SUNWVERSION), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* During version processing we calculated the total size. */ data->d_size = ofl->ofl_verneedsz; shdr->sh_size = (Xword)ofl->ofl_verneedsz; ofl->ofl_osverneed = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_version, NULL); return ((uintptr_t)ofl->ofl_osverneed); } /* * Generate a version definition section. * * o the SHT_SUNW_verdef section defines the versions that exist within this * image. */ static uintptr_t make_verdef(Ofl_desc *ofl) { Shdr *shdr; Elf_Data *data; Is_desc *isec; Ver_desc *vdp; Str_tbl *strtab; /* * Reserve a string table entry for the base version dependency (other * dependencies have symbol representations, which will already be * accounted for during symbol processing). */ vdp = (Ver_desc *)ofl->ofl_verdesc->apl_data[0]; if (OFL_IS_STATIC_OBJ(ofl)) strtab = ofl->ofl_strtab; else strtab = ofl->ofl_dynstrtab; if (st_insert(strtab, vdp->vd_name) == -1) return (S_ERROR); /* * verdef sections do not have a constant element size, so the * value of ent_cnt specified here (0) is meaningless. */ if (new_section(ofl, SHT_SUNW_verdef, MSG_ORIG(MSG_SCN_SUNWVERSION), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); /* During version processing we calculated the total size. */ data->d_size = ofl->ofl_verdefsz; shdr->sh_size = (Xword)ofl->ofl_verdefsz; ofl->ofl_osverdef = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_version, NULL); return ((uintptr_t)ofl->ofl_osverdef); } /* * This routine is called when -z nopartial is in effect. */ uintptr_t ld_make_parexpn_data(Ofl_desc *ofl, size_t size, Xword align) { Shdr *shdr; Elf_Data *data; Is_desc *isec; Os_desc *osp; if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_DATA), 0, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); shdr->sh_flags |= SHF_WRITE; data->d_size = size; shdr->sh_size = (Xword)size; if (align != 0) { data->d_align = align; shdr->sh_addralign = align; } if ((data->d_buf = libld_calloc(size, 1)) == NULL) return (S_ERROR); /* * Retain handle to this .data input section. Variables using move * sections (partial initialization) will be redirected here when * such global references are added and '-z nopartial' is in effect. */ ofl->ofl_isparexpn = isec; osp = ld_place_section(ofl, isec, NULL, ld_targ.t_id.id_data, NULL); if (osp == (Os_desc *)S_ERROR) return (S_ERROR); if (!(osp->os_flags & FLG_OS_OUTREL)) { ofl->ofl_dynshdrcnt++; osp->os_flags |= FLG_OS_OUTREL; } return (1); } /* * Make .sunwmove section */ uintptr_t ld_make_sunwmove(Ofl_desc *ofl, int mv_nums) { Shdr *shdr; Elf_Data *data; Is_desc *isec; Aliste idx; Sym_desc *sdp; int cnt = 1; if (new_section(ofl, SHT_SUNW_move, MSG_ORIG(MSG_SCN_SUNWMOVE), mv_nums, &isec, &shdr, &data) == S_ERROR) return (S_ERROR); if ((data->d_buf = libld_calloc(data->d_size, 1)) == NULL) return (S_ERROR); /* * Copy move entries */ for (APLIST_TRAVERSE(ofl->ofl_parsyms, idx, sdp)) { Aliste idx2; Mv_desc *mdp; if (sdp->sd_flags & FLG_SY_PAREXPN) continue; for (ALIST_TRAVERSE(sdp->sd_move, idx2, mdp)) mdp->md_oidx = cnt++; } if ((ofl->ofl_osmove = ld_place_section(ofl, isec, NULL, 0, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); return (1); } /* * Given a relocation descriptor that references a string table * input section, locate the string referenced and return a pointer * to it. */ static const char * strmerge_get_reloc_str(Ofl_desc *ofl, Rel_desc *rsp) { Sym_desc *sdp = rsp->rel_sym; Xword str_off; /* * In the case of an STT_SECTION symbol, the addend of the * relocation gives the offset into the string section. For * other symbol types, the symbol value is the offset. */ if (ELF_ST_TYPE(sdp->sd_sym->st_info) != STT_SECTION) { str_off = sdp->sd_sym->st_value; } else if ((rsp->rel_flags & FLG_REL_RELA) == FLG_REL_RELA) { /* * For SHT_RELA, the addend value is found in the * rel_raddend field of the relocation. */ str_off = rsp->rel_raddend; } else { /* REL and STT_SECTION */ /* * For SHT_REL, the "addend" is not part of the relocation * record. Instead, it is found at the relocation target * address. */ uchar_t *addr = (uchar_t *)((uintptr_t)rsp->rel_roffset + (uintptr_t)rsp->rel_isdesc->is_indata->d_buf); if (ld_reloc_targval_get(ofl, rsp, addr, &str_off) == 0) return (0); } return (str_off + (char *)sdp->sd_isc->is_indata->d_buf); } /* * First pass over the relocation records for string table merging. * Build lists of relocations and symbols that will need modification, * and insert the strings they reference into the output string table. * * entry: * ofl - Output file descriptor * * exit: On success, the string merging specific members of each output * section descriptor in ofl are updated based on information from the * relocation entries, and 0 is returned. * * On error, S_ERROR is returned. */ static uintptr_t ld_gather_strmerge(Ofl_desc *ofl, Rel_cache *cache) { Rel_cachebuf *rbcp; Rel_desc *rsp; Sym_desc *last_sdp = NULL; Aliste idx1; /* * Pass 1: * * Build lists of relocations and symbols that will need * modification, and insert the strings they reference into * the output string table. */ REL_CACHE_TRAVERSE(cache, idx1, rbcp, rsp) { Sym_desc *sdp = rsp->rel_sym; Os_desc *osp; const char *name; /* * If there's no input section, or the input section is * discarded or does not contain mergable strings, we have * nothing to do. */ if ((sdp->sd_isc == NULL) || ((sdp->sd_isc->is_flags & (FLG_IS_DISCARD | FLG_IS_INSTRMRG)) != FLG_IS_INSTRMRG)) continue; osp = sdp->sd_isc->is_osdesc; /* * Remember symbol for use in the third pass. There is no * reason to save a given symbol more than once, so we take * advantage of the fact that relocations to a given symbol * tend to cluster in the list. If this is the same symbol * we saved last time, don't bother. */ if (last_sdp != sdp) { if (aplist_append(&osp->os_mstrsyms, sdp, AL_CNT_STRMRGSYM) == NULL) return (S_ERROR); last_sdp = sdp; } if ((osp->os_mstrtab == NULL) && (osp->os_mstrtab = st_new(FLG_STNEW_COMPRESS)) == NULL) return (S_ERROR); /* Enter the string into our new string table */ name = strmerge_get_reloc_str(ofl, rsp); if (st_insert(osp->os_mstrtab, name) == -1) return (S_ERROR); /* * If this is an STT_SECTION symbol, then the second pass * will need to modify this relocation, so hang on to it. */ if ((ELF_ST_TYPE(sdp->sd_sym->st_info) == STT_SECTION) && (aplist_append(&osp->os_mstrrels, rsp, AL_CNT_STRMRGREL) == NULL)) { return (S_ERROR); } } return (0); } /* * If any an output section has more than one SHF_MERGE|SHF_STRINGS input * section, replace them with a single merged/compressed input section. * * This is done by making a Str_tbl (as we use for managing SHT_STRTAB * sections) per output section with compression enabled to manage all strings * in the mergeable input sections. We then discard all inputs which * contributed to this table and replace them with an input section we create * taking data from this Str_tbl. References to the now discarded sections * are then updated to refer to our new merged input section, and the string * table and other metadata are freed. * * This process is done in 3 passes. For efficiency reasons half of pass 1 is * done by ld_strmerge_gather() so relocations only need to be processed once. * Steps 1.5 onward are performed here. The steps are: * * 1) In ld_strmerge_gather() examine all relocations, insert strings * from relocations to the mergeable input sections into the string * table. * 1.5) Gather every string from the mergeable input sections, regardless * of whether it is referenced from a relocation. All strings * must be processed, and relocations may point into the middle * of an actual NUL-terminated string, so we must enter both the * precise strings referenced by relocations and full strings * within the section. * 2) Modify the relocation values to be correct for the * new merged section. * 3) Modify the symbols used by the relocations to reference * the new section. * * These passes cannot be combined: * - The string table code works in two passes, and all * strings have to be loaded in pass one before the * offset of any strings can be determined. * - Multiple relocations reference a single symbol, so the * symbol cannot be modified until all relocations are * fixed. * * entry: * ofl - Output file descriptor * osp - Outputs section descriptor * * exit: * If section merging is possible for this output section, it is done. * If no errors are encountered, 0 is returned. On error, S_ERROR is * returned. * * The contents of the string-merging specific members of this output * section descriptor are undefined after this function returns. */ static uintptr_t ld_strmerge_sec(Ofl_desc *ofl, Os_desc *osp) { Is_desc *isp = NULL; Sym_desc *sdp; Rel_desc *rsp; Is_desc *mstrsec = NULL; /* Generated string merge section */ Shdr *mstr_shdr = NULL; Elf_Data *mstr_data = NULL; size_t data_size; Aliste idx; uintptr_t ret = 0; Boolean placed = FALSE; /* * Pass 1.5: Add all strings from all mergeable input sections. * * The last section we find also serves as the template for our * replacement merged section, providing the section attributes, etc. */ for (APLIST_TRAVERSE(osp->os_mstrisdescs, idx, isp)) { const char *str, *end; if (isdesc_discarded(isp)) continue; /* * Input sections of 0 size are dubiously valid since they do * not even contain the NUL string. Ignore them. */ if (isp->is_shdr->sh_size == 0) continue; if ((osp->os_mstrtab == NULL) && (osp->os_mstrtab = st_new(FLG_STNEW_COMPRESS)) == NULL) { ret = S_ERROR; goto out; } end = isp->is_indata->d_buf + isp->is_indata->d_size; for (str = isp->is_indata->d_buf; str < end; str += strlen(str) + 1) { if (st_insert(osp->os_mstrtab, str) != 0) { ret = S_ERROR; goto out; } } } IMPLY(osp->os_mstrtab != NULL, isp != NULL); if (osp->os_mstrtab == NULL) { ret = 0; goto out; } /* * Get the size of the new input section. Requesting the string * table size "cooks" the table, and finalizes its contents. */ data_size = st_getstrtab_sz(osp->os_mstrtab); /* Create a new input section to hold the merged strings */ if (new_section_from_template(ofl, isp, data_size, &mstrsec, &mstr_shdr, &mstr_data) == S_ERROR) { ret = S_ERROR; goto out; } mstrsec->is_flags |= FLG_IS_GNSTRMRG; /* * Allocate a data buffer for the new input section, associate the * buffer with the string table descriptor, and fill it from the * string table. */ if ((mstr_data->d_buf = libld_malloc(data_size)) == NULL) { ret = S_ERROR; goto out; } if ((st_setstrbuf(osp->os_mstrtab, mstr_data->d_buf, data_size) == -1)) { ret = S_ERROR; goto out; } st_setallstrings(osp->os_mstrtab); /* Add the new section to the output image */ if (ld_place_section(ofl, mstrsec, NULL, osp->os_identndx, NULL) == (Os_desc *)S_ERROR) { ret = S_ERROR; goto out; } placed = TRUE; /* * Pass 2: * * Revisit the relocation descriptors with STT_SECTION symbols * that were saved by the first pass. Update each relocation * record so that the offset it contains is for the new section * instead of the original. */ for (APLIST_TRAVERSE(osp->os_mstrrels, idx, rsp)) { const char *name; size_t stoff; /* * Find the string to the merged table's buffer and get its * offset. */ name = strmerge_get_reloc_str(ofl, rsp); stoff = st_findstring(osp->os_mstrtab, name); VERIFY3S(stoff, !=, -1); /* * Alter the relocation to access the string at the * new offset in our new string table. * * For SHT_RELA platforms, it suffices to simply * update the rel_raddend field of the relocation. * * For SHT_REL platforms, the new "addend" value * needs to be written at the address being relocated. * However, we can't alter the input sections which * are mapped readonly, and the output image has not * been created yet. So, we defer this operation, * using the rel_raddend field of the relocation * which is normally 0 on a REL platform, to pass the * new "addend" value to ld_perform_outreloc() or * ld_do_activerelocs(). The FLG_REL_NADDEND flag * tells them that this is the case. */ if ((rsp->rel_flags & FLG_REL_RELA) == 0) { /* REL */ rsp->rel_flags |= FLG_REL_NADDEND; } rsp->rel_raddend = (Sxword)stoff; /* * Generate a symbol name string for STT_SECTION symbols * that might reference our merged section. This shows up * in debug output and helps show how the relocation has * changed from its original input section to our merged * one. */ if (ld_stt_section_sym_name(mstrsec) == NULL) { ret = S_ERROR; goto out; } } /* * Pass 3: * * Modify the symbols referenced by the relocation descriptors * so that they reference the new input section containing the * merged strings instead of the original input sections. */ for (APLIST_TRAVERSE(osp->os_mstrsyms, idx, sdp)) { /* * If we've already redirected this symbol to the merged data, * don't do it again. ld_gather_strmerge() uses a heuristic * (relocations to the same symbol clump together) to avoid * inserting a given symbol more than once, but repeat symbols * in the list can occur. */ if ((sdp->sd_isc->is_flags & FLG_IS_INSTRMRG) == 0) continue; if (ELF_ST_TYPE(sdp->sd_sym->st_info) != STT_SECTION) { /* * This is not an STT_SECTION symbol, so its * value is the offset of the string within the * input section. Update the address to reflect * the address in our new merged section. */ const char *name; size_t stoff; /* * Find the string in the merged table's buffer and get * its offset. */ name = sdp->sd_sym->st_value + (char *)sdp->sd_isc->is_indata->d_buf; stoff = st_findstring(osp->os_mstrtab, name); VERIFY3S(stoff, !=, -1); if (ld_sym_copy(sdp) == S_ERROR) { ret = S_ERROR; goto out; } sdp->sd_sym->st_value = (Word)stoff; } /* Redirect the symbol to our new merged section */ sdp->sd_isc = mstrsec; } /* * There are no references left to the original input string sections. * Mark them as discarded so they don't go into the output image. * At the same time, add up the sizes of the replaced sections. */ data_size = 0; for (APLIST_TRAVERSE(osp->os_mstrisdescs, idx, isp)) { if (isp->is_flags & (FLG_IS_DISCARD | FLG_IS_GNSTRMRG)) continue; data_size += isp->is_indata->d_size; isp->is_flags |= FLG_IS_DISCARD; DBG_CALL(Dbg_sec_discarded(ofl->ofl_lml, isp, mstrsec)); } /* Report how much space we saved in the output section */ DBG_CALL(Dbg_sec_genstr_compress(ofl->ofl_lml, osp->os_name, data_size, mstr_data->d_size)); out: if ((ret == S_ERROR) && !placed) { libld_free(mstrsec); if (mstr_data != NULL) libld_free(mstr_data->d_buf); libld_free(mstr_data); libld_free(mstr_shdr); } libld_free(osp->os_mstrsyms); osp->os_mstrsyms = NULL; libld_free(osp->os_mstrrels); osp->os_mstrrels = NULL; if (osp->os_mstrtab != NULL) { st_destroy(osp->os_mstrtab); osp->os_mstrtab = NULL; } return (ret); } /* * If any output section has SHF_MERGE|SHF_STRINGS input sections, * replace them with a single merged/compressed input section. * * entry: * ofl - Output file descriptor * * exit: * If section merging is possible, it is done. If no errors are * encountered, 0 is returned. On error, S_ERROR is returned. * * The contents of the string-merging specific members of any output * section descriptor are undefined after this function returns. */ static uintptr_t ld_make_strmerge(Ofl_desc *ofl) { Sg_desc *sgp; Aliste idx1; if (ld_gather_strmerge(ofl, &ofl->ofl_actrels) == S_ERROR) return (S_ERROR); if (ld_gather_strmerge(ofl, &ofl->ofl_outrels) == S_ERROR) return (S_ERROR); for (APLIST_TRAVERSE(ofl->ofl_segs, idx1, sgp)) { Os_desc *osp; Aliste idx2; for (APLIST_TRAVERSE(sgp->sg_osdescs, idx2, osp)) { if (ld_strmerge_sec(ofl, osp) == S_ERROR) return (S_ERROR); } } return (0); } /* * Update a data buffers size. A number of sections have to be created, and * the sections header contributes to the size of the eventual section. Thus, * a section may be created, and once all associated sections have been created, * we return to establish the required section size. */ inline static void update_data_size(Os_desc *osp, ulong_t cnt) { Is_desc *isec = ld_os_first_isdesc(osp); Elf_Data *data = isec->is_indata; Shdr *shdr = osp->os_shdr; size_t size = cnt * shdr->sh_entsize; shdr->sh_size = (Xword)size; data->d_size = size; } /* * The following sections are built after all input file processing and symbol * validation has been carried out. The order is important (because the * addition of a section adds a new symbol there is a chicken and egg problem * of maintaining the appropriate counts). By maintaining a known order the * individual routines can compensate for later, known, additions. */ uintptr_t ld_make_sections(Ofl_desc *ofl) { ofl_flag_t flags = ofl->ofl_flags; Sg_desc *sgp; /* * Generate any special sections. */ if (flags & FLG_OF_ADDVERS) if (make_comment(ofl) == S_ERROR) return (S_ERROR); if (make_interp(ofl) == S_ERROR) return (S_ERROR); /* * Create a capabilities section if required. */ if (make_cap(ofl, SHT_SUNW_cap, MSG_ORIG(MSG_SCN_SUNWCAP), ld_targ.t_id.id_cap) == S_ERROR) return (S_ERROR); /* * Create any init/fini array sections. */ if (make_array(ofl, SHT_INIT_ARRAY, MSG_ORIG(MSG_SCN_INITARRAY), ofl->ofl_initarray) == S_ERROR) return (S_ERROR); if (make_array(ofl, SHT_FINI_ARRAY, MSG_ORIG(MSG_SCN_FINIARRAY), ofl->ofl_finiarray) == S_ERROR) return (S_ERROR); if (make_array(ofl, SHT_PREINIT_ARRAY, MSG_ORIG(MSG_SCN_PREINITARRAY), ofl->ofl_preiarray) == S_ERROR) return (S_ERROR); /* * Make the .plt section. This occurs after any other relocation * sections are generated (see reloc_init()) to ensure that the * associated relocation section is after all the other relocation * sections. */ if ((ofl->ofl_pltcnt) || (ofl->ofl_pltpad)) if (make_plt(ofl) == S_ERROR) return (S_ERROR); /* * Determine whether any sections or files are not referenced. Under * -Dunused a diagnostic for any unused components is generated, under * -zignore the component is removed from the final output. */ if (DBG_ENABLED || (ofl->ofl_flags1 & FLG_OF1_IGNPRC)) { if (ignore_section_processing(ofl) == S_ERROR) return (S_ERROR); } /* * If we have detected a situation in which previously placed * output sections may have been discarded, perform the necessary * readjustment. */ if (ofl->ofl_flags & FLG_OF_ADJOSCNT) adjust_os_count(ofl); /* * Do any of the output sections contain input sections that * are candidates for string table merging? For each such case, * we create a replacement section, insert it, and discard the * originals. * * rel_alpp and sym_alpp are used by ld_make_strmerge() * for its internal processing. We are responsible for the * initialization and cleanup, and ld_make_strmerge() handles the rest. * This allows us to reuse a single pair of memory buffers, allocated * for this processing, for all the output sections. */ if ((ofl->ofl_flags1 & FLG_OF1_NCSTTAB) == 0) { if (ld_make_strmerge(ofl) == S_ERROR) return (S_ERROR); } /* * Add any necessary versioning information. */ if (!(flags & FLG_OF_NOVERSEC)) { if ((flags & FLG_OF_VERNEED) && (make_verneed(ofl) == S_ERROR)) return (S_ERROR); if ((flags & FLG_OF_VERDEF) && (make_verdef(ofl) == S_ERROR)) return (S_ERROR); if ((flags & (FLG_OF_VERNEED | FLG_OF_VERDEF)) && ((ofl->ofl_osversym = make_sym_sec(ofl, MSG_ORIG(MSG_SCN_SUNWVERSYM), SHT_SUNW_versym, ld_targ.t_id.id_version)) == (Os_desc*)S_ERROR)) return (S_ERROR); } /* * Create a syminfo section if necessary. */ if (flags & FLG_OF_SYMINFO) { if ((ofl->ofl_ossyminfo = make_sym_sec(ofl, MSG_ORIG(MSG_SCN_SUNWSYMINFO), SHT_SUNW_syminfo, ld_targ.t_id.id_syminfo)) == (Os_desc *)S_ERROR) return (S_ERROR); } if (flags & FLG_OF_COMREL) { /* * If -zcombreloc is enabled then all relocations (except for * the PLT's) are coalesced into a single relocation section. */ if (ofl->ofl_reloccnt) { if (make_reloc(ofl, NULL) == S_ERROR) return (S_ERROR); } } else { Aliste idx1; /* * Create the required output relocation sections. Note, new * sections may be added to the section list that is being * traversed. These insertions can move the elements of the * Alist such that a section descriptor is re-read. Recursion * is prevented by maintaining a previous section pointer and * insuring that this pointer isn't re-examined. */ for (APLIST_TRAVERSE(ofl->ofl_segs, idx1, sgp)) { Os_desc *osp, *posp = 0; Aliste idx2; for (APLIST_TRAVERSE(sgp->sg_osdescs, idx2, osp)) { if ((osp != posp) && osp->os_szoutrels && (osp != ofl->ofl_osplt)) { if (make_reloc(ofl, osp) == S_ERROR) return (S_ERROR); } posp = osp; } } /* * If we're not building a combined relocation section, then * build a .rel[a] section as required. */ if (ofl->ofl_relocrelsz) { if (make_reloc(ofl, NULL) == S_ERROR) return (S_ERROR); } } /* * The PLT relocations are always in their own section, and we try to * keep them at the end of the PLT table. We do this to keep the hot * "data" PLT's at the head of the table nearer the .dynsym & .hash. */ if (ofl->ofl_osplt && ofl->ofl_relocpltsz) { if (make_reloc(ofl, ofl->ofl_osplt) == S_ERROR) return (S_ERROR); } /* * Finally build the symbol and section header sections. */ if (flags & FLG_OF_DYNAMIC) { if (make_dynamic(ofl) == S_ERROR) return (S_ERROR); /* * A number of sections aren't necessary within a relocatable * object, even if -dy has been used. */ if (!(flags & FLG_OF_RELOBJ)) { if (make_hash(ofl) == S_ERROR) return (S_ERROR); if (make_dynstr(ofl) == S_ERROR) return (S_ERROR); if (make_dynsym(ofl) == S_ERROR) return (S_ERROR); if (ld_unwind_make_hdr(ofl) == S_ERROR) return (S_ERROR); if (make_dynsort(ofl) == S_ERROR) return (S_ERROR); } } if (!(flags & FLG_OF_STRIP) || (flags & FLG_OF_RELOBJ) || ((flags & FLG_OF_STATIC) && ofl->ofl_osversym)) { /* * Do we need to make a SHT_SYMTAB_SHNDX section * for the dynsym. If so - do it now. */ if (ofl->ofl_osdynsym && ((ofl->ofl_shdrcnt + 3) >= SHN_LORESERVE)) { if (make_dynsym_shndx(ofl) == S_ERROR) return (S_ERROR); } if (make_strtab(ofl) == S_ERROR) return (S_ERROR); if (make_symtab(ofl) == S_ERROR) return (S_ERROR); } else { /* * Do we need to make a SHT_SYMTAB_SHNDX section * for the dynsym. If so - do it now. */ if (ofl->ofl_osdynsym && ((ofl->ofl_shdrcnt + 1) >= SHN_LORESERVE)) { if (make_dynsym_shndx(ofl) == S_ERROR) return (S_ERROR); } } if (make_shstrtab(ofl) == S_ERROR) return (S_ERROR); /* * Now that we've created all output sections, adjust the size of the * SHT_SUNW_versym and SHT_SUNW_syminfo section, which are dependent on * the associated symbol table sizes. */ if (ofl->ofl_osversym || ofl->ofl_ossyminfo) { ulong_t cnt; Is_desc *isp; Os_desc *osp; if (OFL_IS_STATIC_OBJ(ofl)) osp = ofl->ofl_ossymtab; else osp = ofl->ofl_osdynsym; isp = ld_os_first_isdesc(osp); cnt = (isp->is_shdr->sh_size / isp->is_shdr->sh_entsize); if (ofl->ofl_osversym) update_data_size(ofl->ofl_osversym, cnt); if (ofl->ofl_ossyminfo) update_data_size(ofl->ofl_ossyminfo, cnt); } /* * Now that we've created all output sections, adjust the size of the * SHT_SUNW_capinfo, which is dependent on the associated symbol table * size. */ if (ofl->ofl_oscapinfo) { ulong_t cnt; /* * Symbol capabilities symbols are placed directly after the * STT_FILE symbol, section symbols, and any register symbols. * Effectively these are the first of any series of demoted * (scoped) symbols. */ if (OFL_IS_STATIC_OBJ(ofl)) cnt = SYMTAB_ALL_CNT(ofl); else cnt = DYNSYM_ALL_CNT(ofl); update_data_size(ofl->ofl_oscapinfo, cnt); } return (1); } /* * Build an additional data section - used to back OBJT symbol definitions * added with a mapfile. */ Is_desc * ld_make_data(Ofl_desc *ofl, size_t size) { Shdr *shdr; Elf_Data *data; Is_desc *isec; if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_DATA), 0, &isec, &shdr, &data) == S_ERROR) return ((Is_desc *)S_ERROR); data->d_size = size; shdr->sh_size = (Xword)size; shdr->sh_flags |= SHF_WRITE; if (aplist_append(&ofl->ofl_mapdata, isec, AL_CNT_OFL_MAPSECS) == NULL) return ((Is_desc *)S_ERROR); return (isec); } /* * Build an additional text section - used to back FUNC symbol definitions * added with a mapfile. */ Is_desc * ld_make_text(Ofl_desc *ofl, size_t size) { Shdr *shdr; Elf_Data *data; Is_desc *isec; /* * Insure the size is sufficient to contain the minimum return * instruction. */ if (size < ld_targ.t_nf.nf_size) size = ld_targ.t_nf.nf_size; if (new_section(ofl, SHT_PROGBITS, MSG_ORIG(MSG_SCN_TEXT), 0, &isec, &shdr, &data) == S_ERROR) return ((Is_desc *)S_ERROR); data->d_size = size; shdr->sh_size = (Xword)size; shdr->sh_flags |= SHF_EXECINSTR; /* * Fill the buffer with the appropriate return instruction. * Note that there is no need to swap bytes on a non-native, * link, as the data being copied is given in bytes. */ if ((data->d_buf = libld_calloc(size, 1)) == NULL) return ((Is_desc *)S_ERROR); (void) memcpy(data->d_buf, ld_targ.t_nf.nf_template, ld_targ.t_nf.nf_size); /* * If size was larger than required, and the target supplies * a fill function, use it to fill the balance. If there is no * fill function, we accept the 0-fill supplied by libld_calloc(). */ if ((ld_targ.t_ff.ff_execfill != NULL) && (size > ld_targ.t_nf.nf_size)) ld_targ.t_ff.ff_execfill(data->d_buf, ld_targ.t_nf.nf_size, size - ld_targ.t_nf.nf_size); if (aplist_append(&ofl->ofl_maptext, isec, AL_CNT_OFL_MAPSECS) == NULL) return ((Is_desc *)S_ERROR); return (isec); } void ld_comdat_validate(Ofl_desc *ofl, Ifl_desc *ifl) { int i; for (i = 0; i < ifl->ifl_shnum; i++) { Is_desc *isp = ifl->ifl_isdesc[i]; int types = 0; char buf[1024] = ""; Group_desc *gr = NULL; if ((isp == NULL) || (isp->is_flags & FLG_IS_COMDAT) == 0) continue; if (isp->is_shdr->sh_type == SHT_SUNW_COMDAT) { types++; (void) strlcpy(buf, MSG_ORIG(MSG_STR_SUNW_COMDAT), sizeof (buf)); } if (strncmp(MSG_ORIG(MSG_SCN_GNU_LINKONCE), isp->is_name, MSG_SCN_GNU_LINKONCE_SIZE) == 0) { types++; if (types > 1) (void) strlcat(buf, ", ", sizeof (buf)); (void) strlcat(buf, MSG_ORIG(MSG_SCN_GNU_LINKONCE), sizeof (buf)); } if ((isp->is_shdr->sh_flags & SHF_GROUP) && ((gr = ld_get_group(ofl, isp)) != NULL) && (gr->gd_data[0] & GRP_COMDAT)) { types++; if (types > 1) (void) strlcat(buf, ", ", sizeof (buf)); (void) strlcat(buf, MSG_ORIG(MSG_STR_GROUP), sizeof (buf)); } if (types > 1) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_SCN_MULTICOMDAT), ifl->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, buf); } }