/* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Map file parsing and input section to output segment mapping. */ #include #include #include #include "msg.h" #include "_libld.h" /* * Each time a section is placed, the function set_addralign() * is called. This function performs: * * . if the section is from an external file, check if this is empty or not. * If not, we know the segment this section will belong needs a program * header. (Of course, the program is needed only if this section falls * into a loadable segment.) * . compute the Least Common Multiplier for setting the segment alignment. */ static void set_addralign(Ofl_desc *ofl, Os_desc *osp, Is_desc *isp) { Shdr * shdr = isp->is_shdr; /* A discarded section has no influence on the output */ if (isp->is_flags & FLG_IS_DISCARD) return; /* * If this section has data or will be assigned data * later, mark this segment not-empty. */ if ((shdr->sh_size != 0) || ((isp->is_flags & FLG_IS_EXTERNAL) == 0)) osp->os_sgdesc->sg_flags |= FLG_SG_PHREQ; if ((ofl->ofl_dtflags_1 & DF_1_NOHDR) && (osp->os_sgdesc->sg_phdr).p_type != PT_LOAD) return; osp->os_sgdesc->sg_addralign = ld_lcm(osp->os_sgdesc->sg_addralign, shdr->sh_addralign); } /* * Place a section into the appropriate segment. */ Os_desc * ld_place_section(Ofl_desc * ofl, Is_desc * isp, int ident, Word link) { Listnode * lnp1, * lnp2; Ent_desc * enp; Sg_desc * sgp; Os_desc **ospp, *osp; Aliste off1, off2; int os_ndx; Shdr * shdr = isp->is_shdr; Xword shflagmask, shflags = shdr->sh_flags; Ifl_desc * ifl = isp->is_file; /* * Define any sections that must be thought of as referenced. These * sections may not be referenced externaly in a manner ld(1) can * discover, but they must be retained (ie. not removed by -zignore). */ static const Msg RefSecs[] = { MSG_SCN_INIT, /* MSG_ORIG(MSG_SCN_INIT) */ MSG_SCN_FINI, /* MSG_ORIG(MSG_SCN_FINI) */ MSG_SCN_EX_RANGES, /* MSG_ORIG(MSG_SCN_EX_RANGES) */ MSG_SCN_EX_SHARED, /* MSG_ORIG(MSG_SCN_EX_SHARED) */ MSG_SCN_CTORS, /* MSG_ORIG(MSG_SCN_CTORS) */ MSG_SCN_DTORS, /* MSG_ORIG(MSG_SCN_DTORS) */ MSG_SCN_EHFRAME, /* MSG_ORIG(MSG_SCN_EHFRAME) */ MSG_SCN_EHFRAME_HDR, /* MSG_ORIG(MSG_SCN_EHFRAME_HDR) */ MSG_SCN_JCR, /* MSG_ORIG(MSG_SCN_JCR) */ 0 }; DBG_CALL(Dbg_sec_in(ofl->ofl_lml, isp)); if ((shflags & SHF_GROUP) || (shdr->sh_type == SHT_GROUP)) { Group_desc * gdesc; if ((gdesc = ld_get_group(ofl, isp)) == (Group_desc *)S_ERROR) return ((Os_desc *)S_ERROR); if (gdesc) { DBG_CALL(Dbg_sec_group(ofl->ofl_lml, isp, gdesc)); /* * If this group is marked as discarded, then this * section needs to be discarded. */ if (gdesc->gd_flags & GRP_FLG_DISCARD) { isp->is_flags |= FLG_IS_DISCARD; /* * Since we're discarding the section, we * can skip assigning it to an output section. * The exception is that if the user * specifies -z relaxreloc, then * we need to assign the output section so * that the sloppy relocation logic will have * the information necessary to do its work. */ if (!(ofl->ofl_flags1 & FLG_OF1_RLXREL)) return ((Os_desc *)0); } } /* * SHT_GROUP sections can only be included into relocatable * objects. */ if (shdr->sh_type == SHT_GROUP) { if ((ofl->ofl_flags & FLG_OF_RELOBJ) == 0) { isp->is_flags |= FLG_IS_DISCARD; return ((Os_desc *)0); } } } /* * Always assign SHF_TLS sections to the DATA segment (and then the * PT_TLS embedded inside of there). */ if (shflags & SHF_TLS) shflags |= SHF_WRITE; /* * Traverse the entrance criteria list searching for a segment that * matches the input section we have. If an entrance criterion is set * then there must be an exact match. If we complete the loop without * finding a segment, then sgp will be NULL. */ sgp = NULL; for (LIST_TRAVERSE(&ofl->ofl_ents, lnp1, enp)) { if (enp->ec_segment && (enp->ec_segment->sg_flags & FLG_SG_DISABLED)) continue; if (enp->ec_type && (enp->ec_type != shdr->sh_type)) continue; if (enp->ec_attrmask && /* LINTED */ (enp->ec_attrmask & enp->ec_attrbits) != (enp->ec_attrmask & shflags)) continue; if (enp->ec_name && (strcmp(enp->ec_name, isp->is_name) != 0)) continue; if (enp->ec_files.head) { char *file; int found = 0; if (isp->is_file == 0) continue; for (LIST_TRAVERSE(&(enp->ec_files), lnp2, file)) { const char *name = isp->is_file->ifl_name; if (file[0] == '*') { const char *basename; basename = strrchr(name, '/'); if (basename == NULL) basename = name; else if (basename[1] != '\0') basename++; if (strcmp(&file[1], basename) == 0) { found++; break; } } else { if (strcmp(file, name) == 0) { found++; break; } } } if (!found) continue; } break; } if ((sgp = enp->ec_segment) == 0) sgp = ((Ent_desc *)(ofl->ofl_ents.tail->data))->ec_segment; isp->is_basename = isp->is_name; /* * Strip out the % from the section name in all cases except when '-r' * is used without '-M', and '-r' is used with '-M' without * the ?O flag. */ if (((ofl->ofl_flags & FLG_OF_RELOBJ) && (sgp->sg_flags & FLG_SG_ORDER)) || !(ofl->ofl_flags & FLG_OF_RELOBJ)) { char *cp; if ((cp = strchr(isp->is_name, '%')) != NULL) { char *name; size_t size = (size_t)(cp - isp->is_name); if ((name = libld_malloc(size + 1)) == 0) return ((Os_desc *)S_ERROR); (void) strncpy(name, isp->is_name, size); cp = name + size; *cp = '\0'; isp->is_name = name; } isp->is_txtndx = enp->ec_ndx; } /* * Assign a hash value now that the section name has been finalized. */ isp->is_namehash = sgs_str_hash(isp->is_name); if (sgp->sg_flags & FLG_SG_ORDER) enp->ec_flags |= FLG_EC_USED; /* * If the link is not 0, then the input section is going to be appended * to the output section. The append occurs at the input section * pointed to by the link. */ if (link != 0) { osp = isp->is_file->ifl_isdesc[link]->is_osdesc; /* * If this is a COMDAT section, then see if this * section is a keeper and/or if it is to be discarded. */ if (shdr->sh_type == SHT_SUNW_COMDAT) { Listnode * clist; Is_desc * cisp; for (LIST_TRAVERSE(&(osp->os_comdats), clist, cisp)) { if (strcmp(isp->is_basename, cisp->is_basename)) continue; isp->is_flags |= FLG_IS_DISCARD; isp->is_osdesc = osp; DBG_CALL(Dbg_sec_discarded(ofl->ofl_lml, isp, cisp)); return (0); } /* * This is a new COMDAT section - so keep it. */ if (list_appendc(&(osp->os_comdats), isp) == 0) return ((Os_desc *)S_ERROR); } /* * Set alignment */ set_addralign(ofl, osp, isp); if (list_appendc(&(osp->os_isdescs), isp) == 0) return ((Os_desc *)S_ERROR); isp->is_osdesc = osp; sgp = osp->os_sgdesc; DBG_CALL(Dbg_sec_added(ofl->ofl_lml, osp, sgp)); return (osp); } /* * Determine if section ordering is turned on. If so, return the * appropriate os_txtndx. This information is derived from the * Sg_desc->sg_segorder list that was built up from the Mapfile. */ os_ndx = 0; if (sgp->sg_secorder) { Aliste off; Sec_order **scopp; for (ALIST_TRAVERSE(sgp->sg_secorder, off, scopp)) { Sec_order *scop = *scopp; if (strcmp(scop->sco_secname, isp->is_name) == 0) { scop->sco_flags |= FLG_SGO_USED; os_ndx = scop->sco_index; break; } } } /* * Mask of section header flags to ignore when * matching sections. We are more strict with * relocatable objects, ignoring only the order * flags, and keeping sections apart if they differ * otherwise. This follows the policy that sections * in a relative object should only be merged if their * flags are the same, and avoids destroying information * prematurely. For final products however, we ignore all * flags that do not prevent a merge. */ shflagmask = (ofl->ofl_flags & FLG_OF_RELOBJ) ? ALL_SHF_ORDER : ALL_SHF_IGNORE; /* * Traverse the input section list for the output section we have been * assigned. If we find a matching section simply add this new section. */ off2 = 0; for (ALIST_TRAVERSE(sgp->sg_osdescs, off1, ospp)) { Shdr *_shdr; osp = *ospp; _shdr = osp->os_shdr; if ((ident == osp->os_scnsymndx) && (ident != M_ID_REL) && (isp->is_namehash == osp->os_namehash) && (shdr->sh_type != SHT_GROUP) && (shdr->sh_type != SHT_SUNW_dof) && ((shdr->sh_type == _shdr->sh_type) || ((shdr->sh_type == SHT_SUNW_COMDAT) && (_shdr->sh_type == SHT_PROGBITS))) && ((shflags & ~shflagmask) == (_shdr->sh_flags & ~shflagmask)) && (strcmp(isp->is_name, osp->os_name) == 0)) { /* * If this is a COMDAT section, determine if this * section is a keeper, and/or if it is to be discarded. */ if (shdr->sh_type == SHT_SUNW_COMDAT) { Listnode * clist; Is_desc * cisp; for (LIST_TRAVERSE(&(osp->os_comdats), clist, cisp)) { if (strcmp(isp->is_basename, cisp->is_basename)) continue; isp->is_flags |= FLG_IS_DISCARD; isp->is_osdesc = osp; DBG_CALL(Dbg_sec_discarded(ofl->ofl_lml, isp, cisp)); return (0); } /* * This is a new COMDAT section - so keep it. */ if (list_appendc(&(osp->os_comdats), isp) == 0) return ((Os_desc *)S_ERROR); } /* * Set alignment */ set_addralign(ofl, osp, isp); /* * If this section is a non-empty TLS section indicate * that a PT_TLS program header is required. */ if ((shflags & SHF_TLS) && shdr->sh_size && ((ofl->ofl_flags & FLG_OF_RELOBJ) == 0)) ofl->ofl_flags |= FLG_OF_TLSPHDR; /* * If is_txtndx is 0 then this section was not * seen in mapfile, so put it at the end. * If is_txtndx is not 0 and ?O is turned on * then check to see where this section should * be inserted. */ if ((sgp->sg_flags & FLG_SG_ORDER) && isp->is_txtndx) { Listnode * tlist; tlist = list_where(&(osp->os_isdescs), isp->is_txtndx); if (tlist != NULL) { if (list_insertc(&(osp->os_isdescs), isp, tlist) == 0) return ((Os_desc *)S_ERROR); } else { if (list_prependc(&(osp->os_isdescs), isp) == 0) return ((Os_desc *)S_ERROR); } } else if (list_appendc(&(osp->os_isdescs), isp) == 0) return ((Os_desc *)S_ERROR); isp->is_osdesc = osp; /* * If this input section and file is associated to an * artificially referenced output section, make sure * they are marked as referenced also. This insures this * input section and file isn't eliminated when -zignore * is in effect. * See -zignore comments when creating a new output * section below. */ if (((ifl && (ifl->ifl_flags & FLG_IF_IGNORE)) || DBG_ENABLED) && (osp->os_flags & FLG_OS_SECTREF)) { isp->is_flags |= FLG_IS_SECTREF; if (ifl) ifl->ifl_flags |= FLG_IF_FILEREF; } DBG_CALL(Dbg_sec_added(ofl->ofl_lml, osp, sgp)); return (osp); } /* * Do we need to worry about section ordering? */ if (os_ndx) { if (osp->os_txtndx) { if (os_ndx < osp->os_txtndx) /* insert section here. */ break; else { off2 = off1; continue; } } else { /* insert section here. */ break; } } else if (osp->os_txtndx) { off2 = off1; continue; } /* * If the new sections identifier is less than that of the * present input section we need to insert the new section * at this point. */ if (ident < osp->os_scnsymndx) break; off2 = off1; } /* * We are adding a new output section. Update the section header * count and associated string size. */ ofl->ofl_shdrcnt++; if (st_insert(ofl->ofl_shdrsttab, isp->is_name) == -1) return ((Os_desc *)S_ERROR); /* * Create a new output section descriptor. */ if ((osp = libld_calloc(sizeof (Os_desc), 1)) == 0) return ((Os_desc *)S_ERROR); if ((osp->os_shdr = libld_calloc(sizeof (Shdr), 1)) == 0) return ((Os_desc *)S_ERROR); /* * We convert COMDAT sections to PROGBITS if this is the first * section of a output section. */ if (shdr->sh_type == SHT_SUNW_COMDAT) { Shdr * tshdr; if ((tshdr = libld_malloc(sizeof (Shdr))) == 0) return ((Os_desc *)S_ERROR); *tshdr = *shdr; isp->is_shdr = shdr = tshdr; shdr->sh_type = SHT_PROGBITS; if (list_appendc(&(osp->os_comdats), isp) == 0) return ((Os_desc *)S_ERROR); } osp->os_shdr->sh_type = shdr->sh_type; osp->os_shdr->sh_flags = shdr->sh_flags; osp->os_shdr->sh_entsize = shdr->sh_entsize; osp->os_name = isp->is_name; osp->os_namehash = isp->is_namehash; osp->os_txtndx = os_ndx; osp->os_sgdesc = sgp; if (ifl && (shdr->sh_type == SHT_PROGBITS)) { /* * Try to preserve the intended meaning of sh_link/sh_info. * See the translate_link() in update.c. */ osp->os_shdr->sh_link = shdr->sh_link; if (shdr->sh_flags & SHF_INFO_LINK) osp->os_shdr->sh_info = shdr->sh_info; } /* * When -zignore is in effect, user supplied sections and files that are * not referenced from other sections, are eliminated from the object * being produced. Some sections, although unreferenced, are special, * and must not be eliminated. Determine if this new output section is * one of those special sections, and if so mark it artificially as * referenced. Any input section and file associated to this output * section is also be marked as referenced, and thus won't be eliminated * from the final output. */ if (ifl && ((ofl->ofl_flags1 & FLG_OF1_IGNPRC) || DBG_ENABLED)) { const Msg *refsec; for (refsec = RefSecs; *refsec; refsec++) { if (strcmp(osp->os_name, MSG_ORIG(*refsec)) == 0) { osp->os_flags |= FLG_OS_SECTREF; if ((ifl->ifl_flags & FLG_IF_IGNORE) || DBG_ENABLED) { isp->is_flags |= FLG_IS_SECTREF; ifl->ifl_flags |= FLG_IF_FILEREF; } break; } } } /* * Setions of SHT_GROUP are added to the ofl->ofl_osgroups * list - so that they can be updated as a group later. */ if (shdr->sh_type == SHT_GROUP) { if (list_appendc(&ofl->ofl_osgroups, osp) == 0) return ((Os_desc *)S_ERROR); } /* * If this section is a non-empty TLS section indicate that a PT_TLS * program header is required. */ if ((shflags & SHF_TLS) && shdr->sh_size && ((ofl->ofl_flags & FLG_OF_RELOBJ) == 0)) ofl->ofl_flags |= FLG_OF_TLSPHDR; /* * If a non-allocatable section is going to be put into a loadable * segment then turn on the allocate bit for this section and warn the * user that we have done so. This could only happen through the use * of a mapfile. */ if ((sgp->sg_phdr.p_type == PT_LOAD) && ((osp->os_shdr->sh_flags & SHF_ALLOC) == 0)) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_SCN_NONALLOC), ofl->ofl_name, osp->os_name); osp->os_shdr->sh_flags |= SHF_ALLOC; } /* * Retain this sections identifier for future comparisons when placing * a section (after all sections have been processed this variable will * be used to hold the sections symbol index as we don't need to retain * the identifier any more). */ osp->os_scnsymndx = ident; /* * Set alignment */ set_addralign(ofl, osp, isp); if (list_appendc(&(osp->os_isdescs), isp) == 0) return ((Os_desc *)S_ERROR); DBG_CALL(Dbg_sec_created(ofl->ofl_lml, osp, sgp)); isp->is_osdesc = osp; if (off2) { /* * Insert the new section after the section identified by off2. */ off2 += sizeof (Os_desc *); if (alist_insert(&(sgp->sg_osdescs), &osp, sizeof (Os_desc *), AL_CNT_OSDESC, off2) == 0) return ((Os_desc *)S_ERROR); } else { /* * Prepend this section to the section list. */ if (alist_insert(&(sgp->sg_osdescs), &osp, sizeof (Os_desc *), AL_CNT_OSDESC, ALO_DATA) == 0) return ((Os_desc *)S_ERROR); } return (osp); }