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