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 2007 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 "gprof.h"
30 #include <stdlib.h>
31 #include <sys/file.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sysexits.h>
36 #include <libelf.h>
37 #include "gelf.h"
38
39 #ifdef DEBUG
40 static void debug_dup_del(nltype *, nltype *);
41
42 #define DPRINTF(msg, file) if (debug & ELFDEBUG) \
43 (void) printf(msg, file);
44
45 #define PRINTF(msg) if (debug & ELFDEBUG) \
46 (void) printf(msg);
47
48 #define DEBUG_DUP_DEL(keeper, louser) if (debug & ELFDEBUG) \
49 debug_dup_del(keeper, louser);
50
51 #else
52 #define DPRINTF(msg, file)
53 #define PRINTF(msg)
54 #define DEBUG_DUP_DEL(keeper, louser)
55 #endif
56
57 size_t textbegin, textsize;
58
59 /* Prototype definitions first */
60
61 static void process(char *filename, int fd);
62 static void get_symtab(Elf *elf, mod_info_t *module);
63 static void get_textseg(Elf *elf, int fd);
64 static void save_aout_info(char *);
65
66 static void
fatal_error(char * error)67 fatal_error(char *error)
68 {
69 (void) fprintf(stderr,
70 "Fatal ELF error: %s (%s)\n", error, elf_errmsg(-1));
71 exit(EX_SOFTWARE);
72 }
73
74 bool
is_shared_obj(char * name)75 is_shared_obj(char *name)
76 {
77 int fd;
78 Elf *elf;
79 GElf_Ehdr ehdr;
80
81 if ((fd = open(name, O_RDONLY)) == -1) {
82 (void) fprintf(stderr, "%s: can't open `%s'\n", whoami, name);
83 exit(EX_NOINPUT);
84 }
85
86 if (elf_version(EV_CURRENT) == EV_NONE)
87 fatal_error("libelf is out of date");
88
89 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
90 fatal_error("can't read as ELF file");
91
92 if (gelf_getehdr(elf, &ehdr) == NULL)
93 fatal_error("can't read ehdr");
94
95 (void) elf_end(elf);
96 (void) close(fd);
97
98 if (ehdr.e_type == ET_DYN)
99 return (TRUE);
100 else
101 return (FALSE);
102 }
103
104 static void
save_aout_info(char * aoutname)105 save_aout_info(char *aoutname)
106 {
107 struct stat buf;
108 extern fl_info_t aout_info;
109
110 if (stat(aoutname, &buf) == -1) {
111 (void) fprintf(stderr, "%s: can't get info on `%s'\n",
112 whoami, aoutname);
113 exit(EX_NOINPUT);
114 }
115
116 aout_info.dev = buf.st_dev;
117 aout_info.ino = buf.st_ino;
118 aout_info.mtime = buf.st_mtime;
119 aout_info.size = buf.st_size;
120 }
121
122 void
getnfile(char * aoutname)123 getnfile(char *aoutname)
124 {
125 int fd;
126
127 DPRINTF(" Attempting to open %s \n", aoutname);
128 if ((fd = open((aoutname), O_RDONLY)) == -1) {
129 (void) fprintf(stderr, "%s: can't open `%s'\n",
130 whoami, aoutname);
131 exit(EX_NOINPUT);
132 }
133 process(aoutname, fd);
134 save_aout_info(aoutname);
135
136 (void) close(fd);
137 }
138
139 static GElf_Addr
get_txtorigin(Elf * elf)140 get_txtorigin(Elf *elf)
141 {
142 GElf_Ehdr ehdr;
143 GElf_Phdr phdr;
144 GElf_Half ndx;
145 GElf_Addr txt_origin = 0;
146 bool first_load_seg = TRUE;
147
148 if (gelf_getehdr(elf, &ehdr) == NULL)
149 fatal_error("can't read ehdr");
150
151 for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
152 if (gelf_getphdr(elf, ndx, &phdr) == NULL)
153 continue;
154
155 if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
156 if (first_load_seg || phdr.p_vaddr < txt_origin)
157 txt_origin = phdr.p_vaddr;
158
159 if (first_load_seg)
160 first_load_seg = FALSE;
161 }
162 }
163
164 return (txt_origin);
165 }
166
167 void
process_namelist(mod_info_t * module)168 process_namelist(mod_info_t *module)
169 {
170 int fd;
171 Elf *elf;
172
173 if ((fd = open(module->name, O_RDONLY)) == -1) {
174 (void) fprintf(stderr, "%s: can't read %s\n",
175 whoami, module->name);
176 (void) fprintf(stderr, "Exiting due to error(s)...\n");
177 exit(EX_NOINPUT);
178 }
179
180 /*
181 * libelf's version already verified in processing a.out,
182 * so directly do elf_begin()
183 */
184 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
185 fatal_error("can't read as ELF file");
186
187 module->next = NULL;
188 module->txt_origin = get_txtorigin(elf);
189 get_symtab(elf, module);
190 module->active = TRUE;
191 }
192
193 /*
194 * Get the ELF header and, if it exists, call get_symtab()
195 * to begin processing of the file; otherwise, return from
196 * processing the file with a warning.
197 */
198 static void
process(char * filename,int fd)199 process(char *filename, int fd)
200 {
201 Elf *elf;
202 extern bool cflag;
203 extern bool Bflag;
204
205 if (elf_version(EV_CURRENT) == EV_NONE)
206 fatal_error("libelf is out of date");
207
208 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
209 fatal_error("can't read as ELF file");
210
211 if (gelf_getclass(elf) == ELFCLASS64)
212 Bflag = TRUE;
213
214 /*
215 * Initialize active modules list. Note that we set the end
216 * address while reading the symbol table, in get_symtab
217 */
218 modules.id = 1;
219 modules.next = NULL;
220 modules.txt_origin = get_txtorigin(elf);
221 modules.load_base = modules.txt_origin;
222 if ((modules.name = malloc(strlen(filename) + 1)) == NULL) {
223 (void) fprintf(stderr, "%s: can't malloc %d bytes",
224 whoami, strlen(filename) + 1);
225 exit(EX_UNAVAILABLE);
226 }
227 (void) strcpy(modules.name, filename);
228
229 get_symtab(elf, &modules);
230
231 modules.load_end = modules.data_end;
232 modules.active = TRUE;
233 n_modules = 1;
234
235 if (cflag)
236 get_textseg(elf, fd);
237 }
238
239 static void
get_textseg(Elf * elf,int fd)240 get_textseg(Elf *elf, int fd)
241 {
242 GElf_Ehdr ehdr;
243 GElf_Phdr phdr;
244 GElf_Half i;
245
246 if (gelf_getehdr(elf, &ehdr) == NULL)
247 fatal_error("can't read ehdr");
248
249 for (i = 0; i < ehdr.e_phnum; i++) {
250
251 if (gelf_getphdr(elf, i, &phdr) == NULL)
252 continue;
253
254 if (!(phdr.p_flags & PF_W) && (phdr.p_filesz > textsize)) {
255 size_t chk;
256
257 /*
258 * We could have multiple loadable text segments;
259 * keep the largest we find.
260 */
261 if (textspace)
262 free(textspace);
263
264 /*
265 * gprof is a 32-bit program; if this text segment
266 * has a > 32-bit offset or length, it's too big.
267 */
268 chk = (size_t)phdr.p_vaddr + (size_t)phdr.p_filesz;
269 if (phdr.p_vaddr + phdr.p_filesz != (GElf_Xword)chk)
270 fatal_error("text segment too large for -c");
271
272 textbegin = (size_t)phdr.p_vaddr;
273 textsize = (size_t)phdr.p_filesz;
274
275 textspace = malloc(textsize);
276
277 if (lseek(fd, (off_t)phdr.p_offset, SEEK_SET) !=
278 (off_t)phdr.p_offset)
279 fatal_error("cannot seek to text section");
280
281 if (read(fd, textspace, textsize) != textsize)
282 fatal_error("cannot read text");
283 }
284 }
285
286 if (textsize == 0)
287 fatal_error("can't find text segment");
288 }
289
290 #ifdef DEBUG
291 static void
debug_dup_del(nltype * keeper,nltype * louser)292 debug_dup_del(nltype * keeper, nltype * louser)
293 {
294 (void) printf("remove_dup_syms: discarding sym %s over sym %s\n",
295 louser->name, keeper->name);
296 }
297 #endif /* DEBUG */
298
299 static void
remove_dup_syms(nltype * nl,sztype * sym_count)300 remove_dup_syms(nltype *nl, sztype *sym_count)
301 {
302 int i;
303 int index;
304 int nextsym;
305
306 nltype * orig_list;
307 if ((orig_list = malloc(sizeof (nltype) * *sym_count)) == NULL) {
308 (void) fprintf(stderr,
309 "gprof: remove_dup_syms: malloc failed\n");
310 (void) fprintf(stderr, "Exiting due to error(s)...\n");
311 exit(EX_UNAVAILABLE);
312 }
313 (void) memcpy(orig_list, nl, sizeof (nltype) * *sym_count);
314
315 for (i = 0, index = 0, nextsym = 1; nextsym < *sym_count; nextsym++) {
316 int i_type;
317 int n_bind;
318 int n_type;
319
320 /*
321 * If orig_list[nextsym] points to a new symvalue, then we
322 * will copy our keeper and move on to the next symbol.
323 */
324 if ((orig_list + i)->value < (orig_list + nextsym)->value) {
325 *(nl + index++) = *(orig_list +i);
326 i = nextsym;
327 continue;
328 }
329
330 /*
331 * If these two symbols have the same info, then we
332 * keep the first and keep checking for dups.
333 */
334 if ((orig_list + i)->syminfo ==
335 (orig_list + nextsym)->syminfo) {
336 DEBUG_DUP_DEL(orig_list + i, orig_list + nextsym);
337 continue;
338 }
339 n_bind = ELF32_ST_BIND((orig_list + nextsym)->syminfo);
340 i_type = ELF32_ST_TYPE((orig_list + i)->syminfo);
341 n_type = ELF32_ST_TYPE((orig_list + nextsym)->syminfo);
342
343 /*
344 * If they have the same type we take the stronger
345 * bound function.
346 */
347 if (i_type == n_type) {
348 if (n_bind == STB_WEAK) {
349 DEBUG_DUP_DEL((orig_list + i),
350 (orig_list + nextsym));
351 continue;
352 }
353 DEBUG_DUP_DEL((orig_list + nextsym),
354 (orig_list + i));
355 i = nextsym;
356 continue;
357 }
358
359 /*
360 * If the first symbol isn't of type NOTYPE then it must
361 * be the keeper.
362 */
363 if (i_type != STT_NOTYPE) {
364 DEBUG_DUP_DEL((orig_list + i),
365 (orig_list + nextsym));
366 continue;
367 }
368
369 /*
370 * Throw away the first one and take the new
371 * symbol
372 */
373 DEBUG_DUP_DEL((orig_list + nextsym), (orig_list + i));
374 i = nextsym;
375 }
376
377 if ((orig_list + i)->value > (nl + index - 1)->value)
378 *(nl + index++) = *(orig_list +i);
379
380 *sym_count = index;
381 }
382
383 /*
384 * compare either by name or by value for sorting.
385 * This is the comparison function called by qsort to
386 * sort the symbols either by name or value when requested.
387 */
388 static int
compare(const void * arg1,const void * arg2)389 compare(const void *arg1, const void *arg2)
390 {
391 nltype *a = (nltype *)arg1;
392 nltype *b = (nltype *)arg2;
393
394 if (a->value > b->value)
395 return (1);
396 else
397 return ((a->value == b->value) - 1);
398 }
399
400 static int
is_function(Elf * elf,GElf_Sym * sym)401 is_function(Elf *elf, GElf_Sym *sym)
402 {
403 Elf_Scn *scn;
404 GElf_Shdr shdr;
405
406 /*
407 * With shared objects, it is possible we come across a function
408 * that's global, but is undefined. The definition is probably
409 * elsewhere, so we'll have to skip it as far as this object is
410 * concerned.
411 */
412 if (sym->st_shndx == SHN_UNDEF)
413 return (0);
414
415 if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {
416 if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL)
417 return (1);
418
419 if (GELF_ST_BIND(sym->st_info) == STB_WEAK)
420 return (1);
421
422 if (!aflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)
423 return (1);
424 }
425
426 /*
427 * It's not a function; determine if it's in an executable section.
428 */
429 if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
430 return (0);
431
432 /*
433 * If it isn't global, and it isn't weak, and it either isn't
434 * local or the "all flag" isn't set, then get out.
435 */
436 if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
437 GELF_ST_BIND(sym->st_info) != STB_WEAK &&
438 (GELF_ST_BIND(sym->st_info) != STB_LOCAL || aflag))
439 return (0);
440
441 if (sym->st_shndx >= SHN_LORESERVE)
442 return (0);
443
444 scn = elf_getscn(elf, sym->st_shndx);
445 (void) gelf_getshdr(scn, &shdr);
446
447 if (!(shdr.sh_flags & SHF_EXECINSTR))
448 return (0);
449
450 return (1);
451 }
452
453 static void
get_symtab(Elf * elf,mod_info_t * module)454 get_symtab(Elf *elf, mod_info_t *module)
455 {
456 Elf_Scn *scn = NULL, *sym_pri = NULL, *sym_aux = NULL;
457 GElf_Word strndx = 0;
458 sztype nsyms, i;
459 Elf_Data *symdata_pri;
460 Elf_Data *symdata_aux;
461 GElf_Xword nsyms_pri, nsyms_aux = 0;
462 nltype *etext = NULL;
463 nltype *l_nl, *l_npe;
464 sztype l_nname;
465 extern sztype total_names;
466 int symtab_found = 0;
467
468
469 /*
470 * Scan the section headers looking for a symbol table. Our
471 * preference is to use .symtab, because it contains the full
472 * set of symbols. If we find it, we stop looking immediately
473 * and use it. In the absence of a .symtab section, we are
474 * willing to use the dynamic symbol table (.dynsym), possibly
475 * augmented by the .SUNW_ldynsym, which contains local symbols.
476 */
477 while ((symtab_found == 0) && ((scn = elf_nextscn(elf, scn)) != NULL)) {
478 GElf_Shdr shdr;
479
480 if (gelf_getshdr(scn, &shdr) == NULL)
481 continue;
482
483 switch (shdr.sh_type) {
484 case SHT_SYMTAB:
485 nsyms_pri = shdr.sh_size / shdr.sh_entsize;
486 strndx = shdr.sh_link;
487 sym_pri = scn;
488 /* Throw away .SUNW_ldynsym. It is for .dynsym only */
489 nsyms_aux = 0;
490 sym_aux = NULL;
491 /* We have found the best symbol table. Stop looking */
492 symtab_found = 1;
493 break;
494
495 case SHT_DYNSYM:
496 /* We will use .dynsym if no .symtab is found */
497 nsyms_pri = shdr.sh_size / shdr.sh_entsize;
498 strndx = shdr.sh_link;
499 sym_pri = scn;
500 break;
501
502 case SHT_SUNW_LDYNSYM:
503 /* Auxiliary table, used with .dynsym */
504 nsyms_aux = shdr.sh_size / shdr.sh_entsize;
505 sym_aux = scn;
506 break;
507 }
508 }
509
510 if (sym_pri == NULL || strndx == 0)
511 fatal_error("can't find symbol table.\n");
512
513 nsyms = (sztype)(nsyms_pri + nsyms_aux);
514 if ((nsyms_pri + nsyms_aux) != (GElf_Xword)nsyms)
515 fatal_error(
516 "32-bit gprof cannot handle more than 2^32 symbols");
517
518 if ((symdata_pri = elf_getdata(sym_pri, NULL)) == NULL)
519 fatal_error("can't read symbol data.\n");
520
521 if ((sym_aux != NULL) &&
522 ((symdata_aux = elf_getdata(sym_aux, NULL)) == NULL))
523 fatal_error("can't read .SUNW_ldynsym symbol data.\n");
524
525 if ((l_nl = l_npe = (nltype *)calloc(nsyms + PRF_SYMCNT,
526 sizeof (nltype))) == NULL)
527 fatal_error("cannot allocate symbol data.\n");
528
529 /*
530 * Now we need to cruise through the symbol table eliminating
531 * all non-functions from consideration, and making strings
532 * real.
533 */
534 l_nname = 0;
535
536 for (i = 1; i < nsyms; i++) {
537 GElf_Sym gsym;
538 char *name;
539
540 /*
541 * Look up the symbol. In the case where we have a
542 * .SUNW_ldynsym/.dynsym pair, we treat them as a single
543 * logical table, with the data from .SUNW_ldynsym coming
544 * before the data in .dynsym.
545 */
546 if (i >= nsyms_aux)
547 (void) gelf_getsym(symdata_pri, i - nsyms_aux, &gsym);
548 else
549 (void) gelf_getsym(symdata_aux, i, &gsym);
550
551 name = elf_strptr(elf, strndx, gsym.st_name);
552
553 /*
554 * We're interested in this symbol if it's a function or
555 * if it's the symbol "_etext"
556 */
557 if (is_function(elf, &gsym) || strcmp(name, PRF_ETEXT) == 0) {
558
559 l_npe->name = name;
560 l_npe->value = gsym.st_value;
561 l_npe->sz = gsym.st_size;
562 l_npe->syminfo = gsym.st_info;
563 l_npe->module = module;
564
565 if (strcmp(name, PRF_ETEXT) == 0)
566 etext = l_npe;
567
568 if (lflag == TRUE &&
569 GELF_ST_BIND(gsym.st_info) == STB_LOCAL) {
570 /*
571 * If the "locals only" flag is on, then
572 * we add the local symbols to the
573 * exclusion lists.
574 */
575 addlist(Elist, name);
576 addlist(elist, name);
577 }
578 DPRINTF("Index %lld:", l_nname);
579 DPRINTF("\tValue: 0x%llx\t", l_npe->value);
580 DPRINTF("Name: %s \n", l_npe->name);
581 l_npe++;
582 l_nname++;
583 }
584
585 if (strcmp(name, PRF_END) == 0)
586 module->data_end = gsym.st_value;
587 }
588
589 if (l_npe == l_nl)
590 fatal_error("no valid functions found");
591
592 /*
593 * Finally, we need to construct some dummy entries.
594 */
595 if (etext) {
596 l_npe->name = PRF_EXTSYM;
597 l_npe->value = etext->value + 1;
598 l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
599 l_npe->module = module;
600 l_npe++;
601 l_nname++;
602 }
603
604 l_npe->name = PRF_MEMTERM;
605 l_npe->value = (pctype)-1;
606 l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
607 l_npe->module = module;
608 l_npe++;
609 l_nname++;
610
611 /*
612 * We're almost done; all we need to do is sort the symbols
613 * and then remove the duplicates.
614 */
615 qsort(l_nl, (size_t)l_nname, sizeof (nltype), compare);
616 remove_dup_syms(l_nl, &l_nname);
617
618 module->nl = l_nl;
619 module->npe = l_npe;
620 module->nname = l_nname;
621
622 total_names += l_nname;
623 }
624