]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/file/readelf.c
This commit was generated by cvs2svn to compensate for changes in r94691,
[FreeBSD/FreeBSD.git] / contrib / file / readelf.c
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
17 FILE_RCSID("@(#)$Id: readelf.c,v 1.17 2000/08/05 19:00:12 christos Exp $")
18 #endif
19
20 #ifdef  ELFCORE
21 static void dophn_core __P((int, int, int, off_t, int, size_t));
22 #endif
23 static void dophn_exec __P((int, int, int, off_t, int, size_t));
24 static void doshn __P((int, int, int, off_t, int, size_t));
25
26 static uint16_t getu16 __P((int, int));
27 static uint32_t getu32 __P((int, uint32_t));
28 static uint64_t getu64 __P((int, uint64_t));
29
30 static uint16_t
31 getu16(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
51 static uint32_t
52 getu32(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
74 static uint64_t
75 getu64(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
132 static void
133 doshn(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  */
163 static void
164 dophn_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
197 size_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
204 size_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  */
233 static void
234 dophn_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         }
398 }
399 #endif
400
401 void
402 tryelf(fd, buf, nbytes)
403         int fd;
404         unsigned char *buf;
405         int nbytes;
406 {
407         union {
408                 int32 l;
409                 char c[sizeof (int32)];
410         } u;
411         int class;
412         int swap;
413
414         /*
415          * ELF executables have multiple section headers in arbitrary
416          * file locations and thus file(1) cannot determine it from easily.
417          * Instead we traverse thru all section headers until a symbol table
418          * one is found or else the binary is stripped.
419          */
420         if (buf[EI_MAG0] != ELFMAG0
421             || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
422             || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
423             return;
424
425
426         class = buf[4];
427
428         if (class == ELFCLASS32) {
429                 Elf32_Ehdr elfhdr;
430                 if (nbytes <= sizeof (Elf32_Ehdr))
431                         return;
432
433
434                 u.l = 1;
435                 (void) memcpy(&elfhdr, buf, sizeof elfhdr);
436                 swap = (u.c[sizeof(int32) - 1] + 1) != elfhdr.e_ident[5];
437
438                 if (getu16(swap, elfhdr.e_type) == ET_CORE) 
439 #ifdef ELFCORE
440                         dophn_core(class, swap,
441                                    fd,
442                                    getu32(swap, elfhdr.e_phoff),
443                                    getu16(swap, elfhdr.e_phnum), 
444                                    getu16(swap, elfhdr.e_phentsize));
445 #else
446                         ;
447 #endif
448                 else {
449                         if (getu16(swap, elfhdr.e_type) == ET_EXEC) {
450                                 dophn_exec(class, swap,
451                                            fd,
452                                            getu32(swap, elfhdr.e_phoff),
453                                            getu16(swap, elfhdr.e_phnum), 
454                                            getu16(swap, elfhdr.e_phentsize));
455                         }
456                         doshn(class, swap,
457                               fd,
458                               getu32(swap, elfhdr.e_shoff),
459                               getu16(swap, elfhdr.e_shnum),
460                               getu16(swap, elfhdr.e_shentsize));
461                 }
462                 return;
463         }
464
465         if (class == ELFCLASS64) {
466                 Elf64_Ehdr elfhdr;
467                 if (nbytes <= sizeof (Elf64_Ehdr))
468                         return;
469
470
471                 u.l = 1;
472                 (void) memcpy(&elfhdr, buf, sizeof elfhdr);
473                 swap = (u.c[sizeof(int32) - 1] + 1) != elfhdr.e_ident[5];
474
475                 if (getu16(swap, elfhdr.e_type) == ET_CORE) 
476 #ifdef ELFCORE
477                         dophn_core(class, swap,
478                                    fd,
479 #ifdef USE_ARRAY_FOR_64BIT_TYPES
480                                    getu32(swap, elfhdr.e_phoff[1]),
481 #else
482                                    getu64(swap, elfhdr.e_phoff),
483 #endif
484                                    getu16(swap, elfhdr.e_phnum), 
485                                    getu16(swap, elfhdr.e_phentsize));
486 #else
487                         ;
488 #endif
489                 else
490                 {
491                         if (getu16(swap, elfhdr.e_type) == ET_EXEC) {
492                                 dophn_exec(class, swap,
493                                            fd,
494 #ifdef USE_ARRAY_FOR_64BIT_TYPES
495                                            getu32(swap, elfhdr.e_phoff[1]),
496 #else
497                                            getu64(swap, elfhdr.e_phoff),
498 #endif
499                                            getu16(swap, elfhdr.e_phnum), 
500                                            getu16(swap, elfhdr.e_phentsize));
501                         }
502                         doshn(class, swap,
503                               fd,
504 #ifdef USE_ARRAY_FOR_64BIT_TYPES
505                               getu32(swap, elfhdr.e_shoff[1]),
506 #else
507                               getu64(swap, elfhdr.e_shoff),
508 #endif
509                               getu16(swap, elfhdr.e_shnum),
510                               getu16(swap, elfhdr.e_shentsize));
511                 }
512                 return;
513         }
514 }
515 #endif