]>
Commit | Line | Data |
---|---|---|
bf1431e3 | 1 | /* Structured Exception Handling (SEH) runtime interface routines. |
7adcbafe | 2 | Copyright (C) 2010-2022 Free Software Foundation, Inc. |
bf1431e3 TG |
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 | <http://www.gnu.org/licenses/>. */ | |
24 | ||
25 | #include "tconfig.h" | |
26 | #include "tsystem.h" | |
27 | #include "coretypes.h" | |
28 | #include "tm.h" | |
29 | #include "unwind.h" | |
30 | ||
e5a81c8e | 31 | #if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__) |
bf1431e3 TG |
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 | \f | |
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 | \f | |
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 | { | |
5c7dac8a | 82 | if (index < 0 || index >= 2) |
bf1431e3 TG |
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 | { | |
5c7dac8a | 92 | if (index < 0 || index >= 2) |
bf1431e3 TG |
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 | \f | |
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 <unwind.h>, 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 | { | |
126437c5 | 224 | RtlUnwindEx (this_frame, (PVOID) ms_exc->ExceptionInformation[2], |
bf1431e3 TG |
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. */ | |
126437c5 | 316 | RtlUnwindEx (this_frame, (PVOID)gcc_context.ra, ms_exc, |
0bb4fc05 KT |
317 | (PVOID)gcc_context.reg[0], ms_orig_context, |
318 | ms_disp->HistoryTable); | |
bf1431e3 TG |
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 | ||
126437c5 | 368 | RtlUnwindEx ((void *) gcc_exc->private_[1], (PVOID)gcc_exc->private_[2], |
bf1431e3 TG |
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 | |
54fde020 BE |
434 | _Unwind_Backtrace(_Unwind_Trace_Fn trace, |
435 | void *trace_argument) | |
bf1431e3 | 436 | { |
bf1431e3 TG |
437 | UNWIND_HISTORY_TABLE ms_history; |
438 | CONTEXT ms_context; | |
439 | struct _Unwind_Context gcc_context; | |
54fde020 | 440 | DISPATCHER_CONTEXT disp_context; |
bf1431e3 TG |
441 | |
442 | memset (&ms_history, 0, sizeof(ms_history)); | |
443 | memset (&gcc_context, 0, sizeof(gcc_context)); | |
54fde020 | 444 | memset (&disp_context, 0, sizeof(disp_context)); |
bf1431e3 TG |
445 | |
446 | ms_context.ContextFlags = CONTEXT_ALL; | |
447 | RtlCaptureContext (&ms_context); | |
448 | ||
54fde020 BE |
449 | gcc_context.disp = &disp_context; |
450 | gcc_context.disp->ContextRecord = &ms_context; | |
451 | gcc_context.disp->HistoryTable = &ms_history; | |
bf1431e3 TG |
452 | |
453 | while (1) | |
454 | { | |
54fde020 BE |
455 | gcc_context.disp->ControlPc = ms_context.Rip; |
456 | gcc_context.disp->FunctionEntry | |
457 | = RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp->ImageBase, | |
bf1431e3 TG |
458 | &ms_history); |
459 | ||
54fde020 BE |
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); | |
bf1431e3 | 468 | |
bd6ecbe4 MS |
469 | /* Set values that the callback can inspect via _Unwind_GetIP |
470 | * and _Unwind_GetCFA. */ | |
471 | gcc_context.ra = ms_context.Rip; | |
472 | gcc_context.cfa = ms_context.Rsp; | |
473 | ||
bf1431e3 TG |
474 | /* Call trace function. */ |
475 | if (trace (&gcc_context, trace_argument) != _URC_NO_REASON) | |
476 | return _URC_FATAL_PHASE1_ERROR; | |
477 | ||
478 | /* ??? Check for invalid stack pointer. */ | |
479 | if (ms_context.Rip == 0) | |
480 | return _URC_END_OF_STACK; | |
481 | } | |
bf1431e3 | 482 | } |
e5a81c8e | 483 | #endif /* __SEH__ && !defined (__USING_SJLJ_EXCEPTIONS__) */ |