1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <string.h> 31 #include <link.h> 32 #include <debug.h> 33 #include "msg.h" 34 #include "_libld.h" 35 36 /* 37 * Define an AVL node for maintain group names, together with a compare function 38 * for the Grp_node AVL tree. 39 */ 40 typedef struct { 41 const char *gn_name; /* group name */ 42 avl_node_t gn_avl; /* avl book-keeping (see SGSOFFSETOF) */ 43 uint_t gn_hash; /* group name hash value */ 44 } Grp_node; 45 46 static int 47 gnavl_compare(const void * n1, const void * n2) 48 { 49 uint_t hash1, hash2; 50 const char *st1, *st2; 51 int rc; 52 53 hash1 = ((Grp_node *)n1)->gn_hash; 54 hash2 = ((Grp_node *)n2)->gn_hash; 55 56 if (hash1 > hash2) 57 return (1); 58 if (hash1 < hash2) 59 return (-1); 60 61 st1 = ((Grp_node *)n1)->gn_name; 62 st2 = ((Grp_node *)n2)->gn_name; 63 64 rc = strcmp(st1, st2); 65 if (rc > 0) 66 return (1); 67 if (rc < 0) 68 return (-1); 69 return (0); 70 } 71 72 /* 73 * Determine whether a (COMDAT) group has already been encountered. If so, 74 * tag the new group having the same name as discardable. 75 */ 76 static uintptr_t 77 gpavl_loaded(Ofl_desc *ofl, Group_desc * gdp) 78 { 79 Grp_node gpn, *gpnp; 80 avl_tree_t *avlt; 81 avl_index_t where; 82 83 /* 84 * Create an avl tree if required. 85 */ 86 if ((avlt = ofl->ofl_groups) == 0) { 87 if ((avlt = calloc(sizeof (avl_tree_t), 1)) == NULL) 88 return (S_ERROR); 89 avl_create(avlt, gnavl_compare, sizeof (Grp_node), 90 SGSOFFSETOF(Grp_node, gn_avl)); 91 ofl->ofl_groups = avlt; 92 } 93 94 gpn.gn_name = gdp->gd_symname; 95 gpn.gn_hash = sgs_str_hash(gdp->gd_symname); 96 97 if ((gpnp = avl_find(avlt, &gpn, &where)) != NULL) { 98 gdp->gd_flags |= GRP_FLG_DISCARD; 99 return (1); 100 } 101 102 /* 103 * This is a new group, save it. 104 */ 105 if ((gpnp = calloc(sizeof (Grp_node), 1)) == NULL) 106 return (S_ERROR); 107 108 gpnp->gn_name = gpn.gn_name; 109 gpnp->gn_hash = gpn.gn_hash; 110 111 avl_insert(avlt, gpnp, where); 112 return (0); 113 } 114 115 Group_desc * 116 ld_get_group(Ofl_desc *ofl, Is_desc *isp) 117 { 118 Ifl_desc *ifl = isp->is_file; 119 Elf *elf = ifl->ifl_elf; 120 uint_t scnndx = isp->is_scnndx; 121 Group_desc *gdp; 122 Aliste idx; 123 124 /* 125 * If this is the first SHF_GROUP section encountered for this file, 126 * establish what group sections exist. 127 */ 128 if (ifl->ifl_groups == NULL) { 129 Elf_Scn *scn = 0; 130 131 while (scn = elf_nextscn(elf, scn)) { 132 Shdr *shdr, *_shdr; 133 Sym *sym; 134 Elf_Scn *_scn; 135 Elf_Data *data; 136 Group_desc gd; 137 138 shdr = elf_getshdr(scn); 139 if (shdr->sh_type != SHT_GROUP) 140 continue; 141 142 /* 143 * Confirm that the sh_link points to a valid section. 144 */ 145 if ((shdr->sh_link == SHN_UNDEF) || 146 (shdr->sh_link >= ifl->ifl_shnum)) { 147 eprintf(ofl->ofl_lml, ERR_FATAL, 148 MSG_INTL(MSG_FIL_INVSHLINK), 149 ifl->ifl_name, elf_strptr(elf, 150 ifl->ifl_shstrndx, shdr->sh_name), 151 EC_XWORD(shdr->sh_link)); 152 ofl->ofl_flags |= FLG_OF_FATAL; 153 continue; 154 } 155 156 if (shdr->sh_entsize == 0) { 157 eprintf(ofl->ofl_lml, ERR_FATAL, 158 MSG_INTL(MSG_FIL_INVSHENTSIZE), 159 ifl->ifl_name, elf_strptr(elf, 160 ifl->ifl_shstrndx, shdr->sh_name), 161 EC_XWORD(shdr->sh_entsize)); 162 ofl->ofl_flags |= FLG_OF_FATAL; 163 continue; 164 } 165 166 /* 167 * Get associated symbol table. 168 */ 169 _scn = elf_getscn(elf, shdr->sh_link); 170 _shdr = elf_getshdr(_scn); 171 172 /* 173 * Sanity check the sh_link field (which points to 174 * a symbol table entry) against the size of the 175 * symbol table. 176 */ 177 if ((shdr->sh_info == SHN_UNDEF) || 178 (shdr->sh_info >= (Word)(_shdr->sh_size / 179 _shdr->sh_entsize))) { 180 eprintf(ofl->ofl_lml, ERR_FATAL, 181 MSG_INTL(MSG_FIL_INVSHINFO), 182 ifl->ifl_name, elf_strptr(elf, 183 ifl->ifl_shstrndx, shdr->sh_name), 184 EC_XWORD(shdr->sh_info)); 185 ofl->ofl_flags |= FLG_OF_FATAL; 186 continue; 187 } 188 189 data = elf_getdata(_scn, 0); 190 sym = data->d_buf; 191 sym += shdr->sh_info; 192 data = elf_getdata(scn, 0); 193 194 gd.gd_gsectname = 195 elf_strptr(elf, ifl->ifl_shstrndx, shdr->sh_name); 196 gd.gd_symname = 197 elf_strptr(elf, _shdr->sh_link, sym->st_name); 198 gd.gd_scnndx = elf_ndxscn(scn); 199 gd.gd_data = data->d_buf; 200 gd.gd_cnt = data->d_size / sizeof (Word); 201 gd.gd_flags = 0; 202 203 /* 204 * If this group is a COMDAT group, determine whether 205 * this 'signature' symbol has already been detected. 206 */ 207 if ((gd.gd_data[0] & GRP_COMDAT) && 208 (ELF_ST_BIND(sym->st_info) != STB_LOCAL) && 209 (sym->st_shndx != SHN_UNDEF) && 210 (gpavl_loaded(ofl, &gd) == S_ERROR)) 211 return ((Group_desc *)S_ERROR); 212 213 if (alist_append(&(ifl->ifl_groups), 214 &gd, sizeof (Group_desc), AL_CNT_IFL_GROUPS) == 0) 215 return ((Group_desc *)S_ERROR); 216 } 217 } 218 219 /* 220 * Scan the GROUP sections associated with this file to find the 221 * matching group section. 222 */ 223 for (ALIST_TRAVERSE(ifl->ifl_groups, idx, gdp)) { 224 size_t ndx; 225 Word * data; 226 227 if (isp->is_shdr->sh_type == SHT_GROUP) { 228 if (isp->is_scnndx == gdp->gd_scnndx) 229 return (gdp); 230 continue; 231 } 232 233 data = gdp->gd_data; 234 for (ndx = 1; ndx < gdp->gd_cnt; ndx++) { 235 if (data[ndx] == scnndx) 236 return (gdp); 237 } 238 } 239 240 eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_ELF_NOGROUPSECT), 241 ifl->ifl_name, isp->is_name); 242 ofl->ofl_flags |= FLG_OF_FATAL; 243 return (0); 244 } 245