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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26#pragma ident "@(#)input.c 1.9 06/05/03 SMI" 27 28/* 29 * Routines for retrieving CTF data from a .SUNW_ctf ELF section 30 */ 31 32#include <stdio.h> 33#include <stdlib.h> 34#include <fcntl.h> 35#include <unistd.h> 36#include <gelf.h> 37#include <strings.h> 38#include <sys/types.h> 39 40#include "ctftools.h" 41#include "memory.h" 42#include "symbol.h" 43 44typedef int read_cb_f(tdata_t *, char *, void *); 45 46/* 47 * Return the source types that the object was generated from. 48 */ 49source_types_t 50built_source_types(Elf *elf, char const *file) 51{ 52 source_types_t types = SOURCE_NONE; 53 symit_data_t *si; 54 55 if ((si = symit_new(elf, file)) == NULL) 56 return (SOURCE_NONE); 57 58 while (symit_next(si, STT_FILE) != NULL) { 59 char *name = symit_name(si); 60 size_t len = strlen(name); 61 if (len < 2 || name[len - 2] != '.') { 62 types |= SOURCE_UNKNOWN; 63 continue; 64 } 65 66 switch (name[len - 1]) { 67 case 'c': 68 types |= SOURCE_C; 69 break; 70 case 'h': 71 /* ignore */ 72 break; 73 case 's': 74 types |= SOURCE_S; 75 break; 76 default: 77 types |= SOURCE_UNKNOWN; 78 } 79 } 80 81 symit_free(si); 82 return (types); 83} 84 85static int 86read_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg, 87 int require_ctf) 88{ 89 Elf_Scn *ctfscn; 90 Elf_Data *ctfdata; 91 symit_data_t *si = NULL; 92 int ctfscnidx; 93 tdata_t *td; 94 95 if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) { 96 if (require_ctf && 97 (built_source_types(elf, file) & SOURCE_C)) { 98 terminate("Input file %s was partially built from " 99 "C sources, but no CTF data was present\n", file); 100 } 101 return (0); 102 } 103 104 if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL || 105 (ctfdata = elf_getdata(ctfscn, NULL)) == NULL) 106 elfterminate(file, "Cannot read CTF section"); 107 108 /* Reconstruction of type tree */ 109 if ((si = symit_new(elf, file)) == NULL) { 110 warning("%s has no symbol table - skipping", file); 111 return (0); 112 } 113 114 td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label); 115 tdata_build_hashes(td); 116 117 symit_free(si); 118 119 if (td != NULL) { 120 if (func(td, file, arg) < 0) 121 return (-1); 122 else 123 return (1); 124 } 125 return (0); 126} 127 128static int 129read_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func, 130 void *arg, int require_ctf) 131{ 132 Elf *melf; 133 Elf_Cmd cmd = ELF_C_READ; 134 Elf_Arhdr *arh; 135 int secnum = 1, found = 0; 136 137 while ((melf = elf_begin(fd, cmd, elf)) != NULL) { 138 int rc = 0; 139 140 if ((arh = elf_getarhdr(melf)) == NULL) { 141 elfterminate(file, "Can't get archive header for " 142 "member %d", secnum); 143 } 144 145 /* skip special sections - their names begin with "/" */ 146 if (*arh->ar_name != '/') { 147 size_t memlen = strlen(file) + 1 + 148 strlen(arh->ar_name) + 1 + 1; 149 char *memname = xmalloc(memlen); 150 151 snprintf(memname, memlen, "%s(%s)", file, arh->ar_name); 152 153 switch (elf_kind(melf)) { 154 case ELF_K_AR: 155 rc = read_archive(fd, melf, memname, label, 156 func, arg, require_ctf); 157 break; 158 case ELF_K_ELF: 159#if defined(__APPLE__) 160 case ELF_K_MACHO: /* Underlying file is Mach-o */ 161#endif /* __APPLE__ */ 162 rc = read_file(melf, memname, label, 163 func, arg, require_ctf); 164 break; 165 default: 166 terminate("%s: Unknown elf kind %d\n", 167 memname, elf_kind(melf)); 168 } 169 170 free(memname); 171 } 172 173 cmd = elf_next(melf); 174 (void) elf_end(melf); 175 secnum++; 176 177 if (rc < 0) 178 return (rc); 179 else 180 found += rc; 181 } 182 183 return (found); 184} 185 186static int 187read_ctf_common(char *file, char *label, read_cb_f *func, void *arg, 188 int require_ctf) 189{ 190 Elf *elf; 191 int found = 0; 192 int fd; 193 194 debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE")); 195 196 (void) elf_version(EV_CURRENT); 197 198 if ((fd = open(file, O_RDONLY)) < 0) 199 terminate("%s: Cannot open for reading", file); 200 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) 201 elfterminate(file, "Cannot read"); 202 203 switch (elf_kind(elf)) { 204 case ELF_K_AR: 205 found = read_archive(fd, elf, file, label, 206 func, arg, require_ctf); 207 break; 208 209 case ELF_K_ELF: 210#if defined(__APPLE__) 211 case ELF_K_MACHO: /* Underlying file is Mach-o */ 212#endif /* __APPLE__ */ 213 found = read_file(elf, file, label, 214 func, arg, require_ctf); 215 break; 216 217 default: 218 terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf)); 219 } 220 221 (void) elf_end(elf); 222 (void) close(fd); 223 224 return (found); 225} 226 227/*ARGSUSED*/ 228int 229read_ctf_save_cb(tdata_t *td, char *name, void *retp) 230{ 231 tdata_t **tdp = retp; 232 233 *tdp = td; 234 235 return (1); 236} 237 238int 239read_ctf(char **files, int n, char *label, read_cb_f *func, void *private, 240 int require_ctf) 241{ 242 int found; 243 int i, rc; 244 245 for (i = 0, found = 0; i < n; i++) { 246 if ((rc = read_ctf_common(files[i], label, func, 247 private, require_ctf)) < 0) 248 return (rc); 249 found += rc; 250 } 251 252 return (found); 253} 254 255static int 256count_archive(int fd, Elf *elf, char *file) 257{ 258 Elf *melf; 259 Elf_Cmd cmd = ELF_C_READ; 260 Elf_Arhdr *arh; 261 int nfiles = 0, err = 0; 262 263 while ((melf = elf_begin(fd, cmd, elf)) != NULL) { 264 if ((arh = elf_getarhdr(melf)) == NULL) { 265 warning("Can't process input archive %s\n", 266 file); 267 err++; 268 } 269 270 if (*arh->ar_name != '/') 271 nfiles++; 272 273 cmd = elf_next(melf); 274 (void) elf_end(melf); 275 } 276 277 if (err > 0) 278 return (-1); 279 280 return (nfiles); 281} 282 283int 284count_files(char **files, int n) 285{ 286 int nfiles = 0, err = 0; 287 Elf *elf; 288 int fd, rc, i; 289 290 (void) elf_version(EV_CURRENT); 291 292 for (i = 0; i < n; i++) { 293 char *file = files[i]; 294 295 if ((fd = open(file, O_RDONLY)) < 0) { 296 warning("Can't read input file %s", file); 297 err++; 298 continue; 299 } 300 301 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 302 warning("Can't open input file %s: %s\n", file, 303 elf_errmsg(-1)); 304 err++; 305 (void) close(fd); 306 continue; 307 } 308 309 switch (elf_kind(elf)) { 310 case ELF_K_AR: 311 if ((rc = count_archive(fd, elf, file)) < 0) 312 err++; 313 else 314 nfiles += rc; 315 break; 316 case ELF_K_ELF: 317#if defined(__APPLE__) 318 case ELF_K_MACHO: /* Underlying file is Mach-o */ 319#endif /* __APPLE__ */ 320 nfiles++; 321 break; 322 default: 323 warning("Input file %s is corrupt\n", file); 324 err++; 325 } 326 327 (void) elf_end(elf); 328 (void) close(fd); 329 } 330 331 if (err > 0) 332 return (-1); 333 334 debug(2, "Found %d files in %d input files\n", nfiles, n); 335 336 return (nfiles); 337} 338 339struct symit_data { 340 GElf_Shdr si_shdr; 341 Elf_Data *si_symd; 342 Elf_Data *si_strd; 343 GElf_Sym si_cursym; 344 char *si_curname; 345 char *si_curfile; 346 int si_nument; 347 int si_next; 348}; 349 350symit_data_t * 351symit_new(Elf *elf, const char *file) 352{ 353 symit_data_t *si; 354 Elf_Scn *scn; 355 int symtabidx; 356 357 if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0) 358 return (NULL); 359 360 si = xcalloc(sizeof (symit_data_t)); 361 362 if ((scn = elf_getscn(elf, symtabidx)) == NULL || 363 gelf_getshdr(scn, &si->si_shdr) == NULL || 364 (si->si_symd = elf_getdata(scn, NULL)) == NULL) 365 elfterminate(file, "Cannot read .symtab"); 366 367#if !defined(__APPLE__) 368 if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL || 369 (si->si_strd = elf_getdata(scn, NULL)) == NULL) 370 elfterminate(file, "Cannot read strings for .symtab"); 371#else 372 if (SHN_MACHO != si->si_shdr.sh_link && SHN_MACHO_64 != si->si_shdr.sh_link) { 373 if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL || 374 (si->si_strd = elf_getdata(scn, NULL)) == NULL) 375 elfterminate(file, "Cannot read strings for .symtab"); 376 } else { 377 /* Underlying file is Mach-o */ 378 int dir_idx; 379 380 if ((dir_idx = findelfsecidx(elf, file, ".dir_str_table")) < 0 || 381 (scn = elf_getscn(elf, dir_idx)) == NULL || 382 (si->si_strd = elf_getdata(scn, NULL)) == NULL) 383 elfterminate(file, "Cannot read strings for .dir_str_table"); 384 } 385#endif /* __APPLE__ */ 386 si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize; 387 388 return (si); 389} 390 391void 392symit_free(symit_data_t *si) 393{ 394 free(si); 395} 396 397void 398symit_reset(symit_data_t *si) 399{ 400 si->si_next = 0; 401} 402 403char * 404symit_curfile(symit_data_t *si) 405{ 406 return (si->si_curfile); 407} 408 409#if defined(__APPLE__) 410#include <mach-o/loader.h> 411#include <mach-o/nlist.h> 412 413static GElf_Sym * 414gelf_getsym_macho(Elf_Data * data, int ndx, GElf_Sym * sym, const char *base) 415{ 416 const struct nlist *nsym = (const struct nlist *)(data->d_buf); 417 const char *name; 418 char *tmp; 419 420 nsym += ndx; 421 name = base + nsym->n_un.n_strx; 422 423 if (0 == nsym->n_un.n_strx) // iff a null, "", name. 424 name = "null name"; // return NULL; 425 426 if ('_' == name[0]) 427 name++; // Lop off omnipresent underscore to match DWARF convention 428 429 sym->st_name = (GElf_Sxword)(name - base); 430 sym->st_value = nsym->n_value; 431 sym->st_size = 0; 432 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_NOTYPE)); 433 sym->st_other = 0; 434 sym->st_shndx = SHN_MACHO; /* Mark underlying file as Mach-o */ 435 436 if (nsym->n_type & N_STAB) { /* Detect C++ methods */ 437 438 switch(nsym->n_type) { 439 case N_FUN: 440 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_FUNC)); 441 break; 442 case N_GSYM: 443 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT)); 444 break; 445 default: 446 break; 447 } 448 449 } else if ((N_ABS | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) || 450 (N_SECT | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT))) { 451 452 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (nsym->n_desc)); 453 } else if ((N_UNDF | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) && 454 nsym->n_sect == NO_SECT) { 455 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT)); /* Common */ 456 } 457 458 return sym; 459} 460 461static GElf_Sym * 462gelf_getsym_macho_64(Elf_Data * data, int ndx, GElf_Sym * sym, const char *base) 463{ 464 const struct nlist_64 *nsym = (const struct nlist_64 *)(data->d_buf); 465 const char *name; 466 char *tmp; 467 468 nsym += ndx; 469 name = base + nsym->n_un.n_strx; 470 471 if (0 == nsym->n_un.n_strx) // iff a null, "", name. 472 name = "null name"; // return NULL; 473 474 if ('_' == name[0]) 475 name++; // Lop off omnipresent underscore to match DWARF convention 476 477 sym->st_name = (GElf_Sxword)(name - base); 478 sym->st_value = nsym->n_value; 479 sym->st_size = 0; 480 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_NOTYPE)); 481 sym->st_other = 0; 482 sym->st_shndx = SHN_MACHO_64; /* Mark underlying file as Mach-o 64 */ 483 484 if (nsym->n_type & N_STAB) { /* Detect C++ methods */ 485 486 switch(nsym->n_type) { 487 case N_FUN: 488 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_FUNC)); 489 break; 490 case N_GSYM: 491 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT)); 492 break; 493 default: 494 break; 495 } 496 497 } else if ((N_ABS | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) || 498 (N_SECT | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT))) { 499 500 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (nsym->n_desc)); 501 } else if ((N_UNDF | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) && 502 nsym->n_sect == NO_SECT) { 503 sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT)); /* Common */ 504 } 505 506 return sym; 507} 508 509#endif /* __APPLE__ */ 510 511GElf_Sym * 512symit_next(symit_data_t *si, int type) 513{ 514 GElf_Sym sym; 515 int check_sym = (type == STT_OBJECT || type == STT_FUNC); 516 517 for (; si->si_next < si->si_nument; si->si_next++) { 518#if !defined(__APPLE__) 519 gelf_getsym(si->si_symd, si->si_next, &si->si_cursym); 520 gelf_getsym(si->si_symd, si->si_next, &sym); 521#else 522 if (si->si_shdr.sh_link == SHN_MACHO) { /* Underlying file is Mach-o */ 523 gelf_getsym_macho(si->si_symd, si->si_next, &si->si_cursym, (const char *)(si->si_strd->d_buf)); 524 gelf_getsym_macho(si->si_symd, si->si_next, &sym, (const char *)(si->si_strd->d_buf)); 525 } else if (si->si_shdr.sh_link == SHN_MACHO_64) { /* Underlying file is Mach-o 64 */ 526 gelf_getsym_macho_64(si->si_symd, si->si_next, &si->si_cursym, (const char *)(si->si_strd->d_buf)); 527 gelf_getsym_macho_64(si->si_symd, si->si_next, &sym, (const char *)(si->si_strd->d_buf)); 528 } else { 529 gelf_getsym(si->si_symd, si->si_next, &si->si_cursym); 530 gelf_getsym(si->si_symd, si->si_next, &sym); 531 } 532#endif /* __APPLE__ */ 533 si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name; 534 535 if (GELF_ST_TYPE(sym.st_info) == STT_FILE) 536 si->si_curfile = si->si_curname; 537 538 if (GELF_ST_TYPE(sym.st_info) != type || 539 sym.st_shndx == SHN_UNDEF) 540 continue; 541 542 if (check_sym && ignore_symbol(&sym, si->si_curname)) 543 continue; 544 545 si->si_next++; 546 547 return (&si->si_cursym); 548 } 549 550 return (NULL); 551} 552 553char * 554symit_name(symit_data_t *si) 555{ 556 return (si->si_curname); 557} 558