New debug channel mechanism allowing decentralized channel
[wine] / relay32 / snoop.c
1 /*
2  * 386-specific Win32 dll<->dll snooping functions
3  *
4  * Copyright 1998 Marcus Meissner
5  */
6
7 #include "config.h"
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include "winbase.h"
13 #include "winnt.h"
14 #include "heap.h"
15 #include "snoop.h"
16 #include "neexe.h"
17 #include "selectors.h"
18 #include "stackframe.h"
19 #include "debugtools.h"
20 #include "wine/exception.h"
21
22 DEFAULT_DEBUG_CHANNEL(snoop);
23
24 static WINE_EXCEPTION_FILTER(page_fault)
25 {
26     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
27         GetExceptionCode() == EXCEPTION_PRIV_INSTRUCTION)
28         return EXCEPTION_EXECUTE_HANDLER;
29     return EXCEPTION_CONTINUE_SEARCH;
30 }
31
32 char **debug_snoop_excludelist = NULL, **debug_snoop_includelist = NULL;
33
34 #ifdef __i386__
35
36 extern void WINAPI SNOOP_Entry();
37 extern void WINAPI SNOOP_Return();
38
39 #ifdef NEED_UNDERSCORE_PREFIX
40 # define PREFIX "_"
41 #else
42 # define PREFIX
43 #endif
44
45 #include "pshpack1.h"
46
47 typedef struct tagSNOOP_FUN {
48         /* code part */
49         BYTE            lcall;          /* 0xe8 call snoopentry (relative) */
50         /* NOTE: If you move snoopentry OR nrofargs fix the relative offset
51          * calculation!
52          */
53         DWORD           snoopentry;     /* SNOOP_Entry relative */
54         /* unreached */
55         int             nrofargs;
56         FARPROC origfun;
57         char            *name;
58 } SNOOP_FUN;
59
60 typedef struct tagSNOOP_DLL {
61         HMODULE hmod;
62         SNOOP_FUN       *funs;
63         LPCSTR          name;
64         DWORD           nrofordinals;
65         struct tagSNOOP_DLL     *next;
66 } SNOOP_DLL;
67 typedef struct tagSNOOP_RETURNENTRY {
68         /* code part */
69         BYTE            lcall;          /* 0xe8 call snoopret relative*/
70         /* NOTE: If you move snoopret OR origreturn fix the relative offset
71          * calculation!
72          */
73         DWORD           snoopret;       /* SNOOP_Ret relative */
74         /* unreached */
75         FARPROC origreturn;
76         SNOOP_DLL       *dll;
77         DWORD           ordinal;
78         DWORD           origESP;
79         DWORD           *args;          /* saved args across a stdcall */
80 } SNOOP_RETURNENTRY;
81
82 typedef struct tagSNOOP_RETURNENTRIES {
83         SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)];
84         struct tagSNOOP_RETURNENTRIES   *next;
85 } SNOOP_RETURNENTRIES;
86
87 #include "poppack.h"
88
89 static  SNOOP_DLL               *firstdll = NULL;
90 static  SNOOP_RETURNENTRIES     *firstrets = NULL;
91
92 /***********************************************************************
93  *          SNOOP_ShowDebugmsgSnoop
94  *
95  * Simple function to decide if a particular debugging message is
96  * wanted.
97  */
98 int SNOOP_ShowDebugmsgSnoop(const char *dll, int ord, const char *fname) {
99
100   if(debug_snoop_excludelist || debug_snoop_includelist) {
101     char **listitem;
102     char buf[80];
103     int len, len2, itemlen, show;
104
105     if(debug_snoop_excludelist) {
106       show = 1;
107       listitem = debug_snoop_excludelist;
108     } else {
109       show = 0;
110       listitem = debug_snoop_includelist;
111     }
112     len = strlen(dll);
113     assert(len < 64);
114     sprintf(buf, "%s.%d", dll, ord);
115     len2 = strlen(buf);
116     for(; *listitem; listitem++) {
117       itemlen = strlen(*listitem);
118       if((itemlen == len && !strncmp(*listitem, buf, len)) ||
119          (itemlen == len2 && !strncmp(*listitem, buf, len2)) ||
120          !strcmp(*listitem, fname)) {
121         show = !show;
122        break;
123       }
124     }
125     return show;
126   }
127   return 1;
128 }
129
130 void
131 SNOOP_RegisterDLL(HMODULE hmod,LPCSTR name,DWORD nrofordinals) {
132         SNOOP_DLL       **dll = &(firstdll);
133         char            *s;
134
135         if (!TRACE_ON(snoop)) return;
136         while (*dll) {
137                 if ((*dll)->hmod == hmod)
138                         return; /* already registered */
139                 dll = &((*dll)->next);
140         }
141         *dll = (SNOOP_DLL*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SNOOP_DLL));
142         (*dll)->next    = NULL;
143         (*dll)->hmod    = hmod;
144         (*dll)->nrofordinals = nrofordinals;
145         (*dll)->name    = HEAP_strdupA(GetProcessHeap(),0,name);
146         if ((s=strrchr((*dll)->name,'.')))
147                 *s='\0';
148         (*dll)->funs = VirtualAlloc(NULL,nrofordinals*sizeof(SNOOP_FUN),MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
149         memset((*dll)->funs,0,nrofordinals*sizeof(SNOOP_FUN));
150         if (!(*dll)->funs) {
151                 HeapFree(GetProcessHeap(),0,*dll);
152                 FIXME("out of memory\n");
153                 return;
154         }
155 }
156
157 FARPROC
158 SNOOP_GetProcAddress(HMODULE hmod,LPCSTR name,DWORD ordinal,FARPROC origfun) {
159         SNOOP_DLL                       *dll = firstdll;
160         SNOOP_FUN                       *fun;
161         int                             j;
162         IMAGE_SECTION_HEADER            *pe_seg = PE_SECTIONS(hmod);
163
164         if (!TRACE_ON(snoop)) return origfun;
165         if (!*(LPBYTE)origfun) /* 0x00 is an imposs. opcode, poss. dataref. */
166                 return origfun;
167         for (j=0;j<PE_HEADER(hmod)->FileHeader.NumberOfSections;j++)
168                 /* 0x42 is special ELF loader tag */
169                 if ((pe_seg[j].VirtualAddress==0x42) ||
170                     (((DWORD)origfun-hmod>=pe_seg[j].VirtualAddress)&&
171                      ((DWORD)origfun-hmod <pe_seg[j].VirtualAddress+
172                                    pe_seg[j].SizeOfRawData
173                    ))
174                 )
175                         break;
176         /* If we looked through all sections (and didn't find one)
177          * or if the sectionname contains "data", we return the
178          * original function since it is most likely a datareference.
179          */
180         if (    (j==PE_HEADER(hmod)->FileHeader.NumberOfSections)       ||
181                 (strstr(pe_seg[j].Name,"data"))                         ||
182                 !(pe_seg[j].Characteristics & IMAGE_SCN_CNT_CODE)
183         )
184                 return origfun;
185
186         while (dll) {
187                 if (hmod == dll->hmod)
188                         break;
189                 dll=dll->next;
190         }
191         if (!dll)       /* probably internal */
192                 return origfun;
193         if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,name))
194                 return origfun;
195         assert(ordinal < dll->nrofordinals);
196         fun = dll->funs+ordinal;
197         if (!fun->name) fun->name = HEAP_strdupA(GetProcessHeap(),0,name);
198         fun->lcall      = 0xe8;
199         /* NOTE: origreturn struct member MUST come directly after snoopentry */
200         fun->snoopentry = (char*)SNOOP_Entry-((char*)(&fun->nrofargs));
201         fun->origfun    = origfun;
202         fun->nrofargs   = -1;
203         return (FARPROC)&(fun->lcall);
204 }
205
206 static char*
207 SNOOP_PrintArg(DWORD x) {
208         static char     buf[200];
209         int             i,nostring;
210         char * volatile ret=0;
211
212         __TRY{
213                 LPBYTE  s=(LPBYTE)x;
214                 i=0;nostring=0;
215                 while (i<80) {
216                         if (s[i]==0) break;
217                         if (s[i]<0x20) {nostring=1;break;}
218                         if (s[i]>=0x80) {nostring=1;break;}
219                         i++;
220                 }
221                 if (!nostring) {
222                         if (i>5) {
223                                 snprintf(buf,sizeof(buf),"%08lx %s",x,debugstr_an((LPSTR)x,sizeof(buf)-10));
224                                 ret=buf;
225                         }
226                 }
227         }
228         __EXCEPT(page_fault){}
229         __ENDTRY
230         if (ret)
231           return ret;
232         __TRY{
233                 LPWSTR  s=(LPWSTR)x;
234                 i=0;nostring=0;
235                 while (i<80) {
236                         if (s[i]==0) break;
237                         if (s[i]<0x20) {nostring=1;break;}
238                         if (s[i]>0x100) {nostring=1;break;}
239                         i++;
240                 }
241                 if (!nostring) {
242                         if (i>5) {
243                                 snprintf(buf,sizeof(buf),"%08lx %s",x,debugstr_wn((LPWSTR)x,sizeof(buf)-10));
244                                 ret=buf;
245                         }
246                 }
247         }
248         __EXCEPT(page_fault){}
249         __ENDTRY
250         if (ret)
251           return ret;
252         sprintf(buf,"%08lx",x);
253         return buf;
254 }
255
256 #define CALLER1REF (*(DWORD*)context->Esp)
257
258 void WINAPI SNOOP_DoEntry( CONTEXT86 *context );
259 DEFINE_REGS_ENTRYPOINT_0( SNOOP_Entry, SNOOP_DoEntry );
260 void WINAPI SNOOP_DoEntry( CONTEXT86 *context )
261 {
262         DWORD           ordinal=0,entry = context->Eip - 5;
263         SNOOP_DLL       *dll = firstdll;
264         SNOOP_FUN       *fun = NULL;
265         SNOOP_RETURNENTRIES     **rets = &firstrets;
266         SNOOP_RETURNENTRY       *ret;
267         int             i=0, max;
268
269         while (dll) {
270                 if (    ((char*)entry>=(char*)dll->funs)        &&
271                         ((char*)entry<=(char*)(dll->funs+dll->nrofordinals))
272                 ) {
273                         fun = (SNOOP_FUN*)entry;
274                         ordinal = fun-dll->funs;
275                         break;
276                 }
277                 dll=dll->next;
278         }
279         if (!dll) {
280                 FIXME("entrypoint 0x%08lx not found\n",entry);
281                 return; /* oops */
282         }
283         /* guess cdecl ... */
284         if (fun->nrofargs<0) {
285                 /* Typical cdecl return frame is:
286                  *      add esp, xxxxxxxx
287                  * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
288                  * (after that 81 C2 xx xx xx xx)
289                  */
290                 LPBYTE  reteip = (LPBYTE)CALLER1REF;
291
292                 if (reteip) {
293                         if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
294                                 fun->nrofargs=reteip[2]/4;
295                 }
296         }
297
298
299         while (*rets) {
300                 for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
301                         if (!(*rets)->entry[i].origreturn)
302                                 break;
303                 if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
304                         break;
305                 rets = &((*rets)->next);
306         }
307         if (!*rets) {
308                 *rets = VirtualAlloc(NULL,4096,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
309                 memset(*rets,0,4096);
310                 i = 0;  /* entry 0 is free */
311         }
312         ret = &((*rets)->entry[i]);
313         ret->lcall      = 0xe8;
314         /* NOTE: origreturn struct member MUST come directly after snoopret */
315         ret->snoopret   = ((char*)SNOOP_Return)-(char*)(&ret->origreturn);
316         ret->origreturn = (FARPROC)CALLER1REF;
317         CALLER1REF      = (DWORD)&ret->lcall;
318         ret->dll        = dll;
319         ret->args       = NULL;
320         ret->ordinal    = ordinal;
321         ret->origESP    = context->Esp;
322
323         context->Eip = (DWORD)fun->origfun;
324
325         DPRINTF("CALL %s.%ld: %s(",dll->name,ordinal,fun->name);
326         if (fun->nrofargs>0) {
327                 max = fun->nrofargs; if (max>16) max=16;
328                 for (i=0;i<max;i++)
329                         DPRINTF("%s%s",SNOOP_PrintArg(*(DWORD*)(context->Esp + 4 + sizeof(DWORD)*i)),(i<fun->nrofargs-1)?",":"");
330                 if (max!=fun->nrofargs)
331                         DPRINTF(" ...");
332         } else if (fun->nrofargs<0) {
333                 DPRINTF("<unknown, check return>");
334                 ret->args = HeapAlloc(GetProcessHeap(),0,16*sizeof(DWORD));
335                 memcpy(ret->args,(LPBYTE)(context->Esp + 4),sizeof(DWORD)*16);
336         }
337         DPRINTF(") ret=%08lx fs=%04lx\n",(DWORD)ret->origreturn,context->SegFs);
338 }
339
340 void WINAPI SNOOP_DoReturn( CONTEXT86 *context );
341 DEFINE_REGS_ENTRYPOINT_0( SNOOP_Return, SNOOP_DoReturn );
342 void WINAPI SNOOP_DoReturn( CONTEXT86 *context )
343 {
344         SNOOP_RETURNENTRY       *ret = (SNOOP_RETURNENTRY*)(context->Eip - 5);
345
346         /* We haven't found out the nrofargs yet. If we called a cdecl
347          * function it is too late anyway and we can just set '0' (which
348          * will be the difference between orig and current ESP
349          * If stdcall -> everything ok.
350          */
351         if (ret->dll->funs[ret->ordinal].nrofargs<0)
352                 ret->dll->funs[ret->ordinal].nrofargs=(context->Esp - ret->origESP-4)/4;
353         context->Eip = (DWORD)ret->origreturn;
354         if (ret->args) {
355                 int     i,max;
356
357                 DPRINTF("RET  %s.%ld: %s(",ret->dll->name,ret->ordinal,ret->dll->funs[ret->ordinal].name);
358                 max = ret->dll->funs[ret->ordinal].nrofargs;
359                 if (max>16) max=16;
360
361                 for (i=0;i<max;i++)
362                         DPRINTF("%s%s",SNOOP_PrintArg(ret->args[i]),(i<max-1)?",":"");
363                 DPRINTF(") retval = %08lx ret=%08lx fs=%04lx\n",
364                         context->Eax,(DWORD)ret->origreturn,context->SegFs );
365                 HeapFree(GetProcessHeap(),0,ret->args);
366                 ret->args = NULL;
367         } else
368                 DPRINTF("RET  %s.%ld: %s() retval = %08lx ret=%08lx fs=%04lx\n",
369                         ret->dll->name,ret->ordinal,ret->dll->funs[ret->ordinal].name,
370                         context->Eax,(DWORD)ret->origreturn,context->SegFs );
371         ret->origreturn = NULL; /* mark as empty */
372 }
373 #else   /* !__i386__ */
374 void SNOOP_RegisterDLL(HMODULE hmod,LPCSTR name,DWORD nrofordinals) {
375         FIXME("snooping works only on i386 for now.\n");
376         return;
377 }
378
379 FARPROC SNOOP_GetProcAddress(HMODULE hmod,LPCSTR name,DWORD ordinal,FARPROC origfun) {
380         return origfun;
381 }
382 #endif  /* !__i386__ */