xref: /titanic_41/usr/src/cmd/sgs/gprof/common/readelf.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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  * Copyright (c) 1993-1998 by Sun Microsystems, Inc.
24  * All rights reserved.
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 					printf(msg, file);
44 
45 #define	PRINTF(msg)		if (debug & ELFDEBUG) \
46 					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 
58 
59 #define	VOID_P		void *
60 
61 size_t	textbegin, textsize;
62 
63 /* Prototype definitions first */
64 
65 static void	process(char *filename, int fd);
66 static void	get_symtab(Elf *elf, char *filename, mod_info_t *module);
67 static void	get_textseg(Elf *elf, int fd, char *filename);
68 static void	save_aout_info(char *);
69 static int	compare(nltype *a, nltype *b);
70 
71 static void
72 fatal_error(char *error)
73 {
74 	fprintf(stderr, "Fatal ELF error: %s (%s)\n", error, elf_errmsg(-1));
75 	exit(EX_SOFTWARE);
76 }
77 
78 bool
79 is_shared_obj(char *name)
80 {
81 	int		fd;
82 	Elf		*elf;
83 	GElf_Ehdr	ehdr;
84 
85 	if ((fd = open(name, O_RDONLY)) == -1) {
86 		fprintf(stderr, "%s: can't open `%s'\n", whoami, name);
87 		exit(EX_NOINPUT);
88 	}
89 
90 	if (elf_version(EV_CURRENT) == EV_NONE)
91 		fatal_error("libelf is out of date");
92 
93 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
94 		fatal_error("can't read as ELF file");
95 
96 	if (gelf_getehdr(elf, &ehdr) == NULL)
97 		fatal_error("can't read ehdr");
98 
99 	elf_end(elf);
100 	close(fd);
101 
102 	if (ehdr.e_type == ET_DYN)
103 		return (TRUE);
104 	else
105 		return (FALSE);
106 }
107 
108 static void
109 save_aout_info(char *aoutname)
110 {
111 	struct stat		buf;
112 	extern fl_info_t	aout_info;
113 
114 	if (stat(aoutname, &buf) == -1) {
115 		fprintf(stderr, "%s: can't get info on `%s'\n",
116 							whoami, aoutname);
117 		exit(EX_NOINPUT);
118 	}
119 
120 	aout_info.dev = buf.st_dev;
121 	aout_info.ino = buf.st_ino;
122 	aout_info.mtime = buf.st_mtime;
123 	aout_info.size = buf.st_size;
124 }
125 
126 void
127 getnfile(char *aoutname)
128 {
129 	int	fd;
130 
131 	DPRINTF(" Attempting to open %s  \n", aoutname);
132 	if ((fd = open((aoutname), O_RDONLY)) == -1) {
133 		(void) fprintf(stderr, "%s: can't open `%s'\n",
134 							whoami, aoutname);
135 		exit(EX_NOINPUT);
136 	}
137 	process(aoutname, fd);
138 	save_aout_info(aoutname);
139 
140 	(void) close(fd);
141 }
142 
143 static GElf_Addr
144 get_txtorigin(Elf *elf)
145 {
146 	GElf_Ehdr	ehdr;
147 	GElf_Phdr	phdr;
148 	GElf_Half	ndx;
149 	GElf_Addr	txt_origin = 0;
150 	bool		first_load_seg = TRUE;
151 
152 	if (gelf_getehdr(elf, &ehdr) == NULL)
153 		fatal_error("can't read ehdr");
154 
155 	for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
156 		if (gelf_getphdr(elf, ndx, &phdr) == NULL)
157 			continue;
158 
159 		if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
160 			if (first_load_seg || phdr.p_vaddr < txt_origin)
161 				txt_origin = phdr.p_vaddr;
162 
163 			if (first_load_seg)
164 				first_load_seg = FALSE;
165 		}
166 	}
167 
168 	return (txt_origin);
169 }
170 
171 void
172 process_namelist(mod_info_t *module)
173 {
174 	int		fd;
175 	Elf		*elf;
176 
177 	if ((fd = open(module->name, O_RDONLY)) == -1) {
178 		(void) fprintf(stderr, "%s: can't read %s\n",
179 							whoami, module->name);
180 		fprintf(stderr, "Exiting due to error(s)...\n");
181 		exit(EX_NOINPUT);
182 	}
183 
184 	/*
185 	 * libelf's version already verified in processing a.out,
186 	 * so directly do elf_begin()
187 	 */
188 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
189 		fatal_error("can't read as ELF file");
190 
191 	module->next = NULL;
192 	module->txt_origin = get_txtorigin(elf);
193 	get_symtab(elf, module->name, module);
194 	module->active = TRUE;
195 }
196 
197 /*
198  * Get the ELF header and,  if it exists, call get_symtab()
199  * to begin processing of the file; otherwise, return from
200  * processing the file with a warning.
201  */
202 static void
203 process(char *filename, int fd)
204 {
205 	Elf			*elf;
206 	extern bool		cflag;
207 	extern bool		Bflag;
208 
209 	if (elf_version(EV_CURRENT) == EV_NONE)
210 		fatal_error("libelf is out of date");
211 
212 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
213 		fatal_error("can't read as ELF file");
214 
215 	if (gelf_getclass(elf) == ELFCLASS64)
216 		Bflag = TRUE;
217 
218 	/*
219 	 * Initialize active modules list. Note that we set the end
220 	 * address while reading the symbol table, in get_symtab
221 	 */
222 	modules.id = 1;
223 	modules.next = NULL;
224 	modules.txt_origin = get_txtorigin(elf);
225 	modules.load_base = modules.txt_origin;
226 	if ((modules.name = (char *) malloc(strlen(filename) + 1)) == NULL) {
227 		fprintf(stderr, "%s: can't malloc %d bytes",
228 					    whoami, strlen(filename) + 1);
229 		exit(EX_UNAVAILABLE);
230 	}
231 	strcpy(modules.name, filename);
232 
233 	get_symtab(elf, filename, &modules);
234 
235 	modules.load_end = modules.data_end;
236 	modules.active = TRUE;
237 	n_modules = 1;
238 
239 	if (cflag)
240 		get_textseg(elf, fd, filename);
241 }
242 
243 static void
244 get_textseg(Elf *elf, int fd, char *filename)
245 {
246 	GElf_Ehdr ehdr;
247 	GElf_Phdr phdr;
248 	GElf_Half i;
249 
250 	if (gelf_getehdr(elf, &ehdr) == NULL)
251 		fatal_error("can't read ehdr");
252 
253 	for (i = 0; i < ehdr.e_phnum; i++) {
254 
255 		if (gelf_getphdr(elf, i, &phdr) == NULL)
256 			continue;
257 
258 		if (!(phdr.p_flags & PF_W) && (phdr.p_filesz > textsize)) {
259 			size_t chk;
260 
261 			/*
262 			 * We could have multiple loadable text segments;
263 			 * keep the largest we find.
264 			 */
265 			if (textspace)
266 				free(textspace);
267 
268 			/*
269 			 * gprof is a 32-bit program;  if this text segment
270 			 * has a > 32-bit offset or length, it's too big.
271 			 */
272 			chk = (size_t)phdr.p_vaddr + (size_t)phdr.p_filesz;
273 			if (phdr.p_vaddr + phdr.p_filesz != (GElf_Xword)chk)
274 				fatal_error("text segment too large for -c");
275 
276 			textbegin = (size_t)phdr.p_vaddr;
277 			textsize = (size_t)phdr.p_filesz;
278 
279 			textspace = malloc(textsize);
280 
281 			if (lseek(fd, (off_t)phdr.p_offset, SEEK_SET) !=
282 			    (off_t)phdr.p_offset)
283 				fatal_error("cannot seek to text section");
284 
285 			if (read(fd, textspace, textsize) != textsize)
286 				fatal_error("cannot read text");
287 		}
288 	}
289 
290 	if (textsize == 0)
291 		fatal_error("can't find text segment");
292 }
293 
294 #ifdef DEBUG
295 static void
296 debug_dup_del(nltype * keeper, nltype * louser)
297 {
298 	printf("remove_dup_syms: discarding sym %s over sym %s\n",
299 		louser->name, keeper->name);
300 }
301 #endif DEBUG
302 
303 static void
304 remove_dup_syms(nltype *nl, sztype *sym_count)
305 {
306 	int	i;
307 	int	index;
308 	int	nextsym;
309 
310 	nltype *	orig_list;
311 	if ((orig_list = malloc(sizeof (nltype) * *sym_count)) == NULL) {
312 		fprintf(stderr, "gprof: remove_dup_syms: malloc failed\n");
313 		fprintf(stderr, "Exiting due to error(s)...\n");
314 		exit(EX_UNAVAILABLE);
315 	}
316 	memcpy(orig_list, nl, sizeof (nltype) * *sym_count);
317 
318 	for (i = 0, index = 0, nextsym = 1; nextsym < *sym_count; nextsym++) {
319 		int	i_type;
320 		int	n_bind;
321 		int	n_type;
322 
323 		/*
324 		 * If orig_list[nextsym] points to a new symvalue, then we
325 		 * will copy our keeper and move on to the next symbol.
326 		 */
327 		if ((orig_list + i)->value < (orig_list + nextsym)->value) {
328 			*(nl + index++) = *(orig_list +i);
329 			i = nextsym;
330 			continue;
331 		}
332 
333 		/*
334 		 * If these two symbols have the same info, then we
335 		 * keep the first and keep checking for dups.
336 		 */
337 		if ((orig_list + i)->syminfo ==
338 		    (orig_list + nextsym)->syminfo) {
339 			DEBUG_DUP_DEL(orig_list + i, orig_list + nextsym);
340 			continue;
341 		}
342 		n_bind = ELF32_ST_BIND((orig_list + nextsym)->syminfo);
343 		i_type = ELF32_ST_TYPE((orig_list + i)->syminfo);
344 		n_type = ELF32_ST_TYPE((orig_list + nextsym)->syminfo);
345 
346 		/*
347 		 * If they have the same type we take the stronger
348 		 * bound function.
349 		 */
350 		if (i_type == n_type) {
351 			if (n_bind == STB_WEAK) {
352 				DEBUG_DUP_DEL((orig_list + i),
353 				    (orig_list + nextsym));
354 				continue;
355 			}
356 			DEBUG_DUP_DEL((orig_list + nextsym),
357 			    (orig_list + i));
358 			i = nextsym;
359 			continue;
360 		}
361 
362 		/*
363 		 * If the first symbol isn't of type NOTYPE then it must
364 		 * be the keeper.
365 		 */
366 		if (i_type != STT_NOTYPE) {
367 			DEBUG_DUP_DEL((orig_list + i),
368 			    (orig_list + nextsym));
369 			continue;
370 		}
371 
372 		/*
373 		 * Throw away the first one and take the new
374 		 * symbol
375 		 */
376 		DEBUG_DUP_DEL((orig_list + nextsym), (orig_list + i));
377 		i = nextsym;
378 	}
379 
380 	if ((orig_list + i)->value > (nl + index - 1)->value)
381 		*(nl + index++) = *(orig_list +i);
382 
383 	*sym_count = index;
384 }
385 
386 /*
387  * compare either by name or by value for sorting.
388  * This is the comparison function called by qsort to
389  * sort the symbols either by name or value when requested.
390  */
391 static int
392 compare(nltype *a, nltype *b)
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 	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, char *filename, mod_info_t *module)
455 {
456 	Elf_Scn		*scn = NULL, *sym = NULL;
457 	GElf_Word	strndx = 0;
458 	sztype		nsyms, i;
459 	Elf_Data	*symdata;
460 	nltype		*etext = NULL;
461 
462 	nltype			*l_nl, *l_npe;
463 	sztype			l_nname;
464 	extern sztype		total_names;
465 
466 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
467 		GElf_Shdr shdr;
468 
469 		if (gelf_getshdr(scn, &shdr) == NULL)
470 			continue;
471 
472 		if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
473 			GElf_Xword chk = shdr.sh_size / shdr.sh_entsize;
474 
475 			nsyms = (sztype)(shdr.sh_size / shdr.sh_entsize);
476 
477 			if (chk != (GElf_Xword)nsyms)
478 				fatal_error("32-bit gprof cannot handle"
479 				    "more than 2^32 symbols");
480 
481 			strndx = shdr.sh_link;
482 			sym = scn;
483 		}
484 
485 		/*
486 		 * If we've found a real symbol table, we're done.
487 		 */
488 		if (shdr.sh_type == SHT_SYMTAB)
489 			break;
490 	}
491 
492 	if (sym == NULL || strndx == 0)
493 		fatal_error("can't find symbol table.\n");
494 
495 	if ((symdata = elf_getdata(scn, NULL)) == NULL)
496 		fatal_error("can't read symbol data.\n");
497 
498 	if ((l_nl = l_npe = (nltype *)calloc(nsyms + PRF_SYMCNT,
499 	    sizeof (nltype))) == NULL)
500 		fatal_error("cannot allocate symbol data.\n");
501 
502 	/*
503 	 * Now we need to cruise through the symbol table eliminating
504 	 * all non-functions from consideration, and making strings
505 	 * real.
506 	 */
507 	l_nname = 0;
508 
509 	for (i = 1; i < nsyms; i++) {
510 		GElf_Sym gsym;
511 		char *name;
512 
513 		gelf_getsym(symdata, i, &gsym);
514 
515 		name = elf_strptr(elf, strndx, gsym.st_name);
516 
517 		/*
518 		 * We're interested in this symbol if it's a function or
519 		 * if it's the symbol "_etext"
520 		 */
521 		if (is_function(elf, &gsym) || strcmp(name, PRF_ETEXT) == 0) {
522 
523 			l_npe->name = name;
524 			l_npe->value = gsym.st_value;
525 			l_npe->sz = gsym.st_size;
526 			l_npe->syminfo = gsym.st_info;
527 			l_npe->module = module;
528 
529 			if (strcmp(name, PRF_ETEXT) == 0)
530 				etext = l_npe;
531 
532 			if (lflag == TRUE &&
533 			    GELF_ST_BIND(gsym.st_info) == STB_LOCAL) {
534 				/*
535 				 * If the "locals only" flag is on, then
536 				 * we add the local symbols to the
537 				 * exclusion lists.
538 				 */
539 				addlist(Elist, name);
540 				addlist(elist, name);
541 			}
542 			DPRINTF("Index %lld:", l_nname);
543 			DPRINTF("\tValue: 0x%llx\t", l_npe->value);
544 			DPRINTF("Name: %s \n", l_npe->name);
545 			l_npe++;
546 			l_nname++;
547 		}
548 
549 		if (strcmp(name, PRF_END) == 0)
550 			module->data_end = gsym.st_value;
551 	}
552 
553 	if (l_npe == l_nl)
554 		fatal_error("no valid functions found");
555 
556 	/*
557 	 * Finally, we need to construct some dummy entries.
558 	 */
559 	if (etext) {
560 		l_npe->name = PRF_EXTSYM;
561 		l_npe->value = etext->value + 1;
562 		l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
563 		l_npe->module = module;
564 		l_npe++;
565 		l_nname++;
566 	}
567 
568 	l_npe->name = PRF_MEMTERM;
569 	l_npe->value = (pctype)-1;
570 	l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
571 	l_npe->module = module;
572 	l_npe++;
573 	l_nname++;
574 
575 	/*
576 	 * We're almost done;  all we need to do is sort the symbols
577 	 * and then remove the duplicates.
578 	 */
579 	qsort(l_nl, (size_t)l_nname, sizeof (nltype),
580 	    (int(*)(const void *, const void *)) 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