xref: /illumos-gate/usr/src/cmd/sgs/gprof/common/readelf.c (revision 34bbc83afbf22a6f8e504cb99d76c97c017cb5f4)
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 #include	"gprof.h"
28 #include	<stdlib.h>
29 #include	<sys/file.h>
30 #include	<fcntl.h>
31 #include	<unistd.h>
32 #include	<string.h>
33 #include	<sysexits.h>
34 #include	<libelf.h>
35 #include	"gelf.h"
36 
37 #ifdef DEBUG
38 static void	debug_dup_del(nltype *, nltype *);
39 
40 #define	DPRINTF(msg, file)	if (debug & ELFDEBUG) \
41 					(void) printf(msg, file);
42 
43 #define	PRINTF(msg)		if (debug & ELFDEBUG) \
44 					(void) printf(msg);
45 
46 #define	DEBUG_DUP_DEL(keeper, louser)	if (debug & ELFDEBUG) \
47 						debug_dup_del(keeper, louser);
48 
49 #else
50 #define	DPRINTF(msg, file)
51 #define	PRINTF(msg)
52 #define	DEBUG_DUP_DEL(keeper, louser)
53 #endif
54 
55 size_t	textbegin, textsize;
56 
57 /* Prototype definitions first */
58 
59 static void	process(char *filename, int fd);
60 static void	get_symtab(Elf *elf, mod_info_t *module);
61 static void	get_textseg(Elf *elf, int fd);
62 static void	save_aout_info(char *);
63 
64 static void
65 fatal_error(char *error)
66 {
67 	(void) fprintf(stderr,
68 	    "Fatal ELF error: %s (%s)\n", error, elf_errmsg(-1));
69 	exit(EX_SOFTWARE);
70 }
71 
72 bool
73 is_shared_obj(char *name)
74 {
75 	int		fd;
76 	Elf		*elf;
77 	GElf_Ehdr	ehdr;
78 
79 	if ((fd = open(name, O_RDONLY)) == -1) {
80 		(void) fprintf(stderr, "%s: can't open `%s'\n", whoami, name);
81 		exit(EX_NOINPUT);
82 	}
83 
84 	if (elf_version(EV_CURRENT) == EV_NONE)
85 		fatal_error("libelf is out of date");
86 
87 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
88 		fatal_error("can't read as ELF file");
89 
90 	if (gelf_getehdr(elf, &ehdr) == NULL)
91 		fatal_error("can't read ehdr");
92 
93 	(void) elf_end(elf);
94 	(void) close(fd);
95 
96 	if (ehdr.e_type == ET_DYN)
97 		return (TRUE);
98 	else
99 		return (FALSE);
100 }
101 
102 static void
103 save_aout_info(char *aoutname)
104 {
105 	struct stat		buf;
106 	extern fl_info_t	aout_info;
107 
108 	if (stat(aoutname, &buf) == -1) {
109 		(void) fprintf(stderr, "%s: can't get info on `%s'\n",
110 		    whoami, aoutname);
111 		exit(EX_NOINPUT);
112 	}
113 
114 	aout_info.dev = buf.st_dev;
115 	aout_info.ino = buf.st_ino;
116 	aout_info.mtime = buf.st_mtime;
117 	aout_info.size = buf.st_size;
118 }
119 
120 void
121 getnfile(char *aoutname)
122 {
123 	int	fd;
124 
125 	DPRINTF(" Attempting to open %s  \n", aoutname);
126 	if ((fd = open((aoutname), O_RDONLY)) == -1) {
127 		(void) fprintf(stderr, "%s: can't open `%s'\n",
128 		    whoami, aoutname);
129 		exit(EX_NOINPUT);
130 	}
131 	process(aoutname, fd);
132 	save_aout_info(aoutname);
133 
134 	(void) close(fd);
135 }
136 
137 static GElf_Addr
138 get_txtorigin(Elf *elf)
139 {
140 	GElf_Ehdr	ehdr;
141 	GElf_Phdr	phdr;
142 	GElf_Half	ndx;
143 	GElf_Addr	txt_origin = 0;
144 	bool		first_load_seg = TRUE;
145 
146 	if (gelf_getehdr(elf, &ehdr) == NULL)
147 		fatal_error("can't read ehdr");
148 
149 	for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
150 		if (gelf_getphdr(elf, ndx, &phdr) == NULL)
151 			continue;
152 
153 		if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
154 			if (first_load_seg || phdr.p_vaddr < txt_origin)
155 				txt_origin = phdr.p_vaddr;
156 
157 			if (first_load_seg)
158 				first_load_seg = FALSE;
159 		}
160 	}
161 
162 	return (txt_origin);
163 }
164 
165 void
166 process_namelist(mod_info_t *module)
167 {
168 	int		fd;
169 	Elf		*elf;
170 
171 	if ((fd = open(module->name, O_RDONLY)) == -1) {
172 		(void) fprintf(stderr, "%s: can't read %s\n",
173 		    whoami, module->name);
174 		(void) fprintf(stderr, "Exiting due to error(s)...\n");
175 		exit(EX_NOINPUT);
176 	}
177 
178 	/*
179 	 * libelf's version already verified in processing a.out,
180 	 * so directly do elf_begin()
181 	 */
182 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
183 		fatal_error("can't read as ELF file");
184 
185 	module->next = NULL;
186 	module->txt_origin = get_txtorigin(elf);
187 	get_symtab(elf, module);
188 	module->active = TRUE;
189 }
190 
191 /*
192  * Get the ELF header and,  if it exists, call get_symtab()
193  * to begin processing of the file; otherwise, return from
194  * processing the file with a warning.
195  */
196 static void
197 process(char *filename, int fd)
198 {
199 	Elf			*elf;
200 	extern bool		cflag;
201 	extern bool		Bflag;
202 
203 	if (elf_version(EV_CURRENT) == EV_NONE)
204 		fatal_error("libelf is out of date");
205 
206 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
207 		fatal_error("can't read as ELF file");
208 
209 	if (gelf_getclass(elf) == ELFCLASS64)
210 		Bflag = TRUE;
211 
212 	/*
213 	 * Initialize active modules list. Note that we set the end
214 	 * address while reading the symbol table, in get_symtab
215 	 */
216 	modules.id = 1;
217 	modules.next = NULL;
218 	modules.txt_origin = get_txtorigin(elf);
219 	modules.load_base = modules.txt_origin;
220 	if ((modules.name = malloc(strlen(filename) + 1)) == NULL) {
221 		(void) fprintf(stderr, "%s: can't malloc %d bytes",
222 		    whoami, strlen(filename) + 1);
223 		exit(EX_UNAVAILABLE);
224 	}
225 	(void) strcpy(modules.name, filename);
226 
227 	get_symtab(elf, &modules);
228 
229 	modules.load_end = modules.data_end;
230 	modules.active = TRUE;
231 	n_modules = 1;
232 
233 	if (cflag)
234 		get_textseg(elf, fd);
235 }
236 
237 static void
238 get_textseg(Elf *elf, int fd)
239 {
240 	GElf_Ehdr ehdr;
241 	GElf_Phdr phdr;
242 	GElf_Half i;
243 
244 	if (gelf_getehdr(elf, &ehdr) == NULL)
245 		fatal_error("can't read ehdr");
246 
247 	for (i = 0; i < ehdr.e_phnum; i++) {
248 
249 		if (gelf_getphdr(elf, i, &phdr) == NULL)
250 			continue;
251 
252 		if (!(phdr.p_flags & PF_W) && (phdr.p_filesz > textsize)) {
253 			size_t chk;
254 
255 			/*
256 			 * We could have multiple loadable text segments;
257 			 * keep the largest we find.
258 			 */
259 			if (textspace)
260 				free(textspace);
261 
262 			/*
263 			 * gprof is a 32-bit program;  if this text segment
264 			 * has a > 32-bit offset or length, it's too big.
265 			 */
266 			chk = (size_t)phdr.p_vaddr + (size_t)phdr.p_filesz;
267 			if (phdr.p_vaddr + phdr.p_filesz != (GElf_Xword)chk)
268 				fatal_error("text segment too large for -c");
269 
270 			textbegin = (size_t)phdr.p_vaddr;
271 			textsize = (size_t)phdr.p_filesz;
272 
273 			textspace = malloc(textsize);
274 
275 			if (lseek(fd, (off_t)phdr.p_offset, SEEK_SET) !=
276 			    (off_t)phdr.p_offset)
277 				fatal_error("cannot seek to text section");
278 
279 			if (read(fd, textspace, textsize) != textsize)
280 				fatal_error("cannot read text");
281 		}
282 	}
283 
284 	if (textsize == 0)
285 		fatal_error("can't find text segment");
286 }
287 
288 #ifdef DEBUG
289 static void
290 debug_dup_del(nltype * keeper, nltype * louser)
291 {
292 	(void) printf("remove_dup_syms: discarding sym %s over sym %s\n",
293 	    louser->name, keeper->name);
294 }
295 #endif /* DEBUG */
296 
297 static void
298 remove_dup_syms(nltype *nl, sztype *sym_count)
299 {
300 	int	i;
301 	int	index;
302 	int	nextsym;
303 	nltype *orig_list;
304 
305 	if ((orig_list = malloc(sizeof (nltype) * *sym_count)) == NULL) {
306 		(void) fprintf(stderr,
307 		    "gprof: remove_dup_syms: malloc failed\n");
308 		(void) fprintf(stderr, "Exiting due to error(s)...\n");
309 		exit(EX_UNAVAILABLE);
310 	}
311 	(void) memcpy(orig_list, nl, sizeof (nltype) * *sym_count);
312 
313 	for (i = 0, index = 0, nextsym = 1; nextsym < *sym_count; nextsym++) {
314 		int	i_type;
315 		int	n_bind;
316 		int	n_type;
317 
318 		/*
319 		 * If orig_list[nextsym] points to a new symvalue, then we
320 		 * will copy our keeper and move on to the next symbol.
321 		 */
322 		if ((orig_list + i)->value < (orig_list + nextsym)->value) {
323 			*(nl + index++) = *(orig_list +i);
324 			i = nextsym;
325 			continue;
326 		}
327 
328 		/*
329 		 * If these two symbols have the same info, then we
330 		 * keep the first and keep checking for dups.
331 		 */
332 		if ((orig_list + i)->syminfo ==
333 		    (orig_list + nextsym)->syminfo) {
334 			DEBUG_DUP_DEL(orig_list + i, orig_list + nextsym);
335 			continue;
336 		}
337 		n_bind = ELF32_ST_BIND((orig_list + nextsym)->syminfo);
338 		i_type = ELF32_ST_TYPE((orig_list + i)->syminfo);
339 		n_type = ELF32_ST_TYPE((orig_list + nextsym)->syminfo);
340 
341 		/*
342 		 * If they have the same type we take the stronger
343 		 * bound function.
344 		 */
345 		if (i_type == n_type) {
346 			if (n_bind == STB_WEAK) {
347 				DEBUG_DUP_DEL((orig_list + i),
348 				    (orig_list + nextsym));
349 				continue;
350 			}
351 			DEBUG_DUP_DEL((orig_list + nextsym),
352 			    (orig_list + i));
353 			i = nextsym;
354 			continue;
355 		}
356 
357 		/*
358 		 * If the first symbol isn't of type NOTYPE then it must
359 		 * be the keeper.
360 		 */
361 		if (i_type != STT_NOTYPE) {
362 			DEBUG_DUP_DEL((orig_list + i),
363 			    (orig_list + nextsym));
364 			continue;
365 		}
366 
367 		/*
368 		 * Throw away the first one and take the new
369 		 * symbol
370 		 */
371 		DEBUG_DUP_DEL((orig_list + nextsym), (orig_list + i));
372 		i = nextsym;
373 	}
374 
375 	if (i < *sym_count) {
376 		if ((orig_list + i)->value > (nl + index - 1)->value)
377 			*(nl + index++) = *(orig_list + i);
378 	}
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
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
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
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 = NULL;
461 	GElf_Xword	nsyms_pri = 0, 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