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