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