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