kernel32: FindFirstChangeNotification needs a static IO_STATUS_BLOCK.
[wine] / dlls / dbghelp / symbol.c
1 /*
2  * File symbol.c - management of symbols (lexical tree)
3  *
4  * Copyright (C) 1993, Eric Youngdale.
5  *               2004, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24
25 #include "config.h"
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <limits.h>
31 #include <sys/types.h>
32 #include <assert.h>
33 #ifdef HAVE_REGEX_H
34 # include <regex.h>
35 #endif
36
37 #include "wine/debug.h"
38 #include "dbghelp_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
41 WINE_DECLARE_DEBUG_CHANNEL(dbghelp_symt);
42
43 inline static int cmp_addr(ULONG64 a1, ULONG64 a2)
44 {
45     if (a1 > a2) return 1;
46     if (a1 < a2) return -1;
47     return 0;
48 }
49
50 inline static int cmp_sorttab_addr(const struct module* module, int idx, ULONG64 addr)
51 {
52     ULONG64     ref;
53
54     symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_ADDRESS, &ref);
55     return cmp_addr(ref, addr);
56 }
57
58 int symt_cmp_addr(const void* p1, const void* p2)
59 {
60     const struct symt*  sym1 = *(const struct symt* const *)p1;
61     const struct symt*  sym2 = *(const struct symt* const *)p2;
62     ULONG64     a1, a2;
63
64     symt_get_info(sym1, TI_GET_ADDRESS, &a1);
65     symt_get_info(sym2, TI_GET_ADDRESS, &a2);
66     return cmp_addr(a1, a2);
67 }
68
69 static inline void re_append(char** mask, unsigned* len, char ch)
70 {
71     *mask = HeapReAlloc(GetProcessHeap(), 0, *mask, ++(*len));
72     (*mask)[*len - 2] = ch;
73 }
74
75 /* transforms a dbghelp's regular expression into a POSIX one
76  * Here are the valid dbghelp reg ex characters:
77  *      *       0 or more characters
78  *      ?       a single character
79  *      []      list
80  *      #       0 or more of preceding char
81  *      +       1 or more of preceding char
82  *      escapes \ on #, ?, [, ], *, +. don't work on -
83  */
84 static void compile_regex(const char* str, int numchar, regex_t* re, BOOL _case)
85 {
86     char*       mask = HeapAlloc(GetProcessHeap(), 0, 1);
87     unsigned    len = 1;
88     BOOL        in_escape = FALSE;
89     unsigned    flags = REG_NOSUB;
90
91     re_append(&mask, &len, '^');
92
93     while (*str && numchar--)
94     {
95         /* FIXME: this shouldn't be valid on '-' */
96         if (in_escape)
97         {
98             re_append(&mask, &len, '\\');
99             re_append(&mask, &len, *str);
100             in_escape = FALSE;
101         }
102         else switch (*str)
103         {
104         case '\\': in_escape = TRUE; break;
105         case '*':  re_append(&mask, &len, '.'); re_append(&mask, &len, '*'); break;
106         case '?':  re_append(&mask, &len, '.'); break;
107         case '#':  re_append(&mask, &len, '*'); break;
108         /* escape some valid characters in dbghelp reg exp:s */
109         case '$':  re_append(&mask, &len, '\\'); re_append(&mask, &len, '$'); break;
110         /* +, [, ], - are the same in dbghelp & POSIX, use them as any other char */
111         default:   re_append(&mask, &len, *str); break;
112         }
113         str++;
114     }
115     if (in_escape)
116     {
117         re_append(&mask, &len, '\\');
118         re_append(&mask, &len, '\\');
119     }
120     re_append(&mask, &len, '$');
121     mask[len - 1] = '\0';
122     if (_case) flags |= REG_ICASE;
123     if (regcomp(re, mask, flags)) FIXME("Couldn't compile %s\n", mask);
124     HeapFree(GetProcessHeap(), 0, mask);
125 }
126
127 struct symt_compiland* symt_new_compiland(struct module* module, const char* name)
128 {
129     struct symt_compiland*    sym;
130
131     TRACE_(dbghelp_symt)("Adding compiland symbol %s:%s\n", 
132                          module->module.ModuleName, name);
133     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
134     {
135         sym->symt.tag = SymTagCompiland;
136         sym->source   = source_new(module, name);
137         vector_init(&sym->vchildren, sizeof(struct symt*), 32);
138     }
139     return sym;
140 }
141
142 struct symt_public* symt_new_public(struct module* module, 
143                                     struct symt_compiland* compiland,
144                                     const char* name,
145                                     unsigned long address, unsigned size,
146                                     BOOL in_code, BOOL is_func)
147 {
148     struct symt_public* sym;
149     struct symt**       p;
150
151     TRACE_(dbghelp_symt)("Adding public symbol %s:%s @%lx\n", 
152                          module->module.ModuleName, name, address);
153     if ((dbghelp_options & SYMOPT_AUTO_PUBLICS) && 
154         symt_find_nearest(module, address) != -1)
155         return NULL;
156     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
157     {
158         sym->symt.tag      = SymTagPublicSymbol;
159         sym->hash_elt.name = pool_strdup(&module->pool, name);
160         hash_table_add(&module->ht_symbols, &sym->hash_elt);
161         module->sortlist_valid = FALSE;
162         sym->container     = compiland ? &compiland->symt : NULL;
163         sym->address       = address;
164         sym->size          = size;
165         sym->in_code       = in_code;
166         sym->is_function   = is_func;
167         if (compiland)
168         {
169             p = vector_add(&compiland->vchildren, &module->pool);
170             *p = &sym->symt;
171         }
172     }
173     return sym;
174 }
175
176 struct symt_data* symt_new_global_variable(struct module* module, 
177                                            struct symt_compiland* compiland, 
178                                            const char* name, unsigned is_static,
179                                            unsigned long addr, unsigned long size,
180                                            struct symt* type)
181 {
182     struct symt_data*   sym;
183     struct symt**       p;
184     DWORD64             tsz;
185
186     TRACE_(dbghelp_symt)("Adding global symbol %s:%s @%lx %p\n", 
187                          module->module.ModuleName, name, addr, type);
188     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
189     {
190         sym->symt.tag      = SymTagData;
191         sym->hash_elt.name = pool_strdup(&module->pool, name);
192         hash_table_add(&module->ht_symbols, &sym->hash_elt);
193         module->sortlist_valid = FALSE;
194         sym->kind          = is_static ? DataIsFileStatic : DataIsGlobal;
195         sym->container     = compiland ? &compiland->symt : NULL;
196         sym->type          = type;
197         sym->u.address     = addr;
198         if (type && size && symt_get_info(type, TI_GET_LENGTH, &tsz))
199         {
200             if (tsz != size)
201                 FIXME("Size mismatch for %s.%s between type (%s) and src (%lu)\n",
202                       module->module.ModuleName, name, 
203                       wine_dbgstr_longlong(tsz), size);
204         }
205         if (compiland)
206         {
207             p = vector_add(&compiland->vchildren, &module->pool);
208             *p = &sym->symt;
209         }
210     }
211     return sym;
212 }
213
214 struct symt_function* symt_new_function(struct module* module, 
215                                         struct symt_compiland* compiland, 
216                                         const char* name,
217                                         unsigned long addr, unsigned long size,
218                                         struct symt* sig_type)
219 {
220     struct symt_function*       sym;
221     struct symt**               p;
222
223     TRACE_(dbghelp_symt)("Adding global function %s:%s @%lx-%lx\n", 
224                          module->module.ModuleName, name, addr, addr + size - 1);
225
226     assert(!sig_type || sig_type->tag == SymTagFunctionType);
227     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
228     {
229         sym->symt.tag  = SymTagFunction;
230         sym->hash_elt.name = pool_strdup(&module->pool, name);
231         hash_table_add(&module->ht_symbols, &sym->hash_elt);
232         module->sortlist_valid = FALSE;
233         sym->container = &compiland->symt;
234         sym->address   = addr;
235         sym->type      = sig_type;
236         sym->size      = size;
237         vector_init(&sym->vlines,  sizeof(struct line_info), 64);
238         vector_init(&sym->vchildren, sizeof(struct symt*), 8);
239         if (compiland)
240         {
241             p = vector_add(&compiland->vchildren, &module->pool);
242             *p = &sym->symt;
243         }
244     }
245     return sym;
246 }
247
248 void symt_add_func_line(struct module* module, struct symt_function* func,
249                         unsigned source_idx, int line_num, unsigned long offset)
250 {
251     struct line_info*   dli;
252     BOOL                last_matches = FALSE;
253
254     if (func == NULL || !(dbghelp_options & SYMOPT_LOAD_LINES)) return;
255
256     TRACE_(dbghelp_symt)("(%p)%s:%lx %s:%u\n", 
257                          func, func->hash_elt.name, offset, 
258                          source_get(module, source_idx), line_num);
259
260     assert(func->symt.tag == SymTagFunction);
261
262     dli = NULL;
263     while ((dli = vector_iter_down(&func->vlines, dli)))
264     {
265         if (dli->is_source_file)
266         {
267             last_matches = (source_idx == dli->u.source_file);
268             break;
269         }
270     }
271
272     if (!last_matches)
273     {
274         /* we shouldn't have line changes on first line of function */
275         dli = vector_add(&func->vlines, &module->pool);
276         dli->is_source_file = 1;
277         dli->is_first       = dli->is_last = 0;
278         dli->line_number    = 0;
279         dli->u.source_file  = source_idx;
280     }
281     dli = vector_add(&func->vlines, &module->pool);
282     dli->is_source_file = 0;
283     dli->is_first       = dli->is_last = 0;
284     dli->line_number    = line_num;
285     dli->u.pc_offset    = func->address + offset;
286 }
287
288 struct symt_data* symt_add_func_local(struct module* module, 
289                                       struct symt_function* func, 
290                                       int regno, int offset, 
291                                       struct symt_block* block, 
292                                       struct symt* type, const char* name)
293 {
294     struct symt_data*   locsym;
295     struct symt**       p;
296
297     assert(func);
298     assert(func->symt.tag == SymTagFunction);
299
300     TRACE_(dbghelp_symt)("Adding local symbol (%s:%s): %s %p\n", 
301                          module->module.ModuleName, func->hash_elt.name, 
302                          name, type);
303     locsym = pool_alloc(&module->pool, sizeof(*locsym));
304     locsym->symt.tag      = SymTagData;
305     locsym->hash_elt.name = pool_strdup(&module->pool, name);
306     locsym->hash_elt.next = NULL;
307     locsym->kind          = (offset < 0) ? DataIsParam : DataIsLocal;
308     locsym->container     = &block->symt;
309     locsym->type          = type;
310     if (regno)
311     {
312         locsym->u.s.reg_id = regno;
313         locsym->u.s.offset = 0;
314         locsym->u.s.length = 0;
315     }
316     else
317     {
318         locsym->u.s.reg_id = 0;
319         locsym->u.s.offset = offset * 8;
320         locsym->u.s.length = 0;
321     }
322     if (block)
323         p = vector_add(&block->vchildren, &module->pool);
324     else
325         p = vector_add(&func->vchildren, &module->pool);
326     *p = &locsym->symt;
327     return locsym;
328 }
329
330 struct symt_block* symt_open_func_block(struct module* module, 
331                                         struct symt_function* func,
332                                         struct symt_block* parent_block, 
333                                         unsigned pc, unsigned len)
334 {
335     struct symt_block*  block;
336     struct symt**       p;
337
338     assert(func);
339     assert(func->symt.tag == SymTagFunction);
340
341     assert(!parent_block || parent_block->symt.tag == SymTagBlock);
342     block = pool_alloc(&module->pool, sizeof(*block));
343     block->symt.tag = SymTagBlock;
344     block->address  = func->address + pc;
345     block->size     = len;
346     block->container = parent_block ? &parent_block->symt : &func->symt;
347     vector_init(&block->vchildren, sizeof(struct symt*), 4);
348     if (parent_block)
349         p = vector_add(&parent_block->vchildren, &module->pool);
350     else
351         p = vector_add(&func->vchildren, &module->pool);
352     *p = &block->symt;
353
354     return block;
355 }
356
357 struct symt_block* symt_close_func_block(struct module* module, 
358                                          struct symt_function* func,
359                                          struct symt_block* block, unsigned pc)
360 {
361     assert(func->symt.tag == SymTagFunction);
362
363     if (pc) block->size = func->address + pc - block->address;
364     return (block->container->tag == SymTagBlock) ? 
365         GET_ENTRY(block->container, struct symt_block, symt) : NULL;
366 }
367
368 struct symt_function_point* symt_add_function_point(struct module* module, 
369                                                     struct symt_function* func,
370                                                     enum SymTagEnum point, 
371                                                     unsigned offset, const char* name)
372 {
373     struct symt_function_point* sym;
374     struct symt**               p;
375
376     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
377     {
378         sym->symt.tag = point;
379         sym->parent   = func;
380         sym->offset   = offset;
381         sym->name     = name ? pool_strdup(&module->pool, name) : NULL;
382         p = vector_add(&func->vchildren, &module->pool);
383         *p = &sym->symt;
384     }
385     return sym;
386 }
387
388 BOOL symt_normalize_function(struct module* module, struct symt_function* func)
389 {
390     unsigned            len;
391     struct line_info*   dli;
392
393     assert(func);
394     /* We aren't adding any more locals or line numbers to this function.
395      * Free any spare memory that we might have allocated.
396      */
397     assert(func->symt.tag == SymTagFunction);
398
399 /* EPP     vector_pool_normalize(&func->vlines,    &module->pool); */
400 /* EPP     vector_pool_normalize(&func->vchildren, &module->pool); */
401
402     len = vector_length(&func->vlines);
403     if (len--)
404     {
405         dli = vector_at(&func->vlines,   0);  dli->is_first = 1;
406         dli = vector_at(&func->vlines, len);  dli->is_last  = 1;
407     }
408     return TRUE;
409 }
410
411 struct symt_thunk* symt_new_thunk(struct module* module, 
412                                   struct symt_compiland* compiland, 
413                                   const char* name, THUNK_ORDINAL ord,
414                                   unsigned long addr, unsigned long size)
415 {
416     struct symt_thunk*  sym;
417
418     TRACE_(dbghelp_symt)("Adding global thunk %s:%s @%lx-%lx\n", 
419                          module->module.ModuleName, name, addr, addr + size - 1);
420
421     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
422     {
423         sym->symt.tag  = SymTagThunk;
424         sym->hash_elt.name = pool_strdup(&module->pool, name);
425         hash_table_add(&module->ht_symbols, &sym->hash_elt);
426         module->sortlist_valid = FALSE;
427         sym->container = &compiland->symt;
428         sym->address   = addr;
429         sym->size      = size;
430         sym->ordinal   = ord;
431         if (compiland)
432         {
433             struct symt**       p;
434             p = vector_add(&compiland->vchildren, &module->pool);
435             *p = &sym->symt;
436         }
437     }
438     return sym;
439 }
440
441 /* expect sym_info->MaxNameLen to be set before being called */
442 static void symt_fill_sym_info(const struct module* module, 
443                                const struct symt* sym, SYMBOL_INFO* sym_info)
444 {
445     const char* name;
446     DWORD64 size;
447
448     if (!symt_get_info(sym, TI_GET_TYPE, &sym_info->TypeIndex))
449         sym_info->TypeIndex = 0;
450     sym_info->info = (DWORD)sym;
451     sym_info->Reserved[0] = sym_info->Reserved[1] = 0;
452     if (!symt_get_info(sym, TI_GET_LENGTH, &size) &&
453         (!sym_info->TypeIndex ||
454          !symt_get_info((struct symt*)sym_info->TypeIndex, TI_GET_LENGTH, &size)))
455         size = 0;
456     sym_info->Size = (DWORD)size;
457     sym_info->ModBase = module->module.BaseOfImage;
458     sym_info->Flags = 0;
459     sym_info->Value = 0;
460
461     switch (sym->tag)
462     {
463     case SymTagData:
464         {
465             const struct symt_data*  data = (const struct symt_data*)sym;
466             switch (data->kind)
467             {
468             case DataIsLocal:
469             case DataIsParam:
470                 if (data->u.s.reg_id)
471                 {
472                     sym_info->Flags |= SYMFLAG_REGISTER;
473                     sym_info->Register = data->u.s.reg_id;
474                     sym_info->Address = 0;
475                 }
476                 else
477                 {
478                     sym_info->Flags |= SYMFLAG_LOCAL | SYMFLAG_REGREL;
479                     /* FIXME: this is i386 dependent */
480                     if (data->u.s.offset >= 0) sym_info->Flags |= SYMFLAG_PARAMETER;
481                     /* FIXME: needed ? moreover, it's i386 dependent !!! */
482                     sym_info->Register = CV_REG_EBP;
483                     sym_info->Address = data->u.s.offset / 8;
484                 }
485                 break;
486             case DataIsGlobal:
487             case DataIsFileStatic:
488                 symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
489                 sym_info->Register = 0;
490                 break;
491             case DataIsConstant:
492                 sym_info->Flags |= SYMFLAG_VALUEPRESENT;
493                 switch (data->u.value.n1.n2.vt)
494                 {
495                 case VT_I4:  sym_info->Value = (ULONG)data->u.value.n1.n2.n3.lVal; break;
496                 case VT_I2:  sym_info->Value = (ULONG)(long)data->u.value.n1.n2.n3.iVal; break;
497                 case VT_I1:  sym_info->Value = (ULONG)(long)data->u.value.n1.n2.n3.cVal; break;
498                 case VT_UI4: sym_info->Value = (ULONG)data->u.value.n1.n2.n3.ulVal; break;
499                 case VT_UI2: sym_info->Value = (ULONG)data->u.value.n1.n2.n3.uiVal; break;
500                 case VT_UI1: sym_info->Value = (ULONG)data->u.value.n1.n2.n3.bVal; break;
501                 default:        
502                     FIXME("Unsupported variant type (%u)\n", data->u.value.n1.n2.vt);
503                 }
504                 break;
505             default:
506                 FIXME("Unhandled kind (%u) in sym data\n", data->kind);
507             }
508         }
509         break;
510     case SymTagPublicSymbol:
511         sym_info->Flags |= SYMFLAG_EXPORT;
512         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
513         break;
514     case SymTagFunction:
515         sym_info->Flags |= SYMFLAG_FUNCTION;
516         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
517         break;
518     case SymTagThunk:
519         sym_info->Flags |= SYMFLAG_THUNK;
520         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
521         break;
522     default:
523         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
524         sym_info->Register = 0;
525         break;
526     }
527     sym_info->Scope = 0; /* FIXME */
528     sym_info->Tag = sym->tag;
529     name = symt_get_name(sym);
530     if (sym_info->MaxNameLen)
531     {
532         if (sym->tag != SymTagPublicSymbol || !(dbghelp_options & SYMOPT_UNDNAME) ||
533             (sym_info->NameLen = UnDecorateSymbolName(name, sym_info->Name, 
534                                                       sym_info->MaxNameLen, UNDNAME_COMPLETE) == 0))
535         {
536             sym_info->NameLen = min(strlen(name), sym_info->MaxNameLen - 1);
537             memcpy(sym_info->Name, name, sym_info->NameLen);
538             sym_info->Name[sym_info->NameLen] = '\0';
539         }
540     }
541     TRACE_(dbghelp_symt)("%p => %s %lu %s\n",
542                          sym, sym_info->Name, sym_info->Size,
543                          wine_dbgstr_longlong(sym_info->Address));
544 }
545
546 static BOOL symt_enum_module(struct module* module, regex_t* regex,
547                              PSYM_ENUMERATESYMBOLS_CALLBACK cb, PVOID user)
548 {
549     char                        buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
550     SYMBOL_INFO*                sym_info = (SYMBOL_INFO*)buffer;
551     void*                       ptr;
552     struct symt_ht*             sym = NULL;
553     struct hash_table_iter      hti;
554
555     hash_table_iter_init(&module->ht_symbols, &hti, NULL);
556     while ((ptr = hash_table_iter_up(&hti)))
557     {
558         sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
559         if (sym->hash_elt.name &&
560             regexec(regex, sym->hash_elt.name, 0, NULL, 0) == 0)
561         {
562             sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
563             sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
564             symt_fill_sym_info(module, &sym->symt, sym_info);
565             if (!cb(sym_info, sym_info->Size, user)) return TRUE;
566         }
567     }   
568     return FALSE;
569 }
570
571 /***********************************************************************
572  *              resort_symbols
573  *
574  * Rebuild sorted list of symbols for a module.
575  */
576 static BOOL resort_symbols(struct module* module)
577 {
578     int                         nsym;
579     void*                       ptr;
580     struct symt_ht*             sym;
581     struct hash_table_iter      hti;
582
583     if (!module_compute_num_syms(module)) return FALSE;
584     
585     if (module->addr_sorttab)
586         module->addr_sorttab = HeapReAlloc(GetProcessHeap(), 0,
587                                            module->addr_sorttab, 
588                                            module->module.NumSyms * sizeof(struct symt_ht*));
589     else
590         module->addr_sorttab = HeapAlloc(GetProcessHeap(), 0,
591                                          module->module.NumSyms * sizeof(struct symt_ht*));
592     if (!module->addr_sorttab) return FALSE;
593
594     nsym = 0;
595     hash_table_iter_init(&module->ht_symbols, &hti, NULL);
596     while ((ptr = hash_table_iter_up(&hti)))
597     {
598         sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
599         assert(sym);
600         module->addr_sorttab[nsym++] = sym;
601     }
602     
603     qsort(module->addr_sorttab, nsym, sizeof(struct symt_ht*), symt_cmp_addr);
604     return module->sortlist_valid = TRUE;
605 }
606
607 /* assume addr is in module */
608 int symt_find_nearest(struct module* module, DWORD addr)
609 {
610     int         mid, high, low;
611     ULONG64     ref_addr, ref_size;
612
613     if (!module->sortlist_valid || !module->addr_sorttab)
614     {
615         if (!resort_symbols(module)) return -1;
616     }
617
618     /*
619      * Binary search to find closest symbol.
620      */
621     low = 0;
622     high = module->module.NumSyms;
623
624     symt_get_info(&module->addr_sorttab[0]->symt, TI_GET_ADDRESS, &ref_addr);
625     if (addr < ref_addr) return -1;
626     if (high)
627     {
628         symt_get_info(&module->addr_sorttab[high - 1]->symt, TI_GET_ADDRESS, &ref_addr);
629         if (!symt_get_info(&module->addr_sorttab[high - 1]->symt,  TI_GET_LENGTH, &ref_size) || !ref_size)
630             ref_size = 0x1000; /* arbitrary value */
631         if (addr >= ref_addr + ref_size) return -1;
632     }
633     
634     while (high > low + 1)
635     {
636         mid = (high + low) / 2;
637         if (cmp_sorttab_addr(module, mid, addr) < 0)
638             low = mid;
639         else
640             high = mid;
641     }
642     if (low != high && high != module->module.NumSyms && 
643         cmp_sorttab_addr(module, high, addr) <= 0)
644         low = high;
645
646     /* If found symbol is a public symbol, check if there are any other entries that
647      * might also have the same address, but would get better information
648      */
649     if (module->addr_sorttab[low]->symt.tag == SymTagPublicSymbol)
650     {   
651         symt_get_info(&module->addr_sorttab[low]->symt, TI_GET_ADDRESS, &ref_addr);
652         if (low > 0 &&
653             module->addr_sorttab[low - 1]->symt.tag != SymTagPublicSymbol &&
654             !cmp_sorttab_addr(module, low - 1, ref_addr))
655             low--;
656         else if (low < module->module.NumSyms - 1 && 
657                  module->addr_sorttab[low + 1]->symt.tag != SymTagPublicSymbol &&
658                  !cmp_sorttab_addr(module, low + 1, ref_addr))
659             low++;
660     }
661     /* finally check that we fit into the found symbol */
662     symt_get_info(&module->addr_sorttab[low]->symt, TI_GET_ADDRESS, &ref_addr);
663     if (addr < ref_addr) return -1;
664     if (!symt_get_info(&module->addr_sorttab[high - 1]->symt, TI_GET_LENGTH, &ref_size) || !ref_size)
665         ref_size = 0x1000; /* arbitrary value */
666     if (addr >= ref_addr + ref_size) return -1;
667
668     return low;
669 }
670
671 static BOOL symt_enum_locals_helper(struct process* pcs, struct module* module,
672                                     regex_t* preg, PSYM_ENUMERATESYMBOLS_CALLBACK cb,
673                                     PVOID user, SYMBOL_INFO* sym_info,
674                                     struct vector* v)
675 {
676     struct symt**       plsym = NULL;
677     struct symt*        lsym = NULL;
678     DWORD               pc = pcs->ctx_frame.InstructionOffset;
679
680     while ((plsym = vector_iter_up(v, plsym)))
681     {
682         lsym = *plsym;
683         switch (lsym->tag)
684         {
685         case SymTagBlock:
686             {
687                 struct symt_block*  block = (struct symt_block*)lsym;
688                 if (pc < block->address || block->address + block->size <= pc)
689                     continue;
690                 if (!symt_enum_locals_helper(pcs, module, preg, cb, user, 
691                                              sym_info, &block->vchildren))
692                     return FALSE;
693             }
694             break;
695         case SymTagData:
696             if (regexec(preg, symt_get_name(lsym), 0, NULL, 0) == 0)
697             {
698                 symt_fill_sym_info(module, lsym, sym_info);
699                 if (!cb(sym_info, sym_info->Size, user))
700                     return FALSE;
701             }
702             break;
703         case SymTagLabel:
704         case SymTagFuncDebugStart:
705         case SymTagFuncDebugEnd:
706             break;
707         default:
708             FIXME("Unknown type: %u (%x)\n", lsym->tag, lsym->tag);
709             assert(0);
710         }
711     }
712     return TRUE;
713 }
714
715 static BOOL symt_enum_locals(struct process* pcs, const char* mask,
716                              PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
717                              PVOID UserContext)
718 {
719     struct module*      module;
720     struct symt_ht*     sym;
721     char                buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
722     SYMBOL_INFO*        sym_info = (SYMBOL_INFO*)buffer;
723     DWORD               pc = pcs->ctx_frame.InstructionOffset;
724     int                 idx;
725
726     sym_info->SizeOfStruct = sizeof(*sym_info);
727     sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
728
729     module = module_find_by_addr(pcs, pc, DMT_UNKNOWN);
730     if (!(module = module_get_debug(pcs, module))) return FALSE;
731     if ((idx = symt_find_nearest(module, pc)) == -1) return FALSE;
732
733     sym = module->addr_sorttab[idx];
734     if (sym->symt.tag == SymTagFunction)
735     {
736         BOOL            ret;
737         regex_t         preg;
738
739         compile_regex(mask ? mask : "*", -1, &preg,
740                       dbghelp_options & SYMOPT_CASE_INSENSITIVE);
741         ret = symt_enum_locals_helper(pcs, module, &preg, EnumSymbolsCallback, 
742                                       UserContext, sym_info, 
743                                       &((struct symt_function*)sym)->vchildren);
744         regfree(&preg);
745         return ret;
746         
747     }
748     symt_fill_sym_info(module, &sym->symt, sym_info);
749     return EnumSymbolsCallback(sym_info, sym_info->Size, UserContext);
750 }
751
752 /******************************************************************
753  *              SymEnumSymbols (DBGHELP.@)
754  *
755  * cases BaseOfDll = 0
756  *      !foo fails always (despite what MSDN states)
757  *      RE1!RE2 looks up all modules matching RE1, and in all these modules, lookup RE2
758  *      no ! in Mask, lookup in local Context
759  * cases BaseOfDll != 0
760  *      !foo fails always (despite what MSDN states)
761  *      RE1!RE2 gets RE2 from BaseOfDll (whatever RE1 is)
762  */
763 BOOL WINAPI SymEnumSymbols(HANDLE hProcess, ULONG64 BaseOfDll, PCSTR Mask,
764                            PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
765                            PVOID UserContext)
766 {
767     struct process*     pcs = process_find_by_handle(hProcess);
768     struct module*      module;
769     struct module*      dbg_module;
770     const char*         bang;
771     regex_t             mod_regex, sym_regex;
772
773     TRACE("(%p %s %s %p %p)\n", 
774           hProcess, wine_dbgstr_longlong(BaseOfDll), debugstr_a(Mask),
775           EnumSymbolsCallback, UserContext);
776
777     if (!pcs) return FALSE;
778
779     if (BaseOfDll == 0)
780     {
781         /* do local variables ? */
782         if (!Mask || !(bang = strchr(Mask, '!')))
783             return symt_enum_locals(pcs, Mask, EnumSymbolsCallback, UserContext);
784
785         if (bang == Mask) return FALSE;
786
787         compile_regex(Mask, bang - Mask, &mod_regex, 
788                       dbghelp_options & SYMOPT_CASE_INSENSITIVE);
789         compile_regex(bang + 1, -1, &sym_regex, 
790                       dbghelp_options & SYMOPT_CASE_INSENSITIVE);
791         
792         for (module = pcs->lmodules; module; module = module->next)
793         {
794             if (module->type == DMT_PE && (dbg_module = module_get_debug(pcs, module)))
795             {
796                 if (regexec(&mod_regex, module->module.ModuleName, 0, NULL, 0) == 0 &&
797                     symt_enum_module(dbg_module, &sym_regex, 
798                                      EnumSymbolsCallback, UserContext))
799                     break;
800             }
801         }
802         /* not found in PE modules, retry on the ELF ones
803          */
804         if (!module && (dbghelp_options & SYMOPT_WINE_WITH_ELF_MODULES))
805         {
806             for (module = pcs->lmodules; module; module = module->next)
807             {
808                 if (module->type == DMT_ELF &&
809                     !module_get_containee(pcs, module) &&
810                     (dbg_module = module_get_debug(pcs, module)))
811                 {
812                     if (regexec(&mod_regex, module->module.ModuleName, 0, NULL, 0) == 0 &&
813                         symt_enum_module(dbg_module, &sym_regex, EnumSymbolsCallback, UserContext))
814                     break;
815                 }
816             }
817         }
818         regfree(&mod_regex);
819         regfree(&sym_regex);
820         return TRUE;
821     }
822     module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
823     if (!(module = module_get_debug(pcs, module)))
824         return FALSE;
825
826     /* we always ignore module name from Mask when BaseOfDll is defined */
827     if (Mask && (bang = strchr(Mask, '!')))
828     {
829         if (bang == Mask) return FALSE;
830         Mask = bang + 1;
831     }
832
833     compile_regex(Mask ? Mask : "*", -1, &sym_regex, 
834                       dbghelp_options & SYMOPT_CASE_INSENSITIVE);
835     symt_enum_module(module, &sym_regex, EnumSymbolsCallback, UserContext);
836     regfree(&sym_regex);
837
838     return TRUE;
839 }
840
841 struct sym_enumerate
842 {
843     void*                       ctx;
844     PSYM_ENUMSYMBOLS_CALLBACK   cb;
845 };
846
847 static BOOL CALLBACK sym_enumerate_cb(PSYMBOL_INFO syminfo, ULONG size, void* ctx)
848 {
849     struct sym_enumerate*       se = (struct sym_enumerate*)ctx;
850     return (se->cb)(syminfo->Name, syminfo->Address, syminfo->Size, se->ctx);
851 }
852
853 /***********************************************************************
854  *              SymEnumerateSymbols (DBGHELP.@)
855  */
856 BOOL WINAPI SymEnumerateSymbols(HANDLE hProcess, DWORD BaseOfDll,
857                                 PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback, 
858                                 PVOID UserContext)
859 {
860     struct sym_enumerate        se;
861
862     se.ctx = UserContext;
863     se.cb  = EnumSymbolsCallback;
864     
865     return SymEnumSymbols(hProcess, BaseOfDll, NULL, sym_enumerate_cb, &se);
866 }
867
868 /******************************************************************
869  *              SymFromAddr (DBGHELP.@)
870  *
871  */
872 BOOL WINAPI SymFromAddr(HANDLE hProcess, DWORD64 Address, 
873                         DWORD64* Displacement, PSYMBOL_INFO Symbol)
874 {
875     struct process*     pcs = process_find_by_handle(hProcess);
876     struct module*      module;
877     struct symt_ht*     sym;
878     int                 idx;
879
880     if (!pcs) return FALSE;
881     module = module_find_by_addr(pcs, Address, DMT_UNKNOWN);
882     if (!(module = module_get_debug(pcs, module))) return FALSE;
883     if ((idx = symt_find_nearest(module, Address)) == -1) return FALSE;
884
885     sym = module->addr_sorttab[idx];
886
887     symt_fill_sym_info(module, &sym->symt, Symbol);
888     *Displacement = Address - Symbol->Address;
889     return TRUE;
890 }
891
892 /******************************************************************
893  *              SymGetSymFromAddr (DBGHELP.@)
894  *
895  */
896 BOOL WINAPI SymGetSymFromAddr(HANDLE hProcess, DWORD Address,
897                               PDWORD Displacement, PIMAGEHLP_SYMBOL Symbol)
898 {
899     char        buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
900     SYMBOL_INFO*si = (SYMBOL_INFO*)buffer;
901     size_t      len;
902     DWORD64     Displacement64;
903
904     if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
905     si->SizeOfStruct = sizeof(*si);
906     si->MaxNameLen = MAX_SYM_NAME;
907     if (!SymFromAddr(hProcess, Address, &Displacement64, si))
908         return FALSE;
909
910     if (Displacement)
911         *Displacement = Displacement64;
912     Symbol->Address = si->Address;
913     Symbol->Size    = si->Size;
914     Symbol->Flags   = si->Flags;
915     len = min(Symbol->MaxNameLength, si->MaxNameLen);
916     lstrcpynA(Symbol->Name, si->Name, len);
917     return TRUE;
918 }
919
920 /******************************************************************
921  *              SymFromName (DBGHELP.@)
922  *
923  */
924 BOOL WINAPI SymFromName(HANDLE hProcess, PCSTR Name, PSYMBOL_INFO Symbol)
925 {
926     struct process*             pcs = process_find_by_handle(hProcess);
927     struct module*              module;
928     struct hash_table_iter      hti;
929     void*                       ptr;
930     struct symt_ht*             sym = NULL;
931     const char*                 name;
932
933     TRACE("(%p, %s, %p)\n", hProcess, Name, Symbol);
934     if (!pcs) return FALSE;
935     if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
936     name = strchr(Name, '!');
937     if (name)
938     {
939         char    tmp[128];
940         assert(name - Name < sizeof(tmp));
941         memcpy(tmp, Name, name - Name);
942         tmp[name - Name] = '\0';
943         module = module_find_by_name(pcs, tmp, DMT_UNKNOWN);
944         if (!module) return FALSE;
945         Name = (char*)(name + 1);
946     }
947     else module = pcs->lmodules;
948
949     /* FIXME: Name could be made out of a regular expression */
950     for (; module; module = (name) ? NULL : module->next)
951     {
952         if (module->module.SymType == SymNone) continue;
953         if (module->module.SymType == SymDeferred)
954         {
955             struct module*      xmodule = module_get_debug(pcs, module);
956             if (!xmodule || xmodule != module) continue;
957         }
958         hash_table_iter_init(&module->ht_symbols, &hti, Name);
959         while ((ptr = hash_table_iter_up(&hti)))
960         {
961             sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
962
963             if (!strcmp(sym->hash_elt.name, Name))
964             {
965                 symt_fill_sym_info(module, &sym->symt, Symbol);
966                 return TRUE;
967             }
968         }
969     }
970     return FALSE;
971 }
972
973 /***********************************************************************
974  *              SymGetSymFromName (DBGHELP.@)
975  */
976 BOOL WINAPI SymGetSymFromName(HANDLE hProcess, PCSTR Name, PIMAGEHLP_SYMBOL Symbol)
977 {
978     char        buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
979     SYMBOL_INFO*si = (SYMBOL_INFO*)buffer;
980     size_t      len;
981
982     if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
983     si->SizeOfStruct = sizeof(*si);
984     si->MaxNameLen = MAX_SYM_NAME;
985     if (!SymFromName(hProcess, Name, si)) return FALSE;
986
987     Symbol->Address = si->Address;
988     Symbol->Size    = si->Size;
989     Symbol->Flags   = si->Flags;
990     len = min(Symbol->MaxNameLength, si->MaxNameLen);
991     lstrcpynA(Symbol->Name, si->Name, len);
992     return TRUE;
993 }
994
995 /******************************************************************
996  *              sym_fill_func_line_info
997  *
998  * fills information about a file
999  */
1000 BOOL symt_fill_func_line_info(struct module* module, struct symt_function* func, 
1001                               DWORD addr, IMAGEHLP_LINE* line)
1002 {
1003     struct line_info*   dli = NULL;
1004     BOOL                found = FALSE;
1005
1006     assert(func->symt.tag == SymTagFunction);
1007
1008     while ((dli = vector_iter_down(&func->vlines, dli)))
1009     {
1010         if (!dli->is_source_file)
1011         {
1012             if (found || dli->u.pc_offset > addr) continue;
1013             line->LineNumber = dli->line_number;
1014             line->Address    = dli->u.pc_offset;
1015             line->Key        = dli;
1016             found = TRUE;
1017             continue;
1018         }
1019         if (found)
1020         {
1021             line->FileName = (char*)source_get(module, dli->u.source_file);
1022             return TRUE;
1023         }
1024     }
1025     return FALSE;
1026 }
1027
1028 /***********************************************************************
1029  *              SymGetSymNext (DBGHELP.@)
1030  */
1031 BOOL WINAPI SymGetSymNext(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol)
1032 {
1033     /* algo:
1034      * get module from Symbol.Address
1035      * get index in module.addr_sorttab of Symbol.Address
1036      * increment index
1037      * if out of module bounds, move to next module in process address space
1038      */
1039     FIXME("(%p, %p): stub\n", hProcess, Symbol);
1040     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1041     return FALSE;
1042 }
1043
1044 /***********************************************************************
1045  *              SymGetSymPrev (DBGHELP.@)
1046  */
1047
1048 BOOL WINAPI SymGetSymPrev(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol)
1049 {
1050     FIXME("(%p, %p): stub\n", hProcess, Symbol);
1051     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1052     return FALSE;
1053 }
1054
1055 /******************************************************************
1056  *              SymGetLineFromAddr (DBGHELP.@)
1057  *
1058  */
1059 BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, 
1060                                PDWORD pdwDisplacement, PIMAGEHLP_LINE Line)
1061 {
1062     struct process*     pcs = process_find_by_handle(hProcess);
1063     struct module*      module;
1064     int                 idx;
1065
1066     TRACE("%p %08lx %p %p\n", hProcess, dwAddr, pdwDisplacement, Line);
1067
1068     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
1069
1070     if (!pcs) return FALSE;
1071     module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
1072     if (!(module = module_get_debug(pcs, module))) return FALSE;
1073     if ((idx = symt_find_nearest(module, dwAddr)) == -1) return FALSE;
1074
1075     if (module->addr_sorttab[idx]->symt.tag != SymTagFunction) return FALSE;
1076     if (!symt_fill_func_line_info(module, 
1077                                   (struct symt_function*)module->addr_sorttab[idx],
1078                                   dwAddr, Line)) return FALSE;
1079     *pdwDisplacement = dwAddr - Line->Address;
1080     return TRUE;
1081 }
1082
1083 /******************************************************************
1084  *              copy_line_64_from_32 (internal)
1085  *
1086  */
1087 static void copy_line_64_from_32(IMAGEHLP_LINE64* l64, const IMAGEHLP_LINE* l32)
1088
1089 {
1090     l64->Key = l32->Key;
1091     l64->LineNumber = l32->LineNumber;
1092     l64->FileName = l32->FileName;
1093     l64->Address = l32->Address;
1094 }
1095
1096 /******************************************************************
1097  *              copy_line_32_from_64 (internal)
1098  *
1099  */
1100 static void copy_line_32_from_64(IMAGEHLP_LINE* l32, const IMAGEHLP_LINE64* l64)
1101
1102 {
1103     l32->Key = l64->Key;
1104     l32->LineNumber = l64->LineNumber;
1105     l32->FileName = l64->FileName;
1106     l32->Address = l64->Address;
1107 }
1108
1109 /******************************************************************
1110  *              SymGetLineFromAddr64 (DBGHELP.@)
1111  *
1112  */
1113 BOOL WINAPI SymGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, 
1114                                  PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line)
1115 {
1116     IMAGEHLP_LINE       line32;
1117
1118     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
1119     if (!validate_addr64(dwAddr)) return FALSE;
1120     line32.SizeOfStruct = sizeof(line32);
1121     if (!SymGetLineFromAddr(hProcess, (DWORD)dwAddr, pdwDisplacement, &line32))
1122         return FALSE;
1123     copy_line_64_from_32(Line, &line32);
1124     return TRUE;
1125 }
1126
1127 /******************************************************************
1128  *              SymGetLinePrev (DBGHELP.@)
1129  *
1130  */
1131 BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line)
1132 {
1133     struct process*     pcs = process_find_by_handle(hProcess);
1134     struct module*      module;
1135     struct line_info*   li;
1136     BOOL                in_search = FALSE;
1137
1138     TRACE("(%p %p)\n", hProcess, Line);
1139
1140     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
1141
1142     if (!pcs) return FALSE;
1143     module = module_find_by_addr(pcs, Line->Address, DMT_UNKNOWN);
1144     if (!(module = module_get_debug(pcs, module))) return FALSE;
1145
1146     if (Line->Key == 0) return FALSE;
1147     li = (struct line_info*)Line->Key;
1148     /* things are a bit complicated because when we encounter a DLIT_SOURCEFILE
1149      * element we have to go back until we find the prev one to get the real
1150      * source file name for the DLIT_OFFSET element just before 
1151      * the first DLIT_SOURCEFILE
1152      */
1153     while (!li->is_first)
1154     {
1155         li--;
1156         if (!li->is_source_file)
1157         {
1158             Line->LineNumber = li->line_number;
1159             Line->Address    = li->u.pc_offset;
1160             Line->Key        = li;
1161             if (!in_search) return TRUE;
1162         }
1163         else
1164         {
1165             if (in_search)
1166             {
1167                 Line->FileName = (char*)source_get(module, li->u.source_file);
1168                 return TRUE;
1169             }
1170             in_search = TRUE;
1171         }
1172     }
1173     SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */
1174     return FALSE;
1175 }
1176
1177 /******************************************************************
1178  *              SymGetLinePrev64 (DBGHELP.@)
1179  *
1180  */
1181 BOOL WINAPI SymGetLinePrev64(HANDLE hProcess, PIMAGEHLP_LINE64 Line)
1182 {
1183     IMAGEHLP_LINE       line32;
1184
1185     line32.SizeOfStruct = sizeof(line32);
1186     copy_line_32_from_64(&line32, Line);
1187     if (!SymGetLinePrev(hProcess, &line32)) return FALSE;
1188     copy_line_64_from_32(Line, &line32);
1189     return TRUE;
1190 }
1191     
1192 BOOL symt_get_func_line_next(struct module* module, PIMAGEHLP_LINE line)
1193 {
1194     struct line_info*   li;
1195
1196     if (line->Key == 0) return FALSE;
1197     li = (struct line_info*)line->Key;
1198     while (!li->is_last)
1199     {
1200         li++;
1201         if (!li->is_source_file)
1202         {
1203             line->LineNumber = li->line_number;
1204             line->Address    = li->u.pc_offset;
1205             line->Key        = li;
1206             return TRUE;
1207         }
1208         line->FileName = (char*)source_get(module, li->u.source_file);
1209     }
1210     return FALSE;
1211 }
1212
1213 /******************************************************************
1214  *              SymGetLineNext (DBGHELP.@)
1215  *
1216  */
1217 BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line)
1218 {
1219     struct process*     pcs = process_find_by_handle(hProcess);
1220     struct module*      module;
1221
1222     TRACE("(%p %p)\n", hProcess, Line);
1223
1224     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
1225     if (!pcs) return FALSE;
1226     module = module_find_by_addr(pcs, Line->Address, DMT_UNKNOWN);
1227     if (!(module = module_get_debug(pcs, module))) return FALSE;
1228
1229     if (symt_get_func_line_next(module, Line)) return TRUE;
1230     SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */
1231     return FALSE;
1232 }
1233
1234 /******************************************************************
1235  *              SymGetLineNext64 (DBGHELP.@)
1236  *
1237  */
1238 BOOL WINAPI SymGetLineNext64(HANDLE hProcess, PIMAGEHLP_LINE64 Line)
1239 {
1240     IMAGEHLP_LINE       line32;
1241
1242     line32.SizeOfStruct = sizeof(line32);
1243     copy_line_32_from_64(&line32, Line);
1244     if (!SymGetLineNext(hProcess, &line32)) return FALSE;
1245     copy_line_64_from_32(Line, &line32);
1246     return TRUE;
1247 }
1248     
1249 /***********************************************************************
1250  *              SymFunctionTableAccess (DBGHELP.@)
1251  */
1252 PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase)
1253 {
1254     WARN("(%p, 0x%08lx): stub\n", hProcess, AddrBase);
1255     return NULL;
1256 }
1257
1258 /***********************************************************************
1259  *              SymFunctionTableAccess64 (DBGHELP.@)
1260  */
1261 PVOID WINAPI SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase)
1262 {
1263     WARN("(%p, %s): stub\n", hProcess, wine_dbgstr_longlong(AddrBase));
1264     return NULL;
1265 }
1266
1267 /***********************************************************************
1268  *              SymUnDName (DBGHELP.@)
1269  */
1270 BOOL WINAPI SymUnDName(PIMAGEHLP_SYMBOL sym, LPSTR UnDecName, DWORD UnDecNameLength)
1271 {
1272     TRACE("(%p %s %lu)\n", sym, UnDecName, UnDecNameLength);
1273     return UnDecorateSymbolName(sym->Name, UnDecName, UnDecNameLength, 
1274                                 UNDNAME_COMPLETE) != 0;
1275 }
1276
1277 static void* und_alloc(size_t len) { return HeapAlloc(GetProcessHeap(), 0, len); }
1278 static void  und_free (void* ptr)  { HeapFree(GetProcessHeap(), 0, ptr); }
1279
1280 /***********************************************************************
1281  *              UnDecorateSymbolName (DBGHELP.@)
1282  */
1283 DWORD WINAPI UnDecorateSymbolName(LPCSTR DecoratedName, LPSTR UnDecoratedName,
1284                                   DWORD UndecoratedLength, DWORD Flags)
1285 {
1286     /* undocumented from msvcrt */
1287     static char* (*p_undname)(char*, const char*, int, void* (*)(size_t), void (*)(void*), unsigned short);
1288     static const WCHAR szMsvcrt[] = {'m','s','v','c','r','t','.','d','l','l',0};
1289
1290     TRACE("(%s, %p, %ld, 0x%08lx)\n",
1291           debugstr_a(DecoratedName), UnDecoratedName, UndecoratedLength, Flags);
1292
1293     if (!p_undname)
1294     {
1295         if (!hMsvcrt) hMsvcrt = LoadLibraryW(szMsvcrt);
1296         if (hMsvcrt) p_undname = (void*)GetProcAddress(hMsvcrt, "__unDName");
1297         if (!p_undname) return 0;
1298     }
1299
1300     if (!UnDecoratedName) return 0;
1301     if (!p_undname(UnDecoratedName, DecoratedName, UndecoratedLength, 
1302                    und_alloc, und_free, Flags))
1303         return 0;
1304     return strlen(UnDecoratedName);
1305 }
1306
1307 /******************************************************************
1308  *              SymMatchString (DBGHELP.@)
1309  *
1310  */
1311 BOOL WINAPI SymMatchString(PCSTR string, PCSTR re, BOOL _case)
1312 {
1313     regex_t     preg;
1314     BOOL        ret;
1315
1316     TRACE("%s %s %c\n", string, re, _case ? 'Y' : 'N');
1317
1318     compile_regex(re, -1, &preg, _case);
1319     ret = regexec(&preg, string, 0, NULL, 0) == 0;
1320     regfree(&preg);
1321     return ret;
1322 }
1323
1324 /******************************************************************
1325  *              SymSearch (DBGHELP.@)
1326  */
1327 BOOL WINAPI SymSearch(HANDLE hProcess, ULONG64 BaseOfDll, DWORD Index,
1328                       DWORD SymTag, PCSTR Mask, DWORD64 Address,
1329                       PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
1330                       PVOID UserContext, DWORD Options)
1331 {
1332     TRACE("(%p %s %lu %lu %s %s %p %p %lx)\n",
1333           hProcess, wine_dbgstr_longlong(BaseOfDll), Index, SymTag, Mask, 
1334           wine_dbgstr_longlong(Address), EnumSymbolsCallback,
1335           UserContext, Options);
1336
1337     if (Index != 0)
1338     {
1339         FIXME("Unsupported searching for a given Index (%lu)\n", Index);
1340         SetLastError(ERROR_INVALID_PARAMETER);
1341         return FALSE;
1342     }
1343     if (SymTag != 0)
1344     {
1345         FIXME("Unsupported searching for a given SymTag (%lu)\n", SymTag);
1346         SetLastError(ERROR_INVALID_PARAMETER);
1347         return FALSE;
1348     }
1349     if (Address != 0)
1350     {
1351         FIXME("Unsupported searching for a given Address (%s)\n", wine_dbgstr_longlong(Address));
1352         SetLastError(ERROR_INVALID_PARAMETER);
1353         return FALSE;
1354     }
1355     if (Options != SYMSEARCH_GLOBALSONLY)
1356     {
1357         FIXME("Unsupported searching with options (%lx)\n", Options);
1358         SetLastError(ERROR_INVALID_PARAMETER);
1359         return FALSE;
1360     }
1361     return SymEnumSymbols(hProcess, BaseOfDll, Mask, EnumSymbolsCallback, UserContext);
1362 }