2 * Copyright 1993 Robert J. Amstadt
3 * Copyright 1995 Alexandre Julliard
4 * Copyright 2002 Jukka Heinonen
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.
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.
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
29 #include "wine/winbase16.h"
31 #include "kernel16_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(relay
);
38 * Magic DWORD used to check stack integrity.
40 #define RELAY_MAGIC 0xabcdef00
43 * Memory block for temporary 16-bit stacks used with relay calls.
46 DWORD inuse
; /* non-zero if stack block is in use */
47 DWORD eip
; /* saved ip */
48 DWORD seg_cs
; /* saved cs */
49 DWORD esp
; /* saved sp */
50 DWORD seg_ss
; /* saved ss */
51 DWORD stack_bottom
; /* guard dword */
52 BYTE stack
[256-7*4]; /* 16-bit stack */
53 DWORD stack_top
; /* guard dword */
57 static const char **debug_relay_excludelist
;
58 static const char **debug_relay_includelist
;
59 static const char **debug_snoop_excludelist
;
60 static const char **debug_snoop_includelist
;
63 /***********************************************************************
66 * Build a function list from a ';'-separated string.
68 static const char **build_list( const WCHAR
*buffer
)
71 const WCHAR
*p
= buffer
;
74 while ((p
= wcschr( p
, ';' )))
79 /* allocate count+1 pointers, plus the space for a copy of the string */
80 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0,
81 (count
+ 1) * sizeof(char *) + (lstrlenW(buffer
) + 1) )))
83 char *str
= (char *)(ret
+ count
+ 1);
86 while ((*str
++ = *buffer
++));
91 if (!(p
= strchr( p
, ';' ))) break;
100 /***********************************************************************
101 * RELAY16_InitDebugLists
103 * Build the relay include/exclude function lists.
105 void RELAY16_InitDebugLists(void)
107 OBJECT_ATTRIBUTES attr
;
113 static const WCHAR configW
[] = {'S','o','f','t','w','a','r','e','\\',
114 'W','i','n','e','\\',
115 'D','e','b','u','g',0};
116 static const WCHAR RelayIncludeW
[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
117 static const WCHAR RelayExcludeW
[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
118 static const WCHAR SnoopIncludeW
[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
119 static const WCHAR SnoopExcludeW
[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
121 RtlOpenCurrentUser( KEY_READ
, &root
);
122 attr
.Length
= sizeof(attr
);
123 attr
.RootDirectory
= root
;
124 attr
.ObjectName
= &name
;
126 attr
.SecurityDescriptor
= NULL
;
127 attr
.SecurityQualityOfService
= NULL
;
128 RtlInitUnicodeString( &name
, configW
);
130 /* @@ Wine registry key: HKCU\Software\Wine\Debug */
131 if (NtOpenKey( &hkey
, KEY_READ
, &attr
)) hkey
= 0;
135 str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)buffer
)->Data
;
136 RtlInitUnicodeString( &name
, RelayIncludeW
);
137 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
139 debug_relay_includelist
= build_list( str
);
142 RtlInitUnicodeString( &name
, RelayExcludeW
);
143 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
145 debug_relay_excludelist
= build_list( str
);
148 RtlInitUnicodeString( &name
, SnoopIncludeW
);
149 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
151 debug_snoop_includelist
= build_list( str
);
154 RtlInitUnicodeString( &name
, SnoopExcludeW
);
155 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
157 debug_snoop_excludelist
= build_list( str
);
163 /***********************************************************************
166 * Check if a given module and function is in the list.
168 static BOOL
check_list( const char *module
, int ordinal
, const char *func
, const char **list
)
172 sprintf( ord_str
, "%d", ordinal
);
175 const char *p
= strrchr( *list
, '.' );
176 if (p
&& p
> *list
) /* check module and function */
179 if (_strnicmp( module
, *list
, len
-1 ) || module
[len
]) continue;
180 if (p
[1] == '*' && !p
[2]) return TRUE
;
181 if (!strcmp( ord_str
, p
+ 1 )) return TRUE
;
182 if (func
&& !stricmp( func
, p
+ 1 )) return TRUE
;
184 else /* function only */
186 if (func
&& !stricmp( func
, *list
)) return TRUE
;
193 /***********************************************************************
194 * RELAY_ShowDebugmsgRelay
196 * Simple function to decide if a particular debugging message is
199 static BOOL
RELAY_ShowDebugmsgRelay(const char *module
, int ordinal
, const char *func
)
201 if (debug_relay_excludelist
&& check_list( module
, ordinal
, func
, debug_relay_excludelist
))
203 if (debug_relay_includelist
&& !check_list( module
, ordinal
, func
, debug_relay_includelist
))
209 /***********************************************************************
210 * SNOOP16_ShowDebugmsgSnoop
212 * Simple function to decide if a particular debugging message is
215 BOOL
SNOOP16_ShowDebugmsgSnoop(const char *module
, int ordinal
, const char *func
)
217 if (debug_snoop_excludelist
&& check_list( module
, ordinal
, func
, debug_snoop_excludelist
))
219 if (debug_snoop_includelist
&& !check_list( module
, ordinal
, func
, debug_snoop_includelist
))
225 /***********************************************************************
228 * Return the ordinal, name, and type info corresponding to a CS:IP address.
230 static const CALLFROM16
*get_entry_point( STACK16FRAME
*frame
, LPSTR module
, LPSTR func
, WORD
*pOrd
)
239 if (!(pModule
= NE_GetPtr( FarGetOwner16( GlobalHandle16( frame
->module_cs
) ))))
243 bundle
= (ET_BUNDLE
*)((BYTE
*)pModule
+ pModule
->ne_enttab
);
246 entry
= (ET_ENTRY
*)((BYTE
*)bundle
+6);
247 for (i
= bundle
->first
+ 1; i
<= bundle
->last
; i
++)
249 if ((entry
->offs
< frame
->entry_ip
)
250 && (entry
->segnum
== 1) /* code segment ? */
251 && (entry
->offs
>= max_offset
))
253 max_offset
= entry
->offs
;
258 } while ( (bundle
->next
)
259 && (bundle
= (ET_BUNDLE
*)((BYTE
*)pModule
+bundle
->next
)));
261 /* Search for the name in the resident names table */
262 /* (built-in modules have no non-resident table) */
264 p
= (BYTE
*)pModule
+ pModule
->ne_restab
;
265 memcpy( module
, p
+ 1, *p
);
270 p
+= *p
+ 1 + sizeof(WORD
);
271 if (*(WORD
*)(p
+ *p
+ 1) == *pOrd
) break;
273 memcpy( func
, p
+ 1, *p
);
276 /* Retrieve entry point call structure */
277 p
= MapSL( MAKESEGPTR( frame
->module_cs
, frame
->callfrom_ip
) );
278 /* p now points to lret, get the start of CALLFROM16 structure */
279 return (CALLFROM16
*)(p
- FIELD_OFFSET( CALLFROM16
, ret
));
283 extern int call_entry_point( void *func
, int nb_args
, const int *args
);
284 __ASM_GLOBAL_FUNC( call_entry_point
,
286 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
287 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
289 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
291 __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
293 __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
294 "movl 12(%ebp),%edx\n\t"
299 "movl 12(%ebp),%ecx\n\t"
300 "movl 16(%ebp),%esi\n\t"
304 "1:\tcall *8(%ebp)\n\t"
305 "leal -8(%ebp),%esp\n\t"
307 __ASM_CFI(".cfi_same_value %edi\n\t")
309 __ASM_CFI(".cfi_same_value %esi\n\t")
311 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
312 __ASM_CFI(".cfi_same_value %ebp\n\t")
316 /***********************************************************************
317 * relay_call_from_16_no_debug
319 * Same as relay_call_from_16 but doesn't print any debug information.
321 static int relay_call_from_16_no_debug( void *entry_point
, unsigned char *args16
, CONTEXT
*context
,
322 const CALLFROM16
*call
)
324 unsigned int i
, j
, nb_args
= 0;
327 /* look for the ret instruction */
328 for (j
= 0; j
< ARRAY_SIZE(call
->ret
); j
++)
329 if (call
->ret
[j
] == 0xca66 || call
->ret
[j
] == 0xcb66) break;
331 if (call
->ret
[j
] == 0xcb66) /* cdecl */
333 for (i
= 0; i
< 20; i
++, nb_args
++)
335 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
337 if (type
== ARG_NONE
) break;
341 args32
[nb_args
] = *(WORD
*)args16
;
342 args16
+= sizeof(WORD
);
345 args32
[nb_args
] = *(short *)args16
;
346 args16
+= sizeof(WORD
);
350 args32
[nb_args
] = *(int *)args16
;
351 args16
+= sizeof(int);
355 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
356 args16
+= sizeof(SEGPTR
);
359 args32
[nb_args
] = (int)args16
;
368 /* Start with the last arg */
369 args16
+= call
->ret
[j
+ 1];
370 for (i
= 0; i
< 20; i
++, nb_args
++)
372 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
374 if (type
== ARG_NONE
) break;
378 args16
-= sizeof(WORD
);
379 args32
[nb_args
] = *(WORD
*)args16
;
382 args16
-= sizeof(WORD
);
383 args32
[nb_args
] = *(short *)args16
;
387 args16
-= sizeof(int);
388 args32
[nb_args
] = *(int *)args16
;
392 args16
-= sizeof(SEGPTR
);
393 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
401 if (!j
) /* register function */
402 args32
[nb_args
++] = (int)context
;
404 SYSLEVEL_CheckNotLevel( 2 );
406 return call_entry_point( entry_point
, nb_args
, args32
);
410 /***********************************************************************
413 * Replacement for the 16-bit relay functions when relay debugging is on.
415 int relay_call_from_16( void *entry_point
, unsigned char *args16
, CONTEXT
*context
)
419 unsigned int i
, j
, nb_args
= 0;
420 int ret_val
, args32
[20];
421 char module
[10], func
[64];
422 const CALLFROM16
*call
;
424 frame
= CURRENT_STACK16
;
425 call
= get_entry_point( frame
, module
, func
, &ordinal
);
426 if (!TRACE_ON(relay
) || !RELAY_ShowDebugmsgRelay( module
, ordinal
, func
))
427 return relay_call_from_16_no_debug( entry_point
, args16
, context
, call
);
429 TRACE( "\1Call %s.%d: %s(", module
, ordinal
, func
);
431 /* look for the ret instruction */
432 for (j
= 0; j
< ARRAY_SIZE(call
->ret
); j
++)
433 if (call
->ret
[j
] == 0xca66 || call
->ret
[j
] == 0xcb66) break;
435 if (call
->ret
[j
] == 0xcb66) /* cdecl */
437 for (i
= 0; i
< 20; i
++, nb_args
++)
439 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
441 if (type
== ARG_NONE
) break;
446 TRACE( "%04x", *(WORD
*)args16
);
447 args32
[nb_args
] = *(WORD
*)args16
;
448 args16
+= sizeof(WORD
);
451 TRACE( "%04x", *(WORD
*)args16
);
452 args32
[nb_args
] = *(short *)args16
;
453 args16
+= sizeof(WORD
);
456 TRACE( "%08x", *(int *)args16
);
457 args32
[nb_args
] = *(int *)args16
;
458 args16
+= sizeof(int);
461 TRACE( "%04x:%04x", *(WORD
*)(args16
+2), *(WORD
*)args16
);
462 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
463 args16
+= sizeof(SEGPTR
);
466 TRACE( "%08x %s", *(int *)args16
, debugstr_a( MapSL(*(SEGPTR
*)args16
)));
467 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
468 args16
+= sizeof(int);
471 TRACE( "%04x:%04x %s", *(WORD
*)(args16
+2), *(WORD
*)args16
,
472 debugstr_a( MapSL(*(SEGPTR
*)args16
)) );
473 args32
[nb_args
] = *(SEGPTR
*)args16
;
474 args16
+= sizeof(SEGPTR
);
478 args32
[nb_args
] = (int)args16
;
487 /* Start with the last arg */
488 args16
+= call
->ret
[j
+ 1];
489 for (i
= 0; i
< 20; i
++, nb_args
++)
491 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
493 if (type
== ARG_NONE
) break;
498 args16
-= sizeof(WORD
);
499 args32
[nb_args
] = *(WORD
*)args16
;
500 TRACE( "%04x", *(WORD
*)args16
);
503 args16
-= sizeof(WORD
);
504 args32
[nb_args
] = *(short *)args16
;
505 TRACE( "%04x", *(WORD
*)args16
);
508 args16
-= sizeof(int);
509 args32
[nb_args
] = *(int *)args16
;
510 TRACE( "%08x", *(int *)args16
);
513 args16
-= sizeof(SEGPTR
);
514 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
515 TRACE( "%04x:%04x", *(WORD
*)(args16
+2), *(WORD
*)args16
);
518 args16
-= sizeof(int);
519 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
520 TRACE( "%08x %s", *(int *)args16
, debugstr_a( MapSL(*(SEGPTR
*)args16
)));
523 args16
-= sizeof(SEGPTR
);
524 args32
[nb_args
] = *(SEGPTR
*)args16
;
525 TRACE( "%04x:%04x %s", *(WORD
*)(args16
+2), *(WORD
*)args16
,
526 debugstr_a( MapSL(*(SEGPTR
*)args16
)) );
530 args32
[nb_args
] = (int)args16
;
538 if (!j
) /* register function */
540 args32
[nb_args
++] = (int)context
;
541 TRACE( ") ret=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x ds=%04x es=%04x efl=%08x\n",
542 frame
->cs
, frame
->ip
, (WORD
)context
->Eax
, (WORD
)context
->Ebx
, (WORD
)context
->Ecx
,
543 (WORD
)context
->Edx
, (WORD
)context
->Esi
, (WORD
)context
->Edi
, (WORD
)context
->SegDs
,
544 (WORD
)context
->SegEs
, context
->EFlags
);
546 else TRACE( ") ret=%04x:%04x ds=%04x\n", frame
->cs
, frame
->ip
, frame
->ds
);
548 SYSLEVEL_CheckNotLevel( 2 );
550 ret_val
= call_entry_point( entry_point
, nb_args
, args32
);
552 SYSLEVEL_CheckNotLevel( 2 );
554 TRACE( "\1Ret %s.%d: %s() ", module
, ordinal
, func
);
555 if (!j
) /* register function */
557 TRACE( "retval=none ret=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x ds=%04x es=%04x efl=%08x\n",
558 (WORD
)context
->SegCs
, LOWORD(context
->Eip
), (WORD
)context
->Eax
, (WORD
)context
->Ebx
,
559 (WORD
)context
->Ecx
, (WORD
)context
->Edx
, (WORD
)context
->Esi
, (WORD
)context
->Edi
,
560 (WORD
)context
->SegDs
, (WORD
)context
->SegEs
, context
->EFlags
);
564 frame
= CURRENT_STACK16
; /* might have be changed by the entry point */
565 if (j
== 1) /* 16-bit return sequence */
566 TRACE( "retval=%04x ret=%04x:%04x ds=%04x\n",
567 ret_val
& 0xffff, frame
->cs
, frame
->ip
, frame
->ds
);
569 TRACE( "retval=%08x ret=%04x:%04x ds=%04x\n",
570 ret_val
, frame
->cs
, frame
->ip
, frame
->ds
);
575 /**********************************************************************
578 * Get pointer to stack block when given esp pointing to 16-bit stack
579 * inside relay data segment.
581 static RELAY_Stack16
*RELAY_GetPointer( DWORD offset
)
583 offset
= offset
/ sizeof(RELAY_Stack16
) * sizeof(RELAY_Stack16
);
584 return MapSL(MAKESEGPTR(relay_data_sel
, offset
));
588 /**********************************************************************
589 * RELAY_MakeShortContext
591 * Allocate separate 16-bit stack, make stack pointer point to this
592 * stack and make code pointer point to stub that restores everything.
593 * So, after this routine, SS and CS are guaranteed to be 16-bit.
595 * Note: This might be called from signal handler, so the stack
596 * allocation algorithm must be signal safe.
598 static void RELAY_MakeShortContext( CONTEXT
*context
)
600 DWORD offset
= offsetof(RELAY_Stack16
, stack_top
);
601 RELAY_Stack16
*stack
= RELAY_GetPointer( 0 );
603 while (stack
->inuse
&& offset
< DOSVM_RELAY_DATA_SIZE
) {
605 offset
+= sizeof(RELAY_Stack16
);
608 if (offset
>= DOSVM_RELAY_DATA_SIZE
)
609 ERR( "Too many nested interrupts!\n" );
612 stack
->eip
= context
->Eip
;
613 stack
->seg_cs
= context
->SegCs
;
614 stack
->esp
= context
->Esp
;
615 stack
->seg_ss
= context
->SegSs
;
617 stack
->stack_bottom
= RELAY_MAGIC
;
618 stack
->stack_top
= RELAY_MAGIC
;
620 context
->SegSs
= relay_data_sel
;
621 context
->Esp
= offset
;
622 context
->SegCs
= relay_code_sel
;
627 /**********************************************************************
630 * This stub is called by __wine_call_from_16_regs in order to marshall
633 static void __stdcall
RELAY_RelayStub( DOSRELAY proc
, unsigned char *args
, CONTEXT
*context
)
637 RELAY_Stack16
*stack
= RELAY_GetPointer( context
->Esp
);
639 DWORD old_seg_cs
= context
->SegCs
;
640 DWORD old_eip
= context
->Eip
;
641 DWORD old_seg_ss
= context
->SegSs
;
642 DWORD old_esp
= context
->Esp
;
644 context
->SegCs
= stack
->seg_cs
;
645 context
->Eip
= stack
->eip
;
646 context
->SegSs
= stack
->seg_ss
;
647 context
->Esp
= stack
->esp
;
649 proc( context
, *(LPVOID
*)args
);
651 stack
->seg_cs
= context
->SegCs
;
652 stack
->eip
= context
->Eip
;
653 stack
->seg_ss
= context
->SegSs
;
654 stack
->esp
= context
->Esp
;
656 context
->SegCs
= old_seg_cs
;
657 context
->Eip
= old_eip
;
658 context
->SegSs
= old_seg_ss
;
659 context
->Esp
= old_esp
;
664 /**********************************************************************
667 * Restore saved code and stack pointers and release stack block.
669 void DOSVM_RelayHandler( CONTEXT
*context
)
671 RELAY_Stack16
*stack
= RELAY_GetPointer( context
->Esp
);
673 context
->SegSs
= stack
->seg_ss
;
674 context
->Esp
= stack
->esp
;
675 context
->SegCs
= stack
->seg_cs
;
676 context
->Eip
= stack
->eip
;
679 stack
->stack_bottom
!= RELAY_MAGIC
||
680 stack
->stack_top
!= RELAY_MAGIC
)
681 ERR( "Stack corrupted!\n" );
687 /**********************************************************************
688 * DOSVM_BuildCallFrame
690 * Modifies the context so that return to context calls DOSRELAY and
691 * only after return from DOSRELAY the original context will be returned to.
693 void DOSVM_BuildCallFrame( CONTEXT
*context
, DOSRELAY relay
, LPVOID data
)
695 WORD code_sel
= relay_code_sel
;
698 * Allocate separate stack for relay call.
700 RELAY_MakeShortContext( context
);
705 PUSH_WORD16( context
, HIWORD(data
) ); /* argument.hiword */
706 PUSH_WORD16( context
, LOWORD(data
) ); /* argument.loword */
707 PUSH_WORD16( context
, context
->SegCs
); /* STACK16FRAME.cs */
708 PUSH_WORD16( context
, LOWORD(context
->Eip
) ); /* STACK16FRAME.ip */
709 PUSH_WORD16( context
, LOWORD(context
->Ebp
) ); /* STACK16FRAME.bp */
710 PUSH_WORD16( context
, HIWORD(relay
) ); /* STACK16FRAME.entry_point.hiword */
711 PUSH_WORD16( context
, LOWORD(relay
) ); /* STACK16FRAME.entry_point.loword */
712 PUSH_WORD16( context
, 0 ); /* STACK16FRAME.entry_ip */
713 PUSH_WORD16( context
, HIWORD(RELAY_RelayStub
) ); /* STACK16FRAME.relay.hiword */
714 PUSH_WORD16( context
, LOWORD(RELAY_RelayStub
) ); /* STACK16FRAME.relay.loword */
715 PUSH_WORD16( context
, 0 ); /* STACK16FRAME.module_cs.hiword */
716 PUSH_WORD16( context
, code_sel
); /* STACK16FRAME.module_cs.loword */
717 PUSH_WORD16( context
, 0 ); /* STACK16FRAME.callfrom_ip.hiword */
718 PUSH_WORD16( context
, 0 ); /* STACK16FRAME.callfrom_ip.loword */
721 * Adjust code pointer.
723 context
->SegCs
= get_cs();
724 context
->Eip
= (DWORD
)__wine_call_from_16_regs
;