dbghelp: Implement SymEnumSourceFilesW.
[wine] / dlls / krnl386.exe16 / relay.c
1 /*
2  * Copyright 1993 Robert J. Amstadt
3  * Copyright 1995 Alexandre Julliard
4  * Copyright 2002 Jukka Heinonen
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wine/winbase16.h"
33 #include "winternl.h"
34 #include "kernel16_private.h"
35 #include "dosexe.h"
36 #include "wine/unicode.h"
37 #include "wine/library.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(relay);
41
42 /*
43  * Magic DWORD used to check stack integrity.
44  */
45 #define RELAY_MAGIC 0xabcdef00
46
47 /*
48  * Memory block for temporary 16-bit stacks used with relay calls.
49  */
50 typedef struct {
51     DWORD inuse;          /* non-zero if stack block is in use */
52     DWORD eip;            /* saved ip */
53     DWORD seg_cs;         /* saved cs */
54     DWORD esp;            /* saved sp */
55     DWORD seg_ss;         /* saved ss */
56     DWORD stack_bottom;   /* guard dword */
57     BYTE  stack[256-7*4]; /* 16-bit stack */
58     DWORD stack_top;      /* guard dword */
59 } RELAY_Stack16;
60
61
62 static const WCHAR **debug_relay_excludelist;
63 static const WCHAR **debug_relay_includelist;
64 static const WCHAR **debug_snoop_excludelist;
65 static const WCHAR **debug_snoop_includelist;
66
67 /* compare an ASCII and a Unicode string without depending on the current codepage */
68 static inline int strcmpiAW( const char *strA, const WCHAR *strW )
69 {
70     while (*strA && (toupperW((unsigned char)*strA) == toupperW(*strW))) { strA++; strW++; }
71     return toupperW((unsigned char)*strA) - toupperW(*strW);
72 }
73
74 /* compare an ASCII and a Unicode string without depending on the current codepage */
75 static inline int strncmpiAW( const char *strA, const WCHAR *strW, int n )
76 {
77     int ret = 0;
78     for ( ; n > 0; n--, strA++, strW++)
79         if ((ret = toupperW((unsigned char)*strA) - toupperW(*strW)) || !*strA) break;
80     return ret;
81 }
82
83 /***********************************************************************
84  *           build_list
85  *
86  * Build a function list from a ';'-separated string.
87  */
88 static const WCHAR **build_list( const WCHAR *buffer )
89 {
90     int count = 1;
91     const WCHAR *p = buffer;
92     const WCHAR **ret;
93
94     while ((p = strchrW( p, ';' )))
95     {
96         count++;
97         p++;
98     }
99     /* allocate count+1 pointers, plus the space for a copy of the string */
100     if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
101                                 (count+1) * sizeof(WCHAR*) + (strlenW(buffer)+1) * sizeof(WCHAR) )))
102     {
103         WCHAR *str = (WCHAR *)(ret + count + 1);
104         WCHAR *p = str;
105
106         strcpyW( str, buffer );
107         count = 0;
108         for (;;)
109         {
110             ret[count++] = p;
111             if (!(p = strchrW( p, ';' ))) break;
112             *p++ = 0;
113         }
114         ret[count++] = NULL;
115     }
116     return ret;
117 }
118
119
120 /***********************************************************************
121  *           RELAY16_InitDebugLists
122  *
123  * Build the relay include/exclude function lists.
124  */
125 void RELAY16_InitDebugLists(void)
126 {
127     OBJECT_ATTRIBUTES attr;
128     UNICODE_STRING name;
129     char buffer[1024];
130     HANDLE root, hkey;
131     DWORD count;
132     WCHAR *str;
133     static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
134                                     'W','i','n','e','\\',
135                                     'D','e','b','u','g',0};
136     static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
137     static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
138     static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
139     static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
140
141     RtlOpenCurrentUser( KEY_READ, &root );
142     attr.Length = sizeof(attr);
143     attr.RootDirectory = root;
144     attr.ObjectName = &name;
145     attr.Attributes = 0;
146     attr.SecurityDescriptor = NULL;
147     attr.SecurityQualityOfService = NULL;
148     RtlInitUnicodeString( &name, configW );
149
150     /* @@ Wine registry key: HKCU\Software\Wine\Debug */
151     if (NtOpenKey( &hkey, KEY_READ, &attr )) hkey = 0;
152     NtClose( root );
153     if (!hkey) return;
154
155     str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
156     RtlInitUnicodeString( &name, RelayIncludeW );
157     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
158     {
159         debug_relay_includelist = build_list( str );
160     }
161
162     RtlInitUnicodeString( &name, RelayExcludeW );
163     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
164     {
165         debug_relay_excludelist = build_list( str );
166     }
167
168     RtlInitUnicodeString( &name, SnoopIncludeW );
169     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
170     {
171         debug_snoop_includelist = build_list( str );
172     }
173
174     RtlInitUnicodeString( &name, SnoopExcludeW );
175     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
176     {
177         debug_snoop_excludelist = build_list( str );
178     }
179     NtClose( hkey );
180 }
181
182
183 /***********************************************************************
184  *           check_list
185  *
186  * Check if a given module and function is in the list.
187  */
188 static BOOL check_list( const char *module, int ordinal, const char *func, const WCHAR **list )
189 {
190     char ord_str[10];
191
192     sprintf( ord_str, "%d", ordinal );
193     for(; *list; list++)
194     {
195         const WCHAR *p = strrchrW( *list, '.' );
196         if (p && p > *list)  /* check module and function */
197         {
198             int len = p - *list;
199             if (strncmpiAW( module, *list, len-1 ) || module[len]) continue;
200             if (p[1] == '*' && !p[2]) return TRUE;
201             if (!strcmpiAW( ord_str, p + 1 )) return TRUE;
202             if (func && !strcmpiAW( func, p + 1 )) return TRUE;
203         }
204         else  /* function only */
205         {
206             if (func && !strcmpiAW( func, *list )) return TRUE;
207         }
208     }
209     return FALSE;
210 }
211
212
213 /***********************************************************************
214  *           RELAY_ShowDebugmsgRelay
215  *
216  * Simple function to decide if a particular debugging message is
217  * wanted.
218  */
219 static BOOL RELAY_ShowDebugmsgRelay(const char *module, int ordinal, const char *func)
220 {
221     if (debug_relay_excludelist && check_list( module, ordinal, func, debug_relay_excludelist ))
222         return FALSE;
223     if (debug_relay_includelist && !check_list( module, ordinal, func, debug_relay_includelist ))
224         return FALSE;
225     return TRUE;
226 }
227
228
229 /***********************************************************************
230  *          SNOOP16_ShowDebugmsgSnoop
231  *
232  * Simple function to decide if a particular debugging message is
233  * wanted.
234  */
235 int SNOOP16_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
236 {
237     if (debug_snoop_excludelist && check_list( module, ordinal, func, debug_snoop_excludelist ))
238         return FALSE;
239     if (debug_snoop_includelist && !check_list( module, ordinal, func, debug_snoop_includelist ))
240         return FALSE;
241     return TRUE;
242 }
243
244
245 /***********************************************************************
246  *           get_entry_point
247  *
248  * Return the ordinal, name, and type info corresponding to a CS:IP address.
249  */
250 static const CALLFROM16 *get_entry_point( STACK16FRAME *frame, LPSTR module, LPSTR func, WORD *pOrd )
251 {
252     WORD i, max_offset;
253     register BYTE *p;
254     NE_MODULE *pModule;
255     ET_BUNDLE *bundle;
256     ET_ENTRY *entry;
257
258     *pOrd = 0;
259     if (!(pModule = NE_GetPtr( FarGetOwner16( GlobalHandle16( frame->module_cs ) ))))
260         return NULL;
261
262     max_offset = 0;
263     bundle = (ET_BUNDLE *)((BYTE *)pModule + pModule->ne_enttab);
264     do
265     {
266         entry = (ET_ENTRY *)((BYTE *)bundle+6);
267         for (i = bundle->first + 1; i <= bundle->last; i++)
268         {
269             if ((entry->offs < frame->entry_ip)
270             && (entry->segnum == 1) /* code segment ? */
271             && (entry->offs >= max_offset))
272             {
273                 max_offset = entry->offs;
274                 *pOrd = i;
275             }
276             entry++;
277         }
278     } while ( (bundle->next)
279            && (bundle = (ET_BUNDLE *)((BYTE *)pModule+bundle->next)));
280
281     /* Search for the name in the resident names table */
282     /* (built-in modules have no non-resident table)   */
283
284     p = (BYTE *)pModule + pModule->ne_restab;
285     memcpy( module, p + 1, *p );
286     module[*p] = 0;
287
288     while (*p)
289     {
290         p += *p + 1 + sizeof(WORD);
291         if (*(WORD *)(p + *p + 1) == *pOrd) break;
292     }
293     memcpy( func, p + 1, *p );
294     func[*p] = 0;
295
296     /* Retrieve entry point call structure */
297     p = MapSL( MAKESEGPTR( frame->module_cs, frame->callfrom_ip ) );
298     /* p now points to lret, get the start of CALLFROM16 structure */
299     return (CALLFROM16 *)(p - FIELD_OFFSET( CALLFROM16, ret ));
300 }
301
302
303 extern int call_entry_point( void *func, int nb_args, const int *args );
304 __ASM_GLOBAL_FUNC( call_entry_point,
305                    "pushl %ebp\n\t"
306                    __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
307                    __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
308                    "movl %esp,%ebp\n\t"
309                    __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
310                    "pushl %esi\n\t"
311                    __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
312                    "pushl %edi\n\t"
313                    __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
314                    "movl 12(%ebp),%edx\n\t"
315                    "shll $2,%edx\n\t"
316                    "jz 1f\n\t"
317                    "subl %edx,%esp\n\t"
318                    "andl $~15,%esp\n\t"
319                    "movl 12(%ebp),%ecx\n\t"
320                    "movl 16(%ebp),%esi\n\t"
321                    "movl %esp,%edi\n\t"
322                    "cld\n\t"
323                    "rep; movsl\n"
324                    "1:\tcall *8(%ebp)\n\t"
325                    "leal -8(%ebp),%esp\n\t"
326                    "popl %edi\n\t"
327                    __ASM_CFI(".cfi_same_value %edi\n\t")
328                    "popl %esi\n\t"
329                    __ASM_CFI(".cfi_same_value %esi\n\t")
330                    "popl %ebp\n\t"
331                    __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
332                    __ASM_CFI(".cfi_same_value %ebp\n\t")
333                    "ret" )
334
335
336 /***********************************************************************
337  *           relay_call_from_16_no_debug
338  *
339  * Same as relay_call_from_16 but doesn't print any debug information.
340  */
341 static int relay_call_from_16_no_debug( void *entry_point, unsigned char *args16, CONTEXT *context,
342                                         const CALLFROM16 *call )
343 {
344     unsigned int i, j, nb_args = 0;
345     int args32[20];
346
347     /* look for the ret instruction */
348     for (j = 0; j < sizeof(call->ret)/sizeof(call->ret[0]); j++)
349         if (call->ret[j] == 0xca66 || call->ret[j] == 0xcb66) break;
350
351     if (call->ret[j] == 0xcb66)  /* cdecl */
352     {
353         for (i = 0; i < 20; i++, nb_args++)
354         {
355             int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
356
357             if (type == ARG_NONE) break;
358             switch(type)
359             {
360             case ARG_WORD:
361                 args32[nb_args] = *(WORD *)args16;
362                 args16 += sizeof(WORD);
363                 break;
364             case ARG_SWORD:
365                 args32[nb_args] = *(short *)args16;
366                 args16 += sizeof(WORD);
367                 break;
368             case ARG_LONG:
369             case ARG_SEGSTR:
370                 args32[nb_args] = *(int *)args16;
371                 args16 += sizeof(int);
372                 break;
373             case ARG_PTR:
374             case ARG_STR:
375                 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
376                 args16 += sizeof(SEGPTR);
377                 break;
378             case ARG_VARARG:
379                 args32[nb_args] = (int)args16;
380                 break;
381             default:
382                 break;
383             }
384         }
385     }
386     else  /* not cdecl */
387     {
388         /* Start with the last arg */
389         args16 += call->ret[j + 1];
390         for (i = 0; i < 20; i++, nb_args++)
391         {
392             int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
393
394             if (type == ARG_NONE) break;
395             switch(type)
396             {
397             case ARG_WORD:
398                 args16 -= sizeof(WORD);
399                 args32[nb_args] = *(WORD *)args16;
400                 break;
401             case ARG_SWORD:
402                 args16 -= sizeof(WORD);
403                 args32[nb_args] = *(short *)args16;
404                 break;
405             case ARG_LONG:
406             case ARG_SEGSTR:
407                 args16 -= sizeof(int);
408                 args32[nb_args] = *(int *)args16;
409                 break;
410             case ARG_PTR:
411             case ARG_STR:
412                 args16 -= sizeof(SEGPTR);
413                 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
414                 break;
415             default:
416                 break;
417             }
418         }
419     }
420
421     if (!j)  /* register function */
422         args32[nb_args++] = (int)context;
423
424     SYSLEVEL_CheckNotLevel( 2 );
425
426     return call_entry_point( entry_point, nb_args, args32 );
427 }
428
429
430 /***********************************************************************
431  *           relay_call_from_16
432  *
433  * Replacement for the 16-bit relay functions when relay debugging is on.
434  */
435 int relay_call_from_16( void *entry_point, unsigned char *args16, CONTEXT *context )
436 {
437     STACK16FRAME *frame;
438     WORD ordinal;
439     unsigned int i, j, nb_args = 0;
440     int ret_val, args32[20];
441     char module[10], func[64];
442     const CALLFROM16 *call;
443
444     frame = CURRENT_STACK16;
445     call = get_entry_point( frame, module, func, &ordinal );
446     if (!TRACE_ON(relay) || !RELAY_ShowDebugmsgRelay( module, ordinal, func ))
447         return relay_call_from_16_no_debug( entry_point, args16, context, call );
448
449     DPRINTF( "%04x:Call %s.%d: %s(",GetCurrentThreadId(), module, ordinal, func );
450
451     /* look for the ret instruction */
452     for (j = 0; j < sizeof(call->ret)/sizeof(call->ret[0]); j++)
453         if (call->ret[j] == 0xca66 || call->ret[j] == 0xcb66) break;
454
455     if (call->ret[j] == 0xcb66)  /* cdecl */
456     {
457         for (i = 0; i < 20; i++, nb_args++)
458         {
459             int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
460
461             if (type == ARG_NONE) break;
462             if (i) DPRINTF( "," );
463             switch(type)
464             {
465             case ARG_WORD:
466                 DPRINTF( "%04x", *(WORD *)args16 );
467                 args32[nb_args] = *(WORD *)args16;
468                 args16 += sizeof(WORD);
469                 break;
470             case ARG_SWORD:
471                 DPRINTF( "%04x", *(WORD *)args16 );
472                 args32[nb_args] = *(short *)args16;
473                 args16 += sizeof(WORD);
474                 break;
475             case ARG_LONG:
476                 DPRINTF( "%08x", *(int *)args16 );
477                 args32[nb_args] = *(int *)args16;
478                 args16 += sizeof(int);
479                 break;
480             case ARG_PTR:
481                 DPRINTF( "%04x:%04x", *(WORD *)(args16+2), *(WORD *)args16 );
482                 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
483                 args16 += sizeof(SEGPTR);
484                 break;
485             case ARG_STR:
486                 DPRINTF( "%08x %s", *(int *)args16,
487                          debugstr_a( MapSL(*(SEGPTR *)args16 )));
488                 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
489                 args16 += sizeof(int);
490                 break;
491             case ARG_SEGSTR:
492                 DPRINTF( "%04x:%04x %s", *(WORD *)(args16+2), *(WORD *)args16,
493                          debugstr_a( MapSL(*(SEGPTR *)args16 )) );
494                 args32[nb_args] = *(SEGPTR *)args16;
495                 args16 += sizeof(SEGPTR);
496                 break;
497             case ARG_VARARG:
498                 DPRINTF( "..." );
499                 args32[nb_args] = (int)args16;
500                 break;
501             default:
502                 break;
503             }
504         }
505     }
506     else  /* not cdecl */
507     {
508         /* Start with the last arg */
509         args16 += call->ret[j + 1];
510         for (i = 0; i < 20; i++, nb_args++)
511         {
512             int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
513
514             if (type == ARG_NONE) break;
515             if (i) DPRINTF( "," );
516             switch(type)
517             {
518             case ARG_WORD:
519                 args16 -= sizeof(WORD);
520                 args32[nb_args] = *(WORD *)args16;
521                 DPRINTF( "%04x", *(WORD *)args16 );
522                 break;
523             case ARG_SWORD:
524                 args16 -= sizeof(WORD);
525                 args32[nb_args] = *(short *)args16;
526                 DPRINTF( "%04x", *(WORD *)args16 );
527                 break;
528             case ARG_LONG:
529                 args16 -= sizeof(int);
530                 args32[nb_args] = *(int *)args16;
531                 DPRINTF( "%08x", *(int *)args16 );
532                 break;
533             case ARG_PTR:
534                 args16 -= sizeof(SEGPTR);
535                 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
536                 DPRINTF( "%04x:%04x", *(WORD *)(args16+2), *(WORD *)args16 );
537                 break;
538             case ARG_STR:
539                 args16 -= sizeof(int);
540                 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
541                 DPRINTF( "%08x %s", *(int *)args16,
542                          debugstr_a( MapSL(*(SEGPTR *)args16 )));
543                 break;
544             case ARG_SEGSTR:
545                 args16 -= sizeof(SEGPTR);
546                 args32[nb_args] = *(SEGPTR *)args16;
547                 DPRINTF( "%04x:%04x %s", *(WORD *)(args16+2), *(WORD *)args16,
548                          debugstr_a( MapSL(*(SEGPTR *)args16 )) );
549                 break;
550             case ARG_VARARG:
551                 DPRINTF( "..." );
552                 args32[nb_args] = (int)args16;
553                 break;
554             default:
555                 break;
556             }
557         }
558     }
559
560     DPRINTF( ") ret=%04x:%04x ds=%04x\n", frame->cs, frame->ip, frame->ds );
561
562     if (!j)  /* register function */
563     {
564         args32[nb_args++] = (int)context;
565         DPRINTF("     AX=%04x BX=%04x CX=%04x DX=%04x SI=%04x DI=%04x ES=%04x EFL=%08x\n",
566                 (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
567                 (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
568                 (WORD)context->SegEs, context->EFlags );
569     }
570
571     SYSLEVEL_CheckNotLevel( 2 );
572
573     ret_val = call_entry_point( entry_point, nb_args, args32 );
574
575     SYSLEVEL_CheckNotLevel( 2 );
576
577     DPRINTF( "%04x:Ret  %s.%d: %s() ",GetCurrentThreadId(), module, ordinal, func );
578     if (!j)  /* register function */
579     {
580         DPRINTF("retval=none ret=%04x:%04x ds=%04x\n",
581                 (WORD)context->SegCs, LOWORD(context->Eip), (WORD)context->SegDs);
582         DPRINTF("     AX=%04x BX=%04x CX=%04x DX=%04x SI=%04x DI=%04x ES=%04x EFL=%08x\n",
583                 (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
584                 (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
585                 (WORD)context->SegEs, context->EFlags );
586     }
587     else
588     {
589         frame = CURRENT_STACK16;  /* might have be changed by the entry point */
590         if (j == 1)  /* 16-bit return sequence */
591             DPRINTF( "retval=%04x ret=%04x:%04x ds=%04x\n",
592                      ret_val & 0xffff, frame->cs, frame->ip, frame->ds );
593         else
594             DPRINTF( "retval=%08x ret=%04x:%04x ds=%04x\n",
595                      ret_val, frame->cs, frame->ip, frame->ds );
596     }
597     return ret_val;
598 }
599
600 /**********************************************************************
601  *          RELAY_GetPointer
602  *
603  * Get pointer to stack block when given esp pointing to 16-bit stack
604  * inside relay data segment.
605  */
606 static RELAY_Stack16 *RELAY_GetPointer( DWORD offset )
607 {
608     offset = offset / sizeof(RELAY_Stack16) * sizeof(RELAY_Stack16);
609     return MapSL(MAKESEGPTR(DOSVM_dpmi_segments->relay_data_sel, offset));
610 }
611
612
613 /**********************************************************************
614  *          RELAY_MakeShortContext
615  *
616  * Allocate separate 16-bit stack, make stack pointer point to this
617  * stack and make code pointer point to stub that restores everything.
618  * So, after this routine, SS and CS are guaranteed to be 16-bit.
619  *
620  * Note: This might be called from signal handler, so the stack
621  *       allocation algorithm must be signal safe.
622  */
623 static void RELAY_MakeShortContext( CONTEXT *context )
624 {
625     DWORD offset = offsetof(RELAY_Stack16, stack_top);
626     RELAY_Stack16 *stack = RELAY_GetPointer( 0 );
627
628     while (stack->inuse && offset < DOSVM_RELAY_DATA_SIZE) {
629         stack++;
630         offset += sizeof(RELAY_Stack16);
631     }
632
633     if (offset >= DOSVM_RELAY_DATA_SIZE)
634         ERR( "Too many nested interrupts!\n" );
635
636     stack->inuse = 1;
637     stack->eip = context->Eip;
638     stack->seg_cs = context->SegCs;
639     stack->esp = context->Esp;
640     stack->seg_ss = context->SegSs;
641
642     stack->stack_bottom = RELAY_MAGIC;
643     stack->stack_top = RELAY_MAGIC;
644
645     context->SegSs = DOSVM_dpmi_segments->relay_data_sel;
646     context->Esp = offset;
647     context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
648     context->Eip = 3;
649 }
650
651
652 /**********************************************************************
653  *          RELAY_RelayStub
654  *
655  * This stub is called by __wine_call_from_16_regs in order to marshall
656  * relay parameters.
657  */
658 static void __stdcall RELAY_RelayStub( DOSRELAY proc, unsigned char *args, CONTEXT *context )
659 {
660     if (proc)
661     {
662         RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
663
664         DWORD old_seg_cs = context->SegCs;
665         DWORD old_eip    = context->Eip;
666         DWORD old_seg_ss = context->SegSs;
667         DWORD old_esp    = context->Esp;
668
669         context->SegCs = stack->seg_cs;
670         context->Eip   = stack->eip;
671         context->SegSs = stack->seg_ss;
672         context->Esp   = stack->esp;
673
674         proc( context, *(LPVOID *)args );
675
676         stack->seg_cs = context->SegCs;
677         stack->eip    = context->Eip;
678         stack->seg_ss = context->SegSs;
679         stack->esp    = context->Esp;
680
681         context->SegCs = old_seg_cs;
682         context->Eip   = old_eip;
683         context->SegSs = old_seg_ss;
684         context->Esp   = old_esp;
685     }
686 }
687
688
689 /**********************************************************************
690  *          DOSVM_RelayHandler
691  *
692  * Restore saved code and stack pointers and release stack block.
693  */
694 void DOSVM_RelayHandler( CONTEXT *context )
695 {
696     RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
697
698     context->SegSs = stack->seg_ss;
699     context->Esp = stack->esp;
700     context->SegCs = stack->seg_cs;
701     context->Eip = stack->eip;
702
703     if (!stack->inuse ||
704         stack->stack_bottom != RELAY_MAGIC ||
705         stack->stack_top != RELAY_MAGIC)
706         ERR( "Stack corrupted!\n" );
707
708     stack->inuse = 0;
709 }
710
711
712 /**********************************************************************
713  *          DOSVM_BuildCallFrame
714  *
715  * Modifies the context so that return to context calls DOSRELAY and
716  * only after return from DOSRELAY the original context will be returned to.
717  */
718 void DOSVM_BuildCallFrame( CONTEXT *context, DOSRELAY relay, LPVOID data )
719 {
720     WORD  code_sel = DOSVM_dpmi_segments->relay_code_sel;
721
722     /*
723      * Allocate separate stack for relay call.
724      */
725     RELAY_MakeShortContext( context );
726
727     /*
728      * Build call frame.
729      */
730     PUSH_WORD16( context, HIWORD(data) );            /* argument.hiword */
731     PUSH_WORD16( context, LOWORD(data) );            /* argument.loword */
732     PUSH_WORD16( context, context->SegCs );          /* STACK16FRAME.cs */
733     PUSH_WORD16( context, LOWORD(context->Eip) );    /* STACK16FRAME.ip */
734     PUSH_WORD16( context, LOWORD(context->Ebp) );    /* STACK16FRAME.bp */
735     PUSH_WORD16( context, HIWORD(relay) );           /* STACK16FRAME.entry_point.hiword */
736     PUSH_WORD16( context, LOWORD(relay) );           /* STACK16FRAME.entry_point.loword */
737     PUSH_WORD16( context, 0 );                       /* STACK16FRAME.entry_ip */
738     PUSH_WORD16( context, HIWORD(RELAY_RelayStub) ); /* STACK16FRAME.relay.hiword */
739     PUSH_WORD16( context, LOWORD(RELAY_RelayStub) ); /* STACK16FRAME.relay.loword */
740     PUSH_WORD16( context, 0 );                       /* STACK16FRAME.module_cs.hiword */
741     PUSH_WORD16( context, code_sel );                /* STACK16FRAME.module_cs.loword */
742     PUSH_WORD16( context, 0 );                       /* STACK16FRAME.callfrom_ip.hiword */
743     PUSH_WORD16( context, 0 );                       /* STACK16FRAME.callfrom_ip.loword */
744
745     /*
746      * Adjust code pointer.
747      */
748     context->SegCs = wine_get_cs();
749     context->Eip = (DWORD)__wine_call_from_16_regs;
750 }