#include "file.h" #ifdef BUILTIN_ELF #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "readelf.h" #ifndef lint FILE_RCSID("@(#)$Id: readelf.c,v 1.11 1999/10/31 22:23:04 christos Exp $") #endif #ifdef ELFCORE static void dophn_core __P((int, int, int, off_t, int, size_t)); #endif static void dophn_exec __P((int, int, int, off_t, int, size_t)); static void doshn __P((int, int, int, off_t, int, size_t)); static uint16_t getu16 __P((int, int)); static uint32_t getu32 __P((int, uint32_t)); static uint64_t getu64 __P((int, uint64_t)); static uint16_t getu16(swap, value) int swap; uint16_t value; { union { uint16_t ui; char c[2]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[1]; retval.c[1] = tmpval.c[0]; return retval.ui; } else return value; } static uint32_t getu32(swap, value) int swap; uint32_t value; { union { uint32_t ui; char c[4]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[3]; retval.c[1] = tmpval.c[2]; retval.c[2] = tmpval.c[1]; retval.c[3] = tmpval.c[0]; return retval.ui; } else return value; } static uint64_t getu64(swap, value) int swap; uint64_t value; { union { uint64_t ui; char c[8]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[7]; retval.c[1] = tmpval.c[6]; retval.c[2] = tmpval.c[5]; retval.c[3] = tmpval.c[4]; retval.c[4] = tmpval.c[3]; retval.c[5] = tmpval.c[2]; retval.c[6] = tmpval.c[1]; retval.c[7] = tmpval.c[0]; return retval.ui; } else return value; } #define sh_addr (class == ELFCLASS32 \ ? (void *) &sh32 \ : (void *) &sh64) #define shs_type (class == ELFCLASS32 \ ? getu32(swap, sh32.sh_type) \ : getu32(swap, sh64.sh_type)) #define ph_addr (class == ELFCLASS32 \ ? (void *) &ph32 \ : (void *) &ph64) #define ph_type (class == ELFCLASS32 \ ? getu32(swap, ph32.p_type) \ : getu32(swap, ph64.p_type)) #define ph_offset (class == ELFCLASS32 \ ? getu32(swap, ph32.p_offset) \ : getu64(swap, ph64.p_offset)) #define nh_size (class == ELFCLASS32 \ ? sizeof *nh32 \ : sizeof *nh64) #define nh_type (class == ELFCLASS32 \ ? getu32(swap, nh32->n_type) \ : getu32(swap, nh64->n_type)) #define nh_namesz (class == ELFCLASS32 \ ? getu32(swap, nh32->n_namesz) \ : getu32(swap, nh64->n_namesz)) #define nh_descsz (class == ELFCLASS32 \ ? getu32(swap, nh32->n_descsz) \ : getu32(swap, nh64->n_descsz)) #define prpsoffsets(i) (class == ELFCLASS32 \ ? prpsoffsets32[i] \ : prpsoffsets64[i]) static void doshn(class, swap, fd, off, num, size) int class; int swap; int fd; off_t off; int num; size_t size; { Elf32_Shdr sh32; Elf64_Shdr sh64; if (lseek(fd, off, SEEK_SET) == -1) error("lseek failed (%s).\n", strerror(errno)); for ( ; num; num--) { if (read(fd, sh_addr, size) == -1) error("read failed (%s).\n", strerror(errno)); if (shs_type == SHT_SYMTAB /* || shs_type == SHT_DYNSYM */) { (void) printf (", not stripped"); return; } } (void) printf (", stripped"); } /* * Look through the program headers of an executable image, searching * for a PT_INTERP section; if one is found, it's dynamically linked, * otherwise it's statically linked. */ static void dophn_exec(class, swap, fd, off, num, size) int class; int swap; int fd; off_t off; int num; size_t size; { Elf32_Phdr ph32; Elf64_Phdr ph64; char *linking_style = "statically"; char *shared_libraries = ""; if (lseek(fd, off, SEEK_SET) == -1) error("lseek failed (%s).\n", strerror(errno)); for ( ; num; num--) { if (read(fd, ph_addr, size) == -1) error("read failed (%s).\n", strerror(errno)); switch (ph_type) { case PT_DYNAMIC: linking_style = "dynamically"; break; case PT_INTERP: shared_libraries = " (uses shared libs)"; break; } } printf(", %s linked%s", linking_style, shared_libraries); } #ifdef ELFCORE size_t prpsoffsets32[] = { 84, /* SunOS 5.x */ 32, /* Linux */ }; size_t prpsoffsets64[] = { 120, /* SunOS 5.x, 64-bit */ }; #define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) #define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) #define NOFFSETS (class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) /* * Look through the program headers of an executable image, searching * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE"; if one * is found, try looking in various places in its contents for a 16-character * string containing only printable characters - if found, that string * should be the name of the program that dropped core. * Note: right after that 16-character string is, at least in SunOS 5.x * (and possibly other SVR4-flavored systems) and Linux, a longer string * (80 characters, in 5.x, probably other SVR4-flavored systems, and Linux) * containing the start of the command line for that program. */ static void dophn_core(class, swap, fd, off, num, size) int class; int swap; int fd; off_t off; int num; size_t size; { Elf32_Phdr ph32; Elf32_Nhdr *nh32; Elf64_Phdr ph64; Elf64_Nhdr *nh64; size_t offset, noffset, reloffset; unsigned char c; int i, j; char nbuf[BUFSIZ]; int bufsize; for ( ; num; num--) { if (lseek(fd, off, SEEK_SET) == -1) error("lseek failed (%s).\n", strerror(errno)); if (read(fd, ph_addr, size) == -1) error("read failed (%s).\n", strerror(errno)); off += size; if (ph_type != PT_NOTE) continue; if (lseek(fd, (off_t) ph_offset, SEEK_SET) == -1) error("lseek failed (%s).\n", strerror(errno)); bufsize = read(fd, nbuf, BUFSIZ); if (bufsize == -1) error("read failed (%s).\n", strerror(errno)); offset = 0; for (;;) { if (offset >= bufsize) break; if (class == ELFCLASS32) nh32 = (Elf32_Nhdr *)&nbuf[offset]; else nh64 = (Elf64_Nhdr *)&nbuf[offset]; offset += nh_size; /* * If this note isn't an NT_PRPSINFO note, it's * not what we're looking for. */ if (nh_type != NT_PRPSINFO) { offset += nh_namesz; offset = ((offset + 3)/4)*4; offset += nh_descsz; offset = ((offset + 3)/4)*4; continue; } /* * Make sure this note has the name "CORE". */ if (offset + nh_namesz >= bufsize) { /* * We're past the end of the buffer. */ break; } if (nh_namesz != 5 || strcmp(&nbuf[offset], "CORE") != 0) continue; offset += nh_namesz; offset = ((offset + 3)/4)*4; /* * Extract the program name. We assume it to be * 16 characters (that's what it is in SunOS 5.x * and Linux). * * Unfortunately, it's at a different offset in * SunOS 5.x and Linux, so try multiple offsets. * If the characters aren't all printable, reject * it. */ for (i = 0; i < NOFFSETS; i++) { reloffset = prpsoffsets(i); noffset = offset + reloffset; for (j = 0; j < 16; j++, noffset++, reloffset++) { /* * Make sure we're not past the end * of the buffer; if we are, just * give up. */ if (noffset >= bufsize) return; /* * Make sure we're not past the * end of the contents; if we * are, this obviously isn't * the right offset. */ if (reloffset >= nh_descsz) goto tryanother; c = nbuf[noffset]; if (c != '\0' && !isprint(c)) goto tryanother; } /* * Well, that worked. */ printf(", from '%.16s'", &nbuf[offset + prpsoffsets(i)]); return; tryanother: ; } offset += nh_descsz; offset = ((offset + 3)/4)*4; } } } #endif void tryelf(fd, buf, nbytes) int fd; unsigned char *buf; int nbytes; { union { int32 l; char c[sizeof (int32)]; } u; int class; int swap; /* * ELF executables have multiple section headers in arbitrary * file locations and thus file(1) cannot determine it from easily. * Instead we traverse thru all section headers until a symbol table * one is found or else the binary is stripped. */ if (buf[EI_MAG0] != ELFMAG0 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) return; class = buf[4]; if (class == ELFCLASS32) { Elf32_Ehdr elfhdr; if (nbytes <= sizeof (Elf32_Ehdr)) return; u.l = 1; (void) memcpy(&elfhdr, buf, sizeof elfhdr); swap = (u.c[sizeof(long) - 1] + 1) != elfhdr.e_ident[5]; if (getu16(swap, elfhdr.e_type) == ET_CORE) #ifdef ELFCORE dophn_core(class, swap, fd, getu32(swap, elfhdr.e_phoff), getu16(swap, elfhdr.e_phnum), getu16(swap, elfhdr.e_phentsize)); #else ; #endif else { if (getu16(swap, elfhdr.e_type) == ET_EXEC) { dophn_exec(class, swap, fd, getu32(swap, elfhdr.e_phoff), getu16(swap, elfhdr.e_phnum), getu16(swap, elfhdr.e_phentsize)); } doshn(class, swap, fd, getu32(swap, elfhdr.e_shoff), getu16(swap, elfhdr.e_shnum), getu16(swap, elfhdr.e_shentsize)); } return; } if (class == ELFCLASS64) { Elf64_Ehdr elfhdr; if (nbytes <= sizeof (Elf64_Ehdr)) return; u.l = 1; (void) memcpy(&elfhdr, buf, sizeof elfhdr); swap = (u.c[sizeof(long) - 1] + 1) != elfhdr.e_ident[5]; if (getu16(swap, elfhdr.e_type) == ET_CORE) #ifdef ELFCORE dophn_core(class, swap, fd, #ifdef USE_ARRAY_FOR_64BIT_TYPES getu32(swap, elfhdr.e_phoff[1]), #else getu64(swap, elfhdr.e_phoff), #endif getu16(swap, elfhdr.e_phnum), getu16(swap, elfhdr.e_phentsize)); #else ; #endif else { if (getu16(swap, elfhdr.e_type) == ET_EXEC) { dophn_exec(class, swap, fd, #ifdef USE_ARRAY_FOR_64BIT_TYPES getu32(swap, elfhdr.e_phoff[1]), #else getu64(swap, elfhdr.e_phoff), #endif getu16(swap, elfhdr.e_phnum), getu16(swap, elfhdr.e_phentsize)); } doshn(class, swap, fd, #ifdef USE_ARRAY_FOR_64BIT_TYPES getu32(swap, elfhdr.e_shoff[1]), #else getu64(swap, elfhdr.e_shoff), #endif getu16(swap, elfhdr.e_shnum), getu16(swap, elfhdr.e_shentsize)); } return; } } #endif