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