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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * ELF support routines for processing versioned mon.out files. 30 */ 31 32 #include <stdlib.h> 33 #include <string.h> 34 #include "profv.h" 35 36 bool 37 is_shared_obj(char *name) 38 { 39 int fd; 40 Elf *elf; 41 GElf_Ehdr ehdr; 42 43 if ((fd = open(name, O_RDONLY)) == -1) { 44 (void) fprintf(stderr, "%s: can't open `%s'\n", cmdname, name); 45 exit(ERR_ELF); 46 } 47 48 if (elf_version(EV_CURRENT) == EV_NONE) { 49 (void) fprintf(stderr, "%s: libelf out of date\n", cmdname); 50 exit(ERR_ELF); 51 } 52 53 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 54 (void) fprintf(stderr, "%s: elf_begin failed\n", cmdname); 55 exit(ERR_ELF); 56 } 57 58 if (gelf_getehdr(elf, &ehdr) == NULL) { 59 (void) fprintf(stderr, "%s: can't read ELF header of %s\n", 60 cmdname, name); 61 exit(ERR_ELF); 62 } 63 64 (void) elf_end(elf); 65 (void) close(fd); 66 67 if (ehdr.e_type == ET_DYN) 68 return (TRUE); 69 else 70 return (FALSE); 71 } 72 73 static void 74 rm_dups(nltype *nl, size_t *nfuncs) 75 { 76 size_t i, prev = 0, ndx = 0; 77 int prev_type, prev_bind, cur_type; 78 79 for (i = 1; i < *nfuncs; i++) { 80 /* 81 * If current value is different from prev, proceed. 82 */ 83 if (nl[prev].value < nl[i].value) { 84 prev = i; 85 continue; 86 } 87 88 /* 89 * If current and prev have the syminfo, rm the latter. 90 */ 91 if (nl[prev].info == nl[i].info) { 92 nl[i].name = NULL; 93 continue; 94 } 95 96 prev_type = ELF_ST_TYPE(nl[prev].info); 97 prev_bind = ELF_ST_BIND(nl[prev].info); 98 cur_type = ELF_ST_TYPE(nl[i].info); 99 100 /* 101 * Remove the one with STT_NOTYPE and keep the other. 102 */ 103 if (prev_type != cur_type) { 104 if (prev_type != STT_NOTYPE) 105 nl[i].name = NULL; 106 else { 107 nl[prev].name = NULL; 108 prev = i; 109 } 110 continue; 111 } 112 113 /* 114 * If they have the same type, take the stronger bound 115 * function 116 */ 117 if (prev_bind != STB_WEAK) 118 nl[i].name = NULL; 119 else { 120 nl[prev].name = NULL; 121 prev = i; 122 } 123 } 124 125 126 /* 127 * Actually remove the cleared symbols from namelist. We're not 128 * truncating namelist by realloc, though. 129 */ 130 for (i = 0; (i < *nfuncs) && (nl[i].name != NULL); i++) 131 ; 132 133 ndx = i; 134 for (i = ndx + 1; i < *nfuncs; i++) { 135 if (nl[i].name) { 136 nl[ndx] = nl[i]; 137 ndx++; 138 } 139 } 140 141 *nfuncs = ndx; 142 } 143 144 int 145 cmp_by_address(const void *arg1, const void *arg2) 146 { 147 nltype *a = (nltype *)arg1; 148 nltype *b = (nltype *)arg2; 149 150 if (a->value < b->value) 151 return (-1); 152 else if (a->value > b->value) 153 return (1); 154 else 155 return (0); 156 } 157 158 static int 159 is_function(Elf *elf, GElf_Sym *sym) 160 { 161 Elf_Scn *scn; 162 GElf_Shdr shdr; 163 164 /* 165 * With dynamic linking, it is possible that certain undefined 166 * symbols exist in the objects. The actual definition will be 167 * found elsewhere, so we'll just skip it for this object. 168 */ 169 if (sym->st_shndx == SHN_UNDEF) 170 return (0); 171 172 if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) { 173 if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) 174 return (1); 175 176 if (GELF_ST_BIND(sym->st_info) == STB_WEAK) 177 return (1); 178 179 if (gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL) 180 return (1); 181 } 182 183 /* 184 * It's not a function; determine if it's in an executable section. 185 */ 186 if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE) 187 return (0); 188 189 /* 190 * If it isn't global, and it isn't weak, and it isn't 191 * a 'local with the gflag set', then get out. 192 */ 193 if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL && 194 GELF_ST_BIND(sym->st_info) != STB_WEAK && 195 !(gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)) 196 return (0); 197 198 if (sym->st_shndx >= SHN_LORESERVE) 199 return (0); 200 201 scn = elf_getscn(elf, sym->st_shndx); 202 (void) gelf_getshdr(scn, &shdr); 203 204 if (!(shdr.sh_flags & SHF_EXECINSTR)) 205 return (0); 206 207 return (1); 208 } 209 210 static void 211 fetch_symtab(Elf *elf, char *filename, mod_info_t *module) 212 { 213 Elf_Scn *scn = NULL, *sym_pri = NULL, *sym_aux = NULL; 214 GElf_Word strndx = 0; 215 size_t i, nsyms, nfuncs; 216 GElf_Xword nsyms_pri, nsyms_aux = 0; 217 Elf_Data *symdata_pri, *symdata_aux; 218 nltype *nl, *npe; 219 int symtab_found = 0; 220 221 222 /* 223 * Scan the section headers looking for a symbol table. Our 224 * preference is to use .symtab, because it contains the full 225 * set of symbols. If we find it, we stop looking immediately 226 * and use it. In the absence of a .symtab section, we are 227 * willing to use the dynamic symbol table (.dynsym), possibly 228 * augmented by the .SUNW_ldynsym, which contains local symbols. 229 */ 230 while ((symtab_found == 0) && ((scn = elf_nextscn(elf, scn)) != NULL)) { 231 232 GElf_Shdr shdr; 233 234 if (gelf_getshdr(scn, &shdr) == NULL) 235 continue; 236 237 switch (shdr.sh_type) { 238 case SHT_SYMTAB: 239 nsyms_pri = shdr.sh_size / shdr.sh_entsize; 240 strndx = shdr.sh_link; 241 sym_pri = scn; 242 /* Throw away .SUNW_ldynsym. It is for .dynsym only */ 243 nsyms_aux = 0; 244 sym_aux = NULL; 245 /* We have found the best symbol table. Stop looking */ 246 symtab_found = 1; 247 break; 248 249 case SHT_DYNSYM: 250 /* We will use .dynsym if no .symtab is found */ 251 nsyms_pri = shdr.sh_size / shdr.sh_entsize; 252 strndx = shdr.sh_link; 253 sym_pri = scn; 254 break; 255 256 case SHT_SUNW_LDYNSYM: 257 /* Auxiliary table, used with .dynsym */ 258 nsyms_aux = shdr.sh_size / shdr.sh_entsize; 259 sym_aux = scn; 260 break; 261 } 262 } 263 264 if (sym_pri == NULL || strndx == 0) { 265 (void) fprintf(stderr, "%s: missing symbol table in %s\n", 266 cmdname, filename); 267 exit(ERR_ELF); 268 } 269 270 nsyms = (size_t)(nsyms_pri + nsyms_aux); 271 if ((nsyms_pri + nsyms_aux) != (GElf_Xword)nsyms) { 272 (void) fprintf(stderr, 273 "%s: can't handle more than 2^32 symbols", cmdname); 274 exit(ERR_INPUT); 275 } 276 277 if ((symdata_pri = elf_getdata(sym_pri, NULL)) == NULL) { 278 (void) fprintf(stderr, "%s: can't read symbol data from %s\n", 279 cmdname, filename); 280 exit(ERR_ELF); 281 } 282 283 if ((sym_aux != NULL) && 284 ((symdata_aux = elf_getdata(sym_aux, NULL)) == NULL)) { 285 (void) fprintf(stderr, 286 "%s: can't read .SUNW_ldynsym symbol data from %s\n", 287 cmdname, filename); 288 exit(ERR_ELF); 289 } 290 291 if ((npe = nl = (nltype *) calloc(nsyms, sizeof (nltype))) == NULL) { 292 (void) fprintf(stderr, "%s: can't alloc %x bytes for symbols\n", 293 cmdname, nsyms * sizeof (nltype)); 294 exit(ERR_ELF); 295 } 296 297 /* 298 * Now we need to cruise through the symbol table eliminating 299 * all non-functions from consideration, and making strings 300 * real. 301 */ 302 nfuncs = 0; 303 304 for (i = 1; i < nsyms; i++) { 305 GElf_Sym gsym; 306 char *name; 307 308 /* 309 * Look up the symbol. In the case where we have a 310 * .SUNW_ldynsym/.dynsym pair, we treat them as a single 311 * logical table, with the data in .SUNW_ldynsym coming 312 * before the data in .dynsym. 313 */ 314 if (i >= nsyms_aux) 315 (void) gelf_getsym(symdata_pri, i - nsyms_aux, &gsym); 316 else 317 (void) gelf_getsym(symdata_aux, i, &gsym); 318 319 name = elf_strptr(elf, strndx, gsym.st_name); 320 321 /* 322 * We're interested in this symbol if it's a function 323 */ 324 if (is_function(elf, &gsym)) { 325 326 npe->name = name; 327 npe->value = gsym.st_value; 328 npe->size = gsym.st_size; 329 npe->info = gsym.st_info; 330 331 npe++; 332 nfuncs++; 333 } 334 335 if (strcmp(name, PRF_END) == 0) 336 module->data_end = gsym.st_value; 337 } 338 339 if (npe == nl) { 340 (void) fprintf(stderr, "%s: no valid functions in %s\n", 341 cmdname, filename); 342 exit(ERR_INPUT); 343 } 344 345 /* 346 * And finally, sort the symbols by increasing address 347 * and remove the duplicates. 348 */ 349 qsort(nl, nfuncs, sizeof (nltype), cmp_by_address); 350 rm_dups(nl, &nfuncs); 351 352 module->nl = nl; 353 module->nfuncs = nfuncs; 354 } 355 356 static GElf_Addr 357 get_txtorigin(Elf *elf, char *filename) 358 { 359 GElf_Ehdr ehdr; 360 GElf_Phdr phdr; 361 GElf_Half ndx; 362 GElf_Addr txt_origin = 0; 363 bool first_load_seg = TRUE; 364 365 if (gelf_getehdr(elf, &ehdr) == NULL) { 366 (void) fprintf(stderr, "%s: can't read ELF header of %s\n", 367 cmdname, filename); 368 exit(ERR_ELF); 369 } 370 371 for (ndx = 0; ndx < ehdr.e_phnum; ndx++) { 372 if (gelf_getphdr(elf, ndx, &phdr) == NULL) 373 continue; 374 375 if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) { 376 if (first_load_seg || phdr.p_vaddr < txt_origin) 377 txt_origin = phdr.p_vaddr; 378 379 if (first_load_seg) 380 first_load_seg = FALSE; 381 } 382 } 383 384 return (txt_origin); 385 } 386 387 void 388 get_syms(char *filename, mod_info_t *mi) 389 { 390 int fd; 391 Elf *elf; 392 393 if ((fd = open(filename, O_RDONLY)) == -1) { 394 perror(filename); 395 exit(ERR_SYSCALL); 396 } 397 398 if (elf_version(EV_CURRENT) == EV_NONE) { 399 (void) fprintf(stderr, "%s: libelf out of date\n", cmdname); 400 exit(ERR_ELF); 401 } 402 403 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 404 (void) fprintf(stderr, "%s: elf_begin failed\n", cmdname); 405 exit(ERR_ELF); 406 } 407 408 if (gelf_getclass(elf) != ELFCLASS64) { 409 (void) fprintf(stderr, "%s: unsupported mon.out format for " 410 "this class of object\n", cmdname); 411 exit(ERR_ELF); 412 } 413 414 mi->txt_origin = get_txtorigin(elf, filename); 415 416 fetch_symtab(elf, filename, mi); 417 } 418