/* * Return an initialized Str_tbl - returns NULL on failure. * * flags: * FLG_STTAB_COMPRESS - build a compressed string table */ Str_tbl * st_new(uint_t flags) { Str_tbl *stp; if ((stp = calloc(sizeof (Str_tbl), 1)) == NULL) return (NULL); /* * Start with a leading '\0' - it's tradition. */ stp->st_strsize = stp->st_fullstrsize = stp->st_nextoff = 1; /* * Do we compress this string table? */ stp->st_flags = flags; if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) return (stp); if ((stp->st_lentree = calloc(sizeof (avl_tree_t), 1)) == NULL) return (NULL); avl_create(stp->st_lentree, &avl_len_compare, sizeof (LenNode), SGSOFFSETOF(LenNode, ln_avlnode)); return (stp); }
/* * Determine whether a (COMDAT) group has already been encountered. If so, * indicate that the group descriptor has an overriding group (gd_oisc). This * indication triggers the ld_place_section() to discard this group, while the * gd_oisc information provides for complete diagnostics of the override. * Otherwise, this is the first occurrence of this group, therefore the group * descriptor is saved for future comparisons. */ static uintptr_t gpavl_loaded(Ofl_desc *ofl, Group_desc *gdp) { Isd_node isd, *isdp; avl_tree_t *avlt; avl_index_t where; /* * Create a groups avl tree if required. */ if ((avlt = ofl->ofl_groups) == NULL) { if ((avlt = libld_calloc(sizeof (avl_tree_t), 1)) == NULL) return (S_ERROR); avl_create(avlt, isdavl_compare, sizeof (Isd_node), SGSOFFSETOF(Isd_node, isd_avl)); ofl->ofl_groups = avlt; } /* * An SHT_GROUP section is identified by the name of its signature * symbol rather than section name. Although the section names are * often unique, this is not required, and some compilers set it to * a generic name like ".group". */ isd.isd_name = gdp->gd_name; isd.isd_hash = sgs_str_hash(isd.isd_name); if ((isdp = avl_find(avlt, &isd, &where)) != NULL) { gdp->gd_oisc = isdp->isd_isp; return (1); } /* * This is a new group - so keep it. */ if ((isdp = libld_calloc(sizeof (Isd_node), 1)) == NULL) return (S_ERROR); isdp->isd_name = isd.isd_name; isdp->isd_hash = isd.isd_hash; isdp->isd_isp = gdp->gd_isc; avl_insert(avlt, isdp, where); return (0); }
/* * Insert a new string into the Str_tbl. There are two AVL trees used. * * . The first LenNode AVL tree maintains a tree of nodes based on string * sizes. * . Each LenNode maintains a StrNode AVL tree for each string. Large * applications have been known to contribute thousands of strings of * the same size. Should strings need to be removed (-z ignore), then * the string AVL tree makes this removal efficient and scalable. */ int st_insert(Str_tbl *stp, const char *str) { size_t len; StrNode *snp, sn = { 0 }; LenNode *lnp, ln = { 0 }; avl_index_t where; /* * String table can't have been cooked */ assert((stp->st_flags & FLG_STTAB_COOKED) == 0); /* * Null strings always point to the head of the string * table - no reason to keep searching. */ if ((len = strlen(str)) == 0) return (0); stp->st_fullstrsize += len + 1; stp->st_strcnt++; if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) return (0); /* * From the controlling string table, determine which LenNode AVL node * provides for this string length. If the node doesn't exist, insert * a new node to represent this string length. */ ln.ln_strlen = len; if ((lnp = avl_find(stp->st_lentree, &ln, &where)) == NULL) { if ((lnp = calloc(sizeof (LenNode), 1)) == NULL) return (-1); lnp->ln_strlen = len; avl_insert(stp->st_lentree, lnp, where); if ((lnp->ln_strtree = calloc(sizeof (avl_tree_t), 1)) == NULL) return (0); avl_create(lnp->ln_strtree, &avl_str_compare, sizeof (StrNode), SGSOFFSETOF(StrNode, sn_avlnode)); } /* * From the string length AVL node determine whether a StrNode AVL node * provides this string. If the node doesn't exist, insert a new node * to represent this string. */ sn.sn_str = str; if ((snp = avl_find(lnp->ln_strtree, &sn, &where)) == NULL) { if ((snp = calloc(sizeof (StrNode), 1)) == NULL) return (-1); snp->sn_str = str; avl_insert(lnp->ln_strtree, snp, where); } snp->sn_refcnt++; return (0); }
/* * 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); }
/* * Change the current scope for the given version. * * entry: * mf - Mapfile descriptor * scope_name - Name for new scope * mv - Information related to version being defined * * exit: * On success, mv is updated to change the current scope. * On failure, mv->errcnt is incremented, and mv is otherwise unaltered. */ void ld_map_sym_scope(Mapfile *mf, const char *scope_name, ld_map_ver_t *mv) { typedef struct { const char *name; /* scope keyword string */ ld_map_scope_t type; /* Resulting type */ ofl_flag_t ofl_flags; /* 0, or ofl flags to add */ } scope_t; /* * Valid symbol scope keywords * * All symbols added by a mapfile are actually global entries, and * are assigned the scope that is presently in effect. * * If a protected/symbolic scope is detected, remember this. If * a protected/symbolic scope is the only scope defined in this * (or any other mapfiles), then the mode -Bsymbolic is established. */ static scope_t scope_list[] = { { (MSG_MAPKW_DEFAULT), FLG_SCOPE_DFLT, FLG_OF_MAPGLOB }, { (MSG_MAPKW_ELIMINATE), FLG_SCOPE_ELIM, 0 }, { (MSG_MAPKW_EXPORTED), FLG_SCOPE_EXPT, 0 }, { (MSG_MAPKW_HIDDEN), FLG_SCOPE_HIDD, 0 }, { (MSG_MAPKW_GLOBAL), FLG_SCOPE_DFLT, FLG_OF_MAPGLOB }, { (MSG_MAPKW_LOCAL), FLG_SCOPE_HIDD, 0 }, { (MSG_MAPKW_PROTECTED), FLG_SCOPE_PROT, FLG_OF_MAPSYMB }, { (MSG_MAPKW_SINGLETON), FLG_SCOPE_SNGL, FLG_OF_MAPGLOB }, { (MSG_MAPKW_SYMBOLIC), FLG_SCOPE_PROT, FLG_OF_MAPSYMB }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in scope_list[]. Must * be kept in sync with scope_list. */ static size_t scope_list_bufsize = sizeof(MSG_MAPKW_DEFAULT) + sizeof(MSG_MAPKW_ELIMINATE) + sizeof(MSG_MAPKW_EXPORTED) + sizeof(MSG_MAPKW_HIDDEN) + sizeof(MSG_MAPKW_GLOBAL) + sizeof(MSG_MAPKW_LOCAL) + sizeof(MSG_MAPKW_PROTECTED) + sizeof(MSG_MAPKW_SINGLETON) + sizeof(MSG_MAPKW_SYMBOLIC); scope_t *scope; scope = ld_map_kwfind(scope_name, scope_list, SGSOFFSETOF(scope_t, name), sizeof (scope_list[0])); if (scope == NULL) { char buf[VLA_SIZE(scope_list_bufsize)]; mf_fatal(mf, (MSG_MAP_EXP_SYMSCOPE), ld_map_kwnames(scope_list, SGSOFFSETOF(scope_t, name), sizeof (scope[0]), buf, scope_list_bufsize), scope_name); mv->mv_errcnt++; return; } mv->mv_scope = scope->type; mf->mf_ofl->ofl_flags |= scope->ofl_flags; }