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