1 /* 2 * PROJECT: ReactOS system libraries 3 * LICENSE: GNU GPL - See COPYING in the top level directory 4 * PURPOSE: Support library for PSEH3 5 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org) 6 */ 7 8 /* 9 * - Naming: To avoid naming conflicts, all internal identifiers are prefixed 10 * with _SEH3$_. 11 * - Frame graph: PSEH3 uses the same registration frame for every trylevel. 12 * Only the top trylevel is registered in FS:0, the inner trylevels are linked 13 * to the first trylevel frame. Only the first trylevel frame has the Handler 14 * member set, it's 0 for all others as an identification. The EndOfChain 15 * member of the FS:0 registered frame points to the last internal frame, 16 * which is the frame itself, when only 1 trylevel is present. 17 * 18 * The registration graph looks like this: 19 * 20 * newer handlers 21 * ----------------> 22 * 23 * fs:0 /----------------\ 24 * |-----------|<-\ |-----------|<-\ / |----------|<-\ \->|----------| 25 * | <Next> | \-| <Next> | \--/--| <Next> | \---| <Next> | 26 * | <Handler> | | <Handler> | / | <NULL> | | <NULL> | 27 * |-----------| |-----------| / |----------| |----------| 28 * |EndOfChain |---/ 29 * | ... | 30 * |-----------| 31 */ 32 33 #include <stdarg.h> 34 #include <windef.h> 35 #include <winnt.h> 36 37 /* We need the full structure with all non-volatile */ 38 #define _SEH3$_FRAME_ALL_NONVOLATILES 1 39 #include "pseh3.h" 40 #include "pseh3_asmdef.h" 41 42 /* Make sure the asm definitions match the structures */ 43 C_ASSERT(SEH3_REGISTRATION_FRAME_Next == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Next)); 44 C_ASSERT(SEH3_REGISTRATION_FRAME_Handler == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Handler)); 45 C_ASSERT(SEH3_REGISTRATION_FRAME_EndOfChain == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, EndOfChain)); 46 C_ASSERT(SEH3_REGISTRATION_FRAME_ScopeTable == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ScopeTable)); 47 C_ASSERT(SEH3_REGISTRATION_FRAME_ExceptionPointers == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ExceptionPointers)); 48 C_ASSERT(SEH3_REGISTRATION_FRAME_ExceptionCode == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ExceptionCode)); 49 C_ASSERT(SEH3_REGISTRATION_FRAME_Esp == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Esp)); 50 C_ASSERT(SEH3_REGISTRATION_FRAME_Ebp == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Ebp)); 51 C_ASSERT(SEH3_REGISTRATION_FRAME_AllocaFrame == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, AllocaFrame)); 52 #ifdef _SEH3$_FRAME_ALL_NONVOLATILES 53 C_ASSERT(SEH3_REGISTRATION_FRAME_Ebx == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Ebx)); 54 C_ASSERT(SEH3_REGISTRATION_FRAME_Esi == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Esi)); 55 C_ASSERT(SEH3_REGISTRATION_FRAME_Edi == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Edi)); 56 #endif 57 #ifdef __clang__ 58 C_ASSERT(SEH3_REGISTRATION_FRAME_ReturnAddress == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ReturnAddress)); 59 #endif 60 C_ASSERT(SEH3_SCOPE_TABLE_Filter == FIELD_OFFSET(SEH3$_SCOPE_TABLE, Filter)); 61 C_ASSERT(SEH3_SCOPE_TABLE_Target == FIELD_OFFSET(SEH3$_SCOPE_TABLE, Target)); 62 63 void 64 __attribute__((regparm(1))) 65 _SEH3$_Unregister( 66 volatile SEH3$_REGISTRATION_FRAME *Frame) 67 { 68 if (Frame->Handler) 69 _SEH3$_UnregisterFrame(Frame); 70 else 71 _SEH3$_UnregisterTryLevel(Frame); 72 } 73 74 static inline 75 LONG 76 _SEH3$_InvokeNestedFunctionFilter( 77 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame, 78 PVOID Filter) 79 { 80 LONG FilterResult; 81 82 asm volatile ( 83 /* First call with param = 0 to get the frame layout */ 84 "xorl %%ecx, %%ecx\n\t" 85 "xorl %%eax, %%eax\n\t" 86 "call *%[Filter]\n\t" 87 88 /* The result is the frame base address that we passed in (0) plus the 89 offset to the registration record. */ 90 "negl %%eax\n\t" 91 "addl %[RegistrationFrame], %%eax\n\t" 92 93 /* Second call to get the filter result */ 94 "mov $1, %%ecx\n\t" 95 "call *%[Filter]" 96 : "=a" (FilterResult) 97 : [RegistrationFrame] "m" (RegistrationFrame), [Filter] "m" (Filter) 98 : "ecx", "edx"); 99 100 return FilterResult; 101 } 102 103 long 104 __attribute__((regparm(1))) 105 _SEH3$_InvokeEmbeddedFilter( 106 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame); 107 108 long 109 __attribute__((regparm(1))) 110 _SEH3$_InvokeEmbeddedFilterFromRegistration( 111 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame); 112 113 static inline 114 LONG 115 _SEH3$_InvokeFilter( 116 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame, 117 PVOID Filter) 118 { 119 LONG FilterResult; 120 121 if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_NESTED_HANDLER) 122 { 123 return _SEH3$_InvokeNestedFunctionFilter(RegistrationFrame, Filter); 124 } 125 else if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CPP_HANDLER) 126 { 127 /* Call the embedded filter function */ 128 return _SEH3$_InvokeEmbeddedFilter(RegistrationFrame); 129 } 130 else if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CLANG_HANDLER) 131 { 132 return _SEH3$_InvokeEmbeddedFilterFromRegistration(RegistrationFrame); 133 } 134 else 135 { 136 /* Should not happen! Skip this handler */ 137 FilterResult = EXCEPTION_CONTINUE_SEARCH; 138 } 139 140 return FilterResult; 141 } 142 143 void 144 __attribute__((regparm(1))) 145 _SEH3$_AutoCleanup( 146 volatile SEH3$_REGISTRATION_FRAME *Frame) 147 { 148 if (Frame->Handler) 149 _SEH3$_UnregisterFrame(Frame); 150 else 151 _SEH3$_UnregisterTryLevel(Frame); 152 153 /* Check for __finally frames */ 154 if (Frame->ScopeTable->Target == NULL) 155 { 156 _SEH3$_InvokeFilter(Frame, Frame->ScopeTable->Filter); 157 } 158 159 } 160 161 static inline 162 LONG 163 _SEH3$_GetFilterResult( 164 PSEH3$_REGISTRATION_FRAME Record) 165 { 166 PVOID Filter = Record->ScopeTable->Filter; 167 LONG Result; 168 169 /* Check for __finally frames */ 170 if (Record->ScopeTable->Target == NULL) 171 { 172 return EXCEPTION_CONTINUE_SEARCH; 173 } 174 175 /* Check if we have a constant filter */ 176 if (((ULONG)Filter & 0xFFFFFF00) == 0) 177 { 178 /* Lowest 8 bit are sign extended to give the result */ 179 Result = (LONG)(CHAR)(ULONG)Filter; 180 } 181 else 182 { 183 /* Call the filter function */ 184 Result = _SEH3$_InvokeFilter(Record, Filter); 185 } 186 187 /* Normalize the result */ 188 if (Result < 0) return EXCEPTION_CONTINUE_EXECUTION; 189 else if (Result > 0) return EXCEPTION_EXECUTE_HANDLER; 190 else return EXCEPTION_CONTINUE_SEARCH; 191 } 192 193 static inline 194 VOID 195 _SEH3$_CallFinally( 196 PSEH3$_REGISTRATION_FRAME Record) 197 { 198 _SEH3$_InvokeFilter(Record, Record->ScopeTable->Filter); 199 } 200 201 __attribute__((noreturn)) 202 static inline 203 void 204 _SEH3$_JumpToTarget( 205 PSEH3$_REGISTRATION_FRAME RegistrationFrame) 206 { 207 if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CLANG_HANDLER) 208 { 209 asm volatile ( 210 /* Load the registers */ 211 "movl 24(%%ecx), %%esp\n\t" 212 "movl 28(%%ecx), %%ebp\n\t" 213 214 "movl 36(%%ecx), %%ebx\n\t" 215 "movl 40(%%ecx), %%esi\n\t" 216 "movl 44(%%ecx), %%edi\n\t" 217 218 /* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */ 219 "addl $4, %%esp\n\t" 220 221 /* Jump into the exception handler */ 222 "jmp *%[Target]" 223 : : 224 "c" (RegistrationFrame), 225 "a" (RegistrationFrame->ScopeTable), 226 [Target] "m" (RegistrationFrame->ScopeTable->Target) 227 ); 228 } 229 else 230 { 231 asm volatile ( 232 /* Load the registers */ 233 "movl 24(%%ecx), %%esp\n\t" 234 "movl 28(%%ecx), %%ebp\n\t" 235 236 /* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */ 237 "addl $4, %%esp\n\t" 238 239 /* Jump into the exception handler */ 240 "jmp *%[Target]" 241 : : 242 "c" (RegistrationFrame), 243 "a" (RegistrationFrame->ScopeTable), 244 [Target] "m" (RegistrationFrame->ScopeTable->Target) 245 ); 246 } 247 248 __builtin_unreachable(); 249 } 250 251 void 252 __fastcall 253 _SEH3$_CallRtlUnwind( 254 PSEH3$_REGISTRATION_FRAME RegistrationFrame); 255 256 257 EXCEPTION_DISPOSITION 258 __cdecl 259 #ifndef __clang__ 260 __attribute__ ((__target__ ("cld"))) 261 #endif 262 _SEH3$_except_handler( 263 struct _EXCEPTION_RECORD * ExceptionRecord, 264 PSEH3$_REGISTRATION_FRAME EstablisherFrame, 265 struct _CONTEXT * ContextRecord, 266 void * DispatcherContext) 267 { 268 PSEH3$_REGISTRATION_FRAME CurrentFrame, TargetFrame; 269 SEH3$_EXCEPTION_POINTERS ExceptionPointers; 270 LONG FilterResult; 271 272 /* Clear the direction flag. */ 273 asm volatile ("cld" : : : "memory"); 274 275 /* Save the exception pointers on the stack */ 276 ExceptionPointers.ExceptionRecord = ExceptionRecord; 277 ExceptionPointers.ContextRecord = ContextRecord; 278 279 /* Check if this is an unwind */ 280 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING) 281 { 282 /* Unwind all local frames */ 283 TargetFrame = EstablisherFrame->Next; 284 } 285 else 286 { 287 /* Loop all frames for this registration */ 288 CurrentFrame = EstablisherFrame->EndOfChain; 289 for (;;) 290 { 291 /* Check if we have an exception handler */ 292 if (CurrentFrame->ScopeTable->Target != NULL) 293 { 294 /* Set exception pointers and code for this frame */ 295 CurrentFrame->ExceptionPointers = &ExceptionPointers; 296 CurrentFrame->ExceptionCode = ExceptionRecord->ExceptionCode; 297 298 /* Get the filter result */ 299 FilterResult = _SEH3$_GetFilterResult(CurrentFrame); 300 301 /* Check, if continuuing is requested */ 302 if (FilterResult == EXCEPTION_CONTINUE_EXECUTION) 303 { 304 return ExceptionContinueExecution; 305 } 306 307 /* Check if the except handler shall be executed */ 308 if (FilterResult == EXCEPTION_EXECUTE_HANDLER) break; 309 } 310 311 /* Bail out if this is the last handler */ 312 if (CurrentFrame == EstablisherFrame) 313 return ExceptionContinueSearch; 314 315 /* Go to the next frame */ 316 CurrentFrame = CurrentFrame->Next; 317 } 318 319 /* Call RtlUnwind to unwind the frames below this one */ 320 _SEH3$_CallRtlUnwind(EstablisherFrame); 321 322 /* Do a local unwind up to this frame */ 323 TargetFrame = CurrentFrame; 324 } 325 326 /* Loop frames up to the target frame */ 327 for (CurrentFrame = EstablisherFrame->EndOfChain; 328 CurrentFrame != TargetFrame; 329 CurrentFrame = CurrentFrame->Next) 330 { 331 /* Manually unregister the frame */ 332 _SEH3$_Unregister(CurrentFrame); 333 334 /* Check if this is an unwind frame */ 335 if (CurrentFrame->ScopeTable->Target == NULL) 336 { 337 /* Set exception pointers and code for this frame */ 338 CurrentFrame->ExceptionPointers = &ExceptionPointers; 339 CurrentFrame->ExceptionCode = ExceptionRecord->ExceptionCode; 340 341 /* Call the finally function */ 342 _SEH3$_CallFinally(CurrentFrame); 343 } 344 } 345 346 /* Check if this was an unwind */ 347 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING) 348 { 349 return ExceptionContinueSearch; 350 } 351 352 /* Unregister the frame. It will be unregistered again at the end of the 353 __except block, due to auto cleanup, but that doesn't hurt. 354 All we do is set either fs:[0] or EstablisherFrame->EndOfChain to 355 CurrentFrame->Next, which will not change it's value. */ 356 _SEH3$_Unregister(CurrentFrame); 357 358 /* Jump to the __except block (does not return) */ 359 _SEH3$_JumpToTarget(CurrentFrame); 360 } 361 362