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