1/* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Runtime Library (RTL) 4 * FILE: lib/rtl/i386/except_asm.s 5 * PURPOSE: User-mode exception support for IA-32 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 * Stefan Ginsberg (stefan.ginsberg@reactos.org) 8 */ 9 10/* INCLUDES ******************************************************************/ 11 12#include <asm.inc> 13#include <ks386.inc> 14 15EXTERN _RtlpCheckForActiveDebugger@0:PROC 16EXTERN _RtlDispatchException@8:PROC 17EXTERN _ZwContinue@8:PROC 18EXTERN _ZwRaiseException@12:PROC 19 20#define ExceptionContinueSearch 1 21#define ExceptionNestedException 2 22#define ExceptionCollidedUnwind 3 23 24/* FUNCTIONS *****************************************************************/ 25 26.code 27 28PUBLIC _RtlpGetExceptionList@0 29_RtlpGetExceptionList@0: 30 31 /* Return the exception list */ 32 mov eax, fs:[TEB_EXCEPTION_LIST] 33 ret 34 35 36PUBLIC _RtlpSetExceptionList@4 37_RtlpSetExceptionList@4: 38 39 /* Get the new list */ 40 mov ecx, [esp+4] 41 mov ecx, [ecx] 42 43 /* Write it */ 44 mov fs:[TEB_EXCEPTION_LIST], ecx 45 46 /* Return */ 47 ret 4 48 49 50PUBLIC _RtlCaptureContext@4 51_RtlCaptureContext@4: 52 53 /* Preserve EBX and put the context in it */ 54 push ebx 55 mov ebx, [esp+8] 56 57 /* Save the basic register context */ 58 mov [ebx+CONTEXT_EAX], eax 59 mov [ebx+CONTEXT_ECX], ecx 60 mov [ebx+CONTEXT_EDX], edx 61 mov eax, [esp] 62 mov [ebx+CONTEXT_EBX], eax 63 mov [ebx+CONTEXT_ESI], esi 64 mov [ebx+CONTEXT_EDI], edi 65 66 /* Capture the other regs */ 67 jmp CaptureRest 68 69 70PUBLIC _RtlpCaptureContext@4 71_RtlpCaptureContext@4: 72 73 /* Preserve EBX and put the context in it */ 74 push ebx 75 mov ebx, [esp+8] 76 77 /* Clear the basic register context */ 78 mov dword ptr [ebx+CONTEXT_EAX], 0 79 mov dword ptr [ebx+CONTEXT_ECX], 0 80 mov dword ptr [ebx+CONTEXT_EDX], 0 81 mov dword ptr [ebx+CONTEXT_EBX], 0 82 mov dword ptr [ebx+CONTEXT_ESI], 0 83 mov dword ptr [ebx+CONTEXT_EDI], 0 84 85CaptureRest: 86 /* Capture the segment registers */ 87 mov [ebx+CONTEXT_SEGCS], cs 88 mov [ebx+CONTEXT_SEGDS], ds 89 mov [ebx+CONTEXT_SEGES], es 90 mov [ebx+CONTEXT_SEGFS], fs 91 mov [ebx+CONTEXT_SEGGS], gs 92 mov [ebx+CONTEXT_SEGSS], ss 93 94 /* Capture flags */ 95 pushfd 96 pop [ebx+CONTEXT_EFLAGS] 97 98 /* The return address should be in [ebp+4] */ 99 mov eax, [ebp+4] 100 mov [ebx+CONTEXT_EIP], eax 101 102 /* Get EBP */ 103 mov eax, [ebp+0] 104 mov [ebx+CONTEXT_EBP], eax 105 106 /* And get ESP */ 107 lea eax, [ebp+8] 108 mov [ebx+CONTEXT_ESP], eax 109 110 /* Return to the caller */ 111 pop ebx 112 ret 4 113 114 115PUBLIC _RtlpExecuteHandlerForException@20 116_RtlpExecuteHandlerForException@20: 117 118 /* Copy the routine in EDX */ 119 mov edx, offset _RtlpExceptionProtector 120 121 /* Jump to common routine */ 122 jmp _RtlpExecuteHandler@20 123 124 125PUBLIC _RtlpExecuteHandlerForUnwind@20 126_RtlpExecuteHandlerForUnwind@20: 127 /* Copy the routine in EDX */ 128 mov edx, offset _RtlpUnwindProtector 129 130 131_RtlpExecuteHandler@20: 132 133 /* Save non-volatile */ 134 push ebx 135 push esi 136 push edi 137 138 /* Clear registers */ 139 xor eax, eax 140 xor ebx, ebx 141 xor esi, esi 142 xor edi, edi 143 144 /* Call the 2nd-stage executer */ 145 push [esp+32] 146 push [esp+32] 147 push [esp+32] 148 push [esp+32] 149 push [esp+32] 150 call _RtlpExecuteHandler2@20 151 152 /* Restore non-volatile */ 153 pop edi 154 pop esi 155 pop ebx 156 ret 20 157 158 159PUBLIC _RtlpExecuteHandler2@20 160_RtlpExecuteHandler2@20: 161 162 /* Set up stack frame */ 163 push ebp 164 mov ebp, esp 165 166 /* Save the Frame */ 167 push [ebp+12] 168 169 /* Push handler address */ 170 push edx 171 172 /* Push the exception list */ 173 push [fs:TEB_EXCEPTION_LIST] 174 175 /* Link us to it */ 176 mov [fs:TEB_EXCEPTION_LIST], esp 177 178 /* Call the handler */ 179 push [ebp+20] 180 push [ebp+16] 181 push [ebp+12] 182 push [ebp+8] 183 mov ecx, [ebp+24] 184 call ecx 185 186 /* Unlink us */ 187 mov esp, [fs:TEB_EXCEPTION_LIST] 188 189 /* Restore it */ 190 pop [fs:TEB_EXCEPTION_LIST] 191 192 /* Undo stack frame and return */ 193 mov esp, ebp 194 pop ebp 195 ret 20 196 197 198_RtlpExceptionProtector: 199 200 /* Assume we'll continue */ 201 mov eax, ExceptionContinueSearch 202 203 /* Put the exception record in ECX and check the Flags */ 204 mov ecx, [esp+4] 205 test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND 206 jnz return 207 208 /* Save the frame in ECX and Context in EDX */ 209 mov ecx, [esp+8] 210 mov edx, [esp+16] 211 212 /* Get the nested frame */ 213 mov eax, [ecx+8] 214 215 /* Set it as the dispatcher context */ 216 mov [edx], eax 217 218 /* Return nested exception */ 219 mov eax, ExceptionNestedException 220 221return: 222 ret 16 223 224 225_RtlpUnwindProtector: 226 227 /* Assume we'll continue */ 228 mov eax, ExceptionContinueSearch 229 230 /* Put the exception record in ECX and check the Flags */ 231 mov ecx, [esp+4] 232 test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND 233 jz .return 234 235 /* Save the frame in ECX and Context in EDX */ 236 mov ecx, [esp+8] 237 mov edx, [esp+16] 238 239 /* Get the nested frame */ 240 mov eax, [ecx+8] 241 242 /* Set it as the dispatcher context */ 243 mov [edx], eax 244 245 /* Return collided unwind */ 246 mov eax, ExceptionCollidedUnwind 247 248.return: 249 ret 16 250 251 252PUBLIC _RtlRaiseException@4 253_RtlRaiseException@4: 254 255 /* Set up stack frame */ 256 push ebp 257 mov ebp, esp 258 259 /* 260 * Save the context while preserving everything but ESP and EBP. 261 * This is vital because the caller will be restored with this context 262 * in case the execution is continued, which means we must not clobber 263 * the non-volatiles. We preserve the volatiles too because the context 264 * could get passed to a debugger. 265 */ 266 lea esp, [esp-CONTEXT_FRAME_LENGTH] 267 push esp 268 call _RtlCaptureContext@4 269 270 /* Adjust ESP to account for the argument that was passed */ 271 add dword ptr [esp+CONTEXT_ESP], 4 272 273 /* Save the exception address */ 274 mov edx, [ebp+4] 275 mov eax, [ebp+8] 276 mov [eax+EXCEPTION_RECORD_EXCEPTION_ADDRESS], edx 277 278 /* Write the context flag */ 279 mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL 280 281 /* Check if user mode debugger is active */ 282 call _RtlpCheckForActiveDebugger@0 283 test al, al 284 jnz DebuggerActive1 285 286 /* Dispatch the exception */ 287 push esp 288 push [ebp+8] 289 call _RtlDispatchException@8 290 test al, al 291 jz RaiseException 292 293 /* Continue, go back to previous context */ 294 mov ecx, esp 295 push 0 296 push ecx 297 call _ZwContinue@8 298 jmp RaiseStatus1 299 300DebuggerActive1: 301 302 /* Raise an exception immediately */ 303 mov ecx, esp 304 push 1 305 push ecx 306 push [ebp+8] 307 call _ZwRaiseException@12 308 jmp RaiseStatus1 309 310RaiseException: 311 312 /* Raise the exception */ 313 mov ecx, esp 314 push 0 315 push ecx 316 push [ebp+8] 317 call _ZwRaiseException@12 318 319RaiseStatus1: 320 321 /* If we returned, raise a status */ 322 push eax 323 call _RtlRaiseStatus@4 324 325 326PUBLIC _RtlRaiseStatus@4 327_RtlRaiseStatus@4: 328 329 /* Set up stack frame */ 330 push ebp 331 mov ebp, esp 332 333 /* 334 * Save the context while preserving everything but ESP and EBP. 335 * This is vital because the caller will be restored with this context 336 * in case the execution is continued, which means we must not clobber 337 * the non-volatiles. We preserve the volatiles too because the context 338 * could get passed to a debugger. 339 */ 340 lea esp, [esp-CONTEXT_FRAME_LENGTH-EXCEPTION_RECORD_LENGTH] 341 push esp 342 call _RtlCaptureContext@4 343 344 /* Adjust ESP to account for the argument that was passed */ 345 add dword ptr [esp+CONTEXT_ESP], 4 346 347 /* Set up the exception record */ 348 lea ecx, [esp+CONTEXT_FRAME_LENGTH] 349 mov eax, [ebp+8] 350 mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_CODE], eax 351 mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_NONCONTINUABLE 352 and dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_RECORD], 0 353 mov eax, [ebp+4] 354 mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_ADDRESS], eax 355 and dword ptr [ecx+EXCEPTION_RECORD_NUMBER_PARAMETERS], 0 356 357 /* Write the context flag */ 358 mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL 359 360 /* Check if user mode debugger is active */ 361 call _RtlpCheckForActiveDebugger@0 362 363 /* Restore ECX and jump if debugger is active */ 364 lea ecx, [esp+CONTEXT_FRAME_LENGTH] 365 test al, al 366 jnz DebuggerActive2 367 368 /* Dispatch the exception */ 369 push esp 370 push ecx 371 call _RtlDispatchException@8 372 373 /* Raise exception if we got here */ 374 lea ecx, [esp+CONTEXT_FRAME_LENGTH] 375 mov edx, esp 376 push 0 377 push edx 378 push ecx 379 call _ZwRaiseException@12 380 jmp RaiseStatus2 381 382DebuggerActive2: 383 384 /* Raise an exception immediately */ 385 mov edx, esp 386 push 1 387 push edx 388 push ecx 389 call _ZwRaiseException@12 390 391RaiseStatus2: 392 393 /* If we returned, raise a status */ 394 push eax 395 call _RtlRaiseStatus@4 396 397END 398