Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6515 serge 1
/* Structured Exception Handling (SEH) runtime interface routines.
2
   Copyright (C) 2010-2015 Free Software Foundation, Inc.
3
 
4
   This file is part of GCC.
5
 
6
   GCC is free software; you can redistribute it and/or modify it
7
   under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 3, or (at your option)
9
   any later version.
10
 
11
   GCC is distributed in the hope that it will be useful, but WITHOUT
12
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14
   License for more details.
15
 
16
   Under Section 7 of GPL version 3, you are granted additional
17
   permissions described in the GCC Runtime Library Exception, version
18
   3.1, as published by the Free Software Foundation.
19
 
20
   You should have received a copy of the GNU General Public License and
21
   a copy of the GCC Runtime Library Exception along with this program;
22
   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23
   .  */
24
 
25
#include "tconfig.h"
26
#include "tsystem.h"
27
#include "coretypes.h"
28
#include "tm.h"
29
#include "unwind.h"
30
 
31
#if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__)
32
 
33
/* At the moment everything is written for x64, but in theory this could
34
   also be used for i386, arm, mips and other extant embedded Windows.  */
35
#ifndef __x86_64__
36
#error "Unsupported architecture."
37
#endif
38
 
39
/* Define GCC's exception codes.  See
40
     http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx
41
   In particular, MS defines bits:
42
     [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
43
     [29]    = 1 (user-defined)
44
     [28]    = 0 (reserved)
45
   We define bits:
46
     [24:27] = type
47
     [0:23]  = magic
48
   We set "magic" to "GCC", which is similar to MVC++ which uses "msc"
49
   as the low 3 bytes of its user-defined codes for C++ exceptions.
50
 
51
   We define the ExceptionInformation entries as follows:
52
     [0] = _Unwind_Exception pointer
53
     [1] = target frame
54
     [2] = target ip
55
     [3] = target rdx
56
*/
57
 
58
#define STATUS_USER_DEFINED		(1U << 29)
59
 
60
#define GCC_MAGIC			(('G' << 16) | ('C' << 8) | 'C')
61
#define GCC_EXCEPTION(TYPE)		\
62
       (STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC)
63
 
64
#define STATUS_GCC_THROW		GCC_EXCEPTION (0)
65
#define STATUS_GCC_UNWIND		GCC_EXCEPTION (1)
66
#define STATUS_GCC_FORCED		GCC_EXCEPTION (2)
67
 
68
 
69
struct _Unwind_Context
70
{
71
  _Unwind_Word cfa;
72
  _Unwind_Word ra;
73
  _Unwind_Word reg[2];
74
  PDISPATCHER_CONTEXT disp;
75
};
76
 
77
/* Get the value of register INDEX as saved in CONTEXT.  */
78
 
79
_Unwind_Word
80
_Unwind_GetGR (struct _Unwind_Context *c, int index)
81
{
82
  if (index < 0 || index >= 2)
83
    abort ();
84
  return c->reg[index];
85
}
86
 
87
/* Overwrite the saved value for register INDEX in CONTEXT with VAL.  */
88
 
89
void
90
_Unwind_SetGR (struct _Unwind_Context *c, int index, _Unwind_Word val)
91
{
92
  if (index < 0 || index >= 2)
93
    abort ();
94
  c->reg[index] = val;
95
}
96
 
97
/* Get the value of the CFA as saved in CONTEXT.  */
98
 
99
_Unwind_Word
100
_Unwind_GetCFA (struct _Unwind_Context *c)
101
{
102
  return c->cfa;
103
}
104
 
105
/* Retrieve the return address for CONTEXT.  */
106
 
107
_Unwind_Ptr
108
_Unwind_GetIP (struct _Unwind_Context *c)
109
{
110
  return c->ra;
111
}
112
 
113
/* Retrieve the return address and flag whether that IP is before
114
   or after first not yet fully executed instruction.  */
115
 
116
_Unwind_Ptr
117
_Unwind_GetIPInfo (struct _Unwind_Context *c, int *ip_before_insn)
118
{
119
  /* ??? Is there a concept of a signal context properly?  There's
120
     obviously an UNWP_PUSH_MACHFRAME opcode, but the runtime might
121
     have arranged for that not to matter, really.  */
122
  *ip_before_insn = 0;
123
  return c->ra;
124
}
125
 
126
/* Overwrite the return address for CONTEXT with VAL.  */
127
 
128
void
129
_Unwind_SetIP (struct _Unwind_Context *c, _Unwind_Ptr val)
130
{
131
  c->ra = val;
132
}
133
 
134
void *
135
_Unwind_GetLanguageSpecificData (struct _Unwind_Context *c)
136
{
137
  return c->disp->HandlerData;
138
}
139
 
140
_Unwind_Ptr
141
_Unwind_GetRegionStart (struct _Unwind_Context *c)
142
{
143
  return c->disp->FunctionEntry->BeginAddress + c->disp->ImageBase;
144
}
145
 
146
void *
147
_Unwind_FindEnclosingFunction (void *pc)
148
{
149
  PRUNTIME_FUNCTION entry;
150
  ULONG64 ImageBase;
151
 
152
  entry = RtlLookupFunctionEntry ((ULONG64)pc, &ImageBase, NULL);
153
 
154
  return (entry ? (void *)(entry->BeginAddress + ImageBase) : NULL);
155
}
156
 
157
_Unwind_Ptr
158
_Unwind_GetDataRelBase (struct _Unwind_Context *c ATTRIBUTE_UNUSED)
159
{
160
  return 0;
161
}
162
 
163
_Unwind_Ptr
164
_Unwind_GetTextRelBase (struct _Unwind_Context *c)
165
{
166
  return c->disp->ImageBase;
167
}
168
 
169
 
170
/* The two-phase unwind process that GCC uses is ordered differently
171
   from the two-phase unwind process that SEH uses.  The mechansism
172
   that GCC uses is to have the filter return _URC_HANDER_FOUND; the
173
   mechanism that SEH uses is for the filter function call back into
174
   the unwinder.
175
 
176
   An Ideal port to SEH would have GCC emit handler functions that
177
   can be called, given a pointer to the "EstablisherFrame" (i.e.
178
   the frame pointer base of the user-level function) can manipulate
179
   the user-level variables within the user-level function's stack
180
   frame.  Once done manipulating the variables, it would return
181
   a ExceptionContinueSearch, and the unwind process would continue.
182
 
183
   GCC has always done things a bit differently.  We continue to
184
   transfer control back into the user-level function which, once
185
   done manipulating the user-level variables, re-throws the exception.  */
186
 
187
/* The "real" language-specific personality handler forwards to here
188
   where we handle the MS SEH state and transforms it into the GCC
189
   unwind state as per GCC's , at which point we defer to
190
   the regular language-specfic exception handler, which is passed in.  */
191
 
192
EXCEPTION_DISPOSITION
193
_GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
194
		       PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp,
195
		       _Unwind_Personality_Fn gcc_per)
196
{
197
  DWORD ms_flags = ms_exc->ExceptionFlags;
198
  DWORD ms_code = ms_exc->ExceptionCode;
199
 
200
  struct _Unwind_Exception *gcc_exc
201
    = (struct _Unwind_Exception *) ms_exc->ExceptionInformation[0];
202
  struct _Unwind_Context gcc_context;
203
  _Unwind_Action gcc_action;
204
  _Unwind_Reason_Code gcc_reason;
205
 
206
  if (ms_flags & EXCEPTION_TARGET_UNWIND)
207
    {
208
      /* This frame is known to be the target frame.  We've already
209
         "installed" the target_ip and RAX value via the arguments
210
         to RtlUnwindEx.  All that's left is to set the RDX value
211
         and "continue" to have the context installed.  */
212
      ms_disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
213
      return ExceptionContinueSearch;
214
    }
215
 
216
  if (ms_code == STATUS_GCC_UNWIND)
217
    {
218
      /* This is a colliding exception that we threw so that we could
219
         cancel the already in-flight exception and stop in a frame
220
	 that wanted to perform some unwind action.  The only relevant
221
	 test is that we're the target frame.  */
222
      if (ms_exc->ExceptionInformation[1] == (_Unwind_Ptr) this_frame)
223
	{
224
	  RtlUnwindEx (this_frame, ms_exc->ExceptionInformation[2],
225
		       ms_exc, gcc_exc, ms_orig_context,
226
		       ms_disp->HistoryTable);
227
	  abort ();
228
	}
229
      return ExceptionContinueSearch;
230
    }
231
 
232
  gcc_context.cfa = ms_disp->ContextRecord->Rsp;
233
  gcc_context.ra = ms_disp->ControlPc;
234
  gcc_context.reg[0] = 0xdeadbeef;	/* These are write-only.  */
235
  gcc_context.reg[1] = 0xdeadbeef;
236
  gcc_context.disp = ms_disp;
237
 
238
  if (ms_code == STATUS_GCC_FORCED)
239
    {
240
       _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) gcc_exc->private_[0];
241
       void *stop_argument = (void *) gcc_exc->private_[4];
242
 
243
       gcc_action = _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE;
244
 
245
       stop (1, gcc_action, gcc_exc->exception_class, gcc_exc,
246
             &gcc_context, stop_argument);
247
 
248
       goto phase2;
249
    }
250
 
251
  /* ??? TODO: handling non-gcc user-defined exceptions as foreign.  */
252
  if (ms_code != STATUS_GCC_THROW)
253
    return ExceptionContinueSearch;
254
 
255
  if (ms_flags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
256
    {
257
      /* This is Phase 2.  */
258
      /* We know this isn't the target frame because we've already tested
259
	 EXCEPTION_TARGET_UNWIND.  The remaining possibility is that the
260
	 gcc personality has unwind code to run.  */
261
 
262
      gcc_action = _UA_CLEANUP_PHASE;
263
    phase2:
264
      gcc_reason = gcc_per (1, gcc_action, gcc_exc->exception_class,
265
			    gcc_exc, &gcc_context);
266
 
267
      if (gcc_reason == _URC_CONTINUE_UNWIND)
268
	return ExceptionContinueSearch;
269
 
270
      if (gcc_reason == _URC_INSTALL_CONTEXT)
271
	{
272
	  /* Scratch space for the bits for the unwind catch.  */
273
	  ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame;
274
	  ms_exc->ExceptionInformation[2] = gcc_context.ra;
275
	  ms_exc->ExceptionInformation[3] = gcc_context.reg[1];
276
 
277
	  /* Cancel the current exception by raising another.  */
278
	  RaiseException (STATUS_GCC_UNWIND, EXCEPTION_NONCONTINUABLE,
279
			  4, ms_exc->ExceptionInformation);
280
 
281
	  /* Is RaiseException declared noreturn?  */
282
	}
283
 
284
      /* In _Unwind_RaiseException_Phase2 we return _URC_FATAL_PHASE2_ERROR. */
285
    }
286
  else
287
    {
288
      /* This is Phase 1.  */
289
      gcc_reason = gcc_per (1, _UA_SEARCH_PHASE, gcc_exc->exception_class,
290
			    gcc_exc, &gcc_context);
291
 
292
      if (gcc_reason == _URC_CONTINUE_UNWIND)
293
	return ExceptionContinueSearch;
294
 
295
      if (gcc_reason == _URC_HANDLER_FOUND)
296
	{
297
	  /* We really need some of the information that GCC's personality
298
	     routines compute during phase 2 right now, like the target IP.
299
	     Go ahead and ask for it now, and cache it.  */
300
	  gcc_reason = gcc_per (1, _UA_CLEANUP_PHASE | _UA_HANDLER_FRAME,
301
				gcc_exc->exception_class, gcc_exc,
302
				&gcc_context);
303
	  if (gcc_reason != _URC_INSTALL_CONTEXT)
304
	    abort ();
305
 
306
	  gcc_exc->private_[1] = (_Unwind_Ptr) this_frame;
307
	  gcc_exc->private_[2] = gcc_context.ra;
308
	  gcc_exc->private_[3] = gcc_context.reg[1];
309
 
310
	  ms_exc->NumberParameters = 4;
311
	  ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame;
312
	  ms_exc->ExceptionInformation[2] = gcc_context.ra;
313
	  ms_exc->ExceptionInformation[3] = gcc_context.reg[1];
314
 
315
	  /* Begin phase 2.  Perform the unwinding.  */
316
	  RtlUnwindEx (this_frame, gcc_context.ra, ms_exc,
317
		       (PVOID)gcc_context.reg[0], ms_orig_context,
318
		       ms_disp->HistoryTable);
319
	}
320
 
321
      /* In _Unwind_RaiseException we return _URC_FATAL_PHASE1_ERROR.  */
322
    }
323
  abort ();
324
}
325
 
326
/* Raise an exception, passing along the given exception object.  */
327
 
328
_Unwind_Reason_Code
329
_Unwind_RaiseException (struct _Unwind_Exception *exc)
330
{
331
  memset (exc->private_, 0, sizeof (exc->private_));
332
 
333
  /* The ExceptionInformation array will have only 1 element, EXC.  */
334
  RaiseException (STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exc);
335
 
336
  /* The exception handler installed in crt0 will continue any GCC
337
     exception that reaches there (and isn't marked non-continuable).
338
     Returning allows the C++ runtime to call std::terminate.  */
339
  return _URC_END_OF_STACK;
340
}
341
 
342
/* Resume propagation of an existing exception.  This is used after
343
   e.g. executing cleanup code, and not to implement rethrowing.  */
344
 
345
void
346
_Unwind_Resume (struct _Unwind_Exception *gcc_exc)
347
{
348
  UNWIND_HISTORY_TABLE ms_history;
349
  EXCEPTION_RECORD ms_exc;
350
  CONTEXT ms_context;
351
 
352
  memset (&ms_exc, 0, sizeof(ms_exc));
353
  memset (&ms_history, 0, sizeof(ms_history));
354
 
355
  /* ??? Not 100% perfect, since we aren't passing on the *original*
356
     exception context, but should be good enough.  */
357
  ms_exc.ExceptionCode = STATUS_GCC_THROW;
358
  ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
359
  ms_exc.NumberParameters = 4;
360
  ms_exc.ExceptionInformation[0] = (ULONG_PTR) gcc_exc;
361
  ms_exc.ExceptionInformation[1] = gcc_exc->private_[1];
362
  ms_exc.ExceptionInformation[2] = gcc_exc->private_[2];
363
  ms_exc.ExceptionInformation[3] = gcc_exc->private_[3];
364
 
365
  ms_context.ContextFlags = CONTEXT_ALL;
366
  RtlCaptureContext (&ms_context);
367
 
368
  RtlUnwindEx ((void *) gcc_exc->private_[1], gcc_exc->private_[2],
369
	       &ms_exc, gcc_exc, &ms_context, &ms_history);
370
 
371
  /* Is RtlUnwindEx declared noreturn?  */
372
  abort ();
373
}
374
 
375
static _Unwind_Reason_Code
376
_Unwind_ForcedUnwind_Phase2 (struct _Unwind_Exception *exc)
377
{
378
  _Unwind_Stop_Fn stop;
379
  void * stop_argument;
380
 
381
  RaiseException (STATUS_GCC_FORCED, 0, 1, (ULONG_PTR *)&exc);
382
 
383
  /* If we get here, we got to top-of-stack.  */
384
  /* ??? We no longer have a context pointer to pass in.  */
385
 
386
  stop = (_Unwind_Stop_Fn) exc->private_[0];
387
  stop_argument = (void *) exc->private_[4];
388
  stop (1, _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK,
389
	exc->exception_class, exc, NULL, stop_argument);
390
 
391
  return _UA_END_OF_STACK;
392
}
393
 
394
_Unwind_Reason_Code
395
_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *exc)
396
{
397
  if (exc->private_[0] == 0)
398
    _Unwind_RaiseException (exc);
399
  else
400
    _Unwind_ForcedUnwind_Phase2 (exc);
401
  abort ();
402
}
403
 
404
/* Raise an exception for forced unwinding.  */
405
 
406
_Unwind_Reason_Code
407
_Unwind_ForcedUnwind (struct _Unwind_Exception *exc,
408
		      _Unwind_Stop_Fn stop, void * stop_argument)
409
{
410
  /* ??? This is a hack that only works with _GCC_specific_handler.
411
     There's no way to invoke STOP within frames that use a different
412
     exception handler.  This is essentially just good enough to run
413
     the code within the gcc testsuite.  */
414
 
415
  memset (exc->private_, 0, sizeof (exc->private_));
416
  exc->private_[0] = (_Unwind_Ptr) stop;
417
  exc->private_[4] = (_Unwind_Ptr) stop_argument;
418
 
419
  return _Unwind_ForcedUnwind_Phase2 (exc);
420
}
421
 
422
/* A convenience function that calls the exception_cleanup field.  */
423
 
424
void
425
_Unwind_DeleteException (struct _Unwind_Exception *exc)
426
{
427
  if (exc->exception_cleanup)
428
    (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc);
429
}
430
 
431
/* Perform stack backtrace through unwind data.  */
432
 
433
_Unwind_Reason_Code
434
_Unwind_Backtrace(_Unwind_Trace_Fn trace,
435
		  void *trace_argument)
436
{
437
  UNWIND_HISTORY_TABLE ms_history;
438
  CONTEXT ms_context;
439
  struct _Unwind_Context gcc_context;
440
  DISPATCHER_CONTEXT disp_context;
441
 
442
  memset (&ms_history, 0, sizeof(ms_history));
443
  memset (&gcc_context, 0, sizeof(gcc_context));
444
  memset (&disp_context, 0, sizeof(disp_context));
445
 
446
  ms_context.ContextFlags = CONTEXT_ALL;
447
  RtlCaptureContext (&ms_context);
448
 
449
  gcc_context.disp = &disp_context;
450
  gcc_context.disp->ContextRecord = &ms_context;
451
  gcc_context.disp->HistoryTable = &ms_history;
452
 
453
  while (1)
454
    {
455
      gcc_context.disp->ControlPc = ms_context.Rip;
456
      gcc_context.disp->FunctionEntry
457
	= RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp->ImageBase,
458
				  &ms_history);
459
 
460
      if (!gcc_context.disp->FunctionEntry)
461
	return _URC_END_OF_STACK;
462
 
463
      gcc_context.disp->LanguageHandler
464
	= RtlVirtualUnwind (0, gcc_context.disp->ImageBase, ms_context.Rip,
465
			    gcc_context.disp->FunctionEntry, &ms_context,
466
			    &gcc_context.disp->HandlerData,
467
			    &gcc_context.disp->EstablisherFrame, NULL);
468
 
469
      /* Call trace function.  */
470
      if (trace (&gcc_context, trace_argument) != _URC_NO_REASON)
471
	return _URC_FATAL_PHASE1_ERROR;
472
 
473
      /* ??? Check for invalid stack pointer.  */
474
      if (ms_context.Rip == 0)
475
	return _URC_END_OF_STACK;
476
    }
477
}
478
#endif /* __SEH__  && !defined (__USING_SJLJ_EXCEPTIONS__)  */