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