server: Hold a pointer to the queue from the async operations.
[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(SYMBOL_INFO* sym, ULONG size, void* 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             } while (i < 1 || i > sgv.num);
364
365             /* The array is 0-based, but the choices are 1..n, 
366              * so we have to subtract one before returning.
367              */
368             i--;
369         }
370     }
371     else
372     {
373         /* FIXME: could display the list of non-picked up symbols */
374         if (sgv.num > 1)
375             dbg_printf("More than one symbol named %s, picking the first one\n", name);
376     }
377     *rtn = sgv.syms[i].lvalue;
378     return sglv_found;
379 }
380
381 BOOL symbol_is_local(const char* name)
382 {
383     struct sgv_data             sgv;
384     IMAGEHLP_STACK_FRAME        ihsf;
385
386     sgv.num        = 0;
387     sgv.num_thunks = 0;
388     sgv.name       = name;
389     sgv.do_thunks  = FALSE;
390
391     if (stack_get_current_frame(&ihsf))
392     {
393         sgv.frame_offset = ihsf.FrameOffset;
394         SymEnumSymbols(dbg_curr_process->handle, 0, name, sgv_cb, (void*)&sgv);
395     }
396     return sgv.num > 0;
397 }
398
399 /***********************************************************************
400  *           symbol_read_symtable
401  *
402  * Read a symbol file into the hash table.
403  */
404 void symbol_read_symtable(const char* filename, unsigned long offset)
405 {
406     dbg_printf("No longer supported\n");
407
408 #if 0
409 /* FIXME: have to implement SymAddSymbol in dbghelp, but likely we'll need to link
410  * this with an already loaded module !! 
411  */
412     FILE*       symbolfile;
413     unsigned    addr;
414     char        type;
415     char*       cpnt;
416     char        buffer[256];
417     char        name[256];
418
419     if (!(symbolfile = fopen(filename, "r")))
420     {
421         WINE_WARN("Unable to open symbol table %s\n", filename);
422         return;
423     }
424
425     dbg_printf("Reading symbols from file %s\n", filename);
426
427     while (1)
428     {
429         fgets(buffer, sizeof(buffer), symbolfile);
430         if (feof(symbolfile)) break;
431
432         /* Strip any text after a # sign (i.e. comments) */
433         cpnt = strchr(buffer, '#');
434         if (cpnt) *cpnt = '\0';
435
436         /* Quietly ignore any lines that have just whitespace */
437         for (cpnt = buffer; *cpnt; cpnt++)
438         {
439             if (*cpnt != ' ' && *cpnt != '\t') break;
440         }
441         if (!*cpnt || *cpnt == '\n') continue;
442
443         if (sscanf(buffer, "%lx %c %s", &addr, &type, name) == 3)
444         {
445             if (value.addr.off + offset < value.addr.off)
446                 WINE_WARN("Address wrap around\n");
447             value.addr.off += offset;
448             SymAddSymbol(current_process->handle, BaseOfDll,
449                          name, addr, 0, 0);
450         }
451     }
452     fclose(symbolfile);
453 #endif
454 }
455
456 /***********************************************************************
457  *           symbol_get_function_line_status
458  *
459  * Find the symbol nearest to a given address.
460  */
461 enum dbg_line_status symbol_get_function_line_status(const ADDRESS64* addr)
462 {
463     IMAGEHLP_LINE       il;
464     DWORD               disp;
465     ULONG64             disp64, start;
466     DWORD               lin = (DWORD)memory_to_linear_addr(addr);
467     char                buffer[sizeof(SYMBOL_INFO) + 256];
468     SYMBOL_INFO*        sym = (SYMBOL_INFO*)buffer;
469     struct dbg_type     func;
470
471     il.SizeOfStruct = sizeof(il);
472     sym->SizeOfStruct = sizeof(SYMBOL_INFO);
473     sym->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
474
475     /* do we have some info for lin address ? */
476     if (!SymFromAddr(dbg_curr_process->handle, lin, &disp64, sym))
477         return dbg_no_line_info;
478
479     switch (sym->Tag)
480     {
481     case SymTagThunk:
482         /* FIXME: so far dbghelp doesn't return the 16 <=> 32 thunks
483          * and furthermore, we no longer take care of them !!!
484          */
485         return dbg_in_a_thunk;
486     case SymTagFunction:
487     case SymTagPublicSymbol: break;
488     default:
489         WINE_FIXME("Unexpected sym-tag 0x%08x\n", sym->Tag);
490     case SymTagData:
491         return dbg_no_line_info;
492     }
493     /* we should have a function now */
494     if (!SymGetLineFromAddr(dbg_curr_process->handle, lin, &disp, &il))
495         return dbg_no_line_info;
496
497     func.module = sym->ModBase;
498     func.id     = sym->info;
499
500     if (symbol_get_debug_start(&func, &start) && lin < start)
501         return dbg_not_on_a_line_number;
502
503     if (!sym->Size) sym->Size = 0x100000;
504     if (il.FileName && il.FileName[0] && disp < sym->Size)
505         return (disp == 0) ? dbg_on_a_line_number : dbg_not_on_a_line_number;
506
507     return dbg_no_line_info;
508 }
509
510 /***********************************************************************
511  *           symbol_get_line
512  *
513  * Find the symbol nearest to a given address.
514  * Returns sourcefile name and line number in a format that the listing
515  * handler can deal with.
516  */
517 BOOL symbol_get_line(const char* filename, const char* name, IMAGEHLP_LINE* line)
518 {
519     struct sgv_data     sgv;
520     char                buffer[512];
521     DWORD               opt, disp, linear;
522     unsigned            i, found = FALSE;
523     IMAGEHLP_LINE       il;
524
525     sgv.num        = 0;
526     sgv.num_thunks = 0;
527     sgv.name       = &buffer[2];
528     sgv.do_thunks  = FALSE;
529
530     buffer[0] = '*';
531     buffer[1] = '!';
532     strcpy(&buffer[2], name);
533
534     /* this is a wine specific options to return also ELF modules in the
535      * enumeration
536      */
537     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
538     if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
539     {
540         SymSetOptions(opt);
541         return FALSE;
542     }
543
544     if (!sgv.num && (name[0] != '_'))
545     {
546         buffer[2] = '_';
547         strcpy(&buffer[3], name);
548         if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
549         {
550             SymSetOptions(opt);
551             return FALSE;
552         }
553     }
554     SymSetOptions(opt);
555
556     for (i = 0; i < sgv.num; i++)
557     {
558         linear = (DWORD)memory_to_linear_addr(&sgv.syms[i].lvalue.addr);
559
560         il.SizeOfStruct = sizeof(il);
561         if (!SymGetLineFromAddr(dbg_curr_process->handle, linear, &disp, &il))
562             continue;
563         if (filename && strcmp(line->FileName, filename)) continue;
564         if (found)
565         {
566             WINE_FIXME("Several found, returning first (may not be what you want)...\n");
567             break;
568         }
569         found = TRUE;
570         *line = il;
571     }
572     if (!found)
573     {
574         if (filename)   dbg_printf("No such function %s in %s\n", name, filename);
575         else            dbg_printf("No such function %s\n", name);
576         return FALSE;
577     }
578     return TRUE;
579 }
580
581 /******************************************************************
582  *              symbol_print_local
583  *
584  * Overall format is:
585  * <name>=<value>                       in non detailed form
586  * <name>=<value> (local|pmt <where>)   in detailed form
587  * Note <value> can be an error message in case of error
588  */
589 void symbol_print_local(const SYMBOL_INFO* sym, ULONG base, 
590                         BOOL detailed)
591 {
592     struct dbg_lvalue   lvalue;
593     char                buffer[64];
594
595     dbg_printf("%s=", sym->Name);
596
597     if (fill_sym_lvalue(sym, base, &lvalue, buffer, sizeof(buffer)))
598     {
599         print_value(&lvalue, 'x', 1);
600         if (detailed)
601             dbg_printf(" (%s%s)",
602                        (sym->Flags & SYMFLAG_PARAMETER) ? "parameter" : "local",
603                        buffer);
604     }
605     else
606     {
607         dbg_printf(buffer);
608         if (detailed)
609             dbg_printf(" (%s)",
610                        (sym->Flags & SYMFLAG_PARAMETER) ? "parameter" : "local");
611     }
612 }
613
614 static BOOL CALLBACK info_locals_cb(SYMBOL_INFO* sym, ULONG size, void* ctx)
615 {
616     struct dbg_type     type;
617
618     dbg_printf("\t");
619     type.module = sym->ModBase;
620     type.id = sym->TypeIndex;
621     types_print_type(&type, FALSE);
622
623     dbg_printf(" ");
624     symbol_print_local(sym, (ULONG)ctx, TRUE);
625     dbg_printf("\n");
626
627     return TRUE;
628 }
629
630 int symbol_info_locals(void)
631 {
632     IMAGEHLP_STACK_FRAME        ihsf;
633     ADDRESS64                   addr;
634
635     stack_get_current_frame(&ihsf);
636     addr.Mode = AddrModeFlat;
637     addr.Offset = ihsf.InstructionOffset;
638     print_address(&addr, FALSE);
639     dbg_printf(": (%08lx)\n", (DWORD_PTR)ihsf.FrameOffset);
640     SymEnumSymbols(dbg_curr_process->handle, 0, NULL, info_locals_cb, (void*)(DWORD_PTR)ihsf.FrameOffset);
641
642     return TRUE;
643
644 }
645
646 static BOOL CALLBACK symbols_info_cb(SYMBOL_INFO* sym, ULONG size, void* ctx)
647 {
648     struct dbg_type     type;
649     IMAGEHLP_MODULE     mi;
650
651     mi.SizeOfStruct = sizeof(mi);
652
653     if (!SymGetModuleInfo(dbg_curr_process->handle, sym->ModBase, &mi))
654         mi.ModuleName[0] = '\0';
655     else
656     {
657         size_t  len = strlen(mi.ModuleName);
658         if (len > 5 && !strcmp(mi.ModuleName + len - 5, "<elf>"))
659             mi.ModuleName[len - 5] = '\0';
660     }
661
662     dbg_printf("%08lx: %s!%s", (ULONG_PTR)sym->Address, mi.ModuleName, sym->Name);
663     type.id = sym->TypeIndex;
664     type.module = sym->ModBase;
665
666     if (sym->TypeIndex != dbg_itype_none && sym->TypeIndex != 0)
667     {
668         dbg_printf(" ");
669         types_print_type(&type, FALSE);
670     }
671     dbg_printf("\n");
672     return TRUE;
673 }
674
675 void symbol_info(const char* str)
676 {
677     char        buffer[512];
678     DWORD       opt;
679
680     if (strlen(str) + 3 >= sizeof(buffer))
681     {
682         dbg_printf("Symbol too long (%s)\n", str);
683         return;
684     }
685     buffer[0] = '*';
686     buffer[1] = '!';
687     strcpy(&buffer[2], str);
688     /* this is a wine specific options to return also ELF modules in the
689      * enumeration
690      */
691     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
692     SymEnumSymbols(dbg_curr_process->handle, 0, buffer, symbols_info_cb, NULL);
693     SymSetOptions(opt);
694 }