/* * Create an archive descriptor. By maintaining a list of archives any * duplicate occurrences of the same archive specified by the user enable us to * pick off where the last processing finished. */ Ar_desc * ld_ar_setup(const char *name, Elf *elf, Ofl_desc *ofl) { Ar_desc * adp; size_t number; Elf_Arsym * start; /* * Unless, -z allextract is specified, get the archive symbol table * if one exists, and ignore the file with a warning message otherwise. */ if (ofl->ofl_flags1 & FLG_OF1_ALLEXRT) { start = NULL; } else if ((start = elf_getarsym(elf, &number)) == NULL) { if (elf_errno()) ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETARSYM), name); else ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ELF_ARSYM), name); return (0); } /* * As this is a new archive reference establish a new descriptor. */ if ((adp = libld_malloc(sizeof (Ar_desc))) == NULL) return ((Ar_desc *)S_ERROR); adp->ad_name = name; adp->ad_elf = elf; adp->ad_start = start; if (start) { adp->ad_aux = libld_calloc(sizeof (Ar_aux), number); if (adp->ad_aux == NULL) return ((Ar_desc *)S_ERROR); } else { adp->ad_aux = NULL; } /* * Retain any command line options that are applicable to archive * extraction in case we have to rescan this archive later. */ adp->ad_flags = ofl->ofl_flags1 & MSK_OF1_ARCHIVE; ofl->ofl_arscnt++; /* * Add this new descriptor to the list of archives. */ if (aplist_append(&ofl->ofl_ars, adp, AL_CNT_OFL_LIBS) == NULL) return ((Ar_desc *)S_ERROR); else return (adp); }
Group_desc * ld_get_group(Ofl_desc *ofl, Is_desc *isp) { Ifl_desc *ifl = isp->is_file; uint_t scnndx = isp->is_scnndx; Group_desc *gdp; Aliste idx; /* * Scan the GROUP sections associated with this file to find the * matching group section. */ for (ALIST_TRAVERSE(ifl->ifl_groups, idx, gdp)) { size_t ndx; Word *data; if (isp->is_shdr->sh_type == SHT_GROUP) { if (isp->is_scnndx == gdp->gd_isc->is_scnndx) return (gdp); continue; } data = gdp->gd_data; for (ndx = 1; ndx < gdp->gd_cnt; ndx++) { if (data[ndx] == scnndx) return (gdp); } } ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ELF_NOGROUPSECT), ifl->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name); return (NULL); }
/* * Exit after cleaning up. */ int ld_exit(Ofl_desc *ofl) { /* * If we have created an output file remove it. */ if ((ofl->ofl_fd > 0) && ((ofl->ofl_flags1 & FLG_OF1_NONREG) == 0)) (void) unlink(ofl->ofl_name); /* * Inform any support library that the link-edit has failed. */ ld_sup_atexit(ofl, 1); /* * Wrap up debug output file if one is open */ dbg_cleanup(); /* If any ERR_GUIDANCE messages were issued, add a summary */ if (ofl->ofl_guideflags & FLG_OFG_ISSUED) ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_SUMMARY)); return (1); }
/* * Return the archive member's name. * * entry: * name - Name of archive * arelf - ELF descriptor for archive member. * ofl - output descriptor * * exit: * Returns pointer to archive member name on success, NULL on error. */ static const char * ar_member_name(const char *name, Elf *arelf, Ofl_desc *ofl) { Elf_Arhdr *arhdr; if ((arhdr = elf_getarhdr(arelf)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETARHDR), name); return (NULL); } return (arhdr->ar_name); }
/* * Process the given archive and extract objects for inclusion into * the link. * * entry: * name - Name of archive * fd - Open file descriptor for archive * adp - Archive descriptor * ofl - output descriptor * * exit: * Returns FALSE on fatal error, TRUE otherwise. */ Boolean ld_process_archive(const char *name, int fd, Ar_desc *adp, Ofl_desc *ofl) { Boolean found = FALSE; Rej_desc rej = { 0 }; /* * If a fatal error condition has been set there's really no point in * processing the archive further. Having got to this point we have at * least established that the archive exists (thus verifying that the * command line options that got us to this archive are correct). Very * large archives can take a significant time to process, therefore * continuing on from here may significantly delay the fatal error * message the user is already set to receive. */ if (ofl->ofl_flags & FLG_OF_FATAL) return (TRUE); /* * If this archive was processed with -z allextract, then all members * have already been extracted. */ if (adp->ad_elf == NULL) return (TRUE); if (ofl->ofl_flags1 & FLG_OF1_ALLEXRT) { if (!ar_extract_all(name, fd, adp, ofl, &found, &rej)) return (FALSE); } else { if (!ar_extract_bysym(name, fd, adp, ofl, &found, &rej)) return (FALSE); } /* * If no objects have been found in the archive test for any rejections * and if one had occurred issue a warning - its possible a user has * pointed at an archive containing the wrong class of elf members. */ if ((found == 0) && rej.rej_type) { Conv_reject_desc_buf_t rej_buf; ld_eprintf(ofl, ERR_WARNING, MSG_INTL(reject[rej.rej_type]), rej.rej_name ? rej.rej_name : MSG_INTL(MSG_STR_UNKNOWN), conv_reject_desc(&rej, &rej_buf, ld_targ.t_m.m_mach)); } return (TRUE); }
/* * Define our signal handler. */ static void /* ARGSUSED2 */ handler(int sig, siginfo_t *sip, void *utp) { struct sigaction nact; Signals * sigs; /* * Reset all ignore handlers regardless of how we got here. */ nact.sa_handler = SIG_IGN; nact.sa_flags = 0; (void) sigemptyset(&nact.sa_mask); for (sigs = signals; sigs->signo; sigs++) { if (sigs->defhdl == SIG_IGN) (void) sigaction(sigs->signo, &nact, NULL); } /* * The model for creating an output file is to ftruncate() it to the * required size and mmap() a mapping into which the new contents are * written. Neither of these operations guarantee that the required * disk blocks exist, and should we run out of disk space a bus error * is generated. * Other situations have been reported to result in ld catching a bus * error (one instance was a stale NFS handle from an unstable server). * Thus we catch all bus errors and hope we can decode a better error. */ if ((sig == SIGBUS) && sip && Ofl->ofl_name) { ld_eprintf(Ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INTERRUPT), Ofl->ofl_name, strerror(sip->si_errno)); } /* * This assert(0) causes DEBUG enabled linkers to produce a core file. */ if ((sig != SIGHUP) && (sig != SIGINT)) assert(0); exit(ld_exit(Ofl)); }
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); }
/* * Archive members are typically extracted to resolve an existing undefined * reference. However, other symbol definitions can cause archive members to * be processed to determine if the archive member provides a more appropriate * definition. This routine processes the archive member to determine if the * member is really required. * * i. Tentative symbols may cause the extraction of an archive member. * If the archive member has a strong defined symbol it will be used. * If the archive member simply contains another tentative definition, * or a defined function symbol, then it will not be used. * * ii. A symbol reference may define a hidden or protected visibility. The * reference can only be bound to a definition within a relocatable object * for this restricted visibility to be satisfied. If the archive member * provides a definition of the same symbol type, this definition is * taken. The visibility of the defined symbol is irrelevant, as the most * restrictive visibility of the reference and the definition will be * applied to the final symbol. * * exit: * Returns 1 if there is a match, 0 if no match is seen, and S_ERROR if an * error occurred. */ static uintptr_t process_member(Ar_mem *amp, const char *name, Sym_desc *sdp, Ofl_desc *ofl) { Sym *syms, *osym = sdp->sd_sym; Xword symn, cnt; char *strs; /* * Find the first symbol table in the archive member, obtain its * data buffer and determine the number of global symbols (Note, * there must be a symbol table present otherwise the archive would * never have been able to generate its own symbol entry for this * member). */ if (amp->am_syms == NULL) { Elf_Scn *scn = NULL; Shdr *shdr; Elf_Data *data; while (scn = elf_nextscn(amp->am_elf, scn)) { if ((shdr = elf_getshdr(scn)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETSHDR), amp->am_path); return (S_ERROR); } if ((shdr->sh_type == SHT_SYMTAB) || (shdr->sh_type == SHT_DYNSYM)) break; } if ((data = elf_getdata(scn, NULL)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETDATA), amp->am_path); return (S_ERROR); } syms = (Sym *)data->d_buf; syms += shdr->sh_info; symn = shdr->sh_size / shdr->sh_entsize; symn -= shdr->sh_info; /* * Get the data for the associated string table. */ if ((scn = elf_getscn(amp->am_elf, (size_t)shdr->sh_link)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETSCN), amp->am_path); return (S_ERROR); } if ((data = elf_getdata(scn, NULL)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETDATA), amp->am_path); return (S_ERROR); } strs = data->d_buf; /* * Initialize the archive member structure in case we have to * come through here again. */ amp->am_syms = syms; amp->am_strs = strs; amp->am_symn = symn; } else { syms = amp->am_syms; strs = amp->am_strs; symn = amp->am_symn; } /* * Loop through the symbol table entries looking for a match for the * original symbol. */ for (cnt = 0; cnt < symn; syms++, cnt++) { Word shndx; if ((shndx = syms->st_shndx) == SHN_UNDEF) continue; if (osym->st_shndx == SHN_COMMON) { /* * Determine whether a tentative symbol definition * should be overridden. */ if ((shndx == SHN_ABS) || (shndx == SHN_COMMON) || (ELF_ST_TYPE(syms->st_info) == STT_FUNC)) continue; /* * A historic detail requires that a weak definition * within an archive will not override a strong * definition (see sym_realtent() resolution and ABI * symbol binding description - page 4-27). */ if ((ELF_ST_BIND(syms->st_info) == STB_WEAK) && (ELF_ST_BIND(osym->st_info) != STB_WEAK)) continue; } else { /* * Determine whether a restricted visibility reference * should be overridden. Don't worry about the * visibility of the archive member definition, nor * whether it is weak or global. Any definition is * better than a binding to an external shared object * (which is the only event that must presently exist * for us to be here looking for a better alternative). */ if (ELF_ST_TYPE(syms->st_info) != ELF_ST_TYPE(osym->st_info)) continue; } if (strcmp(strs + syms->st_name, name) == 0) return (1); } return (0); }
/* * Read the archive symbol table. For each symbol in the table, determine * whether that symbol satisfies an unresolved reference, tentative reference, * or a reference that expects hidden or protected visibility. If so, the * corresponding object from the archive is processed. The archive symbol * table is searched until we go through a complete pass without satisfying any * unresolved symbols * * entry: * name - Name of archive * fd - Open file descriptor for archive * adp - Archive descriptor * ofl - output descriptor * found - Address of variable to set to TRUE if any objects are extracted * rej - Rejection descriptor to pass to ld_process_ifl(). * * exit: * Returns FALSE on fatal error. On success, *found will be TRUE * if any object was extracted, rej will be set if any object * was rejected, and TRUE is returned. */ static Boolean ar_extract_bysym(const char *name, int fd, Ar_desc *adp, Ofl_desc *ofl, Boolean *found, Rej_desc *rej) { Elf_Arsym * arsym; Elf * arelf; Ar_aux * aup; Sym_desc * sdp; const char *arname, *arpath; Boolean again = FALSE; uintptr_t err; /* * An archive without a symbol table should not reach this function, * because it can only get past ld_ar_setup() in the case where * the archive is first seen under the influence of '-z allextract'. * That will cause the entire archive to be extracted, and any * subsequent reference to the archive will be ignored by * ld_process_archive(). */ if (adp->ad_start == NULL) { assert(adp->ad_start != NULL); return (TRUE); } /* * Loop through archive symbol table until we make a complete pass * without satisfying an unresolved reference. For each archive * symbol, see if there is a symbol with the same name in ld's * symbol table. If so, and if that symbol is still unresolved or * tentative, process the corresponding archive member. */ do { DBG_CALL(Dbg_file_ar(ofl->ofl_lml, name, again)); DBG_CALL(Dbg_syms_ar_title(ofl->ofl_lml, name, again)); again = FALSE; for (arsym = adp->ad_start, aup = adp->ad_aux; arsym->as_name; ++arsym, ++aup) { Ar_mem *amp; Sym *sym; Boolean visible = TRUE; Boolean vers; Ifl_desc *ifl; /* * If the auxiliary members value indicates that this * member has been processed then this symbol will have * been added to the output file image already or the * object was rejected in which case we don't want to * process it again. */ if (aup->au_mem == FLG_ARMEM_PROC) continue; /* * If the auxiliary symbol element is non-zero lookup * the symbol from the internal symbol table. */ if ((sdp = aup->au_syms) == NULL) { if ((sdp = ld_sym_find(arsym->as_name, /* LINTED */ (Word)arsym->as_hash, NULL, ofl)) == NULL) { DBG_CALL(Dbg_syms_ar_skip(ofl->ofl_lml, name, arsym)); continue; } aup->au_syms = sdp; } /* * With '-z allextract', all members will be extracted. * * This archive member is a candidate for extraction if * the internal symbol originates from an explicit file, * and represents an undefined or tentative symbol. * * By default, weak references do not cause archive * extraction, however the -zweakextract flag overrides * this default. * * If this symbol has already been bound to a versioned * shared object, but the shared objects version is not * available, then a definition of this symbol from * within the archive is a better candidate. Similarly, * if this symbol has been bound to a shared object, but * the original reference expected hidden or protected * visibility, then a definition of this symbol from * within the archive is a better candidate. */ vers = TRUE; ifl = sdp->sd_file; sym = sdp->sd_sym; if (sdp->sd_ref == REF_DYN_NEED) { uchar_t vis; if (ifl->ifl_vercnt) { Word vndx; Ver_index *vip; vndx = sdp->sd_aux->sa_dverndx; vip = &ifl->ifl_verndx[vndx]; if (!(vip->vi_flags & FLG_VER_AVAIL)) vers = FALSE; } vis = ELF_ST_VISIBILITY(sym->st_other); visible = sym_vis[vis]; } if (((ifl->ifl_flags & FLG_IF_NEEDED) == 0) || (visible && vers && (sym->st_shndx != SHN_UNDEF) && (sym->st_shndx != SHN_COMMON)) || ((ELF_ST_BIND(sym->st_info) == STB_WEAK) && (!(ofl->ofl_flags1 & FLG_OF1_WEAKEXT)))) { DBG_CALL(Dbg_syms_ar_skip(ofl->ofl_lml, name, arsym)); continue; } /* * Determine if we have already extracted this member, * and if so reuse the Ar_mem information. */ if ((amp = aup->au_mem) != 0) { arelf = amp->am_elf; arname = amp->am_name; arpath = amp->am_path; } else { /* * Set up a new elf descriptor for this member. */ if (elf_rand(adp->ad_elf, arsym->as_off) != arsym->as_off) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_ARMEM), name, EC_WORD(arsym->as_off), demangle(arsym->as_name)); return (FALSE); } if ((arelf = elf_begin(fd, ELF_C_READ, adp->ad_elf)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_BEGIN), name); return (FALSE); } /* Get member filename */ if ((arname = ar_member_name(name, arelf, ofl)) == NULL) return (FALSE); /* Construct the member's full pathname */ if ((arpath = ar_member_path(name, arname)) == NULL) return (S_ERROR); /* * Determine whether the support libraries wish * to process this open. See comments in * ld_process_open(). */ ld_sup_open(ofl, &arpath, &arname, &fd, (FLG_IF_EXTRACT | FLG_IF_NEEDED), &arelf, adp->ad_elf, arsym->as_off, elf_kind(arelf)); if (arelf == NULL) { /* Ignore this archive member */ aup->au_mem = FLG_ARMEM_PROC; continue; } } /* * The symbol for which this archive member is being * processed may provide a better alternative to the * symbol that is presently known. Two cases are * covered: * * i. The present symbol represents tentative data. * The archive member may provide a data * definition symbol. * ii. The present symbol represents a reference that * has seen a definition within a shared object * dependency, but the reference expects to be * reduced to hidden or protected visibility. */ if ((sym->st_shndx == SHN_COMMON) || (visible == FALSE)) { /* * If we don't already have a member structure * allocate one. */ if (!amp) { if ((amp = libld_calloc(sizeof (Ar_mem), 1)) == NULL) return (FALSE); amp->am_elf = arelf; amp->am_name = arname; amp->am_path = arpath; } DBG_CALL(Dbg_syms_ar_checking(ofl->ofl_lml, name, arname, arsym)); if ((err = process_member(amp, arsym->as_name, sdp, ofl)) == S_ERROR) return (FALSE); /* * If it turns out that we don't need this * member simply initialize all other auxiliary * entries that match this offset with this * members address. In this way we can resuse * this information if we recurse back to this * symbol. */ if (err == 0) { if (aup->au_mem == NULL) ld_ar_member(adp, arsym, aup, amp); continue; } } /* * Process the archive member. Retain any error for * return to the caller. */ DBG_CALL(Dbg_syms_ar_resolve(ofl->ofl_lml, name, arname, arsym)); switch (ar_input(fd, adp, ofl, arelf, arpath, rej)) { case S_ERROR: return (FALSE); case 0: /* * Mark the member as extracted so that we * don't try and process it again on a rescan. */ ld_ar_member(adp, arsym, aup, FLG_ARMEM_PROC); continue; } /* * Note that this archive has contributed something * during this specific operation, and also signal * the need to rescan the archive. */ *found = again = TRUE; ld_ar_member(adp, arsym, aup, FLG_ARMEM_PROC); } } while (again); return (TRUE); }
/* * Assign move descriptors with the associated target symbol. */ static uintptr_t append_move_desc(Ofl_desc *ofl, Sym_desc *sdp, Elf64_Move *mvp, Is_desc *isp) { int i, cnt = mvp->m_repeat; for (i = 0; i < cnt; i++) { size_t idx; Mv_desc *omdp, nmd; /* LINTED */ nmd.md_len = ELF_M_SIZE(mvp->m_info); nmd.md_start = mvp->m_poffset + i * ((mvp->m_stride + 1) * nmd.md_len); nmd.md_move = mvp; /* * Verify that this move descriptor doesn't overlap any existing * move descriptors. */ for (ALIST_TRAVERSE(sdp->sd_move, idx, omdp)) { Mv_desc *smdp, *lmdp; if (nmd.md_start > omdp->md_start) { smdp = omdp; lmdp = &nmd; } else { smdp = &nmd; lmdp = omdp; } /* * If this move entry is exactly the same as that of * a symbol that has overridden this symbol (for example * should two identical COMMON definitions be associated * with the same move data), simply ignore this move * element. */ if ((nmd.md_start == omdp->md_start) && ((nmd.md_len == smdp->md_len) && sdp->sd_file != isp->is_file)) continue; if ((nmd.md_start != omdp->md_start) && ((smdp->md_start + smdp->md_len) <= lmdp->md_start)) continue; ld_eprintf(ofl, ERR_FATAL, MSG_MOVE_OVERLAP, sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name, demangle(sdp->sd_name), EC_XWORD(nmd.md_start), EC_XWORD(nmd.md_len), EC_XWORD(omdp->md_start), EC_XWORD(omdp->md_len)); /* * Indicate that an error has occurred, so that * processing can be terminated once all move errors * are flushed out. */ sdp->sd_flags |= FLG_SY_OVERLAP; return (1); } if (alist_append(&sdp->sd_move, &nmd, sizeof (Mv_desc), AL_CNT_SDP_MOVE) == NULL) return (S_ERROR); } return (1); }
/* * 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); }
/* * Initialize new entrance and segment descriptors and add them as lists to * the output file descriptor. */ uintptr_t ld_ent_setup(Ofl_desc *ofl, Elf64_Xword segalign) { Ent_desc *enp; predef_seg_t *psegs; Sg_desc *sgp; size_t idx; /* * Initialize the elf library. */ if (elf_version(EV_CURRENT) == EV_NONE) { ld_eprintf(ofl, ERR_FATAL, MSG_ELF_LIBELF, EV_CURRENT); return (S_ERROR); } /* * Initialize internal Global Symbol Table AVL tree */ avl_create(&ofl->ofl_symavl, &ld_sym_avl_comp, sizeof (Sym_avlnode), SGSOFFSETOF(Sym_avlnode, sav_node)); /* Initialize segment AVL tree */ avl_create(&ofl->ofl_segs_avl, ofl_segs_avl_cmp, sizeof (Sg_desc), SGSOFFSETOF(Sg_desc, sg_avlnode)); /* Initialize entrance criteria AVL tree */ avl_create(&ofl->ofl_ents_avl, ofl_ents_avl_cmp, sizeof (Ent_desc), SGSOFFSETOF(Ent_desc, ec_avlnode)); /* * Allocate and initialize writable copies of both the entrance and * segment descriptors. * * Note that on non-amd64 targets, this allocates a few more * elements than are needed. For now, we are willing to overallocate * a small amount to simplify the code. */ if ((psegs = libld_malloc(sizeof (sg_desc))) == NULL) return (S_ERROR); (void) memcpy(psegs, &sg_desc, sizeof (sg_desc)); sgp = (Sg_desc *) psegs; /* * The data segment and stack permissions can differ: * * - Architectural/ABI per-platform differences * - Whether the object is built statically or dynamically * * Those segments so affected have their program header flags * set here at runtime, rather than in the sg_desc templates above. */ psegs->psg_data.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm; psegs->psg_bss.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm; psegs->psg_dynamic.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm; psegs->psg_sunwdtrace.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm; #if defined(_ELF64) psegs->psg_ldata.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm; psegs->psg_sunwdtrace.sg_phdr.p_flags |= PF_X; #endif psegs->psg_sunwstack.sg_phdr.p_flags = ld_targ.t_m.m_stack_perm; if ((ofl->ofl_flags & FLG_OF_DYNAMIC) == 0) psegs->psg_data.sg_phdr.p_flags |= PF_X; /* * Traverse the new entrance descriptor list converting the segment * pointer entries to the absolute address within the new segment * descriptor list. Add each entrance descriptor to the output file * list. */ if ((enp = libld_malloc(sizeof (ent_desc))) == NULL) return (S_ERROR); (void) memcpy(enp, ent_desc, sizeof (ent_desc)); for (idx = 0; idx < (sizeof (ent_desc) / sizeof (ent_desc[0])); idx++, enp++) { #if defined(_ELF64) /* Don't use the amd64 entry conditions for non-amd64 targets */ if ((enp->ec_attrmask & SHF_X86_64_LARGE) && (ld_targ.t_m.m_mach != EM_X86_64)) continue; #endif if (aplist_append(&ofl->ofl_ents, enp, AL_CNT_OFL_ENTRANCE) == NULL) return (S_ERROR); /* * The segment pointer is currently pointing at a template * segment descriptor in sg_desc. Compute its array index, * and then use that index to compute the address of the * corresponding descriptor in the writable copy. */ enp->ec_segment = &sgp[(enp->ec_segment - (Sg_desc *) &sg_desc)]; } /* * Add each segment descriptor to the segment descriptor list. The * ones with non-NULL sg_name are also entered into the AVL tree. * For each loadable segment initialize a default alignment. Note * that ld(1) and ld.so.1 initialize this differently. */ for (idx = 0; idx < predef_seg_nelts; idx++, sgp++) { Elf64_Phdr *phdr = &(sgp->sg_phdr); /* Ignore amd64 segment templates for non-amd64 targets */ switch (sgp->sg_id) { case SGID_LRODATA: case SGID_LDATA: if ((ld_targ.t_m.m_mach != EM_X86_64)) continue; } if (phdr->p_type == PT_LOAD) phdr->p_align = segalign; if ((aplist_append(&ofl->ofl_segs, sgp, AL_CNT_SEGMENTS)) == NULL) return (S_ERROR); #ifdef NDEBUG /* assert() is enabled */ /* * Enforce the segment name rule: Any segment that can * be referenced by an entrance descriptor must have * a name. Any segment that cannot, must have a NULL * name pointer. */ switch (phdr->p_type) { case PT_LOAD: case PT_NOTE: case PT_NULL: assert(sgp->sg_name != NULL); break; default: assert(sgp->sg_name == NULL); break; } #endif /* * Add named segment descriptors to the AVL tree to * provide O(logN) lookups. */ if (sgp->sg_name != NULL) avl_add(&ofl->ofl_segs_avl, sgp); } return (1); }
/* * Create an unwind header (.eh_frame_hdr) output section. * The section is created and space reserved, but the data * is not copied into place. That is done by a later call * to ld_unwind_populate(), after active relocations have been * processed. * * When GNU linkonce processing is in effect, we can end up in a situation * where the FDEs related to discarded sections remain in the eh_frame * section. Ideally, we would remove these dead entries from eh_frame. * However, that optimization has not yet been implemented. In the current * implementation, the number of dead FDEs cannot be determined until * active relocations are processed, and that processing follows the * call to this function. This means that we are unable to detect dead FDEs * here, and the section created by this routine is sized for maximum case * where all FDEs are valid. */ uintptr_t ld_unwind_make_hdr(Ofl_desc *ofl) { int bswap = (ofl->ofl_flags1 & FLG_OF1_ENCDIFF) != 0; Shdr *shdr; Elf_Data *elfdata; Is_desc *isp; size_t size; Xword fde_cnt; Aliste idx1; Os_desc *osp; /* * we only build a unwind header if we have * some unwind information in the file. */ if (ofl->ofl_unwind == NULL) return (1); /* * Allocate and initialize the Elf_Data structure. */ if ((elfdata = libld_calloc(sizeof (Elf_Data), 1)) == NULL) return (S_ERROR); elfdata->d_type = ELF_T_BYTE; elfdata->d_align = ld_targ.t_m.m_word_align; elfdata->d_version = ofl->ofl_dehdr->e_version; /* * Allocate and initialize the Shdr structure. */ if ((shdr = libld_calloc(sizeof (Shdr), 1)) == NULL) return (S_ERROR); shdr->sh_type = ld_targ.t_m.m_sht_unwind; shdr->sh_flags = SHF_ALLOC; shdr->sh_addralign = ld_targ.t_m.m_word_align; shdr->sh_entsize = 0; /* * Allocate and initialize the Is_desc structure. */ if ((isp = libld_calloc(1, sizeof (Is_desc))) == NULL) return (S_ERROR); isp->is_name = MSG_ORIG(MSG_SCN_UNWINDHDR); isp->is_shdr = shdr; isp->is_indata = elfdata; if ((ofl->ofl_unwindhdr = ld_place_section(ofl, isp, NULL, ld_targ.t_id.id_unwindhdr, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); /* * Scan through all of the input Frame information, counting each FDE * that requires an index. Each fde_entry gets a corresponding entry * in the binary search table. */ fde_cnt = 0; for (APLIST_TRAVERSE(ofl->ofl_unwind, idx1, osp)) { Aliste idx2; int os_isdescs_idx; OS_ISDESCS_TRAVERSE(os_isdescs_idx, osp, idx2, isp) { uchar_t *data; uint64_t off = 0; data = isp->is_indata->d_buf; size = isp->is_indata->d_size; while (off < size) { uint_t length, id; uint64_t ndx = 0; /* * Extract length in lsb format. A zero length * indicates that this CIE is a terminator and * that processing for unwind information is * complete. */ length = extract_uint(data + off, &ndx, bswap); if (length == 0) break; /* * Extract CIE id in lsb format. */ id = extract_uint(data + off, &ndx, bswap); /* * A CIE record has a id of '0', otherwise * this is a FDE entry and the 'id' is the * CIE pointer. */ if (id == 0) { uint_t cieversion; /* * The only CIE version supported * is '1' - quick sanity check * here. */ cieversion = data[off + ndx]; ndx += 1; /* BEGIN CSTYLED */ if (cieversion != 1) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_UNW_BADCIEVERS), isp->is_file->ifl_name, isp->is_name, off); return (S_ERROR); } /* END CSTYLED */ } else { fde_cnt++; } off += length + 4; } } }
static uintptr_t vers_visit_children(Ofl_desc *ofl, Ver_desc *vp, int flag) { size_t idx; Ver_desc *vdp; static int err = 0; static Ver_Stack ver_stk = {0, 0, 0}; int tmp_sp; /* * If there was any fatal error, * just return. */ if (err == S_ERROR) return (err); /* * if this is called from, ver_check_defs(), initialize sp. */ if (flag == 0) ver_stk.ver_sp = 0; /* * Check if passed version pointer vp is already in the stack. */ for (tmp_sp = 0; tmp_sp < ver_stk.ver_sp; tmp_sp++) { Ver_desc *v; v = ver_stk.ver_stk[tmp_sp]; if (v == vp) { /* * cyclic dependency. */ if (err == 0) { ld_eprintf(ofl, ERR_FATAL, (MSG_VER_CYCLIC)); err = 1; } for (tmp_sp = 0; tmp_sp < ver_stk.ver_sp; tmp_sp++) { v = ver_stk.ver_stk[tmp_sp]; if ((v->vd_flags & FLG_VER_CYCLIC) == 0) { v->vd_flags |= FLG_VER_CYCLIC; ld_eprintf(ofl, ERR_NONE, (MSG_VER_ADDVER), v->vd_name); } } if ((vp->vd_flags & FLG_VER_CYCLIC) == 0) { vp->vd_flags |= FLG_VER_CYCLIC; ld_eprintf(ofl, ERR_NONE, (MSG_VER_ADDVER), vp->vd_name); } return (err); } } /* * Push version on the stack. */ if (ver_stk.ver_sp >= ver_stk.ver_lmt) { ver_stk.ver_lmt += _NUM_OF_VERS_; if ((ver_stk.ver_stk = (Ver_desc **) libld_realloc((void *)ver_stk.ver_stk, ver_stk.ver_lmt * sizeof (Ver_desc *))) == NULL) return (S_ERROR); } ver_stk.ver_stk[(ver_stk.ver_sp)++] = vp; /* * Now visit children. */ for (APLIST_TRAVERSE(vp->vd_deps, idx, vdp)) if (vers_visit_children(ofl, vdp, 1) == S_ERROR) return (S_ERROR); /* * Pop version from the stack. */ (ver_stk.ver_sp)--; return (err); }
uintptr_t ld_vers_check_defs(Ofl_desc *ofl) { size_t idx1; Ver_desc *vdp; uintptr_t is_cyclic = 0; //DBG_CALL(Dbg_ver_def_title(ofl->ofl_lml, ofl->ofl_name)); /* * First check if there are any cyclic dependency */ for (APLIST_TRAVERSE(ofl->ofl_verdesc, idx1, vdp)) if ((is_cyclic = vers_visit_children(ofl, vdp, 0)) == S_ERROR) return (S_ERROR); if (is_cyclic) ofl->ofl_flags |= FLG_OF_FATAL; for (APLIST_TRAVERSE(ofl->ofl_verdesc, idx1, vdp)) { uint8_t cnt; Elf64_Sym *sym; Sym_desc *sdp; const char *name = vdp->vd_name; unsigned char bind; Ver_desc *_vdp; avl_index_t where; size_t idx2; if (vdp->vd_ndx == 0) { ld_eprintf(ofl, ERR_FATAL, (MSG_VER_UNDEF), name, vdp->vd_ref->vd_name, vdp->vd_ref->vd_file->ifl_name); continue; } //DBG_CALL(Dbg_ver_desc_entry(ofl->ofl_lml, vdp)); /* * If a version definition contains no symbols this is possibly * a mapfile error. */ #if 0 if ((vdp->vd_flags & (VER_FLG_BASE | VER_FLG_WEAK | FLG_VER_REFER)) == 0) DBG_CALL(Dbg_ver_nointerface(ofl->ofl_lml, vdp->vd_name)); #endif /* * Update the version entry count to account for this new * version descriptor (the count is the size in bytes). */ ofl->ofl_verdefsz += sizeof (Elf64_Verdef); /* * Traverse this versions dependency list to determine what * additional version dependencies we must account for against * this descriptor. */ cnt = 1; for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) { #if defined(__lint) /* get lint to think `_vdp' is used... */ vdp = _vdp; #endif cnt++; } ofl->ofl_verdefsz += (cnt * sizeof (Elf64_Verdaux)); /* * Except for the base version descriptor, generate an absolute * symbol to reflect this version. */ if (vdp->vd_flags & VER_FLG_BASE) continue; if (vdp->vd_flags & VER_FLG_WEAK) bind = STB_WEAK; else bind = STB_GLOBAL; if (sdp = ld_sym_find(name, vdp->vd_hash, &where, ofl)) { /* * If the symbol already exists and is undefined or was * defined in a shared library, convert it to an * absolute. */ if ((sdp->sd_sym->st_shndx == SHN_UNDEF) || (sdp->sd_ref != REF_REL_NEED)) { sdp->sd_shndx = sdp->sd_sym->st_shndx = SHN_ABS; sdp->sd_sym->st_info = ELF_ST_INFO(bind, STT_OBJECT); sdp->sd_ref = REF_REL_NEED; sdp->sd_flags |= (FLG_SY_SPECSEC | FLG_SY_DEFAULT | FLG_SY_EXPDEF); sdp->sd_aux->sa_overndx = vdp->vd_ndx; /* * If the reference originated from a mapfile * insure we mark the symbol as used. */ if (sdp->sd_flags & FLG_SY_MAPREF) sdp->sd_flags |= FLG_SY_MAPUSED; } else if ((sdp->sd_flags & FLG_SY_SPECSEC) && (sdp->sd_sym->st_shndx != SHN_ABS) && (sdp->sd_ref == REF_REL_NEED)) { ld_eprintf(ofl, ERR_WARNING, (MSG_VER_DEFINED), name, sdp->sd_file->ifl_name); } } else { /* * If the symbol does not exist create it. */ if ((sym = libld_calloc(sizeof (Elf64_Sym), 1)) == NULL) return (S_ERROR); sym->st_shndx = SHN_ABS; sym->st_info = ELF_ST_INFO(bind, STT_OBJECT); //DBG_CALL(Dbg_ver_symbol(ofl->ofl_lml, name)); if ((sdp = ld_sym_enter(name, sym, vdp->vd_hash, vdp->vd_file, ofl, 0, SHN_ABS, (FLG_SY_SPECSEC | FLG_SY_DEFAULT | FLG_SY_EXPDEF), &where)) == (Sym_desc *)S_ERROR) return (S_ERROR); sdp->sd_ref = REF_REL_NEED; sdp->sd_aux->sa_overndx = vdp->vd_ndx; } } return (1); }