rdelf.c revision 3621:2cbc0f92c696
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
36bool
37is_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
73static void
74rm_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
144int
145cmp_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
158static int
159is_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
210static void
211fetch_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
356static GElf_Addr
357get_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
387void
388get_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