/****************************************************************************/
/*                                                                          */
/*                         GNAT COMPILER COMPONENTS                         */
/*                                                                          */
/*                               A - I N I T                                */
/*                                                                          */
/*                            $Revision: 1.14 $                              */
/*                                                                          */
/*                          C Implementation File                           */
/*                                                                          */
/*    Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.   */
/*                                                                          */
/* GNAT is free software;  you can  redistribute it  and/or modify it under */
/* terms of the  GNU General Public License as published  by the Free Soft- */
/* ware  Foundation;  either version 2,  or (at your option) any later ver- */
/* sion.  GNAT is distributed in the hope that it will be useful, but WITH- */
/* OUT 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  distributed with GNAT;  see file COPYING.  If not, write */
/* to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, */
/* MA 02111-1307, USA.                                                      */
/*                                                                          */
/* As a  special  exception,  if you  link  this file  with other  files to */
/* produce an executable,  this file does not by itself cause the resulting */
/* executable to be covered by the GNU General Public License. This except- */
/* ion does not  however invalidate  any other reasons  why the  executable */
/* file might be covered by the  GNU Public License.                        */
/*                                                                          */
/* GNAT was originally developed  by the GNAT team at  New York University. */
/* It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). */
/*                                                                          */
/****************************************************************************/

/* __gnat_initialize  is called right at the start of execution of an Ada
   program (the call is generated by the binder). The standard routine does
   nothing at all, the intention is that this be replaced by system specific
   code where initialization is required. */

extern char constraint_error;
extern char numeric_error;
extern char program_error;
extern char storage_error;
extern char tasking_error;
extern char _abort_signal;


extern void  system__task_specific_data__set_gnat_exception (void* e);
extern void *system__task_specific_data__get_exc_stack_addr ();
extern void  __gnat_reraise ();

#if defined (MSDOS)
#include <stdio.h>

/* This is DOS specific.  It does nothing unless run from an 
   environment that creates a gw-gnat.$$$ file.  If that file is present,
   it forces all program output to the console.  This allows the debugger
   to be run sending output to a file, but the program output still goes
   to the console. */

__gnat_initialize()
{
    if (access("gw-gnat.$$$", 0) == 0)
	freopen("CON", "w", stdout);
}

#elif defined (sgi)

/* This is SGI specific */

#include <signal.h>
#include <sys/siginfo.h>
#include <sys/ucontext.h>
#include <sys/types.h>

/* #include "a-raise.h" */

#define SIGNAL_STACK_SIZE 4096
#define SIGNAL_STACK_ALIGNMENT 64
#define NULL 0
#define SIGADAABORT 34

extern int (*system__tasking_soft_links__check_abort_status) ();

char* signal_stack;                       /* Alternate signal stack */

static void __gnat_error_handler (int sig, int code, struct sigcontext *sc)
{

  char* exception;
  void* exc_stack = system__task_specific_data__get_exc_stack_addr ();

  if (sig == SIGSEGV) {

    if (code == SEGV_MAPERR) {
      /* Assume reference to unmapped pages is a bad pointer dereference
         and map it to constraint_error */
      exception = &constraint_error;

    } else {
      /* Otherwise we: are referencing a mapped page that is protected, or
         have reached our process's stack limit (see setrlimit(2)).
         In both cases, raise Storage_Error */
      exception = &storage_error;
    }

  } else if (sig == SIGBUS) {
    /* Map all bus errors to constraint_error */
    exception = &constraint_error;

  } else if (sig == SIGFPE) {
    /* Map all fpe errors to constraint_error */
    exception = &constraint_error;

  } else if (sig == SIGADAABORT) {
    if ((*system__tasking_soft_links__check_abort_status)()) {
       exception = &_abort_signal;
    } else {
       return;
    }
  } else {
    /* Everything else is a program_error */
    exception = &program_error;
  }

  system__task_specific_data__set_gnat_exception (exception);

  /*  Overwrite the signal context's saved PC with the address of
      the gnat_reraise routine, so the return from this handler
      will resume execution at the raise point. */

  sc->sc_pc = (__uint64_t) ((int)__gnat_reraise);

  /*  Overwrite the saved SP value with the thread's special exception
      stack pointer so the exception propagation code will run on
      that stack.  This is necessary if we are raising Storage_Error
      resulting from a task stack overrun. */

  sc->sc_regs [CTX_SP] = (__uint64_t) ((int)exc_stack);

  /* Set T9 to the address of __gnat_reraise so the GP adjustment in
     the function prologue is correct */

  sc->sc_regs [CTX_T9] = (__uint64_t) ((int)__gnat_reraise);

  /* Set the first argument register (a0/$4) to -1 so that gnat_reraise
     will call abort_defer prior to propagating the exception. */

  sc->sc_regs [CTX_A0] = -1;

}

static __gnat_install_handler ()
{

  stack_t ss;
  struct sigaction act;

  /* Setup alternate signal stack */

  signal_stack = (char*) memalign (SIGNAL_STACK_ALIGNMENT, SIGNAL_STACK_SIZE);

  ss.ss_sp    = &signal_stack[SIGNAL_STACK_SIZE];
  ss.ss_size  = SIGNAL_STACK_SIZE;
  ss.ss_flags = 0;

  (void) sigaltstack (&ss, NULL);

  /* Setup signal handler to map synchronous signals to appropriate
     exceptions.  Make sure that the handler isn't interrupted by another
     signal that might cause a scheduling event! */

  act.sa_handler = __gnat_error_handler;
  act.sa_flags = SA_ONSTACK + SA_NODEFER + SA_RESTART;
  (void) sigfillset (&act.sa_mask);

  (void) sigaction (SIGILL,  &act, NULL);
  (void) sigaction (SIGABRT, &act, NULL);
  (void) sigaction (SIGFPE,  &act, NULL);
  (void) sigaction (SIGSEGV, &act, NULL);
  (void) sigaction (SIGBUS,  &act, NULL);

  (void) sigaction (SIGADAABORT,  &act, NULL);

}
__gnat_initialize()
{
  __gnat_install_handler();
}

#elif defined (VMS)

extern void* (*system__tasking_soft_links__abort_handler)();

#include <stdio.h>

#define SS$_ACCVIO 12
#define SS$_INTDIV 1156
#define SS$_HPARITH 1284
#define SS$_RESIGNAL 2328
#define SS$_CE24VRU 3253636       /* Write to unopened file ??? */
#define SS$_C980VTE 3246436       /* AST requests time slice ??? */
#define CMA$_EXIT_THREAD 4227492
#define CMA$_EXCCOPLOS 4228108
#define CMA$_ALERTED 4227460

extern char* __gnat_error_prehandler_stack;   /* Alternate signal stack */
extern long __gnat_error_prehandler ();

long __gnat_error_handler (sigargs, mechargs)
     int *sigargs;
     void *mechargs;
{
  char* exception;
  char message [100];

  if (sigargs [1] == SS$_ACCVIO)
    exception = &storage_error;
  else if ((sigargs [1] == SS$_INTDIV) || (sigargs [1] == SS$_HPARITH))
    exception = &constraint_error;
  else if (sigargs[1] == SS$_CE24VRU)
    exception = &constraint_error;
  else if (sigargs[1] == SS$_C980VTE)
    exception = &program_error;
  else if (sigargs [1] == CMA$_EXIT_THREAD)
    return SS$_RESIGNAL;
  else
    {
      sprintf (message, "unhandled condition value: %d\n", sigargs[1]);
      fputs (message, stdout);
      return SS$_RESIGNAL;
    }

  system__task_specific_data__set_gnat_exception (exception);
  __gnat_reraise (-1);
  return SS$_RESIGNAL;

}

static __gnat_install_handler ()
{
  long prvhnd;
  char *c;

  c = (char*) malloc (1025);

  __gnat_error_prehandler_stack = &c[1024];

  /* __gnat_error_prehandler is an assembly function */
  sys$setexv (1, __gnat_error_prehandler, 3, &prvhnd);
}

__gnat_initialize()
{
  __gnat_install_handler();
}

#elif defined(__alpha__) && defined(__osf__)

#include <signal.h>
#include <sys/siginfo.h>
#include <sys/ucontext.h>
#include <sys/types.h>

static void
__gnat_error_handler (sig, code, sc)
     int sig;
     int code;
     struct sigcontext *sc;
{
  char *exception;
  void *exc_stack = system__task_specific_data__get_exc_stack_addr ();

  if (sig == SIGSEGV)
    exception = &constraint_error;
  else if (sig == SIGBUS)
    exception = &constraint_error;
  else if (sig == SIGFPE)
    exception = &constraint_error;
  else
    exception = &program_error;

  __gnat_raise (exception);
}

static
__gnat_install_handler ()
{
  struct sigaction act;

  /* Setup signal handler to map synchronous signals to appropriate
     exceptions.  Make sure that the handler isn't interrupted by another
     signal that might cause a scheduling event! */

  act.sa_handler = __gnat_error_handler;
  act.sa_flags = SA_ONSTACK + SA_NODEFER;
  (void) sigfillset (&act.sa_mask);

  (void) sigaction (SIGILL,  &act, NULL);
  (void) sigaction (SIGABRT, &act, NULL);
  (void) sigaction (SIGFPE,  &act, NULL);
  (void) sigaction (SIGSEGV, &act, NULL);
  (void) sigaction (SIGBUS,  &act, NULL);
}

__gnat_initialize()
{
  __gnat_install_handler();
}

#else

__gnat_initialize()
{
}

#endif
