summaryrefslogtreecommitdiff
path: root/gcc/ada/tb-alvms.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/tb-alvms.c')
-rw-r--r--gcc/ada/tb-alvms.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/gcc/ada/tb-alvms.c b/gcc/ada/tb-alvms.c
new file mode 100644
index 00000000000..fecedd396f0
--- /dev/null
+++ b/gcc/ada/tb-alvms.c
@@ -0,0 +1,263 @@
+/****************************************************************************
+ * *
+ * 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>
+
+/* We still use a number of macros similar to the ones for the generic
+ __gnat_backtrace implementation. */
+#define SKIP_FRAME 1
+#define PC_ADJUST -4
+
+#define STOP_FRAME (frame_state.saved_ra == RA_STOP)
+
+/* Mask for PDSC$V_BASE_FRAME in procedure descriptors, missing from the
+ header file included above. */
+#define PDSC$M_BASE_FRAME (1 << 10)
+
+typedef unsigned long REG;
+
+#define REG_AT(address) (*(REG *)(address))
+
+/* The following structure defines the state maintained during the
+ unwinding process. */
+typedef struct
+{
+ void * pc; /* Address of the call insn involved in the chain. */
+ void * sp; /* Stack Pointer at the time of this call. */
+ void * fp; /* Frame Pointer at the time of this call. */
+
+ /* 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_fp saved_regs[29]
+#define saved_sp saved_regs[30]
+#define saved_ra saved_regs[26]
+#define saved_pv saved_regs[27]
+
+/* Special values for saved_ra, used to control the overall unwinding
+ process. */
+#define RA_UNKNOWN ((REG)~0)
+#define RA_STOP ((REG)0)
+
+/**********
+ * unwind *
+ **********/
+
+/* Helper for __gnat_backtrace. Update FS->pc/sp/fp to represent the
+ state computed in FS->saved_regs during the previous call, and update
+ FS->saved_regs in preparation of the next call. */
+
+void
+unwind (frame_state_t * fs)
+{
+ REG frame_base;
+ PDSCDEF * pv;
+
+ /* Don't do anything if requested so. */
+ if (fs->saved_ra == 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 = (void *) fs->saved_ra + PC_ADJUST;
+ fs->sp = (void *) fs->saved_sp;
+ fs->fp = (void *) fs->saved_fp;
+
+ /* Unless we are able to determine otherwise, set the frame state's
+ saved return address such that the unwinding process will stop. */
+ fs->saved_ra = RA_STOP;
+
+ /* Now we want to update fs->saved_regs to reflect what the procedure
+ described by pc/fp/sp has done. */
+
+ /* Compute the corresponding "procedure value", following the rules in
+ ABI-3.6.1 [Current Procedure]. Return immediatly if this value mandates
+ us to stop. */
+ if (fs->fp == 0)
+ return;
+
+ if ((REG_AT (fs->fp) & 0x7) == 0)
+ pv = *(PDSCDEF **)fs->fp;
+ else
+ pv = (PDSCDEF *) fs->fp;
+
+ if (pv == 0
+ || pv->pdsc$w_flags & PDSC$M_BASE_FRAME)
+ return;
+
+ /* Use the procedure value to unwind, in a way depending on the kind of
+ procedure at hand. This is based on ABI-3.3 [Procedure Representation]
+ and ABI-3.4 [Procedure Types]. */
+ frame_base
+ = (REG) ((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. */
+ {
+ REG rsa_base = frame_base + pv->pdsc$w_rsa_offset;
+ int i, j;
+
+ fs->saved_ra = REG_AT (rsa_base);
+ fs->saved_pv = 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_fp, 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_ra = fs->saved_regs[pv->pdsc$b_save_ra];
+ fs->saved_fp = 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_sp = frame_base + pv->pdsc$l_size;
+}
+
+/* Structure representing a traceback entry in the tracebacks array to be
+ filled by __gnat_backtrace below. This should match the declaration of
+ Traceback_Entry in System.Traceback_Entries.
+
+ 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 {
+ void * pc; /* Address of the call instruction in the chain. */
+ void * sp; /* Stack Pointer value at the point of this call. */
+ void * fp; /* Frame Pointer value at the point of this call. */
+} tb_entry_t;
+
+/********************
+ * __gnat_backtrace *
+ ********************/
+
+int
+__gnat_backtrace (array, size, exclude_min, exclude_max, skip_frames)
+ 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_fp = this_FP;
+ frame_state.saved_sp = this_SP;
+ frame_state.saved_ra = 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)
+ {
+ if (STOP_FRAME)
+ break;
+
+ if (frame_state.pc < exclude_min
+ || frame_state.pc > exclude_max)
+ {
+ tbe->pc = frame_state.pc;
+ tbe->sp = frame_state.sp;
+ tbe->fp = frame_state.fp;
+
+ cnt ++;
+ tbe ++;
+ }
+
+ unwind (&frame_state);
+ }
+
+ return cnt;
+}