summaryrefslogtreecommitdiff
path: root/gcc/ada/tb-alvms.c
blob: fecedd396f05fdf9e8a5501406716bb304049dd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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;
}