xref: /titanic_52/usr/src/cmd/sgs/prof/common/rdelf.c (revision 82d33c01b078ed404a986a369750cdb4743773fb)
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 2005 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 /*
30  * ELF support routines for processing versioned mon.out files.
31  */
32 
33 #include <stdlib.h>
34 #include <string.h>
35 #include "profv.h"
36 
37 bool
38 is_shared_obj(char *name)
39 {
40 	int		fd;
41 	Elf		*elf;
42 	GElf_Ehdr	ehdr;
43 
44 	if ((fd = open(name, O_RDONLY)) == -1) {
45 		(void) fprintf(stderr, "%s: can't open `%s'\n", cmdname, name);
46 		exit(ERR_ELF);
47 	}
48 
49 	if (elf_version(EV_CURRENT) == EV_NONE) {
50 		(void) fprintf(stderr, "%s: libelf out of date\n", cmdname);
51 		exit(ERR_ELF);
52 	}
53 
54 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
55 		(void) fprintf(stderr, "%s: elf_begin failed\n", cmdname);
56 		exit(ERR_ELF);
57 	}
58 
59 	if (gelf_getehdr(elf, &ehdr) == NULL) {
60 		(void) fprintf(stderr, "%s: can't read ELF header of %s\n",
61 								cmdname, name);
62 		exit(ERR_ELF);
63 	}
64 
65 	(void) elf_end(elf);
66 	(void) close(fd);
67 
68 	if (ehdr.e_type == ET_DYN)
69 		return (TRUE);
70 	else
71 		return (FALSE);
72 }
73 
74 static void
75 rm_dups(nltype *nl, size_t *nfuncs)
76 {
77 	size_t	i, prev = 0, ndx = 0;
78 	int	prev_type, prev_bind, cur_type;
79 
80 	for (i = 1; i < *nfuncs; i++) {
81 		/*
82 		 * If current value is different from prev, proceed.
83 		 */
84 		if (nl[prev].value < nl[i].value) {
85 			prev = i;
86 			continue;
87 		}
88 
89 		/*
90 		 * If current and prev have the syminfo, rm the latter.
91 		 */
92 		if (nl[prev].info == nl[i].info) {
93 			nl[i].name = NULL;
94 			continue;
95 		}
96 
97 		prev_type = ELF_ST_TYPE(nl[prev].info);
98 		prev_bind = ELF_ST_BIND(nl[prev].info);
99 		cur_type = ELF_ST_TYPE(nl[i].info);
100 
101 		/*
102 		 * Remove the one with STT_NOTYPE and keep the other.
103 		 */
104 		if (prev_type != cur_type) {
105 			if (prev_type != STT_NOTYPE)
106 				nl[i].name = NULL;
107 			else {
108 				nl[prev].name = NULL;
109 				prev = i;
110 			}
111 			continue;
112 		}
113 
114 		/*
115 		 * If they have the same type, take the stronger bound
116 		 * function
117 		 */
118 		if (prev_bind != STB_WEAK)
119 			nl[i].name = NULL;
120 		else {
121 			nl[prev].name = NULL;
122 			prev = i;
123 		}
124 	}
125 
126 
127 	/*
128 	 * Actually remove the cleared symbols from namelist. We're not
129 	 * truncating namelist by realloc, though.
130 	 */
131 	for (i = 0; (i < *nfuncs) && (nl[i].name != NULL); i++)
132 		;
133 
134 	ndx = i;
135 	for (i = ndx + 1; i < *nfuncs; i++) {
136 		if (nl[i].name) {
137 			nl[ndx] = nl[i];
138 			ndx++;
139 		}
140 	}
141 
142 	*nfuncs = ndx;
143 }
144 
145 int
146 cmp_by_address(const void *arg1, const void *arg2)
147 {
148 	nltype *a = (nltype *)arg1;
149 	nltype *b = (nltype *)arg2;
150 
151 	if (a->value < b->value)
152 		return (-1);
153 	else if (a->value > b->value)
154 		return (1);
155 	else
156 		return (0);
157 }
158 
159 static int
160 is_function(Elf *elf, GElf_Sym *sym)
161 {
162 	Elf_Scn		*scn;
163 	GElf_Shdr	shdr;
164 
165 	/*
166 	 * With dynamic linking, it is possible that certain undefined
167 	 * symbols exist in the objects. The actual definition will be
168 	 * found elsewhere, so we'll just skip it for this object.
169 	 */
170 	if (sym->st_shndx == SHN_UNDEF)
171 		return (0);
172 
173 	if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {
174 		if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL)
175 			return (1);
176 
177 		if (GELF_ST_BIND(sym->st_info) == STB_WEAK)
178 			return (1);
179 
180 		if (gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)
181 			return (1);
182 	}
183 
184 	/*
185 	 * It's not a function; determine if it's in an executable section.
186 	 */
187 	if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
188 		return (0);
189 
190 	/*
191 	 * If it isn't global, and it isn't weak, and it isn't
192 	 * a 'local with the gflag set', then get out.
193 	 */
194 	if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
195 			GELF_ST_BIND(sym->st_info) != STB_WEAK &&
196 			!(gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL))
197 		return (0);
198 
199 	if (sym->st_shndx >= SHN_LORESERVE)
200 		return (0);
201 
202 	scn = elf_getscn(elf, sym->st_shndx);
203 	(void) gelf_getshdr(scn, &shdr);
204 
205 	if (!(shdr.sh_flags & SHF_EXECINSTR))
206 		return (0);
207 
208 	return (1);
209 }
210 
211 static void
212 fetch_symtab(Elf *elf, char *filename, mod_info_t *module)
213 {
214 	Elf_Scn		*scn = NULL, *sym = NULL;
215 	GElf_Word	strndx = 0;
216 	size_t		i, nsyms, nfuncs;
217 	Elf_Data	*symdata;
218 	nltype		*nl, *npe;
219 
220 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
221 
222 		GElf_Shdr shdr;
223 
224 		if (gelf_getshdr(scn, &shdr) == NULL)
225 			continue;
226 
227 		if (shdr.sh_type == SHT_SYMTAB ||
228 					shdr.sh_type == SHT_DYNSYM) {
229 			GElf_Xword chk = shdr.sh_size / shdr.sh_entsize;
230 
231 			nsyms = (size_t)(shdr.sh_size / shdr.sh_entsize);
232 
233 			if (chk != (GElf_Xword) nsyms) {
234 				(void) fprintf(stderr, "%s: can't handle"
235 					"more than 2^32 symbols", cmdname);
236 				exit(ERR_INPUT);
237 			}
238 
239 			strndx = shdr.sh_link;
240 			sym = scn;
241 		}
242 
243 		/*
244 		 * If we've found a real symbol table, we're done.
245 		 */
246 		if (shdr.sh_type == SHT_SYMTAB)
247 			break;
248 	}
249 
250 	if (sym == NULL || strndx == 0) {
251 		(void) fprintf(stderr, "%s: missing symbol table in %s\n",
252 						    cmdname, filename);
253 		exit(ERR_ELF);
254 	}
255 
256 	if ((symdata = elf_getdata(scn, NULL)) == NULL) {
257 		(void) fprintf(stderr, "%s: can't read symbol data from %s\n",
258 						    cmdname, filename);
259 		exit(ERR_ELF);
260 	}
261 
262 	if ((npe = nl = (nltype *) calloc(nsyms, sizeof (nltype))) == NULL) {
263 		(void) fprintf(stderr, "%s: can't alloc %x bytes for symbols\n",
264 					cmdname, nsyms * sizeof (nltype));
265 		exit(ERR_ELF);
266 	}
267 
268 	/*
269 	 * Now we need to cruise through the symbol table eliminating
270 	 * all non-functions from consideration, and making strings
271 	 * real.
272 	 */
273 	nfuncs = 0;
274 
275 	for (i = 1; i < nsyms; i++) {
276 		GElf_Sym	gsym;
277 		char		*name;
278 
279 		(void) gelf_getsym(symdata, i, &gsym);
280 
281 		name = elf_strptr(elf, strndx, gsym.st_name);
282 
283 		/*
284 		 * We're interested in this symbol if it's a function
285 		 */
286 		if (is_function(elf, &gsym)) {
287 
288 			npe->name = name;
289 			npe->value = gsym.st_value;
290 			npe->size = gsym.st_size;
291 			npe->info = gsym.st_info;
292 
293 			npe++;
294 			nfuncs++;
295 		}
296 
297 		if (strcmp(name, PRF_END) == 0)
298 			module->data_end = gsym.st_value;
299 	}
300 
301 	if (npe == nl) {
302 		(void) fprintf(stderr, "%s: no valid functions in %s\n",
303 						    cmdname, filename);
304 		exit(ERR_INPUT);
305 	}
306 
307 	/*
308 	 * And finally, sort the symbols by increasing address
309 	 * and remove the duplicates.
310 	 */
311 	qsort(nl, nfuncs, sizeof (nltype), cmp_by_address);
312 	rm_dups(nl, &nfuncs);
313 
314 	module->nl = nl;
315 	module->nfuncs = nfuncs;
316 }
317 
318 static GElf_Addr
319 get_txtorigin(Elf *elf, char *filename)
320 {
321 	GElf_Ehdr	ehdr;
322 	GElf_Phdr	phdr;
323 	GElf_Half	ndx;
324 	GElf_Addr	txt_origin = 0;
325 	bool		first_load_seg = TRUE;
326 
327 	if (gelf_getehdr(elf, &ehdr) == NULL) {
328 		(void) fprintf(stderr, "%s: can't read ELF header of %s\n",
329 						    cmdname, filename);
330 		exit(ERR_ELF);
331 	}
332 
333 	for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
334 		if (gelf_getphdr(elf, ndx, &phdr) == NULL)
335 			continue;
336 
337 		if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
338 			if (first_load_seg || phdr.p_vaddr < txt_origin)
339 				txt_origin = phdr.p_vaddr;
340 
341 			if (first_load_seg)
342 				first_load_seg = FALSE;
343 		}
344 	}
345 
346 	return (txt_origin);
347 }
348 
349 void
350 get_syms(char *filename, mod_info_t *mi)
351 {
352 	int		fd;
353 	Elf		*elf;
354 
355 	if ((fd = open(filename, O_RDONLY)) == -1) {
356 		perror(filename);
357 		exit(ERR_SYSCALL);
358 	}
359 
360 	if (elf_version(EV_CURRENT) == EV_NONE) {
361 		(void) fprintf(stderr, "%s: libelf out of date\n", cmdname);
362 		exit(ERR_ELF);
363 	}
364 
365 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
366 		(void) fprintf(stderr, "%s: elf_begin failed\n", cmdname);
367 		exit(ERR_ELF);
368 	}
369 
370 	if (gelf_getclass(elf) != ELFCLASS64) {
371 		(void) fprintf(stderr, "%s: unsupported mon.out format for "
372 				    "this class of object\n", cmdname);
373 		exit(ERR_ELF);
374 	}
375 
376 	mi->txt_origin = get_txtorigin(elf, filename);
377 
378 	fetch_symtab(elf, filename, mi);
379 }
380