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
);
37 static const char **debug_relay_excludelist
;
38 static const char **debug_relay_includelist
;
39 static const char **debug_snoop_excludelist
;
40 static const char **debug_snoop_includelist
;
43 /***********************************************************************
46 * Build a function list from a ';'-separated string.
48 static const char **build_list( const WCHAR
*buffer
)
51 const WCHAR
*p
= buffer
;
54 while ((p
= wcschr( p
, ';' )))
59 /* allocate count+1 pointers, plus the space for a copy of the string */
60 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0,
61 (count
+ 1) * sizeof(char *) + (lstrlenW(buffer
) + 1) )))
63 char *str
= (char *)(ret
+ count
+ 1);
66 while ((*str
++ = *buffer
++));
71 if (!(p
= strchr( p
, ';' ))) break;
80 /***********************************************************************
81 * RELAY16_InitDebugLists
83 * Build the relay include/exclude function lists.
85 void RELAY16_InitDebugLists(void)
87 OBJECT_ATTRIBUTES attr
;
93 static const WCHAR configW
[] = {'S','o','f','t','w','a','r','e','\\',
95 'D','e','b','u','g',0};
96 static const WCHAR RelayIncludeW
[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
97 static const WCHAR RelayExcludeW
[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
98 static const WCHAR SnoopIncludeW
[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
99 static const WCHAR SnoopExcludeW
[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
101 RtlOpenCurrentUser( KEY_READ
, &root
);
102 attr
.Length
= sizeof(attr
);
103 attr
.RootDirectory
= root
;
104 attr
.ObjectName
= &name
;
106 attr
.SecurityDescriptor
= NULL
;
107 attr
.SecurityQualityOfService
= NULL
;
108 RtlInitUnicodeString( &name
, configW
);
110 /* @@ Wine registry key: HKCU\Software\Wine\Debug */
111 if (NtOpenKey( &hkey
, KEY_READ
, &attr
)) hkey
= 0;
115 str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)buffer
)->Data
;
116 RtlInitUnicodeString( &name
, RelayIncludeW
);
117 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
119 debug_relay_includelist
= build_list( str
);
122 RtlInitUnicodeString( &name
, RelayExcludeW
);
123 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
125 debug_relay_excludelist
= build_list( str
);
128 RtlInitUnicodeString( &name
, SnoopIncludeW
);
129 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
131 debug_snoop_includelist
= build_list( str
);
134 RtlInitUnicodeString( &name
, SnoopExcludeW
);
135 if (!NtQueryValueKey( hkey
, &name
, KeyValuePartialInformation
, buffer
, sizeof(buffer
), &count
))
137 debug_snoop_excludelist
= build_list( str
);
143 /***********************************************************************
146 * Check if a given module and function is in the list.
148 static BOOL
check_list( const char *module
, int ordinal
, const char *func
, const char **list
)
152 sprintf( ord_str
, "%d", ordinal
);
155 const char *p
= strrchr( *list
, '.' );
156 if (p
&& p
> *list
) /* check module and function */
159 if (_strnicmp( module
, *list
, len
-1 ) || module
[len
]) continue;
160 if (p
[1] == '*' && !p
[2]) return TRUE
;
161 if (!strcmp( ord_str
, p
+ 1 )) return TRUE
;
162 if (func
&& !stricmp( func
, p
+ 1 )) return TRUE
;
164 else /* function only */
166 if (func
&& !stricmp( func
, *list
)) return TRUE
;
173 /***********************************************************************
174 * RELAY_ShowDebugmsgRelay
176 * Simple function to decide if a particular debugging message is
179 static BOOL
RELAY_ShowDebugmsgRelay(const char *module
, int ordinal
, const char *func
)
181 if (debug_relay_excludelist
&& check_list( module
, ordinal
, func
, debug_relay_excludelist
))
183 if (debug_relay_includelist
&& !check_list( module
, ordinal
, func
, debug_relay_includelist
))
189 /***********************************************************************
190 * SNOOP16_ShowDebugmsgSnoop
192 * Simple function to decide if a particular debugging message is
195 BOOL
SNOOP16_ShowDebugmsgSnoop(const char *module
, int ordinal
, const char *func
)
197 if (debug_snoop_excludelist
&& check_list( module
, ordinal
, func
, debug_snoop_excludelist
))
199 if (debug_snoop_includelist
&& !check_list( module
, ordinal
, func
, debug_snoop_includelist
))
205 /***********************************************************************
208 * Return the ordinal, name, and type info corresponding to a CS:IP address.
210 static const CALLFROM16
*get_entry_point( STACK16FRAME
*frame
, LPSTR module
, LPSTR func
, WORD
*pOrd
)
219 if (!(pModule
= NE_GetPtr( FarGetOwner16( GlobalHandle16( frame
->module_cs
) ))))
223 bundle
= (ET_BUNDLE
*)((BYTE
*)pModule
+ pModule
->ne_enttab
);
226 entry
= (ET_ENTRY
*)((BYTE
*)bundle
+6);
227 for (i
= bundle
->first
+ 1; i
<= bundle
->last
; i
++)
229 if ((entry
->offs
< frame
->entry_ip
)
230 && (entry
->segnum
== 1) /* code segment ? */
231 && (entry
->offs
>= max_offset
))
233 max_offset
= entry
->offs
;
238 } while ( (bundle
->next
)
239 && (bundle
= (ET_BUNDLE
*)((BYTE
*)pModule
+bundle
->next
)));
241 /* Search for the name in the resident names table */
242 /* (built-in modules have no non-resident table) */
244 p
= (BYTE
*)pModule
+ pModule
->ne_restab
;
245 memcpy( module
, p
+ 1, *p
);
250 p
+= *p
+ 1 + sizeof(WORD
);
251 if (*(WORD
*)(p
+ *p
+ 1) == *pOrd
) break;
253 memcpy( func
, p
+ 1, *p
);
256 /* Retrieve entry point call structure */
257 p
= MapSL( MAKESEGPTR( frame
->module_cs
, frame
->callfrom_ip
) );
258 /* p now points to lret, get the start of CALLFROM16 structure */
259 return (CALLFROM16
*)(p
- FIELD_OFFSET( CALLFROM16
, ret
));
263 extern int call_entry_point( void *func
, int nb_args
, const int *args
);
264 __ASM_GLOBAL_FUNC( call_entry_point
,
266 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
267 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
269 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
271 __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
273 __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
274 "movl 12(%ebp),%edx\n\t"
279 "movl 12(%ebp),%ecx\n\t"
280 "movl 16(%ebp),%esi\n\t"
284 "1:\tcall *8(%ebp)\n\t"
285 "leal -8(%ebp),%esp\n\t"
287 __ASM_CFI(".cfi_same_value %edi\n\t")
289 __ASM_CFI(".cfi_same_value %esi\n\t")
291 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
292 __ASM_CFI(".cfi_same_value %ebp\n\t")
296 /***********************************************************************
297 * relay_call_from_16_no_debug
299 * Same as relay_call_from_16 but doesn't print any debug information.
301 static int relay_call_from_16_no_debug( void *entry_point
, unsigned char *args16
, CONTEXT
*context
,
302 const CALLFROM16
*call
)
304 unsigned int i
, j
, nb_args
= 0;
307 /* look for the ret instruction */
308 for (j
= 0; j
< ARRAY_SIZE(call
->ret
); j
++)
309 if (call
->ret
[j
] == 0xca66 || call
->ret
[j
] == 0xcb66) break;
311 if (call
->ret
[j
] == 0xcb66) /* cdecl */
313 for (i
= 0; i
< 20; i
++, nb_args
++)
315 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
317 if (type
== ARG_NONE
) break;
321 args32
[nb_args
] = *(WORD
*)args16
;
322 args16
+= sizeof(WORD
);
325 args32
[nb_args
] = *(short *)args16
;
326 args16
+= sizeof(WORD
);
330 args32
[nb_args
] = *(int *)args16
;
331 args16
+= sizeof(int);
335 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
336 args16
+= sizeof(SEGPTR
);
339 args32
[nb_args
] = (int)args16
;
348 /* Start with the last arg */
349 args16
+= call
->ret
[j
+ 1];
350 for (i
= 0; i
< 20; i
++, nb_args
++)
352 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
354 if (type
== ARG_NONE
) break;
358 args16
-= sizeof(WORD
);
359 args32
[nb_args
] = *(WORD
*)args16
;
362 args16
-= sizeof(WORD
);
363 args32
[nb_args
] = *(short *)args16
;
367 args16
-= sizeof(int);
368 args32
[nb_args
] = *(int *)args16
;
372 args16
-= sizeof(SEGPTR
);
373 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
381 if (!j
) /* register function */
382 args32
[nb_args
++] = (int)context
;
384 SYSLEVEL_CheckNotLevel( 2 );
386 return call_entry_point( entry_point
, nb_args
, args32
);
390 /***********************************************************************
393 * Replacement for the 16-bit relay functions when relay debugging is on.
395 int relay_call_from_16( void *entry_point
, unsigned char *args16
, CONTEXT
*context
)
399 unsigned int i
, j
, nb_args
= 0;
400 int ret_val
, args32
[20];
401 char module
[10], func
[64];
402 const CALLFROM16
*call
;
404 frame
= CURRENT_STACK16
;
405 call
= get_entry_point( frame
, module
, func
, &ordinal
);
406 if (!TRACE_ON(relay
) || !RELAY_ShowDebugmsgRelay( module
, ordinal
, func
))
407 return relay_call_from_16_no_debug( entry_point
, args16
, context
, call
);
409 TRACE( "\1Call %s.%d: %s(", module
, ordinal
, func
);
411 /* look for the ret instruction */
412 for (j
= 0; j
< ARRAY_SIZE(call
->ret
); j
++)
413 if (call
->ret
[j
] == 0xca66 || call
->ret
[j
] == 0xcb66) break;
415 if (call
->ret
[j
] == 0xcb66) /* cdecl */
417 for (i
= 0; i
< 20; i
++, nb_args
++)
419 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
421 if (type
== ARG_NONE
) break;
426 TRACE( "%04x", *(WORD
*)args16
);
427 args32
[nb_args
] = *(WORD
*)args16
;
428 args16
+= sizeof(WORD
);
431 TRACE( "%04x", *(WORD
*)args16
);
432 args32
[nb_args
] = *(short *)args16
;
433 args16
+= sizeof(WORD
);
436 TRACE( "%08x", *(int *)args16
);
437 args32
[nb_args
] = *(int *)args16
;
438 args16
+= sizeof(int);
441 TRACE( "%04x:%04x", *(WORD
*)(args16
+2), *(WORD
*)args16
);
442 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
443 args16
+= sizeof(SEGPTR
);
446 TRACE( "%08x %s", *(int *)args16
, debugstr_a( MapSL(*(SEGPTR
*)args16
)));
447 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
448 args16
+= sizeof(int);
451 TRACE( "%04x:%04x %s", *(WORD
*)(args16
+2), *(WORD
*)args16
,
452 debugstr_a( MapSL(*(SEGPTR
*)args16
)) );
453 args32
[nb_args
] = *(SEGPTR
*)args16
;
454 args16
+= sizeof(SEGPTR
);
458 args32
[nb_args
] = (int)args16
;
467 /* Start with the last arg */
468 args16
+= call
->ret
[j
+ 1];
469 for (i
= 0; i
< 20; i
++, nb_args
++)
471 int type
= (call
->arg_types
[i
/ 10] >> (3 * (i
% 10))) & 7;
473 if (type
== ARG_NONE
) break;
478 args16
-= sizeof(WORD
);
479 args32
[nb_args
] = *(WORD
*)args16
;
480 TRACE( "%04x", *(WORD
*)args16
);
483 args16
-= sizeof(WORD
);
484 args32
[nb_args
] = *(short *)args16
;
485 TRACE( "%04x", *(WORD
*)args16
);
488 args16
-= sizeof(int);
489 args32
[nb_args
] = *(int *)args16
;
490 TRACE( "%08x", *(int *)args16
);
493 args16
-= sizeof(SEGPTR
);
494 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
495 TRACE( "%04x:%04x", *(WORD
*)(args16
+2), *(WORD
*)args16
);
498 args16
-= sizeof(int);
499 args32
[nb_args
] = (int)MapSL( *(SEGPTR
*)args16
);
500 TRACE( "%08x %s", *(int *)args16
, debugstr_a( MapSL(*(SEGPTR
*)args16
)));
503 args16
-= sizeof(SEGPTR
);
504 args32
[nb_args
] = *(SEGPTR
*)args16
;
505 TRACE( "%04x:%04x %s", *(WORD
*)(args16
+2), *(WORD
*)args16
,
506 debugstr_a( MapSL(*(SEGPTR
*)args16
)) );
510 args32
[nb_args
] = (int)args16
;
518 if (!j
) /* register function */
520 args32
[nb_args
++] = (int)context
;
521 TRACE( ") ret=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x ss:sp=%04x:%04x ds=%04x es=%04x efl=%08x\n",
522 frame
->cs
, frame
->ip
, (WORD
)context
->Eax
, (WORD
)context
->Ebx
, (WORD
)context
->Ecx
,
523 (WORD
)context
->Edx
, (WORD
)context
->Esi
, (WORD
)context
->Edi
, (WORD
)context
->Ebp
,
524 (WORD
)context
->SegSs
, (WORD
)context
->Esp
, (WORD
)context
->SegDs
, (WORD
)context
->SegEs
, context
->EFlags
);
526 else TRACE( ") ret=%04x:%04x ds=%04x\n", frame
->cs
, frame
->ip
, frame
->ds
);
528 SYSLEVEL_CheckNotLevel( 2 );
530 ret_val
= call_entry_point( entry_point
, nb_args
, args32
);
532 SYSLEVEL_CheckNotLevel( 2 );
534 TRACE( "\1Ret %s.%d: %s() ", module
, ordinal
, func
);
535 if (!j
) /* register function */
537 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",
538 (WORD
)context
->SegCs
, LOWORD(context
->Eip
), (WORD
)context
->Eax
, (WORD
)context
->Ebx
,
539 (WORD
)context
->Ecx
, (WORD
)context
->Edx
, (WORD
)context
->Esi
, (WORD
)context
->Edi
,
540 (WORD
)context
->SegDs
, (WORD
)context
->SegEs
, context
->EFlags
);
544 frame
= CURRENT_STACK16
; /* might have be changed by the entry point */
545 if (j
== 1) /* 16-bit return sequence */
546 TRACE( "retval=%04x ret=%04x:%04x ds=%04x\n",
547 ret_val
& 0xffff, frame
->cs
, frame
->ip
, frame
->ds
);
549 TRACE( "retval=%08x ret=%04x:%04x ds=%04x\n",
550 ret_val
, frame
->cs
, frame
->ip
, frame
->ds
);