| 1 | /* | 
|---|
| 2 | * msvcrt.dll exception handling | 
|---|
| 3 | * | 
|---|
| 4 | * Copyright 2000 Jon Griffiths | 
|---|
| 5 | * | 
|---|
| 6 | * This library is free software; you can redistribute it and/or | 
|---|
| 7 | * modify it under the terms of the GNU Lesser General Public | 
|---|
| 8 | * License as published by the Free Software Foundation; either | 
|---|
| 9 | * version 2.1 of the License, or (at your option) any later version. | 
|---|
| 10 | * | 
|---|
| 11 | * This library is distributed in the hope that it will be useful, | 
|---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|---|
| 14 | * Lesser General Public License for more details. | 
|---|
| 15 | * | 
|---|
| 16 | * You should have received a copy of the GNU Lesser General Public | 
|---|
| 17 | * License along with this library; if not, write to the Free Software | 
|---|
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|---|
| 19 | * | 
|---|
| 20 | * NOTES: | 
|---|
| 21 | * | 
|---|
| 22 | * See http://www.microsoft.com/msj/0197/exception/exception.htm, | 
|---|
| 23 | * but don't believe all of it. | 
|---|
| 24 | * | 
|---|
| 25 | * FIXME: Incomplete support for nested exceptions/try block cleanup. | 
|---|
| 26 | */ | 
|---|
| 27 |  | 
|---|
| 28 | #ifdef __WIN32OS2__ | 
|---|
| 29 | #include <emxheader.h> | 
|---|
| 30 | #include <stdlib.h> | 
|---|
| 31 | #include <string.h> | 
|---|
| 32 | #include <winbase.h> | 
|---|
| 33 | #else | 
|---|
| 34 | #include "config.h" | 
|---|
| 35 | #endif | 
|---|
| 36 |  | 
|---|
| 37 | #include "wine/port.h" | 
|---|
| 38 |  | 
|---|
| 39 | #include "winternl.h" | 
|---|
| 40 | #include "wine/exception.h" | 
|---|
| 41 | #include "thread.h" | 
|---|
| 42 | #include "msvcrt.h" | 
|---|
| 43 |  | 
|---|
| 44 | #include "msvcrt/stdlib.h" | 
|---|
| 45 | #include "msvcrt/setjmp.h" | 
|---|
| 46 | #include "excpt.h" | 
|---|
| 47 |  | 
|---|
| 48 |  | 
|---|
| 49 | #include "wine/debug.h" | 
|---|
| 50 |  | 
|---|
| 51 | WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); | 
|---|
| 52 |  | 
|---|
| 53 | typedef void (*MSVCRT_sig_handler_func)(void); | 
|---|
| 54 |  | 
|---|
| 55 | /* VC++ extensions to Win32 SEH */ | 
|---|
| 56 | typedef struct _SCOPETABLE | 
|---|
| 57 | { | 
|---|
| 58 | int previousTryLevel; | 
|---|
| 59 | int (*lpfnFilter)(PEXCEPTION_POINTERS); | 
|---|
| 60 | int (*lpfnHandler)(void); | 
|---|
| 61 | } SCOPETABLE, *PSCOPETABLE; | 
|---|
| 62 |  | 
|---|
| 63 | typedef struct _MSVCRT_EXCEPTION_FRAME | 
|---|
| 64 | { | 
|---|
| 65 | EXCEPTION_FRAME *prev; | 
|---|
| 66 | void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_FRAME, | 
|---|
| 67 | PCONTEXT, PEXCEPTION_RECORD); | 
|---|
| 68 | PSCOPETABLE scopetable; | 
|---|
| 69 | int trylevel; | 
|---|
| 70 | int _ebp; | 
|---|
| 71 | PEXCEPTION_POINTERS xpointers; | 
|---|
| 72 | } MSVCRT_EXCEPTION_FRAME; | 
|---|
| 73 |  | 
|---|
| 74 | #define TRYLEVEL_END (-1) /* End of trylevel list */ | 
|---|
| 75 |  | 
|---|
| 76 | #if defined(__GNUC__) && defined(__i386__) | 
|---|
| 77 | inline static void call_finally_block( void *code_block, void *base_ptr ) | 
|---|
| 78 | { | 
|---|
| 79 | __asm__ __volatile__ ("movl %1,%%ebp; call *%%eax" \ | 
|---|
| 80 | : : "a" (code_block), "g" (base_ptr)); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | inline static DWORD call_filter( void *func, void *arg, void *ebp ) | 
|---|
| 84 | { | 
|---|
| 85 | DWORD ret; | 
|---|
| 86 | __asm__ __volatile__ ("pushl %%ebp; pushl %3; movl %2,%%ebp; call *%%eax; popl %%ebp; popl %%ebp" | 
|---|
| 87 | : "=a" (ret) | 
|---|
| 88 | : "0" (func), "g" (ebp), "g" (arg) | 
|---|
| 89 | : "ecx", "edx", "memory" ); | 
|---|
| 90 | return ret; | 
|---|
| 91 | } | 
|---|
| 92 | #endif | 
|---|
| 93 |  | 
|---|
| 94 | static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, | 
|---|
| 95 | struct __EXCEPTION_FRAME* frame, | 
|---|
| 96 | PCONTEXT context WINE_UNUSED, | 
|---|
| 97 | struct __EXCEPTION_FRAME** dispatch) | 
|---|
| 98 | { | 
|---|
| 99 | if (rec->ExceptionFlags & 0x6) | 
|---|
| 100 | return ExceptionContinueSearch; | 
|---|
| 101 | *dispatch = frame; | 
|---|
| 102 | return ExceptionCollidedUnwind; | 
|---|
| 103 | } | 
|---|
| 104 |  | 
|---|
| 105 |  | 
|---|
| 106 | /********************************************************************* | 
|---|
| 107 | *              _XcptFilter (MSVCRT.@) | 
|---|
| 108 | */ | 
|---|
| 109 | int _XcptFilter(int ex, PEXCEPTION_POINTERS ptr) | 
|---|
| 110 | { | 
|---|
| 111 | FIXME("(%d,%p)semi-stub\n", ex, ptr); | 
|---|
| 112 | return UnhandledExceptionFilter(ptr); | 
|---|
| 113 | } | 
|---|
| 114 |  | 
|---|
| 115 | /********************************************************************* | 
|---|
| 116 | *              _EH_prolog (MSVCRT.@) | 
|---|
| 117 | */ | 
|---|
| 118 | #ifdef __i386__ | 
|---|
| 119 | /* Provided for VC++ binary compatability only */ | 
|---|
| 120 | __ASM_GLOBAL_FUNC(_EH_prolog, | 
|---|
| 121 | "pushl $-1\n\t" | 
|---|
| 122 | "pushl %eax\n\t" | 
|---|
| 123 | "pushl %fs:0\n\t" | 
|---|
| 124 | "movl  %esp, %fs:0\n\t" | 
|---|
| 125 | "movl  12(%esp), %eax\n\t" | 
|---|
| 126 | "movl  %ebp, 12(%esp)\n\t" | 
|---|
| 127 | "leal  12(%esp), %ebp\n\t" | 
|---|
| 128 | "pushl %eax\n\t" | 
|---|
| 129 | "ret"); | 
|---|
| 130 | #endif | 
|---|
| 131 |  | 
|---|
| 132 | /******************************************************************* | 
|---|
| 133 | *              _global_unwind2 (MSVCRT.@) | 
|---|
| 134 | */ | 
|---|
| 135 | void _global_unwind2(PEXCEPTION_FRAME frame) | 
|---|
| 136 | { | 
|---|
| 137 | TRACE("(%p)\n",frame); | 
|---|
| 138 | RtlUnwind( frame, 0, 0, 0 ); | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 | /******************************************************************* | 
|---|
| 142 | *              _local_unwind2 (MSVCRT.@) | 
|---|
| 143 | */ | 
|---|
| 144 | void _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel) | 
|---|
| 145 | { | 
|---|
| 146 | MSVCRT_EXCEPTION_FRAME *curframe = frame; | 
|---|
| 147 | EXCEPTION_FRAME reg; | 
|---|
| 148 |  | 
|---|
| 149 | TRACE("MSVCRT: _local_unwind2 (%p,%d,%d)\n",frame, frame->trylevel, trylevel); | 
|---|
| 150 |  | 
|---|
| 151 | /* Register a handler in case of a nested exception */ | 
|---|
| 152 | reg.Handler = (PEXCEPTION_HANDLER)MSVCRT_nested_handler; | 
|---|
| 153 | reg.Prev = NtCurrentTeb()->except; | 
|---|
| 154 | __wine_push_frame(®); | 
|---|
| 155 |  | 
|---|
| 156 | while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel) | 
|---|
| 157 | { | 
|---|
| 158 | int curtrylevel = frame->scopetable[frame->trylevel].previousTryLevel; | 
|---|
| 159 | curframe = frame; | 
|---|
| 160 | curframe->trylevel = curtrylevel; | 
|---|
| 161 | if (!frame->scopetable[curtrylevel].lpfnFilter) | 
|---|
| 162 | { | 
|---|
| 163 | ERR("__try block cleanup not implemented - expect crash!\n"); | 
|---|
| 164 | /* FIXME: Remove current frame, set ebp, call | 
|---|
| 165 | * frame->scopetable[curtrylevel].lpfnHandler() | 
|---|
| 166 | */ | 
|---|
| 167 | } | 
|---|
| 168 | } | 
|---|
| 169 | __wine_pop_frame(®); | 
|---|
| 170 | TRACE("unwound OK\n"); | 
|---|
| 171 | } | 
|---|
| 172 |  | 
|---|
| 173 | /********************************************************************* | 
|---|
| 174 | *              _except_handler2 (MSVCRT.@) | 
|---|
| 175 | */ | 
|---|
| 176 | int _except_handler2(PEXCEPTION_RECORD rec, | 
|---|
| 177 | PEXCEPTION_FRAME frame, | 
|---|
| 178 | PCONTEXT context, | 
|---|
| 179 | PEXCEPTION_FRAME* dispatcher) | 
|---|
| 180 | { | 
|---|
| 181 | FIXME("exception %lx flags=%lx at %p handler=%p %p %p stub\n", | 
|---|
| 182 | rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, | 
|---|
| 183 | frame->Handler, context, dispatcher); | 
|---|
| 184 | return ExceptionContinueSearch; | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | /********************************************************************* | 
|---|
| 188 | *              _except_handler3 (MSVCRT.@) | 
|---|
| 189 | */ | 
|---|
| 190 | int _except_handler3(PEXCEPTION_RECORD rec, | 
|---|
| 191 | MSVCRT_EXCEPTION_FRAME* frame, | 
|---|
| 192 | PCONTEXT context, void* dispatcher) | 
|---|
| 193 | { | 
|---|
| 194 | #if defined(__GNUC__) && defined(__i386__) | 
|---|
| 195 | long retval; | 
|---|
| 196 | int trylevel; | 
|---|
| 197 | EXCEPTION_POINTERS exceptPtrs; | 
|---|
| 198 | PSCOPETABLE pScopeTable; | 
|---|
| 199 |  | 
|---|
| 200 | TRACE("MSVCRT: exception %lx flags=%lx at %p handler=%p %p %p semi-stub\n", | 
|---|
| 201 | rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, | 
|---|
| 202 | frame->handler, context, dispatcher); | 
|---|
| 203 |  | 
|---|
| 204 | __asm__ __volatile__ ("cld"); | 
|---|
| 205 |  | 
|---|
| 206 | if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) | 
|---|
| 207 | { | 
|---|
| 208 | /* Unwinding the current frame */ | 
|---|
| 209 | _local_unwind2(frame, TRYLEVEL_END); | 
|---|
| 210 | return ExceptionContinueSearch; | 
|---|
| 211 | } | 
|---|
| 212 | else | 
|---|
| 213 | { | 
|---|
| 214 | /* Hunting for handler */ | 
|---|
| 215 | exceptPtrs.ExceptionRecord = rec; | 
|---|
| 216 | exceptPtrs.ContextRecord = context; | 
|---|
| 217 | *((DWORD *)frame-1) = (DWORD)&exceptPtrs; | 
|---|
| 218 | trylevel = frame->trylevel; | 
|---|
| 219 | pScopeTable = frame->scopetable; | 
|---|
| 220 |  | 
|---|
| 221 | while (trylevel != TRYLEVEL_END) | 
|---|
| 222 | { | 
|---|
| 223 | if (pScopeTable[trylevel].lpfnFilter) | 
|---|
| 224 | { | 
|---|
| 225 | TRACE("filter = %p\n", pScopeTable[trylevel].lpfnFilter); | 
|---|
| 226 |  | 
|---|
| 227 | retval = call_filter( pScopeTable[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp ); | 
|---|
| 228 |  | 
|---|
| 229 | TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ? | 
|---|
| 230 | "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ? | 
|---|
| 231 | "EXECUTE_HANDLER" : "CONTINUE_SEARCH"); | 
|---|
| 232 |  | 
|---|
| 233 | if (retval == EXCEPTION_CONTINUE_EXECUTION) | 
|---|
| 234 | return ExceptionContinueExecution; | 
|---|
| 235 |  | 
|---|
| 236 | if (retval == EXCEPTION_EXECUTE_HANDLER) | 
|---|
| 237 | { | 
|---|
| 238 | /* Unwind all higher frames, this one will handle the exception */ | 
|---|
| 239 | _global_unwind2((PEXCEPTION_FRAME)frame); | 
|---|
| 240 | _local_unwind2(frame, trylevel); | 
|---|
| 241 |  | 
|---|
| 242 | /* Set our trylevel to the enclosing block, and call the __finally | 
|---|
| 243 | * code, which won't return | 
|---|
| 244 | */ | 
|---|
| 245 | frame->trylevel = pScopeTable->previousTryLevel; | 
|---|
| 246 | TRACE("__finally block %p\n",pScopeTable[trylevel].lpfnHandler); | 
|---|
| 247 | call_finally_block(pScopeTable[trylevel].lpfnHandler, &frame->_ebp); | 
|---|
| 248 | ERR("Returned from __finally block - expect crash!\n"); | 
|---|
| 249 | } | 
|---|
| 250 | } | 
|---|
| 251 | trylevel = pScopeTable->previousTryLevel; | 
|---|
| 252 | } | 
|---|
| 253 | } | 
|---|
| 254 | #else | 
|---|
| 255 | TRACE("exception %lx flags=%lx at %p handler=%p %p %p stub\n", | 
|---|
| 256 | rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, | 
|---|
| 257 | frame->handler, context, dispatcher); | 
|---|
| 258 | #endif | 
|---|
| 259 | return ExceptionContinueSearch; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | /********************************************************************* | 
|---|
| 263 | *              _abnormal_termination (MSVCRT.@) | 
|---|
| 264 | */ | 
|---|
| 265 | int _abnormal_termination(void) | 
|---|
| 266 | { | 
|---|
| 267 | FIXME("(void)stub\n"); | 
|---|
| 268 | return 0; | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | /* | 
|---|
| 272 | * setjmp/longjmp implementation | 
|---|
| 273 | */ | 
|---|
| 274 |  | 
|---|
| 275 | #ifdef __i386__ | 
|---|
| 276 | #define MSVCRT_JMP_MAGIC 0x56433230 /* ID value for new jump structure */ | 
|---|
| 277 | typedef void (*MSVCRT_unwind_function)(const void*); | 
|---|
| 278 |  | 
|---|
| 279 | /* | 
|---|
| 280 | * The signatures of the setjmp/longjmp functions do not match that | 
|---|
| 281 | * declared in the setjmp header so they don't follow the regular naming | 
|---|
| 282 | * convention to avoid conflicts. | 
|---|
| 283 | */ | 
|---|
| 284 |  | 
|---|
| 285 | /******************************************************************* | 
|---|
| 286 | *              _setjmp (MSVCRT.@) | 
|---|
| 287 | */ | 
|---|
| 288 | #ifdef __WIN32OS2__ | 
|---|
| 289 | DEFINE_REGS_ENTRYPOINT_1( _MSVCRT__setjmp, EXC_MSVCRT__setjmp, _JUMP_BUFFER); | 
|---|
| 290 | void EXC_MSVCRT__setjmp(_JUMP_BUFFER *jmp, CONTEXT86* context) | 
|---|
| 291 | #else | 
|---|
| 292 | void _MSVCRT__setjmp(_JUMP_BUFFER *jmp, CONTEXT86* context) | 
|---|
| 293 | #endif | 
|---|
| 294 | { | 
|---|
| 295 | TRACE("MSVCRT: _setjmp (%p)\n",jmp); | 
|---|
| 296 | jmp->Ebp = context->Ebp; | 
|---|
| 297 | jmp->Ebx = context->Ebx; | 
|---|
| 298 | jmp->Edi = context->Edi; | 
|---|
| 299 | jmp->Esi = context->Esi; | 
|---|
| 300 | jmp->Esp = context->Esp; | 
|---|
| 301 | jmp->Eip = context->Eip; | 
|---|
| 302 | jmp->Registration = (unsigned long)NtCurrentTeb()->except; | 
|---|
| 303 | if (jmp->Registration == TRYLEVEL_END) | 
|---|
| 304 | jmp->TryLevel = TRYLEVEL_END; | 
|---|
| 305 | else | 
|---|
| 306 | jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; | 
|---|
| 307 | TRACE("returning 0\n"); | 
|---|
| 308 | context->Eax=0; | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|
| 311 | /******************************************************************* | 
|---|
| 312 | *              _setjmp3 (MSVCRT.@) | 
|---|
| 313 | */ | 
|---|
| 314 | #ifdef __WIN32OS2__ | 
|---|
| 315 | DEFINE_REGS_ENTRYPOINT_2( _MSVCRT__setjmp3, EXC_MSVCRT__setjmp3, _JUMP_BUFFER *, int ); | 
|---|
| 316 | void EXC_MSVCRT__setjmp3(_JUMP_BUFFER *jmp, int nb_args, CONTEXT86* context) | 
|---|
| 317 | #else | 
|---|
| 318 | void _MSVCRT__setjmp3(_JUMP_BUFFER *jmp, int nb_args, CONTEXT86* context) | 
|---|
| 319 | #endif | 
|---|
| 320 | { | 
|---|
| 321 | TRACE("MSVCRT: _setjmp3 (%p,%d)\n",jmp,nb_args); | 
|---|
| 322 | jmp->Ebp = context->Ebp; | 
|---|
| 323 | jmp->Ebx = context->Ebx; | 
|---|
| 324 | jmp->Edi = context->Edi; | 
|---|
| 325 | jmp->Esi = context->Esi; | 
|---|
| 326 | jmp->Esp = context->Esp; | 
|---|
| 327 | jmp->Eip = context->Eip; | 
|---|
| 328 | jmp->Cookie = MSVCRT_JMP_MAGIC; | 
|---|
| 329 | jmp->UnwindFunc = 0; | 
|---|
| 330 | jmp->Registration = (unsigned long)NtCurrentTeb()->except; | 
|---|
| 331 | if (jmp->Registration == TRYLEVEL_END) | 
|---|
| 332 | { | 
|---|
| 333 | jmp->TryLevel = TRYLEVEL_END; | 
|---|
| 334 | } | 
|---|
| 335 | else | 
|---|
| 336 | { | 
|---|
| 337 | void **args = ((void**)context->Esp)+2; | 
|---|
| 338 |  | 
|---|
| 339 | if (nb_args > 0) jmp->UnwindFunc = (unsigned long)*args++; | 
|---|
| 340 | if (nb_args > 1) jmp->TryLevel = (unsigned long)*args++; | 
|---|
| 341 | else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; | 
|---|
| 342 | if (nb_args > 2) | 
|---|
| 343 | { | 
|---|
| 344 | size_t size = (nb_args - 2) * sizeof(DWORD); | 
|---|
| 345 | memcpy( jmp->UnwindData, args, min( size, sizeof(jmp->UnwindData) )); | 
|---|
| 346 | } | 
|---|
| 347 | } | 
|---|
| 348 | TRACE("returning 0\n"); | 
|---|
| 349 | context->Eax = 0; | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | /********************************************************************* | 
|---|
| 353 | *              longjmp (MSVCRT.@) | 
|---|
| 354 | */ | 
|---|
| 355 | #ifdef __WIN32OS2__ | 
|---|
| 356 | DEFINE_REGS_ENTRYPOINT_2( _MSVCRT_longjmp, EXC_MSVCRT_longjmp, _JUMP_BUFFER *, int ); | 
|---|
| 357 | void EXC_MSVCRT_longjmp(_JUMP_BUFFER *jmp, int retval, CONTEXT86* context) | 
|---|
| 358 | #else | 
|---|
| 359 | void _MSVCRT_longjmp(_JUMP_BUFFER *jmp, int retval, CONTEXT86* context) | 
|---|
| 360 | #endif | 
|---|
| 361 | { | 
|---|
| 362 | unsigned long cur_frame = 0; | 
|---|
| 363 |  | 
|---|
| 364 | TRACE("MSVCRT: longjmp (%p,%d)\n", jmp, retval); | 
|---|
| 365 |  | 
|---|
| 366 | cur_frame=(unsigned long)NtCurrentTeb()->except; | 
|---|
| 367 | TRACE("cur_frame=%lx\n",cur_frame); | 
|---|
| 368 |  | 
|---|
| 369 | if (cur_frame != jmp->Registration) | 
|---|
| 370 | _global_unwind2((PEXCEPTION_FRAME)jmp->Registration); | 
|---|
| 371 |  | 
|---|
| 372 | if (jmp->Registration) | 
|---|
| 373 | { | 
|---|
| 374 | if (!IsBadReadPtr(&jmp->Cookie, sizeof(long)) && | 
|---|
| 375 | jmp->Cookie == MSVCRT_JMP_MAGIC && jmp->UnwindFunc) | 
|---|
| 376 | { | 
|---|
| 377 | MSVCRT_unwind_function unwind_func; | 
|---|
| 378 |  | 
|---|
| 379 | unwind_func=(MSVCRT_unwind_function)jmp->UnwindFunc; | 
|---|
| 380 | unwind_func(jmp); | 
|---|
| 381 | } | 
|---|
| 382 | else | 
|---|
| 383 | _local_unwind2((MSVCRT_EXCEPTION_FRAME*)jmp->Registration, | 
|---|
| 384 | jmp->TryLevel); | 
|---|
| 385 | } | 
|---|
| 386 |  | 
|---|
| 387 | if (!retval) | 
|---|
| 388 | retval = 1; | 
|---|
| 389 |  | 
|---|
| 390 | TRACE("Jump to %lx returning %d\n",jmp->Eip,retval); | 
|---|
| 391 | context->Ebp = jmp->Ebp; | 
|---|
| 392 | context->Ebx = jmp->Ebx; | 
|---|
| 393 | context->Edi = jmp->Edi; | 
|---|
| 394 | context->Esi = jmp->Esi; | 
|---|
| 395 | context->Esp = jmp->Esp; | 
|---|
| 396 | context->Eip = jmp->Eip; | 
|---|
| 397 | context->Eax = retval; | 
|---|
| 398 | } | 
|---|
| 399 |  | 
|---|
| 400 | /********************************************************************* | 
|---|
| 401 | *              _seh_longjmp_unwind (MSVCRT.@) | 
|---|
| 402 | */ | 
|---|
| 403 | void __stdcall _seh_longjmp_unwind(_JUMP_BUFFER *jmp) | 
|---|
| 404 | { | 
|---|
| 405 | _local_unwind2( (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel ); | 
|---|
| 406 | } | 
|---|
| 407 | #endif /* i386 */ | 
|---|
| 408 |  | 
|---|
| 409 |  | 
|---|
| 410 | /********************************************************************* | 
|---|
| 411 | *              signal (MSVCRT.@) | 
|---|
| 412 | */ | 
|---|
| 413 | void* MSVCRT_signal(int sig, MSVCRT_sig_handler_func func) | 
|---|
| 414 | { | 
|---|
| 415 | dprintf(("MSVCRT: signal(%d %p): NOT COMPLETE\n", sig, func)); | 
|---|
| 416 | return SIG_IGN_W; | 
|---|
| 417 | } | 
|---|