2 * Copyright (c) Christos Zoulas 2003.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice immediately at the beginning of the file, without modification,
10 * this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 FILE_RCSID("@(#)Id: readelf.c,v 1.22 2002/07/03 18:26:38 christos Exp ")
39 static size_t donote(const fmagic fm, unsigned char *, size_t, size_t, int);
41 #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align)
45 getu16(const fmagic fm, uint16_t value)
56 retval.c[0] = tmpval.c[1];
57 retval.c[1] = tmpval.c[0];
65 getu32(const fmagic fm, uint32_t value)
76 retval.c[0] = tmpval.c[3];
77 retval.c[1] = tmpval.c[2];
78 retval.c[2] = tmpval.c[1];
79 retval.c[3] = tmpval.c[0];
87 getu64(const fmagic fm, uint64_t value)
98 retval.c[0] = tmpval.c[7];
99 retval.c[1] = tmpval.c[6];
100 retval.c[2] = tmpval.c[5];
101 retval.c[3] = tmpval.c[4];
102 retval.c[4] = tmpval.c[3];
103 retval.c[5] = tmpval.c[2];
104 retval.c[6] = tmpval.c[1];
105 retval.c[7] = tmpval.c[0];
113 #define sh_addr (fm->cls == ELFCLASS32 \
116 #define sh_size (fm->cls == ELFCLASS32 \
119 #define shs_type (fm->cls == ELFCLASS32 \
120 ? getu32(fm, sh32.sh_type) \
121 : getu32(fm, sh64.sh_type))
122 #define ph_addr (fm->cls == ELFCLASS32 \
125 #define ph_size (fm->cls == ELFCLASS32 \
128 #define ph_type (fm->cls == ELFCLASS32 \
129 ? getu32(fm, ph32.p_type) \
130 : getu32(fm, ph64.p_type))
131 #define ph_offset (fm->cls == ELFCLASS32 \
132 ? getu32(fm, ph32.p_offset) \
133 : getu64(fm, ph64.p_offset))
134 #define ph_align (int)((fm->cls == ELFCLASS32 \
135 ? (off_t) (ph32.p_align ? \
136 getu32(fm, ph32.p_align) : 4) \
137 : (off_t) (ph64.p_align ? \
138 getu64(fm, ph64.p_align) : 4)))
139 #define nh_size (fm->cls == ELFCLASS32 \
142 #define nh_type (fm->cls == ELFCLASS32 \
143 ? getu32(fm, nh32.n_type) \
144 : getu32(fm, nh64.n_type))
145 #define nh_namesz (fm->cls == ELFCLASS32 \
146 ? getu32(fm, nh32.n_namesz) \
147 : getu32(fm, nh64.n_namesz))
148 #define nh_descsz (fm->cls == ELFCLASS32 \
149 ? getu32(fm, nh32.n_descsz) \
150 : getu32(fm, nh64.n_descsz))
151 #define prpsoffsets(i) (fm->cls == ELFCLASS32 \
156 /*@unchecked@*/ /*@observer@*/
157 static size_t prpsoffsets32[] = {
159 28, /* Linux 2.0.36 */
160 32, /* Linux (I forget which kernel version) */
164 /*@unchecked@*/ /*@observer@*/
165 static size_t prpsoffsets64[] = {
166 120 /* SunOS 5.x, 64-bit */
169 #define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
170 #define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
172 #define NOFFSETS (fm->cls == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
175 * Look through the program headers of an executable image, searching
176 * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
177 * "FreeBSD"; if one is found, try looking in various places in its
178 * contents for a 16-character string containing only printable
179 * characters - if found, that string should be the name of the program
180 * that dropped core. Note: right after that 16-character string is,
181 * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
182 * Linux, a longer string (80 characters, in 5.x, probably other
183 * SVR4-flavored systems, and Linux) containing the start of the
184 * command line for that program.
186 * The signal number probably appears in a section of type NT_PRSTATUS,
187 * but that's also rather OS-dependent, in ways that are harder to
188 * dissect with heuristics, so I'm not bothering with the signal number.
189 * (I suppose the signal number could be of interest in situations where
190 * you don't have the binary of the program that dropped core; if you
191 * *do* have that binary, the debugger will probably tell you what
195 #define OS_STYLE_SVR4 0
196 #define OS_STYLE_FREEBSD 1
197 #define OS_STYLE_NETBSD 2
199 /*@unchecked@*/ /*@observer@*/
200 static const char *os_style_names[] = {
208 dophn_core(fmagic fm, off_t off, int num, size_t size)
209 /*@globals fileSystem @*/
210 /*@modifies fm, fileSystem @*/
215 unsigned char nbuf[BUFSIZ];
218 if (size != ph_size) {
219 error(EXIT_FAILURE, 0, "corrupted program header size.\n");
224 * Loop through all the program headers.
226 for ( ; num; num--) {
227 if (lseek(fm->fd, off, SEEK_SET) == (off_t)-1) {
228 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
231 if (read(fm->fd, ph_addr, ph_size) == -1) {
232 error(EXIT_FAILURE, 0, "read failed (%s).\n", strerror(errno));
236 if (ph_type != PT_NOTE)
240 * This is a PT_NOTE section; loop through all the notes
243 if (lseek(fm->fd, (off_t) ph_offset, SEEK_SET) == (off_t)-1) {
244 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
247 bufsize = read(fm->fd, nbuf, BUFSIZ);
249 error(EXIT_FAILURE, 0, ": " "read failed (%s).\n", strerror(errno));
254 if (offset >= (size_t)bufsize)
255 /*@innerbreak@*/ break;
256 offset = donote(fm, nbuf, offset, (size_t)bufsize,
267 donote(const fmagic fm, unsigned char *nbuf, size_t offset, size_t size,
277 if (fm->cls == ELFCLASS32)
278 memcpy(&nh32, &nbuf[offset], sizeof(nh32));
280 memcpy(&nh64, &nbuf[offset], sizeof(nh64));
283 if ((nh_namesz == 0) && (nh_descsz == 0)) {
285 * We're out of note headers.
291 doff = ELF_ALIGN(offset + nh_namesz);
293 if (offset + nh_namesz >= size) {
295 * We're past the end of the buffer.
300 offset = ELF_ALIGN(doff + nh_descsz);
301 if (offset + nh_descsz >= size)
304 if (nh_namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
305 nh_type == NT_GNU_VERSION && nh_descsz == 16) {
307 (void)memcpy(desc, &nbuf[doff], sizeof(desc));
309 file_printf(fm, ", for GNU/");
310 switch (getu32(fm, desc[0])) {
312 file_printf(fm, "Linux");
315 file_printf(fm, "Hurd");
318 file_printf(fm, "Solaris");
321 file_printf(fm, "<unknown>");
323 file_printf(fm, " %d.%d.%d", getu32(fm, desc[1]),
324 getu32(fm, desc[2]), getu32(fm, desc[3]));
328 if (nh_namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0 &&
329 nh_type == NT_NETBSD_VERSION && nh_descsz == 4) {
331 (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
332 desc = getu32(fm, desc);
334 file_printf(fm, ", for NetBSD");
336 * The version number used to be stuck as 199905, and was thus
337 * basically content-free. Newer versions of NetBSD have fixed
338 * this and now use the encoding of __NetBSD_Version__:
344 * r = release ["",A-Z,Z[A-Z] but numeric]
347 if (desc > 100000000U) {
348 u_int ver_patch = (desc / 100) % 100;
349 u_int ver_rel = (desc / 10000) % 100;
350 u_int ver_min = (desc / 1000000) % 100;
351 u_int ver_maj = desc / 100000000;
353 file_printf(fm, " %u.%u", ver_maj, ver_min);
354 if (ver_rel == 0 && ver_patch != 0) {
355 file_printf(fm, ".%u", ver_patch);
356 } else if (ver_rel != 0) {
357 while (ver_rel > 26) {
358 file_printf(fm, "Z");
361 file_printf(fm, "%c", 'A' + ver_rel - 1);
367 if (nh_namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0 &&
368 nh_type == NT_FREEBSD_VERSION && nh_descsz == 4) {
370 (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
371 desc = getu32(fm, desc);
372 file_printf(fm, ", for FreeBSD");
374 * Contents is __FreeBSD_version, whose relation to OS versions
375 * is defined by a huge table in the Porters' Handbook. Happily,
376 * the first three digits are the version number, at least in
377 * versions of FreeBSD that use this note.
379 file_printf(fm, " %d.%d", desc / 100000, desc / 10000 % 10);
380 if (desc / 1000 % 10 > 0)
381 file_printf(fm, ".%d", desc / 1000 % 10);
385 if (nh_namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 &&
386 nh_type == NT_OPENBSD_VERSION && nh_descsz == 4) {
387 file_printf(fm, ", for OpenBSD");
388 /* Content of note is always 0 */
393 * Sigh. The 2.0.36 kernel in Debian 2.1, at
394 * least, doesn't correctly implement name
395 * sections, in core dumps, as specified by
396 * the "Program Linking" section of "UNIX(R) System
397 * V Release 4 Programmer's Guide: ANSI C and
398 * Programming Support Tools", because my copy
399 * clearly says "The first 'namesz' bytes in 'name'
400 * contain a *null-terminated* [emphasis mine]
401 * character representation of the entry's owner
402 * or originator", but the 2.0.36 kernel code
403 * doesn't include the terminating null in the
406 if ((nh_namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) ||
407 (nh_namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) {
408 os_style = OS_STYLE_SVR4;
411 if ((nh_namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) {
412 os_style = OS_STYLE_FREEBSD;
415 if ((nh_namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11)
417 os_style = OS_STYLE_NETBSD;
422 file_printf(fm, ", %s-style", os_style_names[os_style]);
424 if (os_style == OS_STYLE_NETBSD && nh_type == NT_NETBSD_CORE_PROCINFO) {
427 * Extract the program name. It is at
428 * offset 0x7c, and is up to 32-bytes,
429 * including the terminating NUL.
431 file_printf(fm, ", from '%.31s'", &nbuf[doff + 0x7c]);
434 * Extract the signal number. It is at
437 memcpy(&signo, &nbuf[doff + 0x08],
439 file_printf(fm, " (signal %u)", getu32(fm, signo));
441 } else if (os_style != OS_STYLE_NETBSD && nh_type == NT_PRPSINFO) {
445 * Extract the program name. We assume
446 * it to be 16 characters (that's what it
447 * is in SunOS 5.x and Linux).
449 * Unfortunately, it's at a different offset
450 * in varous OSes, so try multiple offsets.
451 * If the characters aren't all printable,
454 for (i = 0; i < NOFFSETS; i++) {
455 size_t reloffset = prpsoffsets(i);
456 size_t noffset = doff + reloffset;
457 for (j = 0; j < 16; j++, noffset++, reloffset++) {
459 * Make sure we're not past
460 * the end of the buffer; if
461 * we are, just give up.
467 * Make sure we're not past
468 * the end of the contents;
469 * if we are, this obviously
470 * isn't the right offset.
472 if (reloffset >= nh_descsz)
494 #define isquote(c) (strchr("'\"`", (c)) != NULL)
495 if (!isprint(c) || isquote(c))
503 file_printf(fm, ", from '%.16s'",
504 &nbuf[doff + prpsoffsets(i)]);
517 doshn(fmagic fm, off_t off, int num, size_t size)
518 /*@globals fileSystem @*/
519 /*@modifies fm, fileSystem @*/
524 if (size != sh_size) {
525 error(EXIT_FAILURE, 0, "corrupted program header size.\n");
529 if (lseek(fm->fd, off, SEEK_SET) == (off_t)-1) {
530 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
534 for ( ; num; num--) {
535 if (read(fm->fd, sh_addr, sh_size) == -1) {
536 error(EXIT_FAILURE, 0, "read failed (%s).\n", strerror(errno));
539 if (shs_type == SHT_SYMTAB /* || shs_type == SHT_DYNSYM */) {
540 file_printf(fm, ", not stripped");
544 file_printf(fm, ", stripped");
549 * Look through the program headers of an executable image, searching
550 * for a PT_INTERP section; if one is found, it's dynamically linked,
551 * otherwise it's statically linked.
555 dophn_exec(fmagic fm, off_t off, int num, size_t size)
556 /*@globals fileSystem @*/
557 /*@modifies fm, fileSystem @*/
561 const char *linking_style = "statically";
562 const char *shared_libraries = "";
563 unsigned char nbuf[BUFSIZ];
568 if (size != ph_size) {
569 error(EXIT_FAILURE, 0, "corrupted program header size.\n");
573 if (lseek(fm->fd, off, SEEK_SET) == (off_t)-1) {
574 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
578 for ( ; num; num--) {
579 if (read(fm->fd, ph_addr, ph_size) == -1) {
580 error(EXIT_FAILURE, 0, "read failed (%s).\n", strerror(errno));
583 if ((savedoffset = lseek(fm->fd, (off_t)0, SEEK_CUR)) == (off_t)-1) {
584 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
590 linking_style = "dynamically";
591 /*@switchbreak@*/ break;
593 shared_libraries = " (uses shared libs)";
594 /*@switchbreak@*/ break;
597 * This is a PT_NOTE section; loop through all the notes
600 if (lseek(fm->fd, (off_t) ph_offset, SEEK_SET) == -1) {
601 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
604 bufsize = read(fm->fd, nbuf, sizeof(nbuf));
606 error(EXIT_FAILURE, 0, ": " "read failed (%s).\n",
612 if (offset >= (size_t)bufsize)
613 /*@innerbreak@*/ break;
614 offset = donote(fm, nbuf, offset,
615 (size_t)bufsize, ph_align);
617 if ((lseek(fm->fd, savedoffset + offset, SEEK_SET)) == (off_t)-1) {
618 error(EXIT_FAILURE, 0, "lseek failed (%s).\n", strerror(errno));
621 /*@switchbreak@*/ break;
624 file_printf(fm, ", %s linked%s", linking_style, shared_libraries);
635 char c[sizeof (int32_t)];
640 * If we can't seek, it must be a pipe, socket or fifo.
642 if((lseek(fm->fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
643 fm->fd = file_pipe2file(fm->fd, fm->buf, fm->nb);
646 * ELF executables have multiple section headers in arbitrary
647 * file locations and thus file(1) cannot determine it from easily.
648 * Instead we traverse thru all section headers until a symbol table
649 * one is found or else the binary is stripped.
651 if (fm->buf[EI_MAG0] != ELFMAG0
652 || (fm->buf[EI_MAG1] != ELFMAG1 && fm->buf[EI_MAG1] != OLFMAG1)
653 || fm->buf[EI_MAG2] != ELFMAG2 || fm->buf[EI_MAG3] != ELFMAG3)
656 fm->cls = fm->buf[EI_CLASS];
658 if (fm->cls == ELFCLASS32) {
660 if (fm->nb <= sizeof (elfhdr))
665 (void) memcpy(&elfhdr, fm->buf, sizeof elfhdr);
667 fm->swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[EI_DATA];
670 if (getu16(fm, elfhdr.e_type) == ET_CORE)
673 getu32(fm, elfhdr.e_phoff),
674 getu16(fm, elfhdr.e_phnum),
675 getu16(fm, elfhdr.e_phentsize));
680 if (getu16(fm, elfhdr.e_type) == ET_EXEC) {
682 getu32(fm, elfhdr.e_phoff),
683 getu16(fm, elfhdr.e_phnum),
684 getu16(fm, elfhdr.e_phentsize));
687 getu32(fm, elfhdr.e_shoff),
688 getu16(fm, elfhdr.e_shnum),
689 getu16(fm, elfhdr.e_shentsize));
694 if (fm->cls == ELFCLASS64) {
696 if (fm->nb <= sizeof (elfhdr))
700 (void) memcpy(&elfhdr, fm->buf, sizeof elfhdr);
702 fm->swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[EI_DATA];
705 if (getu16(fm, elfhdr.e_type) == ET_CORE)
708 #ifdef USE_ARRAY_FOR_64BIT_TYPES
709 getu32(fm, elfhdr.e_phoff[1]),
711 getu64(fm, elfhdr.e_phoff),
713 getu16(fm, elfhdr.e_phnum),
714 getu16(fm, elfhdr.e_phentsize));
720 if (getu16(fm, elfhdr.e_type) == ET_EXEC) {
722 #ifdef USE_ARRAY_FOR_64BIT_TYPES
723 getu32(fm, elfhdr.e_phoff[1]),
725 getu64(fm, elfhdr.e_phoff),
727 getu16(fm, elfhdr.e_phnum),
728 getu16(fm, elfhdr.e_phentsize));
731 #ifdef USE_ARRAY_FOR_64BIT_TYPES
732 getu32(fm, elfhdr.e_shoff[1]),
734 getu64(fm, elfhdr.e_shoff),
736 getu16(fm, elfhdr.e_shnum),
737 getu16(fm, elfhdr.e_shentsize));
743 #endif /* BUILTIN_ELF */