1/* BFD back-end for CISCO crash dumps.
2   Copyright 1994, 1997, 1999, 2000, 2001, 2002, 2004, 2006
3   Free Software Foundation, Inc.
4
5This file is part of BFD, the Binary File Descriptor library.
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
20
21#include "bfd.h"
22#include "sysdep.h"
23#include "libbfd.h"
24/* core_file_failing_signal returns a host signal (this probably should
25   be fixed).  */
26#include <signal.h>
27
28/* for MSVC builds */
29#ifndef SIGTRAP
30# define SIGTRAP 5
31#endif
32#ifndef SIGEMT
33# define SIGEMT 6
34#endif
35#ifndef SIGBUS
36# define SIGBUS 10
37#endif
38
39int crash_info_locs[] = {
40  0x0250,	/* mips, ppc, x86, i960 */
41  0x0400,	/* m68k, mips, x86, i960 */
42  0x0FFC,	/* m68k, mips, ppc, x86, i960 */
43  0x3000,	/* ppc */
44  0x4FFC,	/* m68k */
45  -1
46};
47
48#define CRASH_MAGIC	0xdead1234
49#define MASK_ADDR(x)	((x) & 0x0fffffff)	/* Mask crash info address */
50
51typedef enum {
52    CRASH_REASON_NOTCRASHED = 0,
53    CRASH_REASON_EXCEPTION = 1,
54    CRASH_REASON_CORRUPT = 2,
55} crashreason;
56
57typedef struct {
58  char magic[4];		/* Magic number */
59  char version[4];		/* Version number */
60  char reason[4];		/* Crash reason */
61  char cpu_vector[4];		/* CPU vector for exceptions */
62  char registers[4];		/* Pointer to saved registers */
63  char rambase[4];		/* Base of RAM (not in V1 crash info) */
64  char textbase[4];		/* Base of .text section (not in V3 crash info) */
65  char database[4];		/* Base of .data section (not in V3 crash info) */
66  char bssbase[4];		/* Base of .bss section (not in V3 crash info) */
67} crashinfo_external;
68
69struct cisco_core_struct
70{
71  int sig;
72};
73
74static const bfd_target *cisco_core_file_validate PARAMS ((bfd *, int));
75static const bfd_target *cisco_core_file_p PARAMS ((bfd *));
76char *cisco_core_file_failing_command PARAMS ((bfd *));
77int cisco_core_file_failing_signal PARAMS ((bfd *));
78#define cisco_core_file_matches_executable_p generic_core_file_matches_executable_p
79
80/* Examine the file for a crash info struct at the offset given by
81   CRASH_INFO_LOC.  */
82
83static const bfd_target *
84cisco_core_file_validate (abfd, crash_info_loc)
85     bfd *abfd;
86     int crash_info_loc;
87{
88  char buf[4];
89  unsigned int crashinfo_offset;
90  crashinfo_external crashinfo;
91  bfd_size_type nread;
92  unsigned int magic;
93  unsigned int version;
94  unsigned int rambase;
95  sec_ptr asect;
96  struct stat statbuf;
97  bfd_size_type amt;
98  flagword flags;
99
100  if (bfd_seek (abfd, (file_ptr) crash_info_loc, SEEK_SET) != 0)
101    return NULL;
102
103  nread = bfd_bread (buf, (bfd_size_type) 4, abfd);
104  if (nread != 4)
105    {
106      if (bfd_get_error () != bfd_error_system_call)
107	bfd_set_error (bfd_error_wrong_format);
108      return NULL;
109    }
110  crashinfo_offset = MASK_ADDR (bfd_get_32 (abfd, buf));
111
112  if (bfd_seek (abfd, (file_ptr) crashinfo_offset, SEEK_SET) != 0)
113    {
114      /* Most likely we failed because of a bogus (huge) offset */
115      bfd_set_error (bfd_error_wrong_format);
116      return NULL;
117    }
118
119  nread = bfd_bread (&crashinfo, (bfd_size_type) sizeof (crashinfo), abfd);
120  if (nread != sizeof (crashinfo))
121    {
122      if (bfd_get_error () != bfd_error_system_call)
123	bfd_set_error (bfd_error_wrong_format);
124      return NULL;
125    }
126
127  if (bfd_stat (abfd, &statbuf) < 0)
128    {
129      bfd_set_error (bfd_error_system_call);
130      return NULL;
131    }
132
133  magic = bfd_get_32 (abfd, crashinfo.magic);
134  if (magic != CRASH_MAGIC)
135    {
136      bfd_set_error (bfd_error_wrong_format);
137      return NULL;
138    }
139
140  version = bfd_get_32 (abfd, crashinfo.version);
141  if (version == 0)
142    {
143      bfd_set_error (bfd_error_wrong_format);
144      return NULL;
145    }
146  else if (version == 1)
147    {
148      /* V1 core dumps don't specify the dump base, assume 0 */
149      rambase = 0;
150    }
151  else
152    {
153      rambase = bfd_get_32 (abfd, crashinfo.rambase);
154    }
155
156  /* OK, we believe you.  You're a core file.  */
157
158  amt = sizeof (struct cisco_core_struct);
159  abfd->tdata.cisco_core_data = (struct cisco_core_struct *) bfd_zmalloc (amt);
160  if (abfd->tdata.cisco_core_data == NULL)
161    return NULL;
162
163  switch ((crashreason) bfd_get_32 (abfd, crashinfo.reason))
164    {
165    case CRASH_REASON_NOTCRASHED:
166      /* Crash file probably came from write core.  */
167      abfd->tdata.cisco_core_data->sig = 0;
168      break;
169    case CRASH_REASON_CORRUPT:
170      /* The crash context area was corrupt -- proceed with caution.
171	 We have no way of passing this information back to the caller.  */
172      abfd->tdata.cisco_core_data->sig = 0;
173      break;
174    case CRASH_REASON_EXCEPTION:
175      /* Crash occured due to CPU exception.  */
176
177      /* This is 68k-specific; for MIPS we'll need to interpret
178	 cpu_vector differently based on the target configuration
179	 (since CISCO core files don't seem to have the processor
180	 encoded in them).  */
181
182      switch (bfd_get_32 (abfd, crashinfo.cpu_vector))
183	{
184	   /* bus error           */
185	case 2 : abfd->tdata.cisco_core_data->sig = SIGBUS; break;
186	   /* address error       */
187	case 3 : abfd->tdata.cisco_core_data->sig = SIGBUS; break;
188	   /* illegal instruction */
189	case 4 : abfd->tdata.cisco_core_data->sig = SIGILL;  break;
190	   /* zero divide         */
191	case 5 : abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
192	   /* chk instruction     */
193	case 6 : abfd->tdata.cisco_core_data->sig = SIGFPE; break;
194	   /* trapv instruction   */
195	case 7 : abfd->tdata.cisco_core_data->sig = SIGFPE; break;
196	   /* privilege violation */
197	case 8 : abfd->tdata.cisco_core_data->sig = SIGSEGV; break;
198	   /* trace trap          */
199	case 9 : abfd->tdata.cisco_core_data->sig = SIGTRAP;  break;
200	   /* line 1010 emulator  */
201	case 10: abfd->tdata.cisco_core_data->sig = SIGILL;  break;
202	   /* line 1111 emulator  */
203	case 11: abfd->tdata.cisco_core_data->sig = SIGILL;  break;
204
205	  /* Coprocessor protocol violation.  Using a standard MMU or FPU
206	     this cannot be triggered by software.  Call it a SIGBUS.  */
207	case 13: abfd->tdata.cisco_core_data->sig = SIGBUS;  break;
208
209	  /* interrupt           */
210	case 31: abfd->tdata.cisco_core_data->sig = SIGINT;  break;
211	  /* breakpoint          */
212	case 33: abfd->tdata.cisco_core_data->sig = SIGTRAP;  break;
213
214	  /* floating point err  */
215	case 48: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
216	  /* floating point err  */
217	case 49: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
218	  /* zero divide         */
219	case 50: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
220	  /* underflow           */
221	case 51: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
222	  /* operand error       */
223	case 52: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
224	   /* overflow            */
225	case 53: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
226	  /* NAN                 */
227	case 54: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
228	default:
229#ifndef SIGEMT
230#define SIGEMT SIGTRAP
231#endif
232	  /* "software generated"*/
233	  abfd->tdata.cisco_core_data->sig = SIGEMT;
234	}
235      break;
236    default:
237      /* Unknown crash reason.  */
238      abfd->tdata.cisco_core_data->sig = 0;
239      break;
240    }
241
242  /* Create a ".data" section that maps the entire file, which is
243     essentially a dump of the target system's RAM.  */
244
245  flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS;
246  asect = bfd_make_section_anyway_with_flags (abfd, ".data", flags);
247  if (asect == NULL)
248    goto error_return;
249  /* The size of memory is the size of the core file itself.  */
250  asect->size = statbuf.st_size;
251  asect->vma = rambase;
252  asect->filepos = 0;
253
254  /* Create a ".crash" section to allow access to the saved
255     crash information.  */
256
257  flags = SEC_HAS_CONTENTS;
258  asect = bfd_make_section_anyway_with_flags (abfd, ".crash", flags);
259  if (asect == NULL)
260    goto error_return;
261  asect->vma = 0;
262  asect->filepos = crashinfo_offset;
263  asect->size = sizeof (crashinfo);
264
265  /* Create a ".reg" section to allow access to the saved
266     registers.  */
267
268  asect = bfd_make_section_anyway_with_flags (abfd, ".reg", flags);
269  if (asect == NULL)
270    goto error_return;
271  asect->vma = 0;
272  asect->filepos = bfd_get_32 (abfd, crashinfo.registers) - rambase;
273  /* Since we don't know the exact size of the saved register info,
274     choose a register section size that is either the remaining part
275     of the file, or 1024, whichever is smaller.  */
276  nread = statbuf.st_size - asect->filepos;
277  asect->size = (nread < 1024) ? nread : 1024;
278
279  return abfd->xvec;
280
281  /* Get here if we have already started filling out the BFD
282     and there is an error of some kind.  */
283
284 error_return:
285  bfd_release (abfd, abfd->tdata.any);
286  abfd->tdata.any = NULL;
287  bfd_section_list_clear (abfd);
288  return NULL;
289}
290
291static const bfd_target *
292cisco_core_file_p (abfd)
293     bfd *abfd;
294{
295  int *crash_info_locp;
296  const bfd_target *target = NULL;
297
298  for (crash_info_locp = crash_info_locs;
299       *crash_info_locp != -1  &&  target == NULL;
300       crash_info_locp++)
301    {
302      target = cisco_core_file_validate (abfd, *crash_info_locp);
303    }
304  return (target);
305}
306
307char *
308cisco_core_file_failing_command (abfd)
309     bfd *abfd ATTRIBUTE_UNUSED;
310{
311  return NULL;
312}
313
314int
315cisco_core_file_failing_signal (abfd)
316     bfd *abfd ATTRIBUTE_UNUSED;
317{
318  return abfd->tdata.cisco_core_data->sig;
319}
320
321extern const bfd_target cisco_core_little_vec;
322
323const bfd_target cisco_core_big_vec =
324  {
325    "cisco-ios-core-big",
326    bfd_target_unknown_flavour,
327    BFD_ENDIAN_BIG,		/* target byte order */
328    BFD_ENDIAN_BIG,		/* target headers byte order */
329    (HAS_RELOC | EXEC_P |	/* object flags */
330     HAS_LINENO | HAS_DEBUG |
331     HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
332    (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
333    0,			                                   /* symbol prefix */
334    ' ',						   /* ar_pad_char */
335    16,							   /* ar_max_namelen */
336    bfd_getb64, bfd_getb_signed_64, bfd_putb64,
337    bfd_getb32, bfd_getb_signed_32, bfd_putb32,
338    bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
339    bfd_getb64, bfd_getb_signed_64, bfd_putb64,
340    bfd_getb32, bfd_getb_signed_32, bfd_putb32,
341    bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
342
343    {				/* bfd_check_format */
344     _bfd_dummy_target,		/* unknown format */
345     _bfd_dummy_target,		/* object file */
346     _bfd_dummy_target,		/* archive */
347     cisco_core_file_p	/* a core file */
348    },
349    {				/* bfd_set_format */
350     bfd_false, bfd_false,
351     bfd_false, bfd_false
352    },
353    {				/* bfd_write_contents */
354     bfd_false, bfd_false,
355     bfd_false, bfd_false
356    },
357
358       BFD_JUMP_TABLE_GENERIC (_bfd_generic),
359       BFD_JUMP_TABLE_COPY (_bfd_generic),
360       BFD_JUMP_TABLE_CORE (cisco),
361       BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
362       BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols),
363       BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
364       BFD_JUMP_TABLE_WRITE (_bfd_generic),
365       BFD_JUMP_TABLE_LINK (_bfd_nolink),
366       BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
367
368    & cisco_core_little_vec,
369
370    (PTR) 0			/* backend_data */
371};
372
373const bfd_target cisco_core_little_vec =
374  {
375    "cisco-ios-core-little",
376    bfd_target_unknown_flavour,
377    BFD_ENDIAN_LITTLE,		/* target byte order */
378    BFD_ENDIAN_LITTLE,		/* target headers byte order */
379    (HAS_RELOC | EXEC_P |	/* object flags */
380     HAS_LINENO | HAS_DEBUG |
381     HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
382    (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
383    0,			                                   /* symbol prefix */
384    ' ',						   /* ar_pad_char */
385    16,							   /* ar_max_namelen */
386    bfd_getl64, bfd_getl_signed_64, bfd_putl64,
387    bfd_getl32, bfd_getl_signed_32, bfd_putl32,
388    bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
389    bfd_getl64, bfd_getl_signed_64, bfd_putl64,
390    bfd_getl32, bfd_getl_signed_32, bfd_putl32,
391    bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
392
393    {				/* bfd_check_format */
394     _bfd_dummy_target,		/* unknown format */
395     _bfd_dummy_target,		/* object file */
396     _bfd_dummy_target,		/* archive */
397     cisco_core_file_p	/* a core file */
398    },
399    {				/* bfd_set_format */
400     bfd_false, bfd_false,
401     bfd_false, bfd_false
402    },
403    {				/* bfd_write_contents */
404     bfd_false, bfd_false,
405     bfd_false, bfd_false
406    },
407
408       BFD_JUMP_TABLE_GENERIC (_bfd_generic),
409       BFD_JUMP_TABLE_COPY (_bfd_generic),
410       BFD_JUMP_TABLE_CORE (cisco),
411       BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
412       BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols),
413       BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
414       BFD_JUMP_TABLE_WRITE (_bfd_generic),
415       BFD_JUMP_TABLE_LINK (_bfd_nolink),
416       BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
417
418    &cisco_core_big_vec,
419
420    (PTR) 0			/* backend_data */
421};
422