xref: /illumos-gate/usr/src/cmd/sgs/libelf/demo/dispsyms.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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 (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * dispsyms: Display Symbols
27  *
28  * This program demonstrates the use of the libelf interface to
29  * read an ELF file.  dispsyms will open an ELF file using
30  * elf_begin(ELF_C_READ) and examine search the ELF file
31  * for a symbol table (SHT_SYMTAB, SHT_DYNSYM, or SHT_SUNW_LDYNSYM).
32  * It will display the contents of any symbol tables it finds.
33  *
34  * Note:  This program also understands about the use
35  *	  of 'Extended ELF Section indexes' and will
36  *	  decode a corresponding SHT_SYMTAB_SHNDX
37  *	  section if required.
38  */
39 #include <stdio.h>
40 #include <libelf.h>
41 #include <gelf.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 static const char *symbind[STB_NUM] = {
48 /* STB_LOCL */		"LOCL",
49 /* STB_GLOBAL */	"GLOB",
50 /* STB_WEAK */		"WEAK"
51 };
52 
53 static const char *symtype[STT_NUM] = {
54 /* STT_NOTYPE */	"NOTY",
55 /* STT_OBJECT */	"OBJT",
56 /* STT_FUNC */		"FUNC",
57 /* STT_SECTION */	"SECT",
58 /* STT_FILE */		"FILE",
59 /* STT_COMMON */	"COMM",
60 /* STT_TLS */		"TLS"
61 };
62 #if STT_NUM != (STT_TLS + 1)
63 #error "STT_NUM has grown. Update symtype[]."
64 #endif
65 
66 #define	INTSTRLEN	32
67 
68 
69 static void
70 print_symtab(Elf *elf, const char *file)
71 {
72 	Elf_Scn		*scn = NULL;
73 	GElf_Shdr	shdr;
74 	GElf_Ehdr	ehdr;
75 	size_t		shstrndx;
76 
77 
78 	if (gelf_getehdr(elf, &ehdr) == NULL) {
79 		(void) fprintf(stderr, "%s: elf_getehdr() failed: %s\n",
80 		    file, elf_errmsg(0));
81 		return;
82 	}
83 
84 	if (elf_getshdrstrndx(elf, &shstrndx) == -1) {
85 		(void) fprintf(stderr, "%s: elf_getshdrstrndx() failed: %s\n",
86 		    file, elf_errmsg(0));
87 		return;
88 	}
89 
90 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
91 		uint_t		symcnt, ndx, nosymshndx;
92 		Elf_Data	*symdata, *shndxdata;
93 
94 		if (gelf_getshdr(scn, &shdr) == NULL) {
95 			(void) fprintf(stderr,
96 			    "%s: elf_getshdr() failed: %s\n",
97 			    file, elf_errmsg(0));
98 			return;
99 		}
100 		if ((shdr.sh_type != SHT_SYMTAB) &&
101 		    (shdr.sh_type != SHT_DYNSYM) &&
102 		    (shdr.sh_type != SHT_SUNW_LDYNSYM))
103 			continue;
104 
105 		/*
106 		 * Get the data associated with the Symbol
107 		 * section.
108 		 */
109 		if ((symdata = elf_getdata(scn, NULL)) == NULL) {
110 			(void) fprintf(stderr,
111 			    "%s: elf_getdata() failed: %s\n",
112 			    file, elf_errmsg(0));
113 			return;
114 		}
115 
116 		/*
117 		 * Print symbol table title and header for symbol display
118 		 */
119 		(void) printf("\nSymTab: %s:%s\n", file,
120 		    elf_strptr(elf, shstrndx, shdr.sh_name));
121 		(void) printf("  index   value    size     type "
122 		    "bind  oth shndx name\n");
123 
124 		/*
125 		 * We now iterate over the full symbol table printing
126 		 * the symbols as we go.
127 		 */
128 		shndxdata = 0;
129 		nosymshndx = 0;
130 		symcnt = shdr.sh_size / shdr.sh_entsize;
131 		for (ndx = 0; ndx < symcnt; ndx++) {
132 			GElf_Sym	sym;
133 			Elf32_Word	shndx;
134 			uint_t		type, bind, specshndx;
135 			char		bindbuf[INTSTRLEN];
136 			char		typebuf[INTSTRLEN];
137 			char		shndxbuf[INTSTRLEN];
138 			const char	*bindstr, *typestr, *shndxstr;
139 
140 			/*
141 			 * Get a symbol entry
142 			 */
143 			if (gelf_getsymshndx(symdata, shndxdata, ndx,
144 			    &sym, &shndx) == NULL) {
145 				(void) fprintf(stderr,
146 				    "%s: gelf_getsymshndx() failed: %s\n",
147 				    file, elf_errmsg(0));
148 				return;
149 			}
150 			/*
151 			 * Check to see if this symbol's st_shndx is using
152 			 * the 'Extended SHNDX table' for a SYMTAB.
153 			 *
154 			 * If it is - and we haven't searched before, go
155 			 * find the associated SHT_SYMTAB_SHNDX section.
156 			 */
157 			if ((sym.st_shndx == SHN_XINDEX) &&
158 			    (shndxdata == 0) && (nosymshndx == 0)) {
159 				Elf_Scn		*_scn = NULL;
160 				GElf_Shdr	_shdr;
161 				GElf_Word	symscnndx;
162 
163 				specshndx = 0;
164 				symscnndx = elf_ndxscn(scn);
165 
166 				while ((_scn =
167 				    elf_nextscn(elf, _scn)) != NULL) {
168 					if (gelf_getshdr(_scn, &_shdr) == NULL)
169 						break;
170 
171 					/*
172 					 * We've found the Symtab SHNDX table
173 					 * if it's of type SHT_SYMTAB_SHNDX
174 					 * and it's shdr.sh_link points to the
175 					 * section index for the current symbol
176 					 * table.
177 					 */
178 					if ((_shdr.sh_type ==
179 					    SHT_SYMTAB_SHNDX) &&
180 					    (_shdr.sh_link == symscnndx) &&
181 					    ((shndxdata = elf_getdata(_scn,
182 					    NULL)) != NULL))
183 						break;
184 				}
185 				/*
186 				 * Get a symbol entry
187 				 */
188 				if (shndxdata &&
189 				    (gelf_getsymshndx(symdata, shndxdata, ndx,
190 				    &sym, &shndx) == NULL)) {
191 					(void) fprintf(stderr,
192 					    "%s: gelf_getsymshndx() "
193 					    "failed: %s\n",
194 					    file, elf_errmsg(0));
195 					return;
196 				}
197 				/*
198 				 * No Symtab SHNDX table was found.  We could
199 				 * give a fatal error here - instead we'll
200 				 * just mark that fact and display as much of
201 				 * the symbol table as we can.  Any symbol
202 				 * displayed with a XINDX section index has
203 				 * a bogus value.
204 				 */
205 				if (shndxdata == 0)
206 					nosymshndx = 1;
207 			}
208 
209 			/*
210 			 * Decode the type & binding information
211 			 */
212 			type = GELF_ST_TYPE(sym.st_info);
213 			bind = GELF_ST_BIND(sym.st_info);
214 
215 			if (type < STT_NUM)
216 				typestr = symtype[type];
217 			else {
218 				(void) snprintf(typebuf, INTSTRLEN,
219 				    "%d", type);
220 				typestr = typebuf;
221 			}
222 
223 			if (bind < STB_NUM)
224 				bindstr = symbind[bind];
225 			else {
226 				(void) snprintf(bindbuf, INTSTRLEN,
227 				    "%d", bind);
228 				bindstr = bindbuf;
229 			}
230 
231 
232 			specshndx = 0;
233 			if (sym.st_shndx <  SHN_LORESERVE)
234 				shndx = sym.st_shndx;
235 			else if ((sym.st_shndx != SHN_XINDEX) ||
236 			    (shndxdata == NULL)) {
237 				shndx = sym.st_shndx;
238 				specshndx = 1;
239 			}
240 
241 			if (shndx == SHN_UNDEF) {
242 				shndxstr = (const char *)"UNDEF";
243 
244 			} else if (specshndx) {
245 				if (shndx == SHN_ABS)
246 					shndxstr = (const char *)"ABS";
247 				else if (shndx == SHN_COMMON)
248 					shndxstr = (const char *)"COMM";
249 				else if (shndx == SHN_XINDEX)
250 					shndxstr = (const char *)"XIND";
251 				else {
252 					(void) snprintf(shndxbuf, INTSTRLEN,
253 					    "%ld", shndx);
254 					shndxstr = shndxbuf;
255 				}
256 			} else {
257 				(void) snprintf(shndxbuf, INTSTRLEN,
258 				    "%ld", shndx);
259 				shndxstr = shndxbuf;
260 			}
261 
262 			/*
263 			 * Display the symbol entry.
264 			 */
265 			(void) printf("[%3d] 0x%08llx 0x%08llx %-4s "
266 			    "%-6s %2d %5s %s\n",
267 			    ndx, sym.st_value, sym.st_size,
268 			    typestr, bindstr, sym.st_other, shndxstr,
269 			    elf_strptr(elf, shdr.sh_link, sym.st_name));
270 		}
271 	}
272 }
273 
274 
275 static void
276 process_elf(Elf *elf, char *file, int fd, int member)
277 {
278 	Elf_Cmd	cmd;
279 	Elf	*_elf;
280 
281 	switch (elf_kind(elf)) {
282 	case ELF_K_ELF:
283 		/*
284 		 * This is an ELF file, now attempt to find it's
285 		 * .comment section and to display it.
286 		 */
287 		print_symtab(elf, file);
288 		break;
289 	case ELF_K_AR:
290 		/*
291 		 * Archives contain multiple ELF files, which can each
292 		 * in turn be examined with libelf.
293 		 *
294 		 * The below loop will iterate over each member of the
295 		 * archive and recursively call process_elf().
296 		 */
297 		cmd = ELF_C_READ;
298 		while ((_elf = elf_begin(fd, cmd, elf)) != NULL) {
299 			Elf_Arhdr	*arhdr;
300 			char		buffer[1024];
301 
302 			arhdr = elf_getarhdr(_elf);
303 
304 			/*
305 			 * Build up file names based off of
306 			 * 'archivename(membername)'.
307 			 */
308 			(void) snprintf(buffer, 1024, "%s(%s)",
309 			    file, arhdr->ar_name);
310 
311 			/*
312 			 * Recursively process the ELF members.
313 			 */
314 			process_elf(_elf, buffer, fd, 1);
315 			cmd = elf_next(_elf);
316 			(void) elf_end(_elf);
317 		}
318 		break;
319 	default:
320 		if (!member)
321 			(void) fprintf(stderr,
322 			    "%s: unexpected elf_kind(): 0x%x\n",
323 			    file, elf_kind(elf));
324 		return;
325 	}
326 }
327 
328 int
329 main(int argc, char **argv)
330 {
331 	int	i;
332 
333 	if (argc < 2) {
334 		(void) printf("usage: %s elf_file ...\n", argv[0]);
335 		return (1);
336 	}
337 
338 	/*
339 	 * Initialize the elf library, must be called before elf_begin()
340 	 * can be called.
341 	 */
342 	if (elf_version(EV_CURRENT) == EV_NONE) {
343 		(void) fprintf(stderr,
344 		    "elf_version() failed: %s\n", elf_errmsg(0));
345 		return (1);
346 	}
347 
348 	for (i = 1; i < argc; i++) {
349 		int	fd;
350 		Elf	*elf;
351 		char	*elf_fname;
352 
353 		elf_fname = argv[i];
354 		if ((fd = open(elf_fname, O_RDONLY)) == -1) {
355 			perror("open");
356 			continue;
357 		}
358 
359 		/*
360 		 * Attempt to open an Elf descriptor Read-Only
361 		 * for each file.
362 		 */
363 		if ((elf = elf_begin(fd, ELF_C_READ, 0)) == NULL) {
364 			(void) fprintf(stderr, "elf_begin() failed: %s\n",
365 			    elf_errmsg(0));
366 			(void) close(fd);
367 			continue;
368 		}
369 
370 		/*
371 		 * Process each elf descriptor.
372 		 */
373 		process_elf(elf, elf_fname, fd, 0);
374 		(void) elf_end(elf);
375 		(void) close(fd);
376 	}
377 
378 	return (0);
379 }
380