xcopy: Created Dutch translations.
[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 /***********************************************************************
196  *           symbol_get_lvalue
197  *
198  * Get the address of a named symbol.
199  * Return values:
200  *      sglv_found:   if the symbol is found
201  *      sglv_unknown: if the symbol isn't found
202  *      sglv_aborted: some error occurred (likely, many symbols of same name exist,
203  *          and user didn't pick one of them)
204  */
205 enum sym_get_lval symbol_get_lvalue(const char* name, const int lineno,
206                                     struct dbg_lvalue* rtn, BOOL bp_disp)
207 {
208     struct sgv_data             sgv;
209     int                         i;
210     char                        buffer[512];
211     DWORD                       opt;
212     IMAGEHLP_STACK_FRAME        ihsf;
213
214     if (strlen(name) + 4 > sizeof(buffer))
215     {
216         WINE_WARN("Too long symbol (%s)\n", name);
217         return sglv_unknown;
218     }
219
220     sgv.num        = 0;
221     sgv.num_thunks = 0;
222     sgv.name       = &buffer[2];
223     sgv.do_thunks  = DBG_IVAR(AlwaysShowThunks);
224
225     if (strchr(name, '!'))
226     {
227         strcpy(buffer, name);
228     }
229     else
230     {
231         buffer[0] = '*';
232         buffer[1] = '!';
233         strcpy(&buffer[2], name);
234     }
235
236     /* this is a wine specific options to return also ELF modules in the
237      * enumeration
238      */
239     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
240     SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv);
241
242     if (!sgv.num)
243     {
244         const char*   ptr = strchr(name, '!');
245         if ((ptr && ptr[1] != '_') || (!ptr && *name != '_'))
246         {
247             if (ptr)
248             {
249                 int offset = ptr - name;
250                 memcpy(buffer, name, offset + 1);
251                 buffer[offset + 1] = '_';
252                 strcpy(&buffer[offset + 2], ptr + 1);
253             }
254             else
255             {
256                 buffer[0] = '*';
257                 buffer[1] = '!';
258                 buffer[2] = '_';
259                 strcpy(&buffer[3], name);
260             }
261             SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv);
262         }
263     }
264     SymSetOptions(opt);
265
266     /* now grab local symbols */
267     if (stack_get_current_frame(&ihsf) && sgv.num < NUMDBGV)
268     {
269         sgv.frame_offset = ihsf.FrameOffset;
270         SymEnumSymbols(dbg_curr_process->handle, 0, name, sgv_cb, (void*)&sgv);
271     }
272
273     if (!sgv.num)
274     {
275         dbg_printf("No symbols found for %s\n", name);
276         return sglv_unknown;
277     }
278
279     /* recompute potential offsets for functions (linenumber, skip prolog) */
280     for (i = 0; i < sgv.num; i++)
281     {
282         if (sgv.syms[i].flags & (SYMFLAG_REGISTER|SYMFLAG_REGREL|SYMFLAG_LOCAL|SYMFLAG_THUNK))
283             continue;
284
285         if (lineno == -1)
286         {
287             struct dbg_type     type;
288             ULONG64             addr;
289
290             type.module = sgv.syms[i].lvalue.type.module;
291             type.id     = sgv.syms[i].sym_info;
292             if (bp_disp && symbol_get_debug_start(&type, &addr))
293                 sgv.syms[i].lvalue.addr.Offset = addr;
294         }
295         else
296         {
297             DWORD               disp;
298             IMAGEHLP_LINE       il;
299             BOOL                found = FALSE;
300
301             il.SizeOfStruct = sizeof(il);
302             SymGetLineFromAddr(dbg_curr_process->handle,
303                                (DWORD)memory_to_linear_addr(&sgv.syms[i].lvalue.addr),
304                                &disp, &il);
305             do
306             {
307                 if (lineno == il.LineNumber)
308                 {
309                     sgv.syms[i].lvalue.addr.Offset = il.Address;
310                     found = TRUE;
311                     break;
312                 }
313             } while (SymGetLineNext(dbg_curr_process->handle, &il));
314             if (!found)
315                 WINE_FIXME("No line (%d) found for %s (setting to symbol start)\n",
316                            lineno, name);
317         }
318     }
319
320     i = 0;
321     if (dbg_interactiveP)
322     {
323         if (sgv.num - sgv.num_thunks > 1 || /* many symbols non thunks (and showing only non thunks) */
324             (sgv.num > 1 && DBG_IVAR(AlwaysShowThunks)) || /* many symbols (showing symbols & thunks) */
325             (sgv.num == sgv.num_thunks && sgv.num_thunks > 1))
326         {
327             dbg_printf("Many symbols with name '%s', "
328                        "choose the one you want (<cr> to abort):\n", name);
329             for (i = 0; i < sgv.num; i++) 
330             {
331                 if (sgv.num - sgv.num_thunks > 1 && (sgv.syms[i].flags & SYMFLAG_THUNK) && !DBG_IVAR(AlwaysShowThunks))
332                     continue;
333                 dbg_printf("[%d]: ", i + 1);
334                 if (sgv.syms[i].flags & SYMFLAG_LOCAL)
335                 {
336                     dbg_printf("%s %sof %s\n",
337                                sgv.syms[i].flags & SYMFLAG_PARAMETER ? "Parameter" : "Local variable",
338                                sgv.syms[i].flags & (SYMFLAG_REGISTER|SYMFLAG_REGREL) ? "(in a register) " : "",
339                                name);
340                 }
341                 else if (sgv.syms[i].flags & SYMFLAG_THUNK) 
342                 {
343                     print_address(&sgv.syms[i].lvalue.addr, TRUE);
344                     /* FIXME: should display where the thunks points to */
345                     dbg_printf(" thunk %s\n", name);
346                 }
347                 else
348                 {
349                     print_address(&sgv.syms[i].lvalue.addr, TRUE);
350                     dbg_printf("\n");
351                 }
352             }
353             do
354             {
355                 i = 0;
356                 if (input_read_line("=> ", buffer, sizeof(buffer)))
357                 {
358                     if (buffer[0] == '\0') return sglv_aborted;
359                     i = atoi(buffer);
360                     if (i < 1 || i > sgv.num)
361                         dbg_printf("Invalid choice %d\n", i);
362                 }
363                 else return sglv_aborted;
364             } while (i < 1 || i > sgv.num);
365
366             /* The array is 0-based, but the choices are 1..n, 
367              * so we have to subtract one before returning.
368              */
369             i--;
370         }
371     }
372     else
373     {
374         /* FIXME: could display the list of non-picked up symbols */
375         if (sgv.num > 1)
376             dbg_printf("More than one symbol named %s, picking the first one\n", name);
377     }
378     *rtn = sgv.syms[i].lvalue;
379     return sglv_found;
380 }
381
382 BOOL symbol_is_local(const char* name)
383 {
384     struct sgv_data             sgv;
385     IMAGEHLP_STACK_FRAME        ihsf;
386
387     sgv.num        = 0;
388     sgv.num_thunks = 0;
389     sgv.name       = name;
390     sgv.do_thunks  = FALSE;
391
392     if (stack_get_current_frame(&ihsf))
393     {
394         sgv.frame_offset = ihsf.FrameOffset;
395         SymEnumSymbols(dbg_curr_process->handle, 0, name, sgv_cb, (void*)&sgv);
396     }
397     return sgv.num > 0;
398 }
399
400 /***********************************************************************
401  *           symbol_read_symtable
402  *
403  * Read a symbol file into the hash table.
404  */
405 void symbol_read_symtable(const char* filename, unsigned long offset)
406 {
407     dbg_printf("No longer supported\n");
408
409 #if 0
410 /* FIXME: have to implement SymAddSymbol in dbghelp, but likely we'll need to link
411  * this with an already loaded module !! 
412  */
413     FILE*       symbolfile;
414     unsigned    addr;
415     char        type;
416     char*       cpnt;
417     char        buffer[256];
418     char        name[256];
419
420     if (!(symbolfile = fopen(filename, "r")))
421     {
422         WINE_WARN("Unable to open symbol table %s\n", filename);
423         return;
424     }
425
426     dbg_printf("Reading symbols from file %s\n", filename);
427
428     while (1)
429     {
430         fgets(buffer, sizeof(buffer), symbolfile);
431         if (feof(symbolfile)) break;
432
433         /* Strip any text after a # sign (i.e. comments) */
434         cpnt = strchr(buffer, '#');
435         if (cpnt) *cpnt = '\0';
436
437         /* Quietly ignore any lines that have just whitespace */
438         for (cpnt = buffer; *cpnt; cpnt++)
439         {
440             if (*cpnt != ' ' && *cpnt != '\t') break;
441         }
442         if (!*cpnt || *cpnt == '\n') continue;
443
444         if (sscanf(buffer, "%lx %c %s", &addr, &type, name) == 3)
445         {
446             if (value.addr.off + offset < value.addr.off)
447                 WINE_WARN("Address wrap around\n");
448             value.addr.off += offset;
449             SymAddSymbol(current_process->handle, BaseOfDll,
450                          name, addr, 0, 0);
451         }
452     }
453     fclose(symbolfile);
454 #endif
455 }
456
457 /***********************************************************************
458  *           symbol_get_function_line_status
459  *
460  * Find the symbol nearest to a given address.
461  */
462 enum dbg_line_status symbol_get_function_line_status(const ADDRESS64* addr)
463 {
464     IMAGEHLP_LINE       il;
465     DWORD               disp;
466     ULONG64             disp64, start;
467     DWORD               lin = (DWORD)memory_to_linear_addr(addr);
468     char                buffer[sizeof(SYMBOL_INFO) + 256];
469     SYMBOL_INFO*        sym = (SYMBOL_INFO*)buffer;
470     struct dbg_type     func;
471
472     il.SizeOfStruct = sizeof(il);
473     sym->SizeOfStruct = sizeof(SYMBOL_INFO);
474     sym->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
475
476     /* do we have some info for lin address ? */
477     if (!SymFromAddr(dbg_curr_process->handle, lin, &disp64, sym))
478         return dbg_no_line_info;
479
480     switch (sym->Tag)
481     {
482     case SymTagThunk:
483         /* FIXME: so far dbghelp doesn't return the 16 <=> 32 thunks
484          * and furthermore, we no longer take care of them !!!
485          */
486         return dbg_in_a_thunk;
487     case SymTagFunction:
488     case SymTagPublicSymbol: break;
489     default:
490         WINE_FIXME("Unexpected sym-tag 0x%08x\n", sym->Tag);
491     case SymTagData:
492         return dbg_no_line_info;
493     }
494     /* we should have a function now */
495     if (!SymGetLineFromAddr(dbg_curr_process->handle, lin, &disp, &il))
496         return dbg_no_line_info;
497
498     func.module = sym->ModBase;
499     func.id     = sym->info;
500
501     if (symbol_get_debug_start(&func, &start) && lin < start)
502         return dbg_not_on_a_line_number;
503
504     if (!sym->Size) sym->Size = 0x100000;
505     if (il.FileName && il.FileName[0] && disp < sym->Size)
506         return (disp == 0) ? dbg_on_a_line_number : dbg_not_on_a_line_number;
507
508     return dbg_no_line_info;
509 }
510
511 /***********************************************************************
512  *           symbol_get_line
513  *
514  * Find the symbol nearest to a given address.
515  * Returns sourcefile name and line number in a format that the listing
516  * handler can deal with.
517  */
518 BOOL symbol_get_line(const char* filename, const char* name, IMAGEHLP_LINE* line)
519 {
520     struct sgv_data     sgv;
521     char                buffer[512];
522     DWORD               opt, disp, linear;
523     unsigned            i, found = FALSE;
524     IMAGEHLP_LINE       il;
525
526     sgv.num        = 0;
527     sgv.num_thunks = 0;
528     sgv.name       = &buffer[2];
529     sgv.do_thunks  = FALSE;
530
531     buffer[0] = '*';
532     buffer[1] = '!';
533     strcpy(&buffer[2], name);
534
535     /* this is a wine specific options to return also ELF modules in the
536      * enumeration
537      */
538     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
539     if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
540     {
541         SymSetOptions(opt);
542         return FALSE;
543     }
544
545     if (!sgv.num && (name[0] != '_'))
546     {
547         buffer[2] = '_';
548         strcpy(&buffer[3], name);
549         if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
550         {
551             SymSetOptions(opt);
552             return FALSE;
553         }
554     }
555     SymSetOptions(opt);
556
557     for (i = 0; i < sgv.num; i++)
558     {
559         linear = (DWORD)memory_to_linear_addr(&sgv.syms[i].lvalue.addr);
560
561         il.SizeOfStruct = sizeof(il);
562         if (!SymGetLineFromAddr(dbg_curr_process->handle, linear, &disp, &il))
563             continue;
564         if (filename && strcmp(line->FileName, filename)) continue;
565         if (found)
566         {
567             WINE_FIXME("Several found, returning first (may not be what you want)...\n");
568             break;
569         }
570         found = TRUE;
571         *line = il;
572     }
573     if (!found)
574     {
575         if (filename)   dbg_printf("No such function %s in %s\n", name, filename);
576         else            dbg_printf("No such function %s\n", name);
577         return FALSE;
578     }
579     return TRUE;
580 }
581
582 /******************************************************************
583  *              symbol_print_local
584  *
585  * Overall format is:
586  * <name>=<value>                       in non detailed form
587  * <name>=<value> (local|pmt <where>)   in detailed form
588  * Note <value> can be an error message in case of error
589  */
590 void symbol_print_local(const SYMBOL_INFO* sym, ULONG base, 
591                         BOOL detailed)
592 {
593     struct dbg_lvalue   lvalue;
594     char                buffer[64];
595
596     dbg_printf("%s=", sym->Name);
597
598     if (fill_sym_lvalue(sym, base, &lvalue, buffer, sizeof(buffer)))
599     {
600         print_value(&lvalue, 'x', 1);
601         if (detailed)
602             dbg_printf(" (%s%s)",
603                        (sym->Flags & SYMFLAG_PARAMETER) ? "parameter" : "local",
604                        buffer);
605     }
606     else
607     {
608         dbg_printf(buffer);
609         if (detailed)
610             dbg_printf(" (%s)",
611                        (sym->Flags & SYMFLAG_PARAMETER) ? "parameter" : "local");
612     }
613 }
614
615 static BOOL CALLBACK info_locals_cb(PSYMBOL_INFO sym, ULONG size, PVOID ctx)
616 {
617     struct dbg_type     type;
618
619     dbg_printf("\t");
620     type.module = sym->ModBase;
621     type.id = sym->TypeIndex;
622     types_print_type(&type, FALSE);
623
624     dbg_printf(" ");
625     symbol_print_local(sym, (ULONG)ctx, TRUE);
626     dbg_printf("\n");
627
628     return TRUE;
629 }
630
631 int symbol_info_locals(void)
632 {
633     IMAGEHLP_STACK_FRAME        ihsf;
634     ADDRESS64                   addr;
635
636     stack_get_current_frame(&ihsf);
637     addr.Mode = AddrModeFlat;
638     addr.Offset = ihsf.InstructionOffset;
639     print_address(&addr, FALSE);
640     dbg_printf(": (%08lx)\n", (DWORD_PTR)ihsf.FrameOffset);
641     SymEnumSymbols(dbg_curr_process->handle, 0, NULL, info_locals_cb, (void*)(DWORD_PTR)ihsf.FrameOffset);
642
643     return TRUE;
644
645 }
646
647 static BOOL CALLBACK symbols_info_cb(PSYMBOL_INFO sym, ULONG size, PVOID ctx)
648 {
649     struct dbg_type     type;
650     IMAGEHLP_MODULE     mi;
651
652     mi.SizeOfStruct = sizeof(mi);
653
654     if (!SymGetModuleInfo(dbg_curr_process->handle, sym->ModBase, &mi))
655         mi.ModuleName[0] = '\0';
656     else
657     {
658         size_t  len = strlen(mi.ModuleName);
659         if (len > 5 && !strcmp(mi.ModuleName + len - 5, "<elf>"))
660             mi.ModuleName[len - 5] = '\0';
661     }
662
663     dbg_printf("%08lx: %s!%s", (ULONG_PTR)sym->Address, mi.ModuleName, sym->Name);
664     type.id = sym->TypeIndex;
665     type.module = sym->ModBase;
666
667     if (sym->TypeIndex != dbg_itype_none && sym->TypeIndex != 0)
668     {
669         dbg_printf(" ");
670         types_print_type(&type, FALSE);
671     }
672     dbg_printf("\n");
673     return TRUE;
674 }
675
676 void symbol_info(const char* str)
677 {
678     char        buffer[512];
679     DWORD       opt;
680
681     if (strlen(str) + 3 >= sizeof(buffer))
682     {
683         dbg_printf("Symbol too long (%s)\n", str);
684         return;
685     }
686     buffer[0] = '*';
687     buffer[1] = '!';
688     strcpy(&buffer[2], str);
689     /* this is a wine specific options to return also ELF modules in the
690      * enumeration
691      */
692     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
693     SymEnumSymbols(dbg_curr_process->handle, 0, buffer, symbols_info_cb, NULL);
694     SymSetOptions(opt);
695 }