xref: /illumos-gate/usr/src/cmd/sgs/elfdump/common/main.c (revision 4e93fb0f6383eaac21897dcdae56b87118131e4d)
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 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Dump an elf file.
30  */
31 #include	<sys/param.h>
32 #include	<fcntl.h>
33 #include	<stdio.h>
34 #include	<libelf.h>
35 #include	<link.h>
36 #include	<stdarg.h>
37 #include	<unistd.h>
38 #include	<libgen.h>
39 #include	<libintl.h>
40 #include	<locale.h>
41 #include	<errno.h>
42 #include	<strings.h>
43 #include	<debug.h>
44 #include	<conv.h>
45 #include	<msg.h>
46 #include	<_elfdump.h>
47 
48 const Cache	cache_init = {NULL, NULL, NULL};
49 
50 const char *
51 _elfdump_msg(Msg mid)
52 {
53 	return (gettext(MSG_ORIG(mid)));
54 }
55 
56 /*
57  * Determine whether a symbol name should be demangled.
58  */
59 const char *
60 demangle(const char *name, uint_t flags)
61 {
62 	if (flags & FLG_DEMANGLE)
63 		return (Elf_demangle_name(name));
64 	else
65 		return ((char *)name);
66 }
67 
68 /*
69  * Define our own standard error routine.
70  */
71 void
72 failure(const char *file, const char *func)
73 {
74 	(void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE),
75 	    file, func, elf_errmsg(elf_errno()));
76 }
77 
78 /*
79  * The full usage message
80  */
81 static void
82 detail_usage()
83 {
84 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1));
85 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2));
86 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3));
87 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4));
88 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5));
89 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6));
90 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7));
91 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8));
92 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9));
93 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9_1));
94 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10));
95 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11));
96 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12));
97 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13));
98 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14));
99 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15));
100 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16));
101 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17));
102 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18));
103 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19));
104 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20));
105 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21));
106 }
107 
108 static void
109 decide(const char *file, Elf *elf, uint_t flags, char *Nname, int wfd)
110 {
111 	if (gelf_getclass(elf) == ELFCLASS64)
112 		regular64(file, elf, flags, Nname, wfd);
113 	else
114 		regular32(file, elf, flags, Nname, wfd);
115 }
116 
117 static void
118 archive(const char *file, int fd, Elf *elf, uint_t flags, char *Nname,
119     int wfd)
120 {
121 	Elf_Cmd		cmd = ELF_C_READ;
122 	Elf_Arhdr	*arhdr;
123 	Elf		*_elf = 0;
124 	size_t		ptr;
125 	Elf_Arsym	*arsym = 0;
126 
127 	/*
128 	 * Determine if the archive symbol table itself is required.
129 	 */
130 	if ((flags & FLG_SYMBOLS) && ((Nname == NULL) ||
131 	    (strcmp(Nname, MSG_ORIG(MSG_ELF_ARSYM)) == 0))) {
132 		/*
133 		 * Get the archive symbol table.
134 		 */
135 		if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) {
136 			/*
137 			 * The arsym could be 0 even though there was no error.
138 			 * Print the error message only when there was
139 			 * real error from elf_getarsym().
140 			 */
141 			failure(file, MSG_ORIG(MSG_ELF_GETARSYM));
142 			return;
143 		}
144 	}
145 
146 	/*
147 	 * Print the archive symbol table only when the archive symbol
148 	 * table exists and it was requested to print.
149 	 */
150 	if (arsym) {
151 		size_t		cnt;
152 		char		index[MAXNDXSIZE];
153 		size_t		offset = 0, _offset = 0;
154 
155 		/*
156 		 * Print out all the symbol entries.
157 		 */
158 		dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB));
159 		dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS));
160 
161 		for (cnt = 0; cnt < ptr; cnt++, arsym++) {
162 			/*
163 			 * For each object obtain an elf descriptor so that we
164 			 * can establish the members name.  Note, we have had
165 			 * archives where the archive header has not been
166 			 * obtainable so be lenient with errors.
167 			 */
168 			if ((offset == 0) || ((arsym->as_off != 0) &&
169 			    (arsym->as_off != _offset))) {
170 
171 				if (_elf)
172 					(void) elf_end(_elf);
173 
174 				if (elf_rand(elf, arsym->as_off) !=
175 				    arsym->as_off) {
176 					failure(file, MSG_ORIG(MSG_ELF_RAND));
177 					arhdr = 0;
178 				} else if ((_elf = elf_begin(fd,
179 				    ELF_C_READ, elf)) == 0) {
180 					failure(file, MSG_ORIG(MSG_ELF_BEGIN));
181 					arhdr = 0;
182 				} else if ((arhdr = elf_getarhdr(_elf)) == 0) {
183 					failure(file,
184 					    MSG_ORIG(MSG_ELF_GETARHDR));
185 					arhdr = 0;
186 				}
187 
188 				_offset = arsym->as_off;
189 				if (offset == 0)
190 					offset = _offset;
191 			}
192 
193 			(void) snprintf(index, MAXNDXSIZE,
194 			    MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt));
195 			if (arsym->as_off)
196 				dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM1), index,
197 				    /* LINTED */
198 				    (int)arsym->as_off, arhdr ? arhdr->ar_name :
199 				    MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ?
200 				    demangle(arsym->as_name, flags) :
201 				    MSG_INTL(MSG_STR_NULL)));
202 			else
203 				dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM2), index,
204 				    /* LINTED */
205 				    (int)arsym->as_off);
206 		}
207 
208 		if (_elf)
209 			(void) elf_end(_elf);
210 
211 		/*
212 		 * If we only need the archive symbol table return.
213 		 */
214 		if ((flags & FLG_SYMBOLS) && Nname &&
215 		    (strcmp(Nname, MSG_ORIG(MSG_ELF_ARSYM)) == 0))
216 			return;
217 
218 		/*
219 		 * Reset elf descriptor in preparation for processing each
220 		 * member.
221 		 */
222 		if (offset)
223 			(void) elf_rand(elf, offset);
224 	}
225 
226 	/*
227 	 * Process each object within the archive.
228 	 */
229 	while ((_elf = elf_begin(fd, cmd, elf)) != NULL) {
230 		char	name[MAXPATHLEN];
231 
232 		if ((arhdr = elf_getarhdr(_elf)) == NULL) {
233 			failure(file, MSG_ORIG(MSG_ELF_GETARHDR));
234 			return;
235 		}
236 		if (*arhdr->ar_name != '/') {
237 			(void) snprintf(name, MAXPATHLEN,
238 			    MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name);
239 			dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name);
240 
241 			switch (elf_kind(_elf)) {
242 			case ELF_K_AR:
243 				archive(name, fd, _elf, flags, Nname, wfd);
244 				break;
245 			case ELF_K_ELF:
246 				decide(name, _elf, flags, Nname, wfd);
247 				break;
248 			default:
249 				(void) fprintf(stderr,
250 				    MSG_INTL(MSG_ERR_BADFILE), name);
251 				break;
252 			}
253 		}
254 
255 		cmd = elf_next(_elf);
256 		(void) elf_end(_elf);
257 	}
258 }
259 
260 int
261 main(int argc, char **argv, char **envp)
262 {
263 	Elf		*elf;
264 	int		var, fd, wfd = 0;
265 	char		*Nname = NULL, *wname = 0;
266 	uint_t		flags = 0;
267 
268 	/*
269 	 * If we're on a 64-bit kernel, try to exec a full 64-bit version of
270 	 * the binary.  If successful, conv_check_native() won't return.
271 	 */
272 	(void) conv_check_native(argv, envp);
273 
274 	/*
275 	 * Establish locale.
276 	 */
277 	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
278 	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
279 
280 	(void) setvbuf(stdout, NULL, _IOLBF, 0);
281 	(void) setvbuf(stderr, NULL, _IOLBF, 0);
282 
283 	opterr = 0;
284 	while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
285 		switch (var) {
286 		case 'C':
287 			flags |= FLG_DEMANGLE;
288 			break;
289 		case 'c':
290 			flags |= FLG_SHDR;
291 			break;
292 		case 'd':
293 			flags |= FLG_DYNAMIC;
294 			break;
295 		case 'e':
296 			flags |= FLG_EHDR;
297 			break;
298 		case 'G':
299 			flags |= FLG_GOT;
300 			break;
301 		case 'g':
302 			flags |= FLG_GROUP;
303 			break;
304 		case 'H':
305 			flags |= FLG_CAP;
306 			break;
307 		case 'h':
308 			flags |= FLG_HASH;
309 			break;
310 		case 'i':
311 			flags |= FLG_INTERP;
312 			break;
313 		case 'k':
314 			flags |= FLG_CHECKSUM;
315 			break;
316 		case 'l':
317 			flags |= FLG_LONGNAME;
318 			break;
319 		case 'm':
320 			flags |= FLG_MOVE;
321 			break;
322 		case 'N':
323 			Nname = optarg;
324 			break;
325 		case 'n':
326 			flags |= FLG_NOTE;
327 			break;
328 		case 'p':
329 			flags |= FLG_PHDR;
330 			break;
331 		case 'r':
332 			flags |= FLG_RELOC;
333 			break;
334 		case 'S':
335 			flags |= FLG_SORT;
336 			break;
337 		case 's':
338 			flags |= FLG_SYMBOLS;
339 			break;
340 		case 'u':
341 			flags |= FLG_UNWIND;
342 			break;
343 		case 'v':
344 			flags |= FLG_VERSIONS;
345 			break;
346 		case 'w':
347 			wname = optarg;
348 			break;
349 		case 'y':
350 			flags |= FLG_SYMINFO;
351 			break;
352 		case '?':
353 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
354 			    basename(argv[0]));
355 			detail_usage();
356 			return (1);
357 		default:
358 			break;
359 		}
360 	}
361 
362 	/*
363 	 * Validate any arguments.
364 	 */
365 	if ((flags & ~(FLG_DEMANGLE | FLG_LONGNAME)) == 0) {
366 		if (!wname && !Nname) {
367 			flags |= FLG_EVERYTHING;
368 		} else if (!wname || !Nname) {
369 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
370 			    basename(argv[0]));
371 			return (1);
372 		}
373 	}
374 
375 	if ((var = argc - optind) == 0) {
376 		(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
377 		    basename(argv[0]));
378 		return (1);
379 	}
380 
381 	/*
382 	 * If the -l/-C option is specified, set up the liblddbg.so.
383 	 */
384 	if (flags & FLG_LONGNAME)
385 		dbg_desc->d_extra |= DBG_E_LONG;
386 	if (flags & FLG_DEMANGLE)
387 		dbg_desc->d_extra |= DBG_E_DEMANGLE;
388 
389 	/*
390 	 * If the -w option has indicated an output file open it.  It's
391 	 * arguable whether this option has much use when multiple files are
392 	 * being processed.
393 	 */
394 	if (wname) {
395 		if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC),
396 		    0666)) < 0) {
397 			int err = errno;
398 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
399 			    wname, strerror(err));
400 			wfd = 0;
401 		}
402 	}
403 
404 	/*
405 	 * Open the input file and initialize the elf interface.
406 	 */
407 	for (; optind < argc; optind++) {
408 		const char	*file = argv[optind];
409 
410 		if ((fd = open(argv[optind], O_RDONLY)) == -1) {
411 			int err = errno;
412 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
413 			    file, strerror(err));
414 			continue;
415 		}
416 		(void) elf_version(EV_CURRENT);
417 		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
418 			failure(file, MSG_ORIG(MSG_ELF_BEGIN));
419 			(void) close(fd);
420 			continue;
421 		}
422 
423 		if (var > 1)
424 			dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file);
425 
426 		switch (elf_kind(elf)) {
427 		case ELF_K_AR:
428 			archive(file, fd, elf, flags, Nname, wfd);
429 			break;
430 		case ELF_K_ELF:
431 			decide(file, elf, flags, Nname, wfd);
432 			break;
433 		default:
434 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file);
435 			break;
436 		}
437 
438 		(void) close(fd);
439 		(void) elf_end(elf);
440 	}
441 
442 	if (wfd)
443 		(void) close(wfd);
444 	return (0);
445 }
446