/* Extract registers from a "standard" core file, for GDB.
   Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
   1999, 2000, 2001 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

/* Typically used on systems that have a.out format executables.
   corefile.c is supposed to contain the more machine-independent
   aspects of reading registers from core files, while this file is
   more machine specific.  */

#include "defs.h"

#ifdef HAVE_PTRACE_H
#include <ptrace.h>
#else
#ifdef HAVE_SYS_PTRACE_H
#include <sys/ptrace.h>
#endif
#endif

#include <sys/types.h>
#include <sys/param.h>
#include "gdbcore.h"
#include "value.h"
#include "regcache.h"

/* These are needed on various systems to expand REGISTER_U_ADDR.  */
#include "gdb_dirent.h"
#include <sys/file.h>
#include "gdb_stat.h"
#include <sys/user.h>

#ifndef CORE_REGISTER_ADDR
#define CORE_REGISTER_ADDR(regno, regptr) register_addr(regno, regptr)
#endif /* CORE_REGISTER_ADDR */

#ifdef NEED_SYS_CORE_H
#include <sys/core.h>
#endif

static void fetch_core_registers (char *, unsigned, int, CORE_ADDR);

void _initialize_core_aout (void);

/* Extract the register values out of the core file and store
   them where `read_register' will find them.

   CORE_REG_SECT points to the register values themselves, read into memory.
   CORE_REG_SIZE is the size of that area.
   WHICH says which set of registers we are handling (0 = int, 2 = float
   on machines where they are discontiguous).
   REG_ADDR is the offset from u.u_ar0 to the register values relative to
   core_reg_sect.  This is used with old-fashioned core files to
   locate the registers in a large upage-plus-stack ".reg" section.
   Original upage address X is at location core_reg_sect+x+reg_addr.
 */

static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which,
		      CORE_ADDR reg_addr)
{
  int regno;
  CORE_ADDR addr;
  int bad_reg = -1;
  CORE_ADDR reg_ptr = -reg_addr;	/* Original u.u_ar0 is -reg_addr. */
  int numregs = NUM_REGS;

  /* If u.u_ar0 was an absolute address in the core file, relativize it now,
     so we can use it as an offset into core_reg_sect.  When we're done,
     "register 0" will be at core_reg_sect+reg_ptr, and we can use
     CORE_REGISTER_ADDR to offset to the other registers.  If this is a modern
     core file without a upage, reg_ptr will be zero and this is all a big
     NOP.  */
  if (reg_ptr > core_reg_size)
    reg_ptr -= KERNEL_U_ADDR;

  for (regno = 0; regno < numregs; regno++)
    {
      addr = CORE_REGISTER_ADDR (regno, reg_ptr);
      if (addr >= core_reg_size
	  && bad_reg < 0)
	bad_reg = regno;
      else
	regcache_raw_supply (current_regcache, regno, core_reg_sect + addr);
    }

  if (bad_reg >= 0)
    error (_("Register %s not found in core file."), REGISTER_NAME (bad_reg));
}


#ifdef REGISTER_U_ADDR

/* Return the address in the core dump or inferior of register REGNO.
   BLOCKEND is the address of the end of the user structure.  */

CORE_ADDR
register_addr (int regno, CORE_ADDR blockend)
{
  CORE_ADDR addr;

  if (regno < 0 || regno >= NUM_REGS)
    error (_("Invalid register number %d."), regno);

  REGISTER_U_ADDR (addr, blockend, regno);

  return addr;
}

#endif /* REGISTER_U_ADDR */


/* Register that we are able to handle aout (trad-core) file formats.  */

static struct core_fns aout_core_fns =
{
  bfd_target_unknown_flavour,		/* core_flavour */
  default_check_format,			/* check_format */
  default_core_sniffer,			/* core_sniffer */
  fetch_core_registers,			/* core_read_registers */
  NULL					/* next */
};

void
_initialize_core_aout (void)
{
  deprecated_add_core_fns (&aout_core_fns);
}
