readelf.c revision 68349
168349Sobrien#include "file.h" 268349Sobrien 368349Sobrien#ifdef BUILTIN_ELF 468349Sobrien#include <sys/types.h> 568349Sobrien#include <string.h> 668349Sobrien#include <stdio.h> 768349Sobrien#include <ctype.h> 868349Sobrien#include <stdlib.h> 968349Sobrien#ifdef HAVE_UNISTD_H 1068349Sobrien#include <unistd.h> 1168349Sobrien#endif 1268349Sobrien#include <errno.h> 1368349Sobrien 1468349Sobrien#include "readelf.h" 1568349Sobrien 1668349Sobrien#ifndef lint 1768349SobrienFILE_RCSID("@(#)$Id: readelf.c,v 1.16 2000/08/05 18:18:50 christos Exp $") 1868349Sobrien#endif 1968349Sobrien 2068349Sobrien#ifdef ELFCORE 2168349Sobrienstatic void dophn_core __P((int, int, int, off_t, int, size_t)); 2268349Sobrien#endif 2368349Sobrienstatic void dophn_exec __P((int, int, int, off_t, int, size_t)); 2468349Sobrienstatic void doshn __P((int, int, int, off_t, int, size_t)); 2568349Sobrien 2668349Sobrienstatic uint16_t getu16 __P((int, int)); 2768349Sobrienstatic uint32_t getu32 __P((int, uint32_t)); 2868349Sobrienstatic uint64_t getu64 __P((int, uint64_t)); 2968349Sobrien 3068349Sobrienstatic uint16_t 3168349Sobriengetu16(swap, value) 3268349Sobrien int swap; 3368349Sobrien uint16_t value; 3468349Sobrien{ 3568349Sobrien union { 3668349Sobrien uint16_t ui; 3768349Sobrien char c[2]; 3868349Sobrien } retval, tmpval; 3968349Sobrien 4068349Sobrien if (swap) { 4168349Sobrien tmpval.ui = value; 4268349Sobrien 4368349Sobrien retval.c[0] = tmpval.c[1]; 4468349Sobrien retval.c[1] = tmpval.c[0]; 4568349Sobrien 4668349Sobrien return retval.ui; 4768349Sobrien } else 4868349Sobrien return value; 4968349Sobrien} 5068349Sobrien 5168349Sobrienstatic uint32_t 5268349Sobriengetu32(swap, value) 5368349Sobrien int swap; 5468349Sobrien uint32_t value; 5568349Sobrien{ 5668349Sobrien union { 5768349Sobrien uint32_t ui; 5868349Sobrien char c[4]; 5968349Sobrien } retval, tmpval; 6068349Sobrien 6168349Sobrien if (swap) { 6268349Sobrien tmpval.ui = value; 6368349Sobrien 6468349Sobrien retval.c[0] = tmpval.c[3]; 6568349Sobrien retval.c[1] = tmpval.c[2]; 6668349Sobrien retval.c[2] = tmpval.c[1]; 6768349Sobrien retval.c[3] = tmpval.c[0]; 6868349Sobrien 6968349Sobrien return retval.ui; 7068349Sobrien } else 7168349Sobrien return value; 7268349Sobrien} 7368349Sobrien 7468349Sobrienstatic uint64_t 7568349Sobriengetu64(swap, value) 7668349Sobrien int swap; 7768349Sobrien uint64_t value; 7868349Sobrien{ 7968349Sobrien union { 8068349Sobrien uint64_t ui; 8168349Sobrien char c[8]; 8268349Sobrien } retval, tmpval; 8368349Sobrien 8468349Sobrien if (swap) { 8568349Sobrien tmpval.ui = value; 8668349Sobrien 8768349Sobrien retval.c[0] = tmpval.c[7]; 8868349Sobrien retval.c[1] = tmpval.c[6]; 8968349Sobrien retval.c[2] = tmpval.c[5]; 9068349Sobrien retval.c[3] = tmpval.c[4]; 9168349Sobrien retval.c[4] = tmpval.c[3]; 9268349Sobrien retval.c[5] = tmpval.c[2]; 9368349Sobrien retval.c[6] = tmpval.c[1]; 9468349Sobrien retval.c[7] = tmpval.c[0]; 9568349Sobrien 9668349Sobrien return retval.ui; 9768349Sobrien } else 9868349Sobrien return value; 9968349Sobrien} 10068349Sobrien 10168349Sobrien#define sh_addr (class == ELFCLASS32 \ 10268349Sobrien ? (void *) &sh32 \ 10368349Sobrien : (void *) &sh64) 10468349Sobrien#define shs_type (class == ELFCLASS32 \ 10568349Sobrien ? getu32(swap, sh32.sh_type) \ 10668349Sobrien : getu32(swap, sh64.sh_type)) 10768349Sobrien#define ph_addr (class == ELFCLASS32 \ 10868349Sobrien ? (void *) &ph32 \ 10968349Sobrien : (void *) &ph64) 11068349Sobrien#define ph_type (class == ELFCLASS32 \ 11168349Sobrien ? getu32(swap, ph32.p_type) \ 11268349Sobrien : getu32(swap, ph64.p_type)) 11368349Sobrien#define ph_offset (class == ELFCLASS32 \ 11468349Sobrien ? getu32(swap, ph32.p_offset) \ 11568349Sobrien : getu64(swap, ph64.p_offset)) 11668349Sobrien#define nh_size (class == ELFCLASS32 \ 11768349Sobrien ? sizeof *nh32 \ 11868349Sobrien : sizeof *nh64) 11968349Sobrien#define nh_type (class == ELFCLASS32 \ 12068349Sobrien ? getu32(swap, nh32->n_type) \ 12168349Sobrien : getu32(swap, nh64->n_type)) 12268349Sobrien#define nh_namesz (class == ELFCLASS32 \ 12368349Sobrien ? getu32(swap, nh32->n_namesz) \ 12468349Sobrien : getu32(swap, nh64->n_namesz)) 12568349Sobrien#define nh_descsz (class == ELFCLASS32 \ 12668349Sobrien ? getu32(swap, nh32->n_descsz) \ 12768349Sobrien : getu32(swap, nh64->n_descsz)) 12868349Sobrien#define prpsoffsets(i) (class == ELFCLASS32 \ 12968349Sobrien ? prpsoffsets32[i] \ 13068349Sobrien : prpsoffsets64[i]) 13168349Sobrien 13268349Sobrienstatic void 13368349Sobriendoshn(class, swap, fd, off, num, size) 13468349Sobrien int class; 13568349Sobrien int swap; 13668349Sobrien int fd; 13768349Sobrien off_t off; 13868349Sobrien int num; 13968349Sobrien size_t size; 14068349Sobrien{ 14168349Sobrien Elf32_Shdr sh32; 14268349Sobrien Elf64_Shdr sh64; 14368349Sobrien 14468349Sobrien if (lseek(fd, off, SEEK_SET) == -1) 14568349Sobrien error("lseek failed (%s).\n", strerror(errno)); 14668349Sobrien 14768349Sobrien for ( ; num; num--) { 14868349Sobrien if (read(fd, sh_addr, size) == -1) 14968349Sobrien error("read failed (%s).\n", strerror(errno)); 15068349Sobrien if (shs_type == SHT_SYMTAB /* || shs_type == SHT_DYNSYM */) { 15168349Sobrien (void) printf (", not stripped"); 15268349Sobrien return; 15368349Sobrien } 15468349Sobrien } 15568349Sobrien (void) printf (", stripped"); 15668349Sobrien} 15768349Sobrien 15868349Sobrien/* 15968349Sobrien * Look through the program headers of an executable image, searching 16068349Sobrien * for a PT_INTERP section; if one is found, it's dynamically linked, 16168349Sobrien * otherwise it's statically linked. 16268349Sobrien */ 16368349Sobrienstatic void 16468349Sobriendophn_exec(class, swap, fd, off, num, size) 16568349Sobrien int class; 16668349Sobrien int swap; 16768349Sobrien int fd; 16868349Sobrien off_t off; 16968349Sobrien int num; 17068349Sobrien size_t size; 17168349Sobrien{ 17268349Sobrien Elf32_Phdr ph32; 17368349Sobrien Elf64_Phdr ph64; 17468349Sobrien char *linking_style = "statically"; 17568349Sobrien char *shared_libraries = ""; 17668349Sobrien 17768349Sobrien if (lseek(fd, off, SEEK_SET) == -1) 17868349Sobrien error("lseek failed (%s).\n", strerror(errno)); 17968349Sobrien 18068349Sobrien for ( ; num; num--) { 18168349Sobrien if (read(fd, ph_addr, size) == -1) 18268349Sobrien error("read failed (%s).\n", strerror(errno)); 18368349Sobrien 18468349Sobrien switch (ph_type) { 18568349Sobrien case PT_DYNAMIC: 18668349Sobrien linking_style = "dynamically"; 18768349Sobrien break; 18868349Sobrien case PT_INTERP: 18968349Sobrien shared_libraries = " (uses shared libs)"; 19068349Sobrien break; 19168349Sobrien } 19268349Sobrien } 19368349Sobrien printf(", %s linked%s", linking_style, shared_libraries); 19468349Sobrien} 19568349Sobrien 19668349Sobrien#ifdef ELFCORE 19768349Sobriensize_t prpsoffsets32[] = { 19868349Sobrien 8, /* FreeBSD */ 19968349Sobrien 28, /* Linux 2.0.36 */ 20068349Sobrien 32, /* Linux (I forget which kernel version) */ 20168349Sobrien 84, /* SunOS 5.x */ 20268349Sobrien}; 20368349Sobrien 20468349Sobriensize_t prpsoffsets64[] = { 20568349Sobrien 120, /* SunOS 5.x, 64-bit */ 20668349Sobrien}; 20768349Sobrien 20868349Sobrien#define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) 20968349Sobrien#define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) 21068349Sobrien 21168349Sobrien#define NOFFSETS (class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) 21268349Sobrien 21368349Sobrien/* 21468349Sobrien * Look through the program headers of an executable image, searching 21568349Sobrien * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or 21668349Sobrien * "FreeBSD"; if one is found, try looking in various places in its 21768349Sobrien * contents for a 16-character string containing only printable 21868349Sobrien * characters - if found, that string should be the name of the program 21968349Sobrien * that dropped core. Note: right after that 16-character string is, 22068349Sobrien * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and 22168349Sobrien * Linux, a longer string (80 characters, in 5.x, probably other 22268349Sobrien * SVR4-flavored systems, and Linux) containing the start of the 22368349Sobrien * command line for that program. 22468349Sobrien * 22568349Sobrien * The signal number probably appears in a section of type NT_PRSTATUS, 22668349Sobrien * but that's also rather OS-dependent, in ways that are harder to 22768349Sobrien * dissect with heuristics, so I'm not bothering with the signal number. 22868349Sobrien * (I suppose the signal number could be of interest in situations where 22968349Sobrien * you don't have the binary of the program that dropped core; if you 23068349Sobrien * *do* have that binary, the debugger will probably tell you what 23168349Sobrien * signal it was.) 23268349Sobrien */ 23368349Sobrienstatic void 23468349Sobriendophn_core(class, swap, fd, off, num, size) 23568349Sobrien int class; 23668349Sobrien int swap; 23768349Sobrien int fd; 23868349Sobrien off_t off; 23968349Sobrien int num; 24068349Sobrien size_t size; 24168349Sobrien{ 24268349Sobrien Elf32_Phdr ph32; 24368349Sobrien Elf32_Nhdr *nh32; 24468349Sobrien Elf64_Phdr ph64; 24568349Sobrien Elf64_Nhdr *nh64; 24668349Sobrien size_t offset, nameoffset, noffset, reloffset; 24768349Sobrien unsigned char c; 24868349Sobrien int i, j; 24968349Sobrien char nbuf[BUFSIZ]; 25068349Sobrien int bufsize; 25168349Sobrien int is_freebsd; 25268349Sobrien 25368349Sobrien /* 25468349Sobrien * Loop through all the program headers. 25568349Sobrien */ 25668349Sobrien for ( ; num; num--) { 25768349Sobrien if (lseek(fd, off, SEEK_SET) == -1) 25868349Sobrien error("lseek failed (%s).\n", strerror(errno)); 25968349Sobrien if (read(fd, ph_addr, size) == -1) 26068349Sobrien error("read failed (%s).\n", strerror(errno)); 26168349Sobrien off += size; 26268349Sobrien if (ph_type != PT_NOTE) 26368349Sobrien continue; 26468349Sobrien 26568349Sobrien /* 26668349Sobrien * This is a PT_NOTE section; loop through all the notes 26768349Sobrien * in the section. 26868349Sobrien */ 26968349Sobrien if (lseek(fd, (off_t) ph_offset, SEEK_SET) == -1) 27068349Sobrien error("lseek failed (%s).\n", strerror(errno)); 27168349Sobrien bufsize = read(fd, nbuf, BUFSIZ); 27268349Sobrien if (bufsize == -1) 27368349Sobrien error(": " "read failed (%s).\n", strerror(errno)); 27468349Sobrien offset = 0; 27568349Sobrien for (;;) { 27668349Sobrien if (offset >= bufsize) 27768349Sobrien break; 27868349Sobrien if (class == ELFCLASS32) 27968349Sobrien nh32 = (Elf32_Nhdr *)&nbuf[offset]; 28068349Sobrien else 28168349Sobrien nh64 = (Elf64_Nhdr *)&nbuf[offset]; 28268349Sobrien offset += nh_size; 28368349Sobrien 28468349Sobrien /* 28568349Sobrien * Check whether this note has the name "CORE" or 28668349Sobrien * "FreeBSD". 28768349Sobrien */ 28868349Sobrien if (offset + nh_namesz >= bufsize) { 28968349Sobrien /* 29068349Sobrien * We're past the end of the buffer. 29168349Sobrien */ 29268349Sobrien break; 29368349Sobrien } 29468349Sobrien 29568349Sobrien nameoffset = offset; 29668349Sobrien offset += nh_namesz; 29768349Sobrien offset = ((offset + 3)/4)*4; 29868349Sobrien 29968349Sobrien /* 30068349Sobrien * Sigh. The 2.0.36 kernel in Debian 2.1, at 30168349Sobrien * least, doesn't correctly implement name 30268349Sobrien * sections, in core dumps, as specified by 30368349Sobrien * the "Program Linking" section of "UNIX(R) System 30468349Sobrien * V Release 4 Programmer's Guide: ANSI C and 30568349Sobrien * Programming Support Tools", because my copy 30668349Sobrien * clearly says "The first 'namesz' bytes in 'name' 30768349Sobrien * contain a *null-terminated* [emphasis mine] 30868349Sobrien * character representation of the entry's owner 30968349Sobrien * or originator", but the 2.0.36 kernel code 31068349Sobrien * doesn't include the terminating null in the 31168349Sobrien * name.... 31268349Sobrien */ 31368349Sobrien if ((nh_namesz == 4 && 31468349Sobrien strncmp(&nbuf[nameoffset], "CORE", 4) == 0) || 31568349Sobrien (nh_namesz == 5 && 31668349Sobrien strcmp(&nbuf[nameoffset], "CORE") == 0)) 31768349Sobrien is_freebsd = 0; 31868349Sobrien else if ((nh_namesz == 8 && 31968349Sobrien strcmp(&nbuf[nameoffset], "FreeBSD") == 0)) 32068349Sobrien is_freebsd = 1; 32168349Sobrien else 32268349Sobrien continue; 32368349Sobrien if (nh_type == NT_PRPSINFO) { 32468349Sobrien /* 32568349Sobrien * Extract the program name. We assume 32668349Sobrien * it to be 16 characters (that's what it 32768349Sobrien * is in SunOS 5.x and Linux). 32868349Sobrien * 32968349Sobrien * Unfortunately, it's at a different offset 33068349Sobrien * in varous OSes, so try multiple offsets. 33168349Sobrien * If the characters aren't all printable, 33268349Sobrien * reject it. 33368349Sobrien */ 33468349Sobrien for (i = 0; i < NOFFSETS; i++) { 33568349Sobrien reloffset = prpsoffsets(i); 33668349Sobrien noffset = offset + reloffset; 33768349Sobrien for (j = 0; j < 16; 33868349Sobrien j++, noffset++, reloffset++) { 33968349Sobrien /* 34068349Sobrien * Make sure we're not past 34168349Sobrien * the end of the buffer; if 34268349Sobrien * we are, just give up. 34368349Sobrien */ 34468349Sobrien if (noffset >= bufsize) 34568349Sobrien goto tryanother; 34668349Sobrien 34768349Sobrien /* 34868349Sobrien * Make sure we're not past 34968349Sobrien * the end of the contents; 35068349Sobrien * if we are, this obviously 35168349Sobrien * isn't the right offset. 35268349Sobrien */ 35368349Sobrien if (reloffset >= nh_descsz) 35468349Sobrien goto tryanother; 35568349Sobrien 35668349Sobrien c = nbuf[noffset]; 35768349Sobrien if (c == '\0') { 35868349Sobrien /* 35968349Sobrien * A '\0' at the 36068349Sobrien * beginning is 36168349Sobrien * obviously wrong. 36268349Sobrien * Any other '\0' 36368349Sobrien * means we're done. 36468349Sobrien */ 36568349Sobrien if (j == 0) 36668349Sobrien goto tryanother; 36768349Sobrien else 36868349Sobrien break; 36968349Sobrien } else { 37068349Sobrien /* 37168349Sobrien * A nonprintable 37268349Sobrien * character is also 37368349Sobrien * wrong. 37468349Sobrien */ 37568349Sobrien#define isquote(c) (strchr("'\"`", (c)) != NULL) 37668349Sobrien if (!isprint(c) || 37768349Sobrien isquote(c)) 37868349Sobrien goto tryanother; 37968349Sobrien } 38068349Sobrien } 38168349Sobrien 38268349Sobrien /* 38368349Sobrien * Well, that worked. 38468349Sobrien */ 38568349Sobrien printf(", from '%.16s'", 38668349Sobrien &nbuf[offset + prpsoffsets(i)]); 38768349Sobrien break; 38868349Sobrien 38968349Sobrien tryanother: 39068349Sobrien ; 39168349Sobrien } 39268349Sobrien break; 39368349Sobrien } 39468349Sobrien offset += nh_descsz; 39568349Sobrien offset = ((offset + 3)/4)*4; 39668349Sobrien } 39768349Sobrien out: 39868349Sobrien ; 39968349Sobrien } 40068349Sobrien} 40168349Sobrien#endif 40268349Sobrien 40368349Sobrienvoid 40468349Sobrientryelf(fd, buf, nbytes) 40568349Sobrien int fd; 40668349Sobrien unsigned char *buf; 40768349Sobrien int nbytes; 40868349Sobrien{ 40968349Sobrien union { 41068349Sobrien int32 l; 41168349Sobrien char c[sizeof (int32)]; 41268349Sobrien } u; 41368349Sobrien int class; 41468349Sobrien int swap; 41568349Sobrien 41668349Sobrien /* 41768349Sobrien * ELF executables have multiple section headers in arbitrary 41868349Sobrien * file locations and thus file(1) cannot determine it from easily. 41968349Sobrien * Instead we traverse thru all section headers until a symbol table 42068349Sobrien * one is found or else the binary is stripped. 42168349Sobrien */ 42268349Sobrien if (buf[EI_MAG0] != ELFMAG0 42368349Sobrien || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) 42468349Sobrien || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) 42568349Sobrien return; 42668349Sobrien 42768349Sobrien 42868349Sobrien class = buf[4]; 42968349Sobrien 43068349Sobrien if (class == ELFCLASS32) { 43168349Sobrien Elf32_Ehdr elfhdr; 43268349Sobrien if (nbytes <= sizeof (Elf32_Ehdr)) 43368349Sobrien return; 43468349Sobrien 43568349Sobrien 43668349Sobrien u.l = 1; 43768349Sobrien (void) memcpy(&elfhdr, buf, sizeof elfhdr); 43868349Sobrien swap = (u.c[sizeof(int32) - 1] + 1) != elfhdr.e_ident[5]; 43968349Sobrien 44068349Sobrien if (getu16(swap, elfhdr.e_type) == ET_CORE) 44168349Sobrien#ifdef ELFCORE 44268349Sobrien dophn_core(class, swap, 44368349Sobrien fd, 44468349Sobrien getu32(swap, elfhdr.e_phoff), 44568349Sobrien getu16(swap, elfhdr.e_phnum), 44668349Sobrien getu16(swap, elfhdr.e_phentsize)); 44768349Sobrien#else 44868349Sobrien ; 44968349Sobrien#endif 45068349Sobrien else { 45168349Sobrien if (getu16(swap, elfhdr.e_type) == ET_EXEC) { 45268349Sobrien dophn_exec(class, swap, 45368349Sobrien fd, 45468349Sobrien getu32(swap, elfhdr.e_phoff), 45568349Sobrien getu16(swap, elfhdr.e_phnum), 45668349Sobrien getu16(swap, elfhdr.e_phentsize)); 45768349Sobrien } 45868349Sobrien doshn(class, swap, 45968349Sobrien fd, 46068349Sobrien getu32(swap, elfhdr.e_shoff), 46168349Sobrien getu16(swap, elfhdr.e_shnum), 46268349Sobrien getu16(swap, elfhdr.e_shentsize)); 46368349Sobrien } 46468349Sobrien return; 46568349Sobrien } 46668349Sobrien 46768349Sobrien if (class == ELFCLASS64) { 46868349Sobrien Elf64_Ehdr elfhdr; 46968349Sobrien if (nbytes <= sizeof (Elf64_Ehdr)) 47068349Sobrien return; 47168349Sobrien 47268349Sobrien 47368349Sobrien u.l = 1; 47468349Sobrien (void) memcpy(&elfhdr, buf, sizeof elfhdr); 47568349Sobrien swap = (u.c[sizeof(int32) - 1] + 1) != elfhdr.e_ident[5]; 47668349Sobrien 47768349Sobrien if (getu16(swap, elfhdr.e_type) == ET_CORE) 47868349Sobrien#ifdef ELFCORE 47968349Sobrien dophn_core(class, swap, 48068349Sobrien fd, 48168349Sobrien#ifdef USE_ARRAY_FOR_64BIT_TYPES 48268349Sobrien getu32(swap, elfhdr.e_phoff[1]), 48368349Sobrien#else 48468349Sobrien getu64(swap, elfhdr.e_phoff), 48568349Sobrien#endif 48668349Sobrien getu16(swap, elfhdr.e_phnum), 48768349Sobrien getu16(swap, elfhdr.e_phentsize)); 48868349Sobrien#else 48968349Sobrien ; 49068349Sobrien#endif 49168349Sobrien else 49268349Sobrien { 49368349Sobrien if (getu16(swap, elfhdr.e_type) == ET_EXEC) { 49468349Sobrien dophn_exec(class, swap, 49568349Sobrien fd, 49668349Sobrien#ifdef USE_ARRAY_FOR_64BIT_TYPES 49768349Sobrien getu32(swap, elfhdr.e_phoff[1]), 49868349Sobrien#else 49968349Sobrien getu64(swap, elfhdr.e_phoff), 50068349Sobrien#endif 50168349Sobrien getu16(swap, elfhdr.e_phnum), 50268349Sobrien getu16(swap, elfhdr.e_phentsize)); 50368349Sobrien } 50468349Sobrien doshn(class, swap, 50568349Sobrien fd, 50668349Sobrien#ifdef USE_ARRAY_FOR_64BIT_TYPES 50768349Sobrien getu32(swap, elfhdr.e_shoff[1]), 50868349Sobrien#else 50968349Sobrien getu64(swap, elfhdr.e_shoff), 51068349Sobrien#endif 51168349Sobrien getu16(swap, elfhdr.e_shnum), 51268349Sobrien getu16(swap, elfhdr.e_shentsize)); 51368349Sobrien } 51468349Sobrien return; 51568349Sobrien } 51668349Sobrien} 51768349Sobrien#endif 518