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