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