155682Smarkm/* BFD back-end for CISCO crash dumps.
2102644Snectar   Copyright 1994, 1997, 1999, 2000, 2001, 2002, 2004
355682Smarkm   Free Software Foundation, Inc.
455682Smarkm
555682SmarkmThis file is part of BFD, the Binary File Descriptor library.
655682Smarkm
755682SmarkmThis program is free software; you can redistribute it and/or modify
855682Smarkmit under the terms of the GNU General Public License as published by
955682Smarkmthe Free Software Foundation; either version 2 of the License, or
1055682Smarkm(at your option) any later version.
1155682Smarkm
1255682SmarkmThis program is distributed in the hope that it will be useful,
1355682Smarkmbut WITHOUT ANY WARRANTY; without even the implied warranty of
1455682SmarkmMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1555682SmarkmGNU General Public License for more details.
1655682Smarkm
1755682SmarkmYou should have received a copy of the GNU General Public License
1855682Smarkmalong with this program; if not, write to the Free Software
1955682SmarkmFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2055682Smarkm
2155682Smarkm#include "bfd.h"
2255682Smarkm#include "sysdep.h"
2355682Smarkm#include "libbfd.h"
2455682Smarkm/* core_file_failing_signal returns a host signal (this probably should
2555682Smarkm   be fixed).  */
2655682Smarkm#include <signal.h>
2755682Smarkm
2855682Smarkm/* for MSVC builds */
2955682Smarkm#ifndef SIGTRAP
3055682Smarkm# define SIGTRAP 5
3155682Smarkm#endif
3255682Smarkm#ifndef SIGEMT
3355682Smarkm# define SIGEMT 6
3455682Smarkm#endif
3555682Smarkm#ifndef SIGBUS
36178825Sdfr# define SIGBUS 10
37178825Sdfr#endif
38178825Sdfr
3955682Smarkmint crash_info_locs[] = {
4055682Smarkm  0x0250,	/* mips, ppc, x86, i960 */
41178825Sdfr  0x0400,	/* m68k, mips, x86, i960 */
4255682Smarkm  0x0FFC,	/* m68k, mips, ppc, x86, i960 */
4355682Smarkm  0x3000,	/* ppc */
4455682Smarkm  0x4FFC,	/* m68k */
4555682Smarkm  -1
4655682Smarkm};
4755682Smarkm
4855682Smarkm#define CRASH_MAGIC	0xdead1234
4955682Smarkm#define MASK_ADDR(x)	((x) & 0x0fffffff)	/* Mask crash info address */
5055682Smarkm
5155682Smarkmtypedef enum {
5255682Smarkm    CRASH_REASON_NOTCRASHED = 0,
5355682Smarkm    CRASH_REASON_EXCEPTION = 1,
5455682Smarkm    CRASH_REASON_CORRUPT = 2,
5555682Smarkm} crashreason;
5655682Smarkm
5755682Smarkmtypedef struct {
5855682Smarkm  char magic[4];		/* Magic number */
5955682Smarkm  char version[4];		/* Version number */
6055682Smarkm  char reason[4];		/* Crash reason */
6155682Smarkm  char cpu_vector[4];		/* CPU vector for exceptions */
6255682Smarkm  char registers[4];		/* Pointer to saved registers */
6355682Smarkm  char rambase[4];		/* Base of RAM (not in V1 crash info) */
6455682Smarkm  char textbase[4];		/* Base of .text section (not in V3 crash info) */
6555682Smarkm  char database[4];		/* Base of .data section (not in V3 crash info) */
6655682Smarkm  char bssbase[4];		/* Base of .bss section (not in V3 crash info) */
6755682Smarkm} crashinfo_external;
6855682Smarkm
6955682Smarkmstruct cisco_core_struct
7055682Smarkm{
7155682Smarkm  int sig;
7255682Smarkm};
7355682Smarkm
7455682Smarkmstatic const bfd_target *cisco_core_file_validate PARAMS ((bfd *, int));
7555682Smarkmstatic const bfd_target *cisco_core_file_p PARAMS ((bfd *));
7655682Smarkmchar *cisco_core_file_failing_command PARAMS ((bfd *));
7755682Smarkmint cisco_core_file_failing_signal PARAMS ((bfd *));
7855682Smarkmbfd_boolean cisco_core_file_matches_executable_p PARAMS ((bfd *, bfd *));
7955682Smarkm
8055682Smarkm/* Examine the file for a crash info struct at the offset given by
8155682Smarkm   CRASH_INFO_LOC.  */
8255682Smarkm
8355682Smarkmstatic const bfd_target *
8455682Smarkmcisco_core_file_validate (abfd, crash_info_loc)
8555682Smarkm     bfd *abfd;
8655682Smarkm     int crash_info_loc;
8755682Smarkm{
8855682Smarkm  char buf[4];
8955682Smarkm  unsigned int crashinfo_offset;
9055682Smarkm  crashinfo_external crashinfo;
9155682Smarkm  int nread;
9255682Smarkm  unsigned int magic;
9355682Smarkm  unsigned int version;
9455682Smarkm  unsigned int rambase;
9555682Smarkm  sec_ptr asect;
9655682Smarkm  struct stat statbuf;
9755682Smarkm  bfd_size_type amt;
9855682Smarkm
9955682Smarkm  if (bfd_seek (abfd, (file_ptr) crash_info_loc, SEEK_SET) != 0)
10055682Smarkm    return NULL;
10155682Smarkm
10255682Smarkm  nread = bfd_bread (buf, (bfd_size_type) 4, abfd);
10355682Smarkm  if (nread != 4)
10455682Smarkm    {
10555682Smarkm      if (bfd_get_error () != bfd_error_system_call)
10655682Smarkm	bfd_set_error (bfd_error_wrong_format);
10755682Smarkm      return NULL;
10855682Smarkm    }
10955682Smarkm  crashinfo_offset = MASK_ADDR (bfd_get_32 (abfd, buf));
11055682Smarkm
11155682Smarkm  if (bfd_seek (abfd, (file_ptr) crashinfo_offset, SEEK_SET) != 0)
11255682Smarkm    {
11355682Smarkm      /* Most likely we failed because of a bogus (huge) offset */
11455682Smarkm      bfd_set_error (bfd_error_wrong_format);
11555682Smarkm      return NULL;
11655682Smarkm    }
11755682Smarkm
11855682Smarkm  nread = bfd_bread (&crashinfo, (bfd_size_type) sizeof (crashinfo), abfd);
11955682Smarkm  if (nread != sizeof (crashinfo))
12055682Smarkm    {
12155682Smarkm      if (bfd_get_error () != bfd_error_system_call)
12255682Smarkm	bfd_set_error (bfd_error_wrong_format);
12355682Smarkm      return NULL;
12455682Smarkm    }
12555682Smarkm
12655682Smarkm  if (bfd_stat (abfd, &statbuf) < 0)
12755682Smarkm    {
12855682Smarkm      bfd_set_error (bfd_error_system_call);
12955682Smarkm      return NULL;
13055682Smarkm    }
13155682Smarkm
13255682Smarkm  magic = bfd_get_32 (abfd, crashinfo.magic);
13355682Smarkm  if (magic != CRASH_MAGIC)
13455682Smarkm    {
13555682Smarkm      bfd_set_error (bfd_error_wrong_format);
13655682Smarkm      return NULL;
13755682Smarkm    }
13855682Smarkm
13955682Smarkm  version = bfd_get_32 (abfd, crashinfo.version);
14055682Smarkm  if (version == 0)
14155682Smarkm    {
14255682Smarkm      bfd_set_error (bfd_error_wrong_format);
14355682Smarkm      return NULL;
14455682Smarkm    }
14555682Smarkm  else if (version == 1)
14655682Smarkm    {
14755682Smarkm      /* V1 core dumps don't specify the dump base, assume 0 */
14855682Smarkm      rambase = 0;
14955682Smarkm    }
15055682Smarkm  else
15155682Smarkm    {
15255682Smarkm      rambase = bfd_get_32 (abfd, crashinfo.rambase);
15355682Smarkm    }
15455682Smarkm
15555682Smarkm  /* OK, we believe you.  You're a core file.  */
15655682Smarkm
15755682Smarkm  amt = sizeof (struct cisco_core_struct);
15855682Smarkm  abfd->tdata.cisco_core_data = (struct cisco_core_struct *) bfd_zmalloc (amt);
15955682Smarkm  if (abfd->tdata.cisco_core_data == NULL)
16055682Smarkm    return NULL;
16155682Smarkm
16255682Smarkm  switch ((crashreason) bfd_get_32 (abfd, crashinfo.reason))
16355682Smarkm    {
16455682Smarkm    case CRASH_REASON_NOTCRASHED:
16555682Smarkm      /* Crash file probably came from write core.  */
16655682Smarkm      abfd->tdata.cisco_core_data->sig = 0;
16755682Smarkm      break;
16855682Smarkm    case CRASH_REASON_CORRUPT:
16955682Smarkm      /* The crash context area was corrupt -- proceed with caution.
17055682Smarkm	 We have no way of passing this information back to the caller.  */
17155682Smarkm      abfd->tdata.cisco_core_data->sig = 0;
17255682Smarkm      break;
173178825Sdfr    case CRASH_REASON_EXCEPTION:
17455682Smarkm      /* Crash occured due to CPU exception.  */
17555682Smarkm
17655682Smarkm      /* This is 68k-specific; for MIPS we'll need to interpret
17755682Smarkm	 cpu_vector differently based on the target configuration
17890926Snectar	 (since CISCO core files don't seem to have the processor
17955682Smarkm	 encoded in them).  */
18055682Smarkm
18155682Smarkm      switch (bfd_get_32 (abfd, crashinfo.cpu_vector))
18255682Smarkm	{
18355682Smarkm	   /* bus error           */
18455682Smarkm	case 2 : abfd->tdata.cisco_core_data->sig = SIGBUS; break;
18555682Smarkm	   /* address error       */
18655682Smarkm	case 3 : abfd->tdata.cisco_core_data->sig = SIGBUS; break;
18755682Smarkm	   /* illegal instruction */
18855682Smarkm	case 4 : abfd->tdata.cisco_core_data->sig = SIGILL;  break;
18955682Smarkm	   /* zero divide         */
19055682Smarkm	case 5 : abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
19155682Smarkm	   /* chk instruction     */
19255682Smarkm	case 6 : abfd->tdata.cisco_core_data->sig = SIGFPE; break;
19355682Smarkm	   /* trapv instruction   */
19455682Smarkm	case 7 : abfd->tdata.cisco_core_data->sig = SIGFPE; break;
19555682Smarkm	   /* privilege violation */
19655682Smarkm	case 8 : abfd->tdata.cisco_core_data->sig = SIGSEGV; break;
19755682Smarkm	   /* trace trap          */
19855682Smarkm	case 9 : abfd->tdata.cisco_core_data->sig = SIGTRAP;  break;
19955682Smarkm	   /* line 1010 emulator  */
20055682Smarkm	case 10: abfd->tdata.cisco_core_data->sig = SIGILL;  break;
20155682Smarkm	   /* line 1111 emulator  */
20255682Smarkm	case 11: abfd->tdata.cisco_core_data->sig = SIGILL;  break;
20355682Smarkm
20455682Smarkm	  /* Coprocessor protocol violation.  Using a standard MMU or FPU
20555682Smarkm	     this cannot be triggered by software.  Call it a SIGBUS.  */
20655682Smarkm	case 13: abfd->tdata.cisco_core_data->sig = SIGBUS;  break;
20755682Smarkm
20855682Smarkm	  /* interrupt           */
20955682Smarkm	case 31: abfd->tdata.cisco_core_data->sig = SIGINT;  break;
21055682Smarkm	  /* breakpoint          */
21155682Smarkm	case 33: abfd->tdata.cisco_core_data->sig = SIGTRAP;  break;
21255682Smarkm
21355682Smarkm	  /* floating point err  */
21455682Smarkm	case 48: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
21555682Smarkm	  /* floating point err  */
21655682Smarkm	case 49: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
21755682Smarkm	  /* zero divide         */
21855682Smarkm	case 50: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
21955682Smarkm	  /* underflow           */
22055682Smarkm	case 51: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
22155682Smarkm	  /* operand error       */
22255682Smarkm	case 52: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
22355682Smarkm	   /* overflow            */
22455682Smarkm	case 53: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
22555682Smarkm	  /* NAN                 */
22655682Smarkm	case 54: abfd->tdata.cisco_core_data->sig = SIGFPE;  break;
22755682Smarkm	default:
22855682Smarkm#ifndef SIGEMT
22955682Smarkm#define SIGEMT SIGTRAP
23055682Smarkm#endif
23155682Smarkm	  /* "software generated"*/
23255682Smarkm	  abfd->tdata.cisco_core_data->sig = SIGEMT;
23355682Smarkm	}
23455682Smarkm      break;
23555682Smarkm    default:
23655682Smarkm      /* Unknown crash reason.  */
23755682Smarkm      abfd->tdata.cisco_core_data->sig = 0;
23855682Smarkm      break;
23955682Smarkm    }
24055682Smarkm
24155682Smarkm  /* Create a ".data" section that maps the entire file, which is
24255682Smarkm     essentially a dump of the target system's RAM.  */
24355682Smarkm
24455682Smarkm  asect = bfd_make_section_anyway (abfd, ".data");
24555682Smarkm  if (asect == NULL)
24655682Smarkm    goto error_return;
24755682Smarkm  asect->flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS;
24855682Smarkm  /* The size of memory is the size of the core file itself.  */
24955682Smarkm  asect->size = statbuf.st_size;
25055682Smarkm  asect->vma = rambase;
25155682Smarkm  asect->filepos = 0;
25255682Smarkm
25355682Smarkm  /* Create a ".crash" section to allow access to the saved
25455682Smarkm     crash information.  */
25555682Smarkm
25655682Smarkm  asect = bfd_make_section_anyway (abfd, ".crash");
25755682Smarkm  if (asect == NULL)
25855682Smarkm    goto error_return;
25955682Smarkm  asect->flags = SEC_HAS_CONTENTS;
26055682Smarkm  asect->vma = 0;
26155682Smarkm  asect->filepos = crashinfo_offset;
26255682Smarkm  asect->size = sizeof (crashinfo);
26355682Smarkm
26455682Smarkm  /* Create a ".reg" section to allow access to the saved
26555682Smarkm     registers.  */
26655682Smarkm
26755682Smarkm  asect = bfd_make_section_anyway (abfd, ".reg");
26855682Smarkm  if (asect == NULL)
26955682Smarkm    goto error_return;
27055682Smarkm  asect->flags = SEC_HAS_CONTENTS;
27155682Smarkm  asect->vma = 0;
27255682Smarkm  asect->filepos = bfd_get_32 (abfd, crashinfo.registers) - rambase;
27355682Smarkm  /* Since we don't know the exact size of the saved register info,
27455682Smarkm     choose a register section size that is either the remaining part
27555682Smarkm     of the file, or 1024, whichever is smaller.  */
27655682Smarkm  nread = statbuf.st_size - asect->filepos;
27755682Smarkm  asect->size = (nread < 1024) ? nread : 1024;
27855682Smarkm
27955682Smarkm  return abfd->xvec;
28055682Smarkm
28155682Smarkm  /* Get here if we have already started filling out the BFD
28255682Smarkm     and there is an error of some kind.  */
28355682Smarkm
28455682Smarkm error_return:
28555682Smarkm  bfd_release (abfd, abfd->tdata.any);
28655682Smarkm  abfd->tdata.any = NULL;
28755682Smarkm  bfd_section_list_clear (abfd);
28855682Smarkm  return NULL;
28955682Smarkm}
29055682Smarkm
29155682Smarkmstatic const bfd_target *
29255682Smarkmcisco_core_file_p (abfd)
29355682Smarkm     bfd *abfd;
29455682Smarkm{
29555682Smarkm  int *crash_info_locp;
296178825Sdfr  const bfd_target *target = NULL;
29755682Smarkm
29855682Smarkm  for (crash_info_locp = crash_info_locs;
29955682Smarkm       *crash_info_locp != -1  &&  target == NULL;
30055682Smarkm       crash_info_locp++)
30155682Smarkm    {
30255682Smarkm      target = cisco_core_file_validate (abfd, *crash_info_locp);
30355682Smarkm    }
30455682Smarkm  return (target);
30555682Smarkm}
30655682Smarkm
30755682Smarkmchar *
30855682Smarkmcisco_core_file_failing_command (abfd)
30955682Smarkm     bfd *abfd ATTRIBUTE_UNUSED;
31055682Smarkm{
31155682Smarkm  return NULL;
31255682Smarkm}
31355682Smarkm
31455682Smarkmint
31555682Smarkmcisco_core_file_failing_signal (abfd)
31655682Smarkm     bfd *abfd ATTRIBUTE_UNUSED;
31755682Smarkm{
31855682Smarkm  return abfd->tdata.cisco_core_data->sig;
31955682Smarkm}
32055682Smarkm
32155682Smarkmbfd_boolean
32255682Smarkmcisco_core_file_matches_executable_p (core_bfd, exec_bfd)
32355682Smarkm     bfd *core_bfd ATTRIBUTE_UNUSED;
32455682Smarkm     bfd *exec_bfd ATTRIBUTE_UNUSED;
32555682Smarkm{
32655682Smarkm  return TRUE;
32755682Smarkm}
32855682Smarkm
32955682Smarkmextern const bfd_target cisco_core_little_vec;
33055682Smarkm
33155682Smarkmconst bfd_target cisco_core_big_vec =
33255682Smarkm  {
33355682Smarkm    "cisco-ios-core-big",
33455682Smarkm    bfd_target_unknown_flavour,
33555682Smarkm    BFD_ENDIAN_BIG,		/* target byte order */
33655682Smarkm    BFD_ENDIAN_BIG,		/* target headers byte order */
33755682Smarkm    (HAS_RELOC | EXEC_P |	/* object flags */
33855682Smarkm     HAS_LINENO | HAS_DEBUG |
33955682Smarkm     HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
34055682Smarkm    (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
34155682Smarkm    0,			                                   /* symbol prefix */
34255682Smarkm    ' ',						   /* ar_pad_char */
34355682Smarkm    16,							   /* ar_max_namelen */
34455682Smarkm    bfd_getb64, bfd_getb_signed_64, bfd_putb64,
34555682Smarkm    bfd_getb32, bfd_getb_signed_32, bfd_putb32,
34655682Smarkm    bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
34755682Smarkm    bfd_getb64, bfd_getb_signed_64, bfd_putb64,
34855682Smarkm    bfd_getb32, bfd_getb_signed_32, bfd_putb32,
34955682Smarkm    bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
35055682Smarkm
35155682Smarkm    {				/* bfd_check_format */
35255682Smarkm     _bfd_dummy_target,		/* unknown format */
35355682Smarkm     _bfd_dummy_target,		/* object file */
354102644Snectar     _bfd_dummy_target,		/* archive */
35555682Smarkm     cisco_core_file_p	/* a core file */
35655682Smarkm    },
357102644Snectar    {				/* bfd_set_format */
35855682Smarkm     bfd_false, bfd_false,
35955682Smarkm     bfd_false, bfd_false
36055682Smarkm    },
36155682Smarkm    {				/* bfd_write_contents */
36255682Smarkm     bfd_false, bfd_false,
36355682Smarkm     bfd_false, bfd_false
36455682Smarkm    },
36555682Smarkm
36655682Smarkm       BFD_JUMP_TABLE_GENERIC (_bfd_generic),
36755682Smarkm       BFD_JUMP_TABLE_COPY (_bfd_generic),
36855682Smarkm       BFD_JUMP_TABLE_CORE (cisco),
36955682Smarkm       BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
37055682Smarkm       BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols),
37155682Smarkm       BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
37255682Smarkm       BFD_JUMP_TABLE_WRITE (_bfd_generic),
37355682Smarkm       BFD_JUMP_TABLE_LINK (_bfd_nolink),
37455682Smarkm       BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
37555682Smarkm
37655682Smarkm    & cisco_core_little_vec,
37755682Smarkm
37855682Smarkm    (PTR) 0			/* backend_data */
37955682Smarkm};
38055682Smarkm
38155682Smarkmconst bfd_target cisco_core_little_vec =
38255682Smarkm  {
38355682Smarkm    "cisco-ios-core-little",
38455682Smarkm    bfd_target_unknown_flavour,
38555682Smarkm    BFD_ENDIAN_LITTLE,		/* target byte order */
38655682Smarkm    BFD_ENDIAN_LITTLE,		/* target headers byte order */
38755682Smarkm    (HAS_RELOC | EXEC_P |	/* object flags */
38855682Smarkm     HAS_LINENO | HAS_DEBUG |
38990926Snectar     HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
39055682Smarkm    (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
39155682Smarkm    0,			                                   /* symbol prefix */
39255682Smarkm    ' ',						   /* ar_pad_char */
39355682Smarkm    16,							   /* ar_max_namelen */
39455682Smarkm    bfd_getl64, bfd_getl_signed_64, bfd_putl64,
39555682Smarkm    bfd_getl32, bfd_getl_signed_32, bfd_putl32,
39655682Smarkm    bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
39755682Smarkm    bfd_getl64, bfd_getl_signed_64, bfd_putl64,
39855682Smarkm    bfd_getl32, bfd_getl_signed_32, bfd_putl32,
39955682Smarkm    bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
40055682Smarkm
40155682Smarkm    {				/* bfd_check_format */
402     _bfd_dummy_target,		/* unknown format */
403     _bfd_dummy_target,		/* object file */
404     _bfd_dummy_target,		/* archive */
405     cisco_core_file_p	/* a core file */
406    },
407    {				/* bfd_set_format */
408     bfd_false, bfd_false,
409     bfd_false, bfd_false
410    },
411    {				/* bfd_write_contents */
412     bfd_false, bfd_false,
413     bfd_false, bfd_false
414    },
415
416       BFD_JUMP_TABLE_GENERIC (_bfd_generic),
417       BFD_JUMP_TABLE_COPY (_bfd_generic),
418       BFD_JUMP_TABLE_CORE (cisco),
419       BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
420       BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols),
421       BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
422       BFD_JUMP_TABLE_WRITE (_bfd_generic),
423       BFD_JUMP_TABLE_LINK (_bfd_nolink),
424       BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
425
426    &cisco_core_big_vec,
427
428    (PTR) 0			/* backend_data */
429};
430