xref: /titanic_51/usr/src/cmd/sgs/prof/common/rdelf.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) 1998 by Sun Microsystems, Inc.
24  * All rights reserved.
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 "profv.h"
33 
34 bool
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 		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 		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 		fprintf(stderr, "%s: elf_begin failed\n", cmdname);
53 		exit(ERR_ELF);
54 	}
55 
56 	if (gelf_getehdr(elf, &ehdr) == NULL) {
57 		fprintf(stderr, "%s: can't read ELF header of %s\n",
58 								cmdname, name);
59 		exit(ERR_ELF);
60 	}
61 
62 	elf_end(elf);
63 	close(fd);
64 
65 	if (ehdr.e_type == ET_DYN)
66 		return (TRUE);
67 	else
68 		return (FALSE);
69 }
70 
71 static void
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
143 cmp_by_address(nltype *a, nltype *b)
144 {
145 	if (a->value < b->value)
146 		return (-1);
147 	else if (a->value > b->value)
148 		return (1);
149 	else
150 		return (0);
151 }
152 
153 static int
154 is_function(Elf *elf, GElf_Sym *sym)
155 {
156 	Elf_Scn		*scn;
157 	GElf_Shdr	shdr;
158 
159 	/*
160 	 * With dynamic linking, it is possible that certain undefined
161 	 * symbols exist in the objects. The actual definition will be
162 	 * found elsewhere, so we'll just skip it for this object.
163 	 */
164 	if (sym->st_shndx == SHN_UNDEF)
165 		return (0);
166 
167 	if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {
168 		if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL)
169 			return (1);
170 
171 		if (GELF_ST_BIND(sym->st_info) == STB_WEAK)
172 			return (1);
173 
174 		if (gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)
175 			return (1);
176 	}
177 
178 	/*
179 	 * It's not a function; determine if it's in an executable section.
180 	 */
181 	if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
182 		return (0);
183 
184 	/*
185 	 * If it isn't global, and it isn't weak, and it isn't
186 	 * a 'local with the gflag set', then get out.
187 	 */
188 	if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
189 			GELF_ST_BIND(sym->st_info) != STB_WEAK &&
190 			!(gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL))
191 		return (0);
192 
193 	if (sym->st_shndx >= SHN_LORESERVE)
194 		return (0);
195 
196 	scn = elf_getscn(elf, sym->st_shndx);
197 	gelf_getshdr(scn, &shdr);
198 
199 	if (!(shdr.sh_flags & SHF_EXECINSTR))
200 		return (0);
201 
202 	return (1);
203 }
204 
205 static void
206 fetch_symtab(Elf *elf, char *filename, mod_info_t *module)
207 {
208 	Elf_Scn		*scn = NULL, *sym = NULL;
209 	GElf_Word	strndx = 0;
210 	size_t		i, nsyms, nfuncs;
211 	Elf_Data	*symdata;
212 	nltype		*nl, *npe;
213 
214 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
215 
216 		GElf_Shdr shdr;
217 
218 		if (gelf_getshdr(scn, &shdr) == NULL)
219 			continue;
220 
221 		if (shdr.sh_type == SHT_SYMTAB ||
222 					shdr.sh_type == SHT_DYNSYM) {
223 			GElf_Xword chk = shdr.sh_size / shdr.sh_entsize;
224 
225 			nsyms = (size_t) (shdr.sh_size / shdr.sh_entsize);
226 
227 			if (chk != (GElf_Xword) nsyms) {
228 				fprintf(stderr, "%s: can't handle"
229 					"more than 2^32 symbols", cmdname);
230 				exit(ERR_INPUT);
231 			}
232 
233 			strndx = shdr.sh_link;
234 			sym = scn;
235 		}
236 
237 		/*
238 		 * If we've found a real symbol table, we're done.
239 		 */
240 		if (shdr.sh_type == SHT_SYMTAB)
241 			break;
242 	}
243 
244 	if (sym == NULL || strndx == 0) {
245 		fprintf(stderr, "%s: missing symbol table in %s\n",
246 						    cmdname, filename);
247 		exit(ERR_ELF);
248 	}
249 
250 	if ((symdata = elf_getdata(scn, NULL)) == NULL) {
251 		fprintf(stderr, "%s: can't read symbol data from %s\n",
252 						    cmdname, filename);
253 		exit(ERR_ELF);
254 	}
255 
256 	if ((npe = nl = (nltype *) calloc(nsyms, sizeof (nltype))) == NULL) {
257 		fprintf(stderr, "%s: can't alloc %llx bytes for symbols\n",
258 					cmdname, nsyms * sizeof (nltype));
259 		exit(ERR_ELF);
260 	}
261 
262 	/*
263 	 * Now we need to cruise through the symbol table eliminating
264 	 * all non-functions from consideration, and making strings
265 	 * real.
266 	 */
267 	nfuncs = 0;
268 
269 	for (i = 1; i < nsyms; i++) {
270 		GElf_Sym	gsym;
271 		char		*name;
272 
273 		gelf_getsym(symdata, i, &gsym);
274 
275 		name = elf_strptr(elf, strndx, gsym.st_name);
276 
277 		/*
278 		 * We're interested in this symbol if it's a function
279 		 */
280 		if (is_function(elf, &gsym)) {
281 
282 			npe->name = name;
283 			npe->value = gsym.st_value;
284 			npe->size = gsym.st_size;
285 			npe->info = gsym.st_info;
286 
287 			npe++;
288 			nfuncs++;
289 		}
290 
291 		if (strcmp(name, PRF_END) == 0)
292 			module->data_end = gsym.st_value;
293 	}
294 
295 	if (npe == nl) {
296 		fprintf(stderr, "%s: no valid functions in %s\n",
297 						    cmdname, filename);
298 		exit(ERR_INPUT);
299 	}
300 
301 	/*
302 	 * And finally, sort the symbols by increasing address
303 	 * and remove the duplicates.
304 	 */
305 	qsort(nl, nfuncs, sizeof (nltype),
306 			(int(*)(const void *, const void *)) cmp_by_address);
307 	rm_dups(nl, &nfuncs);
308 
309 	module->nl = nl;
310 	module->nfuncs = nfuncs;
311 }
312 
313 static GElf_Addr
314 get_txtorigin(Elf *elf, char *filename)
315 {
316 	GElf_Ehdr	ehdr;
317 	GElf_Phdr	phdr;
318 	GElf_Half	ndx;
319 	GElf_Addr	txt_origin = 0;
320 	bool		first_load_seg = TRUE;
321 
322 	if (gelf_getehdr(elf, &ehdr) == NULL) {
323 		fprintf(stderr, "%s: can't read ELF header of %s\n",
324 						    cmdname, filename);
325 		exit(ERR_ELF);
326 	}
327 
328 	for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
329 		if (gelf_getphdr(elf, ndx, &phdr) == NULL)
330 			continue;
331 
332 		if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
333 			if (first_load_seg || phdr.p_vaddr < txt_origin)
334 				txt_origin = phdr.p_vaddr;
335 
336 			if (first_load_seg)
337 				first_load_seg = FALSE;
338 		}
339 	}
340 
341 	return (txt_origin);
342 }
343 
344 void
345 get_syms(char *filename, mod_info_t *mi)
346 {
347 	int		fd;
348 	Elf		*elf;
349 
350 	if ((fd = open(filename, O_RDONLY)) == -1) {
351 		perror(filename);
352 		exit(ERR_SYSCALL);
353 	}
354 
355 	if (elf_version(EV_CURRENT) == EV_NONE) {
356 		fprintf(stderr, "%s: libelf out of date\n", cmdname);
357 		exit(ERR_ELF);
358 	}
359 
360 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
361 		fprintf(stderr, "%s: elf_begin failed\n", cmdname);
362 		exit(ERR_ELF);
363 	}
364 
365 	if (gelf_getclass(elf) != ELFCLASS64) {
366 		fprintf(stderr, "%s: unsupported mon.out format for "
367 				    "this class of object\n", cmdname);
368 		exit(ERR_ELF);
369 	}
370 
371 	mi->txt_origin = get_txtorigin(elf, filename);
372 
373 	fetch_symtab(elf, filename, mi);
374 }
375