readelf.c revision 103373
1#include "file.h"
2
3#ifdef BUILTIN_ELF
4#include <string.h>
5#include <ctype.h>
6#include <stdlib.h>
7#ifdef HAVE_UNISTD_H
8#include <unistd.h>
9#endif
10
11#include "readelf.h"
12
13#ifndef lint
14FILE_RCSID("@(#)$Id: readelf.c,v 1.22 2002/07/03 18:26:38 christos Exp $")
15#endif
16
17#ifdef	ELFCORE
18static void dophn_core(int, int, int, off_t, int, size_t);
19#endif
20static void dophn_exec(int, int, int, off_t, int, size_t);
21static void doshn(int, int, int, off_t, int, size_t);
22
23static uint16_t getu16(int, uint16_t);
24static uint32_t getu32(int, uint32_t);
25static uint64_t getu64(int, uint64_t);
26
27static uint16_t
28getu16(int swap, uint16_t value)
29{
30	union {
31		uint16_t ui;
32		char c[2];
33	} retval, tmpval;
34
35	if (swap) {
36		tmpval.ui = value;
37
38		retval.c[0] = tmpval.c[1];
39		retval.c[1] = tmpval.c[0];
40
41		return retval.ui;
42	} else
43		return value;
44}
45
46static uint32_t
47getu32(int swap, uint32_t value)
48{
49	union {
50		uint32_t ui;
51		char c[4];
52	} retval, tmpval;
53
54	if (swap) {
55		tmpval.ui = value;
56
57		retval.c[0] = tmpval.c[3];
58		retval.c[1] = tmpval.c[2];
59		retval.c[2] = tmpval.c[1];
60		retval.c[3] = tmpval.c[0];
61
62		return retval.ui;
63	} else
64		return value;
65}
66
67static uint64_t
68getu64(int swap, uint64_t value)
69{
70	union {
71		uint64_t ui;
72		char c[8];
73	} retval, tmpval;
74
75	if (swap) {
76		tmpval.ui = value;
77
78		retval.c[0] = tmpval.c[7];
79		retval.c[1] = tmpval.c[6];
80		retval.c[2] = tmpval.c[5];
81		retval.c[3] = tmpval.c[4];
82		retval.c[4] = tmpval.c[3];
83		retval.c[5] = tmpval.c[2];
84		retval.c[6] = tmpval.c[1];
85		retval.c[7] = tmpval.c[0];
86
87		return retval.ui;
88	} else
89		return value;
90}
91
92#define sh_addr		(class == ELFCLASS32		\
93			 ? (void *) &sh32		\
94			 : (void *) &sh64)
95#define shs_type	(class == ELFCLASS32		\
96			 ? getu32(swap, sh32.sh_type)	\
97			 : getu32(swap, sh64.sh_type))
98#define ph_addr		(class == ELFCLASS32		\
99			 ? (void *) &ph32		\
100			 : (void *) &ph64)
101#define ph_type		(class == ELFCLASS32		\
102			 ? getu32(swap, ph32.p_type)	\
103			 : getu32(swap, ph64.p_type))
104#define ph_offset	(class == ELFCLASS32		\
105			 ? getu32(swap, ph32.p_offset)	\
106			 : getu64(swap, ph64.p_offset))
107#define nh_size		(class == ELFCLASS32		\
108			 ? sizeof *nh32			\
109			 : sizeof *nh64)
110#define nh_type		(class == ELFCLASS32		\
111			 ? getu32(swap, nh32->n_type)	\
112			 : getu32(swap, nh64->n_type))
113#define nh_namesz	(class == ELFCLASS32		\
114			 ? getu32(swap, nh32->n_namesz)	\
115			 : getu32(swap, nh64->n_namesz))
116#define nh_descsz	(class == ELFCLASS32		\
117			 ? getu32(swap, nh32->n_descsz)	\
118			 : getu32(swap, nh64->n_descsz))
119#define prpsoffsets(i)	(class == ELFCLASS32		\
120			 ? prpsoffsets32[i]		\
121			 : prpsoffsets64[i])
122
123static void
124doshn(int class, int swap, int fd, off_t off, int num, size_t size)
125{
126	Elf32_Shdr sh32;
127	Elf64_Shdr sh64;
128
129	if (lseek(fd, off, SEEK_SET) == -1)
130		error("lseek failed (%s).\n", strerror(errno));
131
132	for ( ; num; num--) {
133		if (read(fd, sh_addr, size) == -1)
134			error("read failed (%s).\n", strerror(errno));
135		if (shs_type == SHT_SYMTAB /* || shs_type == SHT_DYNSYM */) {
136			(void) printf (", not stripped");
137			return;
138		}
139	}
140	(void) printf (", stripped");
141}
142
143/*
144 * Look through the program headers of an executable image, searching
145 * for a PT_INTERP section; if one is found, it's dynamically linked,
146 * otherwise it's statically linked.
147 */
148static void
149dophn_exec(int class, int swap, int fd, off_t off, int num, size_t size)
150{
151	Elf32_Phdr ph32;
152	Elf32_Nhdr *nh32 = NULL;
153	Elf64_Phdr ph64;
154	Elf64_Nhdr *nh64 = NULL;
155	char *linking_style = "statically";
156	char *shared_libraries = "";
157	char nbuf[BUFSIZ];
158	int bufsize;
159	size_t offset, nameoffset;
160
161	if (lseek(fd, off, SEEK_SET) == -1)
162		error("lseek failed (%s).\n", strerror(errno));
163
164  	for ( ; num; num--) {
165  		if (read(fd, ph_addr, size) == -1)
166  			error("read failed (%s).\n", strerror(errno));
167
168		switch (ph_type) {
169		case PT_DYNAMIC:
170			linking_style = "dynamically";
171			break;
172		case PT_INTERP:
173			shared_libraries = " (uses shared libs)";
174			break;
175		case PT_NOTE:
176			/*
177			 * This is a PT_NOTE section; loop through all the notes
178			 * in the section.
179			 */
180			if (lseek(fd, (off_t) ph_offset, SEEK_SET) == -1)
181				error("lseek failed (%s).\n", strerror(errno));
182			bufsize = read(fd, nbuf, BUFSIZ);
183			if (bufsize == -1)
184				error(": " "read failed (%s).\n",
185				    strerror(errno));
186			offset = 0;
187			for (;;) {
188				if (offset >= bufsize)
189					break;
190				if (class == ELFCLASS32)
191					nh32 = (Elf32_Nhdr *)&nbuf[offset];
192				else
193					nh64 = (Elf64_Nhdr *)&nbuf[offset];
194				offset += nh_size;
195
196				if (offset + nh_namesz >= bufsize) {
197					/*
198					 * We're past the end of the buffer.
199					 */
200					break;
201				}
202
203				nameoffset = offset;
204				offset += nh_namesz;
205				offset = ((offset + 3)/4)*4;
206
207				if (offset + nh_descsz >= bufsize)
208					break;
209
210				if (nh_namesz == 4 &&
211				    strcmp(&nbuf[nameoffset], "GNU") == 0 &&
212				    nh_type == NT_GNU_VERSION &&
213				    nh_descsz == 16) {
214					uint32_t *desc =
215					    (uint32_t *)&nbuf[offset];
216
217					printf(", for GNU/");
218					switch (getu32(swap, desc[0])) {
219					case GNU_OS_LINUX:
220						printf("Linux");
221						break;
222					case GNU_OS_HURD:
223						printf("Hurd");
224						break;
225					case GNU_OS_SOLARIS:
226						printf("Solaris");
227						break;
228					default:
229						printf("<unknown>");
230					}
231					printf(" %d.%d.%d",
232					    getu32(swap, desc[1]),
233					    getu32(swap, desc[2]),
234					    getu32(swap, desc[3]));
235				}
236
237				if (nh_namesz == 7 &&
238				    strcmp(&nbuf[nameoffset], "NetBSD") == 0 &&
239				    nh_type == NT_NETBSD_VERSION &&
240				    nh_descsz == 4) {
241					printf(", for NetBSD");
242					/*
243					 * Version number is stuck at 199905,
244					 * and hence is basically content-free.
245					 */
246				}
247
248				if (nh_namesz == 8 &&
249				    strcmp(&nbuf[nameoffset], "FreeBSD") == 0 &&
250				    nh_type == NT_FREEBSD_VERSION &&
251				    nh_descsz == 4) {
252					uint32_t desc = getu32(swap,
253					    *(uint32_t *)&nbuf[offset]);
254					printf(", for FreeBSD");
255					/*
256					 * Contents is __FreeBSD_version,
257					 * whose relation to OS versions is
258					 * defined by a huge table in the
259					 * Porters' Handbook.  Happily, the
260					 * first three digits are the version
261					 * number, at least in versions of
262					 * FreeBSD that use this note.
263					 */
264
265					printf(" %d.%d", desc / 100000,
266					    desc / 10000 % 10);
267					if (desc / 1000 % 10 > 0)
268						printf(".%d",
269						    desc / 1000 % 10);
270				}
271
272				if (nh_namesz == 8 &&
273				    strcmp(&nbuf[nameoffset], "OpenBSD") == 0 &&
274				    nh_type == NT_OPENBSD_VERSION &&
275				    nh_descsz == 4) {
276					printf(", for OpenBSD");
277					/* Content of note is always 0 */
278				}
279			}
280			break;
281		}
282	}
283	printf(", %s linked%s", linking_style, shared_libraries);
284}
285
286#ifdef ELFCORE
287size_t	prpsoffsets32[] = {
288	8,		/* FreeBSD */
289	28,		/* Linux 2.0.36 */
290	32,		/* Linux (I forget which kernel version) */
291	84,		/* SunOS 5.x */
292};
293
294size_t	prpsoffsets64[] = {
295       120,		/* SunOS 5.x, 64-bit */
296};
297
298#define	NOFFSETS32	(sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
299#define NOFFSETS64	(sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
300
301#define NOFFSETS	(class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
302
303/*
304 * Look through the program headers of an executable image, searching
305 * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
306 * "FreeBSD"; if one is found, try looking in various places in its
307 * contents for a 16-character string containing only printable
308 * characters - if found, that string should be the name of the program
309 * that dropped core.  Note: right after that 16-character string is,
310 * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
311 * Linux, a longer string (80 characters, in 5.x, probably other
312 * SVR4-flavored systems, and Linux) containing the start of the
313 * command line for that program.
314 *
315 * The signal number probably appears in a section of type NT_PRSTATUS,
316 * but that's also rather OS-dependent, in ways that are harder to
317 * dissect with heuristics, so I'm not bothering with the signal number.
318 * (I suppose the signal number could be of interest in situations where
319 * you don't have the binary of the program that dropped core; if you
320 * *do* have that binary, the debugger will probably tell you what
321 * signal it was.)
322 */
323
324#define	OS_STYLE_SVR4		0
325#define	OS_STYLE_FREEBSD	1
326#define	OS_STYLE_NETBSD		2
327
328static const char *os_style_names[] = {
329	"SVR4",
330	"FreeBSD",
331	"NetBSD",
332};
333
334static void
335dophn_core(int class, int swap, int fd, off_t off, int num, size_t size)
336{
337	Elf32_Phdr ph32;
338	Elf32_Nhdr *nh32 = NULL;
339	Elf64_Phdr ph64;
340	Elf64_Nhdr *nh64 = NULL;
341	size_t offset, nameoffset, noffset, reloffset;
342	unsigned char c;
343	int i, j;
344	char nbuf[BUFSIZ];
345	int bufsize;
346	int os_style = -1;
347
348	/*
349	 * Loop through all the program headers.
350	 */
351	for ( ; num; num--) {
352		if (lseek(fd, off, SEEK_SET) == -1)
353			error("lseek failed (%s).\n", strerror(errno));
354		if (read(fd, ph_addr, size) == -1)
355			error("read failed (%s).\n", strerror(errno));
356		off += size;
357		if (ph_type != PT_NOTE)
358			continue;
359
360		/*
361		 * This is a PT_NOTE section; loop through all the notes
362		 * in the section.
363		 */
364		if (lseek(fd, (off_t) ph_offset, SEEK_SET) == -1)
365			error("lseek failed (%s).\n", strerror(errno));
366		bufsize = read(fd, nbuf, BUFSIZ);
367		if (bufsize == -1)
368			error(": " "read failed (%s).\n", strerror(errno));
369		offset = 0;
370		for (;;) {
371			if (offset >= bufsize)
372				break;
373			if (class == ELFCLASS32)
374				nh32 = (Elf32_Nhdr *)&nbuf[offset];
375			else
376				nh64 = (Elf64_Nhdr *)&nbuf[offset];
377			offset += nh_size;
378
379			/*
380			 * Check whether this note has the name "CORE" or
381			 * "FreeBSD", or "NetBSD-CORE".
382			 */
383			if (offset + nh_namesz >= bufsize) {
384				/*
385				 * We're past the end of the buffer.
386				 */
387				break;
388			}
389
390			nameoffset = offset;
391			offset += nh_namesz;
392			offset = ((offset + 3)/4)*4;
393
394			/*
395			 * Sigh.  The 2.0.36 kernel in Debian 2.1, at
396			 * least, doesn't correctly implement name
397			 * sections, in core dumps, as specified by
398			 * the "Program Linking" section of "UNIX(R) System
399			 * V Release 4 Programmer's Guide: ANSI C and
400			 * Programming Support Tools", because my copy
401			 * clearly says "The first 'namesz' bytes in 'name'
402			 * contain a *null-terminated* [emphasis mine]
403			 * character representation of the entry's owner
404			 * or originator", but the 2.0.36 kernel code
405			 * doesn't include the terminating null in the
406			 * name....
407			 */
408			if (os_style == -1) {
409				if ((nh_namesz == 4 &&
410				     strncmp(&nbuf[nameoffset],
411					    "CORE", 4) == 0) ||
412				    (nh_namesz == 5 &&
413				     strcmp(&nbuf[nameoffset],
414				     	    "CORE") == 0)) {
415					os_style = OS_STYLE_SVR4;
416				} else
417				if ((nh_namesz == 8 &&
418				     strcmp(&nbuf[nameoffset],
419				     	    "FreeBSD") == 0)) {
420					os_style = OS_STYLE_FREEBSD;
421				} else
422				if ((nh_namesz >= 11 &&
423				     strncmp(&nbuf[nameoffset],
424				     	     "NetBSD-CORE", 11) == 0)) {
425					os_style = OS_STYLE_NETBSD;
426				} else
427					continue;
428				printf(", %s-style", os_style_names[os_style]);
429			}
430
431			if (os_style == OS_STYLE_NETBSD &&
432			    nh_type == NT_NETBSD_CORE_PROCINFO) {
433				uint32_t signo;
434
435				/*
436				 * Extract the program name.  It is at
437				 * offset 0x7c, and is up to 32-bytes,
438				 * including the terminating NUL.
439				 */
440				printf(", from '%.31s'", &nbuf[offset + 0x7c]);
441
442				/*
443				 * Extract the signal number.  It is at
444				 * offset 0x08.
445				 */
446				memcpy(&signo, &nbuf[offset + 0x08],
447				    sizeof(signo));
448				printf(" (signal %u)", getu32(swap, signo));
449			} else
450			if (os_style != OS_STYLE_NETBSD &&
451			    nh_type == NT_PRPSINFO) {
452				/*
453				 * Extract the program name.  We assume
454				 * it to be 16 characters (that's what it
455				 * is in SunOS 5.x and Linux).
456				 *
457				 * Unfortunately, it's at a different offset
458				 * in varous OSes, so try multiple offsets.
459				 * If the characters aren't all printable,
460				 * reject it.
461				 */
462				for (i = 0; i < NOFFSETS; i++) {
463					reloffset = prpsoffsets(i);
464					noffset = offset + reloffset;
465					for (j = 0; j < 16;
466					    j++, noffset++, reloffset++) {
467						/*
468						 * Make sure we're not past
469						 * the end of the buffer; if
470						 * we are, just give up.
471						 */
472						if (noffset >= bufsize)
473							goto tryanother;
474
475						/*
476						 * Make sure we're not past
477						 * the end of the contents;
478						 * if we are, this obviously
479						 * isn't the right offset.
480						 */
481						if (reloffset >= nh_descsz)
482							goto tryanother;
483
484						c = nbuf[noffset];
485						if (c == '\0') {
486							/*
487							 * A '\0' at the
488							 * beginning is
489							 * obviously wrong.
490							 * Any other '\0'
491							 * means we're done.
492							 */
493							if (j == 0)
494								goto tryanother;
495							else
496								break;
497						} else {
498							/*
499							 * A nonprintable
500							 * character is also
501							 * wrong.
502							 */
503#define isquote(c) (strchr("'\"`", (c)) != NULL)
504							if (!isprint(c) ||
505							     isquote(c))
506								goto tryanother;
507						}
508					}
509
510					/*
511					 * Well, that worked.
512					 */
513					printf(", from '%.16s'",
514					    &nbuf[offset + prpsoffsets(i)]);
515					break;
516
517				tryanother:
518					;
519				}
520				break;
521			}
522			offset += nh_descsz;
523			offset = ((offset + 3)/4)*4;
524		}
525	}
526}
527#endif
528
529void
530tryelf(int fd, unsigned char *buf, int nbytes)
531{
532	union {
533		int32_t l;
534		char c[sizeof (int32_t)];
535	} u;
536	int class;
537	int swap;
538
539	/*
540	 * If we can't seek, it must be a pipe, socket or fifo.
541	 */
542	if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
543		fd = pipe2file(fd, buf, nbytes);
544
545	/*
546	 * ELF executables have multiple section headers in arbitrary
547	 * file locations and thus file(1) cannot determine it from easily.
548	 * Instead we traverse thru all section headers until a symbol table
549	 * one is found or else the binary is stripped.
550	 */
551	if (buf[EI_MAG0] != ELFMAG0
552	    || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
553	    || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
554	    return;
555
556
557	class = buf[4];
558
559	if (class == ELFCLASS32) {
560		Elf32_Ehdr elfhdr;
561		if (nbytes <= sizeof (Elf32_Ehdr))
562			return;
563
564
565		u.l = 1;
566		(void) memcpy(&elfhdr, buf, sizeof elfhdr);
567		swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[5];
568
569		if (getu16(swap, elfhdr.e_type) == ET_CORE)
570#ifdef ELFCORE
571			dophn_core(class, swap,
572				   fd,
573				   getu32(swap, elfhdr.e_phoff),
574				   getu16(swap, elfhdr.e_phnum),
575				   getu16(swap, elfhdr.e_phentsize));
576#else
577			;
578#endif
579		else {
580			if (getu16(swap, elfhdr.e_type) == ET_EXEC) {
581				dophn_exec(class, swap,
582					   fd,
583					   getu32(swap, elfhdr.e_phoff),
584					   getu16(swap, elfhdr.e_phnum),
585					   getu16(swap, elfhdr.e_phentsize));
586			}
587			doshn(class, swap,
588			      fd,
589			      getu32(swap, elfhdr.e_shoff),
590			      getu16(swap, elfhdr.e_shnum),
591			      getu16(swap, elfhdr.e_shentsize));
592		}
593		return;
594	}
595
596        if (class == ELFCLASS64) {
597		Elf64_Ehdr elfhdr;
598		if (nbytes <= sizeof (Elf64_Ehdr))
599			return;
600
601
602		u.l = 1;
603		(void) memcpy(&elfhdr, buf, sizeof elfhdr);
604		swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[5];
605
606		if (getu16(swap, elfhdr.e_type) == ET_CORE)
607#ifdef ELFCORE
608			dophn_core(class, swap,
609				   fd,
610#ifdef USE_ARRAY_FOR_64BIT_TYPES
611				   getu32(swap, elfhdr.e_phoff[1]),
612#else
613				   getu64(swap, elfhdr.e_phoff),
614#endif
615				   getu16(swap, elfhdr.e_phnum),
616				   getu16(swap, elfhdr.e_phentsize));
617#else
618			;
619#endif
620		else
621		{
622			if (getu16(swap, elfhdr.e_type) == ET_EXEC) {
623				dophn_exec(class, swap,
624					   fd,
625#ifdef USE_ARRAY_FOR_64BIT_TYPES
626					   getu32(swap, elfhdr.e_phoff[1]),
627#else
628					   getu64(swap, elfhdr.e_phoff),
629#endif
630					   getu16(swap, elfhdr.e_phnum),
631					   getu16(swap, elfhdr.e_phentsize));
632			}
633			doshn(class, swap,
634			      fd,
635#ifdef USE_ARRAY_FOR_64BIT_TYPES
636			      getu32(swap, elfhdr.e_shoff[1]),
637#else
638			      getu64(swap, elfhdr.e_shoff),
639#endif
640			      getu16(swap, elfhdr.e_shnum),
641			      getu16(swap, elfhdr.e_shentsize));
642		}
643		return;
644	}
645}
646#endif
647