winedbg: Added scoped symbol picker, and options to handle it.
[wine] / programs / winedbg / symbol.c
1 /*
2  * Generate hash tables for Wine debugger symbols
3  *
4  * Copyright (C) 1993, Eric Youngdale.
5  *               2004-2005, Eric Pouech.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24
25 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "debugger.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
34
35 static BOOL symbol_get_debug_start(const struct dbg_type* func, ULONG64* start)
36 {
37     DWORD                       count, tag;
38     char                        buffer[sizeof(TI_FINDCHILDREN_PARAMS) + 256 * sizeof(DWORD)];
39     TI_FINDCHILDREN_PARAMS*     fcp = (TI_FINDCHILDREN_PARAMS*)buffer;
40     int                         i;
41     struct dbg_type             child;
42
43     if (!func->id) return FALSE; /* native dbghelp not always fills the info field */
44
45     if (!types_get_info(func, TI_GET_CHILDRENCOUNT, &count)) return FALSE;
46     fcp->Start = 0;
47     while (count)
48     {
49         fcp->Count = min(count, 256);
50         if (types_get_info(func, TI_FINDCHILDREN, fcp))
51         {
52             for (i = 0; i < min(fcp->Count, count); i++)
53             {
54                 child.module = func->module;
55                 child.id = fcp->ChildId[i];
56                 types_get_info(&child, TI_GET_SYMTAG, &tag);
57                 if (tag != SymTagFuncDebugStart) continue;
58                 return types_get_info(&child, TI_GET_ADDRESS, start);
59             }
60             count -= min(count, 256);
61             fcp->Start += 256;
62             fcp->Start += 256;
63         }
64     }
65     return FALSE;
66 }
67
68 static BOOL fill_sym_lvalue(const SYMBOL_INFO* sym, ULONG base,
69                             struct dbg_lvalue* lvalue, char* buffer, size_t sz)
70 {
71     if (buffer) buffer[0] = '\0';
72     if (sym->Flags & SYMFLAG_REGISTER)
73     {
74         DWORD* pval;
75
76         if (!memory_get_register(sym->Register, &pval, buffer, sz))
77             return FALSE;
78         lvalue->cookie = DLV_HOST;
79         lvalue->addr.Offset = (DWORD_PTR)pval;
80     }
81     else if (sym->Flags & SYMFLAG_REGREL)
82     {
83         DWORD* pval;
84
85         if (!memory_get_register(sym->Register, &pval, buffer, sz))
86             return FALSE;
87         lvalue->cookie = DLV_TARGET;
88         lvalue->addr.Offset = (ULONG)((ULONG64)*pval + sym->Address);
89     }
90     else if (sym->Flags & SYMFLAG_VALUEPRESENT)
91     {
92         struct dbg_type type;
93         VARIANT         v;
94         DWORD*          pdw;
95
96         type.module = sym->ModBase;
97         type.id = sym->info;
98
99         /* FIXME: this won't work for pointers, as we always for the
100          * dereference to be in debuggee address space while here
101          * it's in debugger address space
102          */
103         if (!types_get_info(&type, TI_GET_VALUE, &v) || (v.n1.n2.vt & VT_BYREF))
104         {
105             snprintf(buffer, sz, "Couldn't dereference pointer for const value");
106             return FALSE;
107         }
108         pdw = (DWORD*)lexeme_alloc_size(sizeof(*pdw));
109         lvalue->cookie = DLV_HOST;
110         lvalue->addr.Offset = (ULONG)(DWORD_PTR)pdw;
111         *pdw = sym->Value;
112     }
113     else if (sym->Flags & SYMFLAG_LOCAL)
114     {
115         lvalue->cookie = DLV_TARGET;
116         lvalue->addr.Offset = base + sym->Address;
117     }
118     else
119     {
120         lvalue->cookie = DLV_TARGET;
121         lvalue->addr.Offset = sym->Address;
122     }
123     lvalue->addr.Mode = AddrModeFlat;
124     lvalue->type.module = sym->ModBase;
125     lvalue->type.id = sym->TypeIndex;
126
127     return TRUE;
128 }
129
130 struct sgv_data
131 {
132 #define NUMDBGV                 100
133     struct
134     {
135         /* FIXME: NUMDBGV should be made variable */
136         struct dbg_lvalue               lvalue;
137         DWORD                           flags;
138         DWORD                           sym_info;
139     }                           syms[NUMDBGV];  /* out     : will be filled in with various found symbols */
140     int                         num;            /* out     : number of found symbols */
141     int                         num_thunks;     /* out     : number of thunks found */
142     const char*                 name;           /* in      : name of symbol to look up */
143     unsigned                    do_thunks : 1;  /* in      : whether we return thunks tags */
144     ULONG64                     frame_offset;   /* in      : frame for local & parameter variables look up */
145 };
146
147 static BOOL CALLBACK sgv_cb(PSYMBOL_INFO sym, ULONG size, PVOID ctx)
148 {
149     struct sgv_data*    sgv = (struct sgv_data*)ctx;
150     unsigned            insp;
151     char                tmp[64];
152
153     if (sym->Flags & SYMFLAG_THUNK)
154     {
155         if (!sgv->do_thunks) return TRUE;
156         sgv->num_thunks++;
157     }
158
159     if (sgv->num >= NUMDBGV)
160     {
161         dbg_printf("Too many addresses for symbol '%s', limiting the first %d\n",
162                    sgv->name, NUMDBGV);
163         return FALSE;
164     }
165     WINE_TRACE("==> %s %s%s%s%s%s%s%s\n", 
166                sym->Name, 
167                (sym->Flags & SYMFLAG_FUNCTION) ? "func " : "",
168                (sym->Flags & SYMFLAG_FRAMEREL) ? "framerel " : "",
169                (sym->Flags & SYMFLAG_REGISTER) ? "register " : "",
170                (sym->Flags & SYMFLAG_REGREL) ? "regrel " : "",
171                (sym->Flags & SYMFLAG_PARAMETER) ? "param " : "",
172                (sym->Flags & SYMFLAG_LOCAL) ? "local " : "",
173                (sym->Flags & SYMFLAG_THUNK) ? "thunk " : "");
174
175     /* always keep the thunks at end of the array */
176     insp = sgv->num;
177     if (sgv->num_thunks && !(sym->Flags & SYMFLAG_THUNK))
178     {
179         insp -= sgv->num_thunks;
180         memmove(&sgv->syms[insp + 1], &sgv->syms[insp],
181                 sizeof(sgv->syms[0]) * sgv->num_thunks);
182     }
183     if (!fill_sym_lvalue(sym, sgv->frame_offset, &sgv->syms[insp].lvalue, tmp, sizeof(tmp)))
184     {
185         dbg_printf("%s: %s\n", sym->Name, tmp);
186         return TRUE;
187     }
188     sgv->syms[insp].flags              = sym->Flags;
189     sgv->syms[insp].sym_info           = sym->info;
190     sgv->num++;
191
192     return TRUE;
193 }
194
195 enum sym_get_lval symbol_picker_interactive(const char* name, const struct sgv_data* sgv,
196                                             struct dbg_lvalue* rtn)
197 {
198     char        buffer[512];
199     unsigned    i;
200
201     if (!dbg_interactiveP)
202     {
203         dbg_printf("More than one symbol named %s, picking the first one\n", name);
204         *rtn = sgv->syms[0].lvalue;
205         return sglv_found;
206     }
207
208     dbg_printf("Many symbols with name '%s', "
209                "choose the one you want (<cr> to abort):\n", name);
210     for (i = 0; i < sgv->num; i++)
211     {
212         if (sgv->num - sgv->num_thunks > 1 && (sgv->syms[i].flags & SYMFLAG_THUNK) && !DBG_IVAR(AlwaysShowThunks))
213             continue;
214         dbg_printf("[%d]: ", i + 1);
215         if (sgv->syms[i].flags & SYMFLAG_LOCAL)
216         {
217             dbg_printf("%s %sof %s\n",
218                        sgv->syms[i].flags & SYMFLAG_PARAMETER ? "Parameter" : "Local variable",
219                        sgv->syms[i].flags & (SYMFLAG_REGISTER|SYMFLAG_REGREL) ? "(in a register) " : "",
220                        name);
221         }
222         else if (sgv->syms[i].flags & SYMFLAG_THUNK)
223         {
224             print_address(&sgv->syms[i].lvalue.addr, TRUE);
225             /* FIXME: should display where the thunks points to */
226             dbg_printf(" thunk %s\n", name);
227         }
228         else
229         {
230             print_address(&sgv->syms[i].lvalue.addr, TRUE);
231             dbg_printf("\n");
232         }
233     }
234     do
235     {
236         i = 0;
237         if (input_read_line("=> ", buffer, sizeof(buffer)))
238         {
239             if (buffer[0] == '\0') return sglv_aborted;
240             i = atoi(buffer);
241             if (i < 1 || i > sgv->num)
242                 dbg_printf("Invalid choice %d\n", i);
243         }
244         else return sglv_aborted;
245     } while (i < 1 || i > sgv->num);
246
247     /* The array is 0-based, but the choices are 1..n,
248      * so we have to subtract one before returning.
249      */
250     *rtn = sgv->syms[i - 1].lvalue;
251     return sglv_found;
252 }
253
254 enum sym_get_lval symbol_picker_scoped(const char* name, const struct sgv_data* sgv,
255                                        struct dbg_lvalue* rtn)
256 {
257     unsigned i;
258     int local = -1;
259
260     for (i = 0; i < sgv->num; i++)
261     {
262         if (sgv->num - sgv->num_thunks > 1 && (sgv->syms[i].flags & SYMFLAG_THUNK) && !DBG_IVAR(AlwaysShowThunks))
263             continue;
264         if (sgv->syms[i].flags & SYMFLAG_LOCAL)
265         {
266             if (local == -1)
267                 local = i;
268             else
269             {
270                 /* FIXME: several locals with same name... which one to pick ?? */
271                 dbg_printf("Several local variables/parameters for %s, aborting\n", name);
272                 return sglv_aborted;
273             }
274         }
275     }
276     if (local != -1)
277     {
278         *rtn = sgv->syms[local].lvalue;
279         return sglv_found;
280     }
281     /* no locals found, multiple globals... abort for now */
282     dbg_printf("Several global variables for %s, aborting\n", name);
283     return sglv_aborted;
284 }
285
286 symbol_picker_t symbol_current_picker = symbol_picker_interactive;
287
288 /***********************************************************************
289  *           symbol_get_lvalue
290  *
291  * Get the address of a named symbol.
292  * Return values:
293  *      sglv_found:   if the symbol is found
294  *      sglv_unknown: if the symbol isn't found
295  *      sglv_aborted: some error occurred (likely, many symbols of same name exist,
296  *          and user didn't pick one of them)
297  */
298 enum sym_get_lval symbol_get_lvalue(const char* name, const int lineno,
299                                     struct dbg_lvalue* rtn, BOOL bp_disp)
300 {
301     struct sgv_data             sgv;
302     int                         i;
303     char                        buffer[512];
304     DWORD                       opt;
305     IMAGEHLP_STACK_FRAME        ihsf;
306
307     if (strlen(name) + 4 > sizeof(buffer))
308     {
309         WINE_WARN("Too long symbol (%s)\n", name);
310         return sglv_unknown;
311     }
312
313     sgv.num        = 0;
314     sgv.num_thunks = 0;
315     sgv.name       = &buffer[2];
316     sgv.do_thunks  = DBG_IVAR(AlwaysShowThunks);
317
318     if (strchr(name, '!'))
319     {
320         strcpy(buffer, name);
321     }
322     else
323     {
324         buffer[0] = '*';
325         buffer[1] = '!';
326         strcpy(&buffer[2], name);
327     }
328
329     /* this is a wine specific options to return also ELF modules in the
330      * enumeration
331      */
332     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
333     SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv);
334
335     if (!sgv.num)
336     {
337         const char*   ptr = strchr(name, '!');
338         if ((ptr && ptr[1] != '_') || (!ptr && *name != '_'))
339         {
340             if (ptr)
341             {
342                 int offset = ptr - name;
343                 memcpy(buffer, name, offset + 1);
344                 buffer[offset + 1] = '_';
345                 strcpy(&buffer[offset + 2], ptr + 1);
346             }
347             else
348             {
349                 buffer[0] = '*';
350                 buffer[1] = '!';
351                 buffer[2] = '_';
352                 strcpy(&buffer[3], name);
353             }
354             SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv);
355         }
356     }
357     SymSetOptions(opt);
358
359     /* now grab local symbols */
360     if (stack_get_current_frame(&ihsf) && sgv.num < NUMDBGV)
361     {
362         sgv.frame_offset = ihsf.FrameOffset;
363         SymEnumSymbols(dbg_curr_process->handle, 0, name, sgv_cb, (void*)&sgv);
364     }
365
366     if (!sgv.num)
367     {
368         dbg_printf("No symbols found for %s\n", name);
369         return sglv_unknown;
370     }
371
372     /* recompute potential offsets for functions (linenumber, skip prolog) */
373     for (i = 0; i < sgv.num; i++)
374     {
375         if (sgv.syms[i].flags & (SYMFLAG_REGISTER|SYMFLAG_REGREL|SYMFLAG_LOCAL|SYMFLAG_THUNK))
376             continue;
377
378         if (lineno == -1)
379         {
380             struct dbg_type     type;
381             ULONG64             addr;
382
383             type.module = sgv.syms[i].lvalue.type.module;
384             type.id     = sgv.syms[i].sym_info;
385             if (bp_disp && symbol_get_debug_start(&type, &addr))
386                 sgv.syms[i].lvalue.addr.Offset = addr;
387         }
388         else
389         {
390             DWORD               disp;
391             IMAGEHLP_LINE       il;
392             BOOL                found = FALSE;
393
394             il.SizeOfStruct = sizeof(il);
395             SymGetLineFromAddr(dbg_curr_process->handle,
396                                (DWORD)memory_to_linear_addr(&sgv.syms[i].lvalue.addr),
397                                &disp, &il);
398             do
399             {
400                 if (lineno == il.LineNumber)
401                 {
402                     sgv.syms[i].lvalue.addr.Offset = il.Address;
403                     found = TRUE;
404                     break;
405                 }
406             } while (SymGetLineNext(dbg_curr_process->handle, &il));
407             if (!found)
408                 WINE_FIXME("No line (%d) found for %s (setting to symbol start)\n",
409                            lineno, name);
410         }
411     }
412
413     if (sgv.num - sgv.num_thunks > 1 || /* many symbols non thunks (and showing only non thunks) */
414         (sgv.num > 1 && DBG_IVAR(AlwaysShowThunks)) || /* many symbols (showing symbols & thunks) */
415         (sgv.num == sgv.num_thunks && sgv.num_thunks > 1))
416     {
417         return symbol_current_picker(name, &sgv, rtn);
418     }
419     /* first symbol is the one we want:
420      * - only one symbol found,
421      * - or many symbols but only one non thunk when AlwaysShowThunks is FALSE
422      */
423     *rtn = sgv.syms[0].lvalue;
424     return sglv_found;
425 }
426
427 BOOL symbol_is_local(const char* name)
428 {
429     struct sgv_data             sgv;
430     IMAGEHLP_STACK_FRAME        ihsf;
431
432     sgv.num        = 0;
433     sgv.num_thunks = 0;
434     sgv.name       = name;
435     sgv.do_thunks  = FALSE;
436
437     if (stack_get_current_frame(&ihsf))
438     {
439         sgv.frame_offset = ihsf.FrameOffset;
440         SymEnumSymbols(dbg_curr_process->handle, 0, name, sgv_cb, (void*)&sgv);
441     }
442     return sgv.num > 0;
443 }
444
445 /***********************************************************************
446  *           symbol_read_symtable
447  *
448  * Read a symbol file into the hash table.
449  */
450 void symbol_read_symtable(const char* filename, unsigned long offset)
451 {
452     dbg_printf("No longer supported\n");
453
454 #if 0
455 /* FIXME: have to implement SymAddSymbol in dbghelp, but likely we'll need to link
456  * this with an already loaded module !! 
457  */
458     FILE*       symbolfile;
459     unsigned    addr;
460     char        type;
461     char*       cpnt;
462     char        buffer[256];
463     char        name[256];
464
465     if (!(symbolfile = fopen(filename, "r")))
466     {
467         WINE_WARN("Unable to open symbol table %s\n", filename);
468         return;
469     }
470
471     dbg_printf("Reading symbols from file %s\n", filename);
472
473     while (1)
474     {
475         fgets(buffer, sizeof(buffer), symbolfile);
476         if (feof(symbolfile)) break;
477
478         /* Strip any text after a # sign (i.e. comments) */
479         cpnt = strchr(buffer, '#');
480         if (cpnt) *cpnt = '\0';
481
482         /* Quietly ignore any lines that have just whitespace */
483         for (cpnt = buffer; *cpnt; cpnt++)
484         {
485             if (*cpnt != ' ' && *cpnt != '\t') break;
486         }
487         if (!*cpnt || *cpnt == '\n') continue;
488
489         if (sscanf(buffer, "%lx %c %s", &addr, &type, name) == 3)
490         {
491             if (value.addr.off + offset < value.addr.off)
492                 WINE_WARN("Address wrap around\n");
493             value.addr.off += offset;
494             SymAddSymbol(current_process->handle, BaseOfDll,
495                          name, addr, 0, 0);
496         }
497     }
498     fclose(symbolfile);
499 #endif
500 }
501
502 /***********************************************************************
503  *           symbol_get_function_line_status
504  *
505  * Find the symbol nearest to a given address.
506  */
507 enum dbg_line_status symbol_get_function_line_status(const ADDRESS64* addr)
508 {
509     IMAGEHLP_LINE       il;
510     DWORD               disp;
511     ULONG64             disp64, start;
512     DWORD               lin = (DWORD)memory_to_linear_addr(addr);
513     char                buffer[sizeof(SYMBOL_INFO) + 256];
514     SYMBOL_INFO*        sym = (SYMBOL_INFO*)buffer;
515     struct dbg_type     func;
516
517     il.SizeOfStruct = sizeof(il);
518     sym->SizeOfStruct = sizeof(SYMBOL_INFO);
519     sym->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
520
521     /* do we have some info for lin address ? */
522     if (!SymFromAddr(dbg_curr_process->handle, lin, &disp64, sym))
523         return dbg_no_line_info;
524
525     switch (sym->Tag)
526     {
527     case SymTagThunk:
528         /* FIXME: so far dbghelp doesn't return the 16 <=> 32 thunks
529          * and furthermore, we no longer take care of them !!!
530          */
531         return dbg_in_a_thunk;
532     case SymTagFunction:
533     case SymTagPublicSymbol: break;
534     default:
535         WINE_FIXME("Unexpected sym-tag 0x%08x\n", sym->Tag);
536     case SymTagData:
537         return dbg_no_line_info;
538     }
539     /* we should have a function now */
540     if (!SymGetLineFromAddr(dbg_curr_process->handle, lin, &disp, &il))
541         return dbg_no_line_info;
542
543     func.module = sym->ModBase;
544     func.id     = sym->info;
545
546     if (symbol_get_debug_start(&func, &start) && lin < start)
547         return dbg_not_on_a_line_number;
548
549     if (!sym->Size) sym->Size = 0x100000;
550     if (il.FileName && il.FileName[0] && disp < sym->Size)
551         return (disp == 0) ? dbg_on_a_line_number : dbg_not_on_a_line_number;
552
553     return dbg_no_line_info;
554 }
555
556 /***********************************************************************
557  *           symbol_get_line
558  *
559  * Find the symbol nearest to a given address.
560  * Returns sourcefile name and line number in a format that the listing
561  * handler can deal with.
562  */
563 BOOL symbol_get_line(const char* filename, const char* name, IMAGEHLP_LINE* line)
564 {
565     struct sgv_data     sgv;
566     char                buffer[512];
567     DWORD               opt, disp, linear;
568     unsigned            i, found = FALSE;
569     IMAGEHLP_LINE       il;
570
571     sgv.num        = 0;
572     sgv.num_thunks = 0;
573     sgv.name       = &buffer[2];
574     sgv.do_thunks  = FALSE;
575
576     buffer[0] = '*';
577     buffer[1] = '!';
578     strcpy(&buffer[2], name);
579
580     /* this is a wine specific options to return also ELF modules in the
581      * enumeration
582      */
583     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
584     if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
585     {
586         SymSetOptions(opt);
587         return FALSE;
588     }
589
590     if (!sgv.num && (name[0] != '_'))
591     {
592         buffer[2] = '_';
593         strcpy(&buffer[3], name);
594         if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
595         {
596             SymSetOptions(opt);
597             return FALSE;
598         }
599     }
600     SymSetOptions(opt);
601
602     for (i = 0; i < sgv.num; i++)
603     {
604         linear = (DWORD)memory_to_linear_addr(&sgv.syms[i].lvalue.addr);
605
606         il.SizeOfStruct = sizeof(il);
607         if (!SymGetLineFromAddr(dbg_curr_process->handle, linear, &disp, &il))
608             continue;
609         if (filename && strcmp(line->FileName, filename)) continue;
610         if (found)
611         {
612             WINE_FIXME("Several found, returning first (may not be what you want)...\n");
613             break;
614         }
615         found = TRUE;
616         *line = il;
617     }
618     if (!found)
619     {
620         if (filename)   dbg_printf("No such function %s in %s\n", name, filename);
621         else            dbg_printf("No such function %s\n", name);
622         return FALSE;
623     }
624     return TRUE;
625 }
626
627 /******************************************************************
628  *              symbol_print_local
629  *
630  * Overall format is:
631  * <name>=<value>                       in non detailed form
632  * <name>=<value> (local|pmt <where>)   in detailed form
633  * Note <value> can be an error message in case of error
634  */
635 void symbol_print_local(const SYMBOL_INFO* sym, ULONG base, 
636                         BOOL detailed)
637 {
638     struct dbg_lvalue   lvalue;
639     char                buffer[64];
640
641     dbg_printf("%s=", sym->Name);
642
643     if (fill_sym_lvalue(sym, base, &lvalue, buffer, sizeof(buffer)))
644     {
645         print_value(&lvalue, 0, 1);
646         if (detailed)
647             dbg_printf(" (%s%s)",
648                        (sym->Flags & SYMFLAG_PARAMETER) ? "parameter" : "local",
649                        buffer);
650     }
651     else
652     {
653         dbg_printf(buffer);
654         if (detailed)
655             dbg_printf(" (%s)",
656                        (sym->Flags & SYMFLAG_PARAMETER) ? "parameter" : "local");
657     }
658 }
659
660 static BOOL CALLBACK info_locals_cb(PSYMBOL_INFO sym, ULONG size, PVOID ctx)
661 {
662     struct dbg_type     type;
663
664     dbg_printf("\t");
665     type.module = sym->ModBase;
666     type.id = sym->TypeIndex;
667     types_print_type(&type, FALSE);
668
669     dbg_printf(" ");
670     symbol_print_local(sym, (ULONG)ctx, TRUE);
671     dbg_printf("\n");
672
673     return TRUE;
674 }
675
676 int symbol_info_locals(void)
677 {
678     IMAGEHLP_STACK_FRAME        ihsf;
679     ADDRESS64                   addr;
680
681     stack_get_current_frame(&ihsf);
682     addr.Mode = AddrModeFlat;
683     addr.Offset = ihsf.InstructionOffset;
684     print_address(&addr, FALSE);
685     dbg_printf(": (%08lx)\n", (DWORD_PTR)ihsf.FrameOffset);
686     SymEnumSymbols(dbg_curr_process->handle, 0, NULL, info_locals_cb, (void*)(DWORD_PTR)ihsf.FrameOffset);
687
688     return TRUE;
689
690 }
691
692 static BOOL CALLBACK symbols_info_cb(PSYMBOL_INFO sym, ULONG size, PVOID ctx)
693 {
694     struct dbg_type     type;
695     IMAGEHLP_MODULE     mi;
696
697     mi.SizeOfStruct = sizeof(mi);
698
699     if (!SymGetModuleInfo(dbg_curr_process->handle, sym->ModBase, &mi))
700         mi.ModuleName[0] = '\0';
701     else
702     {
703         size_t  len = strlen(mi.ModuleName);
704         if (len > 5 && !strcmp(mi.ModuleName + len - 5, "<elf>"))
705             mi.ModuleName[len - 5] = '\0';
706     }
707
708     dbg_printf("%08lx: %s!%s", (ULONG_PTR)sym->Address, mi.ModuleName, sym->Name);
709     type.id = sym->TypeIndex;
710     type.module = sym->ModBase;
711
712     if (sym->TypeIndex != dbg_itype_none && sym->TypeIndex != 0)
713     {
714         dbg_printf(" ");
715         types_print_type(&type, FALSE);
716     }
717     dbg_printf("\n");
718     return TRUE;
719 }
720
721 void symbol_info(const char* str)
722 {
723     char        buffer[512];
724     DWORD       opt;
725
726     if (strlen(str) + 3 >= sizeof(buffer))
727     {
728         dbg_printf("Symbol too long (%s)\n", str);
729         return;
730     }
731     buffer[0] = '*';
732     buffer[1] = '!';
733     strcpy(&buffer[2], str);
734     /* this is a wine specific options to return also ELF modules in the
735      * enumeration
736      */
737     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
738     SymEnumSymbols(dbg_curr_process->handle, 0, buffer, symbols_info_cb, NULL);
739     SymSetOptions(opt);
740 }