tb-alvms.c   [plain text]


/****************************************************************************
 *                                                                          *
 *                         GNAT COMPILER COMPONENTS                         *
 *                                                                          *
 *                   T R A C E B A C K - A l p h a / V M S                  *
 *                                                                          *
 *                          C Implementation File                           *
 *                                                                          *
 *              Copyright (C) 2003 Ada Core Technologies, 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. *
 * Extensive contributions were provided by Ada Core Technologies Inc.      *
 *                                                                          *
 ****************************************************************************/


/* Alpha VMS requires a special treatment due to the complexity of the ABI.
   What is here is along the lines of what the MD_FALLBACK_FRAME_STATE_FOR
   macro does for frame unwinding during exception propagation. This file is
   #included within tracebak.c in the appropriate case.

   Most of the contents is directed by the OpenVMS/Alpha Conventions (ABI)
   document, sections of which we will refer to as ABI-<section_number>.  */

#include <pdscdef.h>
#include <libicb.h>
#include <chfctxdef.h>
#include <chfdef.h>

/* A couple of items missing from the header file included above.  */
extern void * SYS$GL_CALL_HANDL;
#define PDSC$M_BASE_FRAME (1 << 10)

/* Registers are 64bit wide and addresses are 32bit wide on alpha-vms.  */
typedef void * ADDR;
typedef unsigned long long REG;

#define REG_AT(addr) (*(REG *)(addr))

#define AS_REG(addr) ((REG)(unsigned long)(addr))
#define AS_ADDR(reg) ((ADDR)(unsigned long)(reg))
#define ADDR_IN(reg) (AS_ADDR(reg))

/* The following structure defines the state maintained during the
   unwinding process.  */
typedef struct
{
  ADDR pc;  /* Address of the call insn involved in the chain.  */
  ADDR sp;  /* Stack Pointer at the time of this call.  */
  ADDR fp;  /* Frame Pointer at the time of this call.  */

  /* The values above are fetched as saved REGisters on the stack. They are
     typed ADDR because this is what the values in those registers are.  */

  /* Values of the registers saved by the functions in the chain,
     incrementally updated through consecutive calls to the "unwind" function
     below.  */
  REG saved_regs [32];
} frame_state_t;

/* Shortcuts for saved_regs of specific interest:

   Frame Pointer   is r29,
   Stack Pointer   is r30,
   Return Address  is r26,
   Procedure Value is r27.

   This is from ABI-3.1.1 [Integer Registers].  */

#define saved_fpr saved_regs[29]
#define saved_spr saved_regs[30]
#define saved_rar saved_regs[26]
#define saved_pvr saved_regs[27]

/* Special values for saved_rar, used to control the overall unwinding
   process.  */
#define RA_UNKNOWN ((REG)~0)
#define RA_STOP    ((REG)0)

/* We still use a number of macros similar to the ones for the generic
   __gnat_backtrace implementation.  */
#define PC_ADJUST 4
#define STOP_FRAME (frame_state.saved_rar == RA_STOP)

/* Compute Procedure Value from Frame Pointer value.  This follows the rules
   in ABI-3.6.1 [Current Procedure].  */
#define PV_FOR(FP) \
  (((FP) != 0) \
    ? (((REG_AT (FP) & 0x7) == 0) ? *(PDSCDEF **)(FP) : (PDSCDEF *)(FP)) : 0)


/**********
 * unwind *
 **********/

/* Helper for __gnat_backtrace.

   FS represents some call frame, identified by a pc and associated frame
   pointer in FS->pc and FS->fp. FS->saved_regs contains the state of the
   general registers upon entry in this frame. Of most interest in this set
   are the saved return address and frame pointer registers, which actually
   allow identifying the caller's frame.

   This routine "unwinds" the input frame state by adjusting it to eventually
   represent its caller's frame. The basic principle is to shift the fp and pc
   saved values into the current state, and then compute the corresponding new
   saved registers set.

   If the call chain goes through a signal handler, special processing is
   required when we process the kernel frame which has called the handler, to
   switch it to the interrupted context frame.  */

#define K_HANDLER_FRAME(fs) (PV_FOR ((fs)->fp) == SYS$GL_CALL_HANDL)

static void unwind_regular_code (frame_state_t * fs);
static void unwind_kernel_handler (frame_state_t * fs);

void
unwind (frame_state_t * fs)
{
  /* Don't do anything if requested so.  */
  if (fs->saved_rar == RA_STOP)
    return;

  /* Retrieve the values of interest computed during the previous
     call. PC_ADJUST gets us from the return address to the call insn
     address.  */
  fs->pc = ADDR_IN (fs->saved_rar) - PC_ADJUST;
  fs->sp = ADDR_IN (fs->saved_spr);
  fs->fp = ADDR_IN (fs->saved_fpr);

  /* Unless we are able to determine otherwise, set the frame state's
     saved return address such that the unwinding process will stop.  */
  fs->saved_rar = RA_STOP;

  /* Now we want to update fs->saved_regs to reflect the state of the caller
     of the procedure described by pc/fp.

     The condition to check for a special kernel frame which has called a
     signal handler is stated in ABI-6.7.1 [Signaler's Registers] : "The frame
     of the call to the handler can be identified by the return address of
     SYS$CALL_HANDL+4". We use the equivalent procedure value identification
     here because SYS$CALL_HANDL appears to be undefined. */

  if (K_HANDLER_FRAME (fs))
    unwind_kernel_handler (fs);
  else
    unwind_regular_code (fs);
}

/***********************
 * unwind_regular_code *
 ***********************/

/* Helper for unwind, for the case of unwinding through regular code which
   is not a signal handler.  */

static void
unwind_regular_code (frame_state_t * fs)
{
  PDSCDEF * pv = PV_FOR (fs->fp);

  ADDR frame_base;

  /* Use the procedure value to unwind, in a way depending on the kind of
     procedure at hand. See ABI-3.3 [Procedure Representation] and ABI-3.4
     [Procedure Types].  */

  if (pv == 0
      || pv->pdsc$w_flags & PDSC$M_BASE_FRAME)
    return;

  frame_base
    = (pv->pdsc$w_flags & PDSC$M_BASE_REG_IS_FP) ? fs->fp : fs->sp;

  switch (pv->pdsc$w_flags & 0xf)
    {
    case PDSC$K_KIND_FP_STACK:
      /* Stack Frame Procedure (ABI-3.4.1). Retrieve the necessary registers
	 from the Register Save Area in the frame.  */
      {
	ADDR rsa_base = frame_base + pv->pdsc$w_rsa_offset;
	int i, j;

	fs->saved_rar = REG_AT (rsa_base);
	fs->saved_pvr = REG_AT (frame_base);

	for (i = 0, j = 0; i < 32; i++)
	  if (pv->pdsc$l_ireg_mask & (1 << i))
	    fs->saved_regs[i] = REG_AT (rsa_base + 8 * ++j);

	/* Note that the loop above is guaranteed to set fs->saved_fpr,
	   because "The preserved register set must always include R29(FP)
	   since it will always be used." (ABI-3.4.3.4 [Register Save Area for
	   All Stack Frames]).

	   Also note that we need to run through all the registers to ensure
	   that unwinding through register procedures (see below) gets the
	   right values out of the saved_regs array.  */
      }
      break;

    case PDSC$K_KIND_FP_REGISTER:
      /* Register Procedure (ABI-3.4.4). Retrieve the necessary registers from
	 the registers where they have been saved.  */
      {
	fs->saved_rar = fs->saved_regs[pv->pdsc$b_save_ra];
	fs->saved_fpr = fs->saved_regs[pv->pdsc$b_save_fp];
      }
      break;

    default:
      /* ??? Are we supposed to ever get here ?  Don't think so.  */
      break;
    }

  /* SP is actually never part of the saved registers area, so we use the
     corresponding entry in the saved_regs array to manually keep track of
     it's evolution.  */
  fs->saved_spr = AS_REG (frame_base) + pv->pdsc$l_size;
}

/*************************
 * unwind_kernel_handler *
 *************************/

/* Helper for unwind, for the specific case of unwinding through a signal
   handler.

   The input frame state describes the kernel frame which has called a signal
   handler. We fill the corresponding saved_regs to have it's "caller" frame
   represented as the interrupted context.  */

static void
unwind_kernel_handler (frame_state_t * fs)
{
  PDSCDEF * pv = PV_FOR (fs->fp);

  CHFDEF1 *sigargs;
  CHFDEF2 *mechargs;

  /* Retrieve the arguments passed to the handler, by way of a VMS service
     providing the corresponding "Invocation Context Block".  */
  {
    long handler_ivhandle;
    INVO_CONTEXT_BLK handler_ivcb;

    CHFCTX *chfctx;

    handler_ivcb.libicb$q_ireg [29] = AS_REG (fs->fp);
    handler_ivcb.libicb$q_ireg [30] = 0;

    handler_ivhandle = LIB$GET_INVO_HANDLE (&handler_ivcb);

    if ((LIB$GET_INVO_CONTEXT (handler_ivhandle, &handler_ivcb) & 1) != 1)
      return;

    chfctx = (CHFCTX *) AS_ADDR (handler_ivcb.libicb$ph_chfctx_addr);

    sigargs = (CHFDEF1 *) AS_ADDR (chfctx->chfctx$q_sigarglst);
    mechargs = (CHFDEF2 *) AS_ADDR (chfctx->chfctx$q_mcharglst);
  }

  /* Compute the saved return address as the PC of the instruction causing the
     condition, accounting for the fact that it will be adjusted by the next
     call to "unwind" as if it was an actual call return address.  */
  {
    /* ABI-6.5.1.1 [Signal Argument Vector]: The signal occurrence address
       is available from the sigargs argument to the handler, designed to
       support both 32 and 64 bit addresses.  The initial reference we get
       is a pointer to the 32bit form, from which one may extract a pointer
       to the 64bit version if need be.  We work directly from the 32bit
       form here.  */

    /* The sigargs vector structure for 32bits addresses is:

       <......32bit......>
       +-----------------+
       |      Vsize      | :chf$is_sig_args
       +-----------------+ -+-
       | Condition Value |  : [0]
       +-----------------+  :
       |       ...       |  :
       +-----------------+  : vector of Vsize entries
       |    Signal PC    |  :
       +-----------------+  :
       |       PS        |  : [Vsize - 1]
       +-----------------+ -+-

       */

    unsigned long * sigargs_vector
      = ((unsigned long *) (&sigargs->chf$is_sig_args)) + 1;

    long sigargs_vsize
      = sigargs->chf$is_sig_args;

    fs->saved_rar = (REG) sigargs_vector [sigargs_vsize - 2] + PC_ADJUST;
  }

  fs->saved_spr = RA_UNKNOWN;
  fs->saved_fpr = (REG) mechargs->chf$q_mch_frame;
  fs->saved_pvr = (REG) mechargs->chf$q_mch_savr27;

  fs->saved_regs[16] = (REG) mechargs->chf$q_mch_savr16;
  fs->saved_regs[17] = (REG) mechargs->chf$q_mch_savr17;
  fs->saved_regs[18] = (REG) mechargs->chf$q_mch_savr18;
  fs->saved_regs[19] = (REG) mechargs->chf$q_mch_savr19;
  fs->saved_regs[20] = (REG) mechargs->chf$q_mch_savr20;
}

/* Structure representing a traceback entry in the tracebacks array to be
   filled by __gnat_backtrace below.

   !! This should match what is in System.Traceback_Entries, so beware of
   !! the REG/ADDR difference here.

   The use of a structure is motivated by the potential necessity of having
   several fields to fill for each entry, for instance if later calls to VMS
   system functions need more than just a mere PC to compute info on a frame
   (e.g. for non-symbolic->symbolic translation purposes).  */
typedef struct {
  ADDR pc;
  ADDR pv;
} tb_entry_t;

/********************
 * __gnat_backtrace *
 ********************/

int
__gnat_backtrace (void **array, int size,
                  void *exclude_min, void *exclude_max, int skip_frames)
{
  int cnt;

  tb_entry_t * tbe = (tb_entry_t *)&array [0];

  frame_state_t frame_state;

  /* Setup the frame state before initiating the unwinding sequence.  */
  register REG this_FP __asm__("$29");
  register REG this_SP __asm__("$30");

  frame_state.saved_fpr = this_FP;
  frame_state.saved_spr = this_SP;
  frame_state.saved_rar = RA_UNKNOWN;

  unwind (&frame_state);

  /* At this point frame_state describes this very function. Skip the
     requested number of calls.  */
  for (cnt = 0; cnt < skip_frames; cnt ++)
    unwind (&frame_state);

  /* Now consider each frame as a potential candidate for insertion inside
     the provided array.  */
  cnt = 0;
  while (cnt < size)
    {
      PDSCDEF * pv = PV_FOR (frame_state.fp);

      /* Stop if either the frame contents or the unwinder say so.  */
      if (STOP_FRAME)
        break;

      if (! K_HANDLER_FRAME (&frame_state)
	  && (frame_state.pc < exclude_min || frame_state.pc > exclude_max))
	{
	  tbe->pc = (ADDR) frame_state.pc;
	  tbe->pv = (ADDR) PV_FOR (frame_state.fp);

	  cnt ++;
	  tbe ++;
	}

      unwind (&frame_state);
    }

  return cnt;
}