/* Mac OS X support for GDB, the GNU debugger.
   Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2005
   Free Software Foundation, Inc.

   Contributed by Apple Computer, 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.  */

#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "gdbcore.h"
#include "target.h"
#include "floatformat.h"
#include "symtab.h"
#include "regcache.h"
#include "i387-tdep.h"
#include "i386-tdep.h"
#include "osabi.h"

#include "i386-macosx-thread-status.h"

#include <mach/thread_status.h>

#include "i386-macosx-tdep.h"

/* Unused on the x86 side of things -- this is only needed to
   help distinguish between ppc32 and ppc64 binaries on the PPC side. */

enum gdb_osabi osabi_seen_in_attached_dyld = GDB_OSABI_UNKNOWN;


#define supply_unsigned_int(regnum, val)\
store_unsigned_integer (buf, 4, val); \
supply_register(regnum, buf);

#define collect_unsigned_int(regnum, addr)\
regcache_collect (regnum, buf); \
(* (addr)) = extract_unsigned_integer (buf, 4);

void
i386_macosx_fetch_gp_registers (gdb_i386_thread_state_t *sp_regs)
{
  char buf[4];
  supply_unsigned_int (0, sp_regs->eax);
  supply_unsigned_int (1, sp_regs->ecx);
  supply_unsigned_int (2, sp_regs->edx);
  supply_unsigned_int (3, sp_regs->ebx);
  supply_unsigned_int (4, sp_regs->esp);
  supply_unsigned_int (5, sp_regs->ebp);
  supply_unsigned_int (6, sp_regs->esi);
  supply_unsigned_int (7, sp_regs->edi);
  supply_unsigned_int (8, sp_regs->eip);
  supply_unsigned_int (9, sp_regs->efl);
  supply_unsigned_int (10, sp_regs->cs);
  supply_unsigned_int (11, sp_regs->ss);
  supply_unsigned_int (12, sp_regs->ds);
  supply_unsigned_int (13, sp_regs->es);
  supply_unsigned_int (14, sp_regs->fs);
  supply_unsigned_int (15, sp_regs->gs);
}

void
i386_macosx_store_gp_registers (gdb_i386_thread_state_t *sp_regs)
{
  unsigned char buf[4];
  collect_unsigned_int (0, &sp_regs->eax);
  collect_unsigned_int (1, &sp_regs->ecx);
  collect_unsigned_int (2, &sp_regs->edx);
  collect_unsigned_int (3, &sp_regs->ebx);
  collect_unsigned_int (4, &sp_regs->esp);
  collect_unsigned_int (5, &sp_regs->ebp);
  collect_unsigned_int (6, &sp_regs->esi);
  collect_unsigned_int (7, &sp_regs->edi);
  collect_unsigned_int (8, &sp_regs->eip);
  collect_unsigned_int (9, &sp_regs->efl);
  collect_unsigned_int (10, &sp_regs->cs);
  collect_unsigned_int (11, &sp_regs->ss);
  collect_unsigned_int (12, &sp_regs->ds);
  collect_unsigned_int (13, &sp_regs->es);
  collect_unsigned_int (14, &sp_regs->fs);
  collect_unsigned_int (15, &sp_regs->gs);
}

/* Fetching the the registers from the inferior into our reg cache.
   FP_REGS is a structure that mirrors the Mach structure
   struct i386_float_state.  The "hw_fu_state" buffer inside that
   structure mirrors the Mach struct i386_fx_save, which is identical
   to the FXSAVE/FXRSTOR instructions' format.  */

void
i386_macosx_fetch_fp_registers (gdb_i386_thread_fpstate_t *fp_regs)
{
  i387_swap_fxsave (current_regcache, &fp_regs->hw_fu_state);
  i387_supply_fxsave (current_regcache, -1, &fp_regs->hw_fu_state);
}

/* Get the floating point registers from our local register cache
   and stick them in FP_REGS in for sending to the inferior via a
   syscall.  If the local register cache has valid FP values, this
   function returns 1.  If the local register cache does not have
   valid FP values -- and so FP_REGS should not be pushed into the
   inferior -- this function returns 0.  */

int
i386_macosx_store_fp_registers (gdb_i386_thread_fpstate_t *fp_regs)
{
  memset (fp_regs, 0, sizeof (gdb_i386_thread_fpstate_t));
  fp_regs->fpkind = GDB_i386_FP_SSE2; /* Corresponds to Mach's FP_FXSR */
  fp_regs->initialized = 1;
  fp_regs->exc_status = 0;
  i387_fill_fxsave ((unsigned char *) &fp_regs->hw_fu_state, -1);
  i387_swap_fxsave (current_regcache, &fp_regs->hw_fu_state);

  return 1;
}

/* The sigcontext (struct sigcontext) is put on the stack by the kernel
   and then _sigtramp() is called with an ESP below the sigcontext.
   This function exists to tell gdb how to find the start of the sigcontext
   when the PC is somewhere in the middle of _sigtramp(). 
   Because it's on the stack, its exact offset is different depending on
   whether you're before, in-the-middle-of, or after the _sigtramp 
   prologue frame-setting-up instructions. */

static CORE_ADDR
i386_macosx_sigcontext_addr (struct frame_info *frame)
{
  CORE_ADDR start_of_func = get_frame_func (frame);
  int offset = 0;
  CORE_ADDR push_ebp_addr = 0;
  CORE_ADDR mov_esp_ebp_addr = 0;
  CORE_ADDR esp, ebp, pc;
  CORE_ADDR address_of_pointer_to_struct_ucontext;
  CORE_ADDR address_of_struct_ucontext;
  CORE_ADDR address_of_struct_sigcontext;
  char buf[4];
  int limit;

  pc = get_frame_pc (frame);

  /* We begin our function with a fun little hand-rolled prologue parser.
     These sorts of things NEVER come back to bite us years down the road,
     no sir-ee bob.  The only saving grace is that _sigtramp() is a tough
     function to screw up as it stands today.  Oh, and if we get this wrong,
     signal backtraces should break outright and we get nice little testsuite 
     failures. */

  limit = min (pc - start_of_func + 1, 16);
  while (offset < limit)
    {
      if (!push_ebp_addr)
        {
          /* push   %ebp   [ 0x55 ] */
          if (read_memory_unsigned_integer (start_of_func + offset, 1) == 0x55)
            {
              push_ebp_addr = start_of_func + offset;
              offset++;
            }
          else
            {
              /* If this isn't the push %ebp, and we haven't seen push %ebp yet,
                 skip whatever insn we're sitting on and keep looking for 
                 push %ebp.  It must occur before mov %esp, %ebp.  */
              offset++;
              continue;
            }
        }

      /* We've already seen push %ebp */
      /* Look for mov %esp, %ebp  [ 0x89 0xe5 || 0x8b 0xec ] */
      if (read_memory_unsigned_integer (start_of_func + offset, 2) == 0xe589
          || read_memory_unsigned_integer (start_of_func + offset, 2) == 0xec8b)
        {
          mov_esp_ebp_addr = start_of_func + offset;
          break;
        }
      offset++;  /* I'm single byte stepping through unknown instructions.  
                    SURELY this won't cause an improper match, cough cough. */
    }
  if (!push_ebp_addr || !mov_esp_ebp_addr)
    error ("Unable to analyze the prologue of _sigtramp(), giving up.");

  frame_unwind_register (frame, I386_ESP_REGNUM, buf);
  esp = extract_unsigned_integer (buf, 4);
  frame_unwind_register (frame, I386_EBP_REGNUM, buf);
  ebp = extract_unsigned_integer (buf, 4);

  /* I'm not sure how to make the nomenclature clear here.
     address_of_pointer_to_struct_ucontext is like a "struct ucontext **";
     address_of_struct ucontext is like a "struct ucontext *";
     address_of_struct_sigcontext is like a "struct sigcontext *".  */

  if (pc <= push_ebp_addr)
    address_of_pointer_to_struct_ucontext = esp + 20;

  if (pc > push_ebp_addr && pc <= mov_esp_ebp_addr)
    address_of_pointer_to_struct_ucontext = esp + 24;

  if (pc > mov_esp_ebp_addr)
    address_of_pointer_to_struct_ucontext = ebp + 24;

  address_of_struct_ucontext = read_memory_unsigned_integer 
                                (address_of_pointer_to_struct_ucontext, 4);

  /* the element 'uc_mcontext' -- the pointer to the struct sigcontext -- 
     is 28 bytes into the 'struct ucontext' */
  address_of_struct_sigcontext = read_memory_unsigned_integer 
                                (address_of_struct_ucontext + 28, 4); 

  return address_of_struct_sigcontext;
}

/* Offsets into the struct sigcontext where we'll find the saved regs.  */
/* From /usr/include/i386/signal.h and i386-tdep.h */
static int i386_macosx_sc_reg_offset[] =
{
   2 * 4,   /* EAX */
   4 * 4,   /* ECX */
   5 * 4,   /* EDX */
   3 * 4,   /* EBX */
   9 * 4,   /* ESP */
   8 * 4,   /* EBP */
   7 * 4,   /* ESI */
   6 * 4,   /* EDI */
  12 * 4,   /* EIP */
  11 * 4,   /* EFLAGS */
  13 * 4,   /* CS */
  -1,       /* SS */
  14 * 4,   /* DS */
  15 * 4,   /* ES */
  16 * 4,   /* FS */
  17 * 4    /* GS */
};

static CORE_ADDR
i386_integer_to_address (struct type *type, void *buf)
{
  char *tmp = alloca (TYPE_LENGTH (builtin_type_void_data_ptr));
  LONGEST val = unpack_long (type, buf);
  store_unsigned_integer (tmp, TYPE_LENGTH (builtin_type_void_data_ptr), val);
  return extract_unsigned_integer (tmp,
                                   TYPE_LENGTH (builtin_type_void_data_ptr));
}

static void
i386_macosx_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

  /* We support the SSE registers.  */
  tdep->num_xmm_regs = I386_NUM_XREGS - 1;
  set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS);

  set_gdbarch_skip_trampoline_code (gdbarch, macosx_skip_trampoline_code);

  set_gdbarch_in_solib_call_trampoline (gdbarch,
                                        macosx_in_solib_call_trampoline);
  set_gdbarch_in_solib_return_trampoline (gdbarch,
                                          macosx_in_solib_return_trampoline);

  tdep->struct_return = reg_struct_return;

  tdep->sigcontext_addr = i386_macosx_sigcontext_addr;
  tdep->sc_reg_offset = i386_macosx_sc_reg_offset;
  tdep->sc_num_regs = 16;

  tdep->jb_pc_offset = 20;
  set_gdbarch_integer_to_address (gdbarch, i386_integer_to_address);
}

static enum gdb_osabi
i386_mach_o_osabi_sniffer (bfd *abfd)
{
  if (strcmp (bfd_get_target (abfd), "mach-o-le") == 0
      || strcmp (bfd_get_target (abfd), "mach-o-fat") == 0)
    return GDB_OSABI_DARWIN;

  return GDB_OSABI_UNKNOWN;
}


void
_initialize_i386_macosx_tdep (void)
{
  gdbarch_register_osabi_sniffer (bfd_arch_unknown, bfd_target_mach_o_flavour,
                                  i386_mach_o_osabi_sniffer);

  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_DARWIN,
                          i386_macosx_init_abi);

}
