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.PROC _RtlpExecuteHandler@20 132 FPO 0, 0, 0, 0, 0, FRAME_FPO 133 134 /* Save non-volatile */ 135 push ebx 136 push esi 137 push edi 138 139 /* Clear registers */ 140 xor eax, eax 141 xor ebx, ebx 142 xor esi, esi 143 xor edi, edi 144 145 /* Call the 2nd-stage executer */ 146 push [esp+32] 147 push [esp+32] 148 push [esp+32] 149 push [esp+32] 150 push [esp+32] 151 call _RtlpExecuteHandler2@20 152 153 /* Restore non-volatile */ 154 pop edi 155 pop esi 156 pop ebx 157 ret 20 158 159.ENDP 160 161PUBLIC _RtlpExecuteHandler2@20 162_RtlpExecuteHandler2@20: 163 164 /* Set up stack frame */ 165 push ebp 166 mov ebp, esp 167 168 /* Save the Frame */ 169 push [ebp+12] 170 171 /* Push handler address */ 172 push edx 173 174 /* Push the exception list */ 175 push [fs:TEB_EXCEPTION_LIST] 176 177 /* Link us to it */ 178 mov [fs:TEB_EXCEPTION_LIST], esp 179 180 /* Call the handler */ 181 push [ebp+20] 182 push [ebp+16] 183 push [ebp+12] 184 push [ebp+8] 185 mov ecx, [ebp+24] 186 call ecx 187 188 /* Unlink us */ 189 mov esp, [fs:TEB_EXCEPTION_LIST] 190 191 /* Restore it */ 192 pop [fs:TEB_EXCEPTION_LIST] 193 194 /* Undo stack frame and return */ 195 mov esp, ebp 196 pop ebp 197 ret 20 198 199 200_RtlpExceptionProtector: 201 202 /* Assume we'll continue */ 203 mov eax, ExceptionContinueSearch 204 205 /* Put the exception record in ECX and check the Flags */ 206 mov ecx, [esp+4] 207 test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND 208 jnz return 209 210 /* Save the frame in ECX and Context in EDX */ 211 mov ecx, [esp+8] 212 mov edx, [esp+16] 213 214 /* Get the nested frame */ 215 mov eax, [ecx+8] 216 217 /* Set it as the dispatcher context */ 218 mov [edx], eax 219 220 /* Return nested exception */ 221 mov eax, ExceptionNestedException 222 223return: 224 ret 16 225 226 227_RtlpUnwindProtector: 228 229 /* Assume we'll continue */ 230 mov eax, ExceptionContinueSearch 231 232 /* Put the exception record in ECX and check the Flags */ 233 mov ecx, [esp+4] 234 test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND 235 jz .return 236 237 /* Save the frame in ECX and Context in EDX */ 238 mov ecx, [esp+8] 239 mov edx, [esp+16] 240 241 /* Get the nested frame */ 242 mov eax, [ecx+8] 243 244 /* Set it as the dispatcher context */ 245 mov [edx], eax 246 247 /* Return collided unwind */ 248 mov eax, ExceptionCollidedUnwind 249 250.return: 251 ret 16 252 253 254PUBLIC _RtlRaiseException@4 255_RtlRaiseException@4: 256 257 /* Set up stack frame */ 258 push ebp 259 mov ebp, esp 260 261 /* 262 * Save the context while preserving everything but ESP and EBP. 263 * This is vital because the caller will be restored with this context 264 * in case the execution is continued, which means we must not clobber 265 * the non-volatiles. We preserve the volatiles too because the context 266 * could get passed to a debugger. 267 */ 268 lea esp, [esp-CONTEXT_FRAME_LENGTH] 269 push esp 270 call _RtlCaptureContext@4 271 272 /* Adjust ESP to account for the argument that was passed */ 273 add dword ptr [esp+CONTEXT_ESP], 4 274 275 /* Save the exception address */ 276 mov edx, [ebp+4] 277 mov eax, [ebp+8] 278 mov [eax+EXCEPTION_RECORD_EXCEPTION_ADDRESS], edx 279 280 /* Write the context flag */ 281 mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL 282 283 /* Check if user mode debugger is active */ 284 call _RtlpCheckForActiveDebugger@0 285 test al, al 286 jnz DebuggerActive1 287 288 /* Dispatch the exception */ 289 push esp 290 push [ebp+8] 291 call _RtlDispatchException@8 292 test al, al 293 jz RaiseException 294 295 /* Continue, go back to previous context */ 296 mov ecx, esp 297 push 0 298 push ecx 299 call _ZwContinue@8 300 jmp RaiseStatus1 301 302DebuggerActive1: 303 304 /* Raise an exception immediately */ 305 mov ecx, esp 306 push 1 307 push ecx 308 push [ebp+8] 309 call _ZwRaiseException@12 310 jmp RaiseStatus1 311 312RaiseException: 313 314 /* Raise the exception */ 315 mov ecx, esp 316 push 0 317 push ecx 318 push [ebp+8] 319 call _ZwRaiseException@12 320 321RaiseStatus1: 322 323 /* If we returned, raise a status */ 324 push eax 325 call _RtlRaiseStatus@4 326 327 328PUBLIC _RtlRaiseStatus@4 329_RtlRaiseStatus@4: 330 331 /* Set up stack frame */ 332 push ebp 333 mov ebp, esp 334 335 /* 336 * Save the context while preserving everything but ESP and EBP. 337 * This is vital because the caller will be restored with this context 338 * in case the execution is continued, which means we must not clobber 339 * the non-volatiles. We preserve the volatiles too because the context 340 * could get passed to a debugger. 341 */ 342 lea esp, [esp-CONTEXT_FRAME_LENGTH-EXCEPTION_RECORD_LENGTH] 343 push esp 344 call _RtlCaptureContext@4 345 346 /* Adjust ESP to account for the argument that was passed */ 347 add dword ptr [esp+CONTEXT_ESP], 4 348 349 /* Set up the exception record */ 350 lea ecx, [esp+CONTEXT_FRAME_LENGTH] 351 mov eax, [ebp+8] 352 mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_CODE], eax 353 mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_NONCONTINUABLE 354 and dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_RECORD], 0 355 mov eax, [ebp+4] 356 mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_ADDRESS], eax 357 and dword ptr [ecx+EXCEPTION_RECORD_NUMBER_PARAMETERS], 0 358 359 /* Write the context flag */ 360 mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL 361 362 /* Check if user mode debugger is active */ 363 call _RtlpCheckForActiveDebugger@0 364 365 /* Restore ECX and jump if debugger is active */ 366 lea ecx, [esp+CONTEXT_FRAME_LENGTH] 367 test al, al 368 jnz DebuggerActive2 369 370 /* Dispatch the exception */ 371 push esp 372 push ecx 373 call _RtlDispatchException@8 374 375 /* Raise exception if we got here */ 376 lea ecx, [esp+CONTEXT_FRAME_LENGTH] 377 mov edx, esp 378 push 0 379 push edx 380 push ecx 381 call _ZwRaiseException@12 382 jmp RaiseStatus2 383 384DebuggerActive2: 385 386 /* Raise an exception immediately */ 387 mov edx, esp 388 push 1 389 push edx 390 push ecx 391 call _ZwRaiseException@12 392 393RaiseStatus2: 394 395 /* If we returned, raise a status */ 396 push eax 397 call _RtlRaiseStatus@4 398 399END 400