1/* Definitions for Dwarf2 EH unwind support for Windows32 targets 2 Copyright (C) 2007, 2009 3 Free Software Foundation, Inc. 4 Contributed by Pascal Obry <obry@adacore.com> 5 6This file is part of GCC. 7 8GCC is free software; you can redistribute it and/or modify it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 3, or (at your option) any later 11version. 12 13GCC is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18Under Section 7 of GPL version 3, you are granted additional 19permissions described in the GCC Runtime Library Exception, version 203.1, as published by the Free Software Foundation. 21 22You should have received a copy of the GNU General Public License and 23a copy of the GCC Runtime Library Exception along with this program; 24see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 25<http://www.gnu.org/licenses/>. */ 26 27 28/* This file implements the md_fallback_frame_state_for routine for 29 Windows, triggered when the GCC table based unwinding process hits a 30 frame for which no unwind info has been registered. This typically 31 occurs when raising an exception from a signal handler, because the 32 handler is actually called from the OS kernel. 33 34 The basic idea is to detect that we are indeed trying to unwind past a 35 signal handler and to fill out the GCC internal unwinding structures for 36 the OS kernel frame as if it had been directly called from the 37 interrupted context. 38 39 This is all assuming that the code to set the handler asked the kernel 40 to pass a pointer to such context information. 41 42 There is three main parts. 43 44 1) The first thing to do is to check if we are in a signal context. If 45 not we can just return as there is nothing to do. We are probably on 46 some foreign code for which no unwind frame can be found. If this is 47 a call from the Windows signal handler, then: 48 49 2) We must get the signal context information. 50 51 * With the standard exception filter: 52 53 This is on Windows pointed to by an EXCEPTION_POINTERS. We know that 54 the signal handle will call an UnhandledExceptionFilter with this 55 parameter. The spec for this routine is: 56 57 LONG WINAPI UnhandledExceptionFilter(struct _EXCEPTION_POINTERS*); 58 59 So the pointer to struct _EXCEPTION_POINTERS must be somewhere on the 60 stack. 61 62 This was found experimentally to always be at offset 0 of the context 63 frame in all cases handled by this implementation. 64 65 * With the SEH exception handler: 66 67 In this case the signal context is directly on the stack as the SEH 68 exception handler has the following prototype: 69 70 DWORD 71 SEH_error_handler (PEXCEPTION_RECORD ExceptionRecord, 72 PVOID EstablisherFrame, 73 PCONTEXT ContextRecord, 74 PVOID DispatcherContext) 75 76 This was found experimentally to always be at offset 56 of the 77 context frame in all cases handled by this implementation. 78 79 3) When we have the signal context we just have to save some registers 80 and set the return address based on the program counter (Eip). 81 82 Note that this implementation follows closely the same principles as the 83 GNU/Linux and OSF ones. */ 84 85#define WIN32_MEAN_AND_LEAN 86#include <windows.h> 87/* Patterns found experimentally to be on a Windows signal handler */ 88 89/* In a standard exception filter */ 90 91#define SIG_PAT1 \ 92 (pc_[-2] == 0xff && pc_[-1] == 0xd0 /* call %eax */ \ 93 && pc_[0] == 0x83 && pc_[1] == 0xf8) /* cmp 0xdepl,%eax */ 94 95#define SIG_PAT2 \ 96 (pc_[-5] == 0xe8 && pc_[-4] == 0x68 /* call (depl16) */ \ 97 && pc_[0] == 0xc3) /* ret */ 98 99/* In a Win32 SEH handler */ 100 101#define SIG_SEH1 \ 102 (pc_[-5] == 0xe8 /* call addr */ \ 103 && pc_[0] == 0x83 && pc_[1] == 0xc4 /* add 0xval,%esp */ \ 104 && pc_[3] == 0xb8) /* mov 0xval,%eax */ 105 106#define SIG_SEH2 \ 107 (pc_[-5] == 0x8b && pc_[-4] == 0x4d /* mov depl(%ebp),%ecx */ \ 108 && pc_[0] == 0x64 && pc_[1] == 0x8b) /* mov %fs:(0),<reg> */ \ 109 110/* In the GCC alloca (stack probing) */ 111 112#define SIG_ALLOCA \ 113 (pc_[-1] == 0x83 /* orl $0x0,(%ecx) */ \ 114 && pc_[0] == 0x9 && pc_[1] == 0 \ 115 && pc_[2] == 0x2d && pc_[3] == 0 /* subl $0x1000,%eax */ \ 116 && pc_[4] == 0x10 && pc_[5] == 0) 117 118 119#define MD_FALLBACK_FRAME_STATE_FOR i386_w32_fallback_frame_state 120 121static _Unwind_Reason_Code 122i386_w32_fallback_frame_state (struct _Unwind_Context *context, 123 _Unwind_FrameState *fs) 124 125{ 126 void * ctx_ra_ = (void *)(context->ra); /* return address */ 127 void * ctx_cfa_ = (void *)(context->cfa); /* context frame address */ 128 unsigned char * pc_ = (unsigned char *) ctx_ra_; 129 130 /* In the test below we look for two specific patterns found 131 experimentally to be in the Windows signal handler. */ 132 133 if (SIG_PAT1 || SIG_PAT2 || SIG_SEH1 || SIG_SEH2) 134 { 135 PEXCEPTION_POINTERS weinfo_; 136 PCONTEXT proc_ctx_; 137 long new_cfa_; 138 139 if (SIG_SEH1) 140 proc_ctx_ = (PCONTEXT) (*(int*)(ctx_cfa_ + 56)); 141 else if (SIG_SEH2) 142 proc_ctx_ = (PCONTEXT) (*(int*)(ctx_cfa_ + 8)); 143 else 144 { 145 weinfo_ = (PEXCEPTION_POINTERS) (*(int*)ctx_cfa_); 146 proc_ctx_ = weinfo_->ContextRecord; 147 } 148 149 /* The new context frame address is the stack pointer. */ 150 151 new_cfa_ = proc_ctx_->Esp; 152 fs->regs.cfa_how = CFA_REG_OFFSET; 153 fs->regs.cfa_reg = __builtin_dwarf_sp_column(); 154 fs->regs.cfa_offset = new_cfa_ - (long) ctx_cfa_; 155 156 /* Save some registers. */ 157 158 fs->regs.reg[0].how = REG_SAVED_OFFSET; 159 fs->regs.reg[0].loc.offset = (long)&proc_ctx_->Eax - new_cfa_; 160 fs->regs.reg[3].how = REG_SAVED_OFFSET; 161 fs->regs.reg[3].loc.offset = (long)&proc_ctx_->Ebx - new_cfa_; 162 fs->regs.reg[1].how = REG_SAVED_OFFSET; 163 fs->regs.reg[1].loc.offset = (long)&proc_ctx_->Ecx - new_cfa_; 164 fs->regs.reg[2].how = REG_SAVED_OFFSET; 165 fs->regs.reg[2].loc.offset = (long)&proc_ctx_->Edx - new_cfa_; 166 fs->regs.reg[6].how = REG_SAVED_OFFSET; 167 fs->regs.reg[6].loc.offset = (long)&proc_ctx_->Esi - new_cfa_; 168 fs->regs.reg[7].how = REG_SAVED_OFFSET; 169 fs->regs.reg[7].loc.offset = (long)&proc_ctx_->Edi - new_cfa_; 170 fs->regs.reg[9].how = REG_SAVED_OFFSET; 171 fs->regs.reg[9].loc.offset = (long)&proc_ctx_->Eip - new_cfa_; 172 fs->regs.reg[4].how = REG_SAVED_OFFSET; 173 fs->regs.reg[4].loc.offset = (long)&proc_ctx_->Ebp - new_cfa_; 174 175 /* Set the return address to Eip + 1. As we can be called multiple 176 times we use another register for this. */ 177 178 proc_ctx_->Dr0 = proc_ctx_->Eip + 1; 179 fs->regs.reg[8].how = REG_SAVED_OFFSET; 180 fs->regs.reg[8].loc.offset = (long)&proc_ctx_->Dr0 - new_cfa_; 181 fs->retaddr_column = 8; 182 return _URC_NO_REASON; 183 } 184 185 /* Unwinding through _alloca, propagating from a trap triggered by 186 one of it's probes prior to the real SP adjustment. The only 187 operations of interest performed is "pushl %ecx", followed by 188 ecx clobbering. */ 189 190 else if (SIG_ALLOCA) 191 { 192 /* Only one push between entry in _alloca and the probe trap. */ 193 long new_cfa_ = (long) ctx_cfa_ + 4; 194 195 fs->regs.cfa_how = CFA_REG_OFFSET; 196 fs->regs.cfa_reg = __builtin_dwarf_sp_column(); 197 fs->regs.cfa_offset = new_cfa_ - (long) ctx_cfa_; 198 199 /* The saved value of %ecx is at CFA - 4 */ 200 fs->regs.reg[1].how = REG_SAVED_OFFSET; 201 fs->regs.reg[1].loc.offset = -4; 202 203 /* and what is stored at the CFA is the return address. */ 204 fs->retaddr_column = 8; 205 fs->regs.reg[8].how = REG_SAVED_OFFSET; 206 fs->regs.reg[8].loc.offset = 0; 207 208 return _URC_NO_REASON; 209 } 210 else 211 return _URC_END_OF_STACK; 212} 213