- fixed some bugs in StackWalk (claimed for but forgotten in last
[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
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <limits.h>
30 #include <sys/types.h>
31 #include <assert.h>
32 #include <regex.h>
33 #include "wine/debug.h"
34
35 #include "dbghelp_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
38 WINE_DECLARE_DEBUG_CHANNEL(dbghelp_symt);
39
40 struct line_info
41 {
42     unsigned long               is_first : 1,
43                                 is_last : 1,
44                                 is_source_file : 1,
45                                 line_number;
46     union
47     {
48         unsigned long               pc_offset;   /* if is_source_file isn't set */
49         unsigned                    source_file; /* if is_source_file is set */
50     } u;
51 };
52
53 inline static int cmp_addr(DWORD a1, DWORD a2)
54 {
55     if (a1 > a2) return 1;
56     if (a1 < a2) return -1;
57     return 0;
58 }
59
60 inline static int cmp_sorttab_addr(const struct module* module, int idx, DWORD addr)
61 {
62     DWORD       ref;
63
64     symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_ADDRESS, &ref);
65     return cmp_addr(ref, addr);
66 }
67
68 int symt_cmp_addr(const void* p1, const void* p2)
69 {
70     struct symt*        sym1 = *(struct symt**)p1;
71     struct symt*        sym2 = *(struct symt**)p2;
72     DWORD               a1, a2;
73
74     symt_get_info(sym1, TI_GET_ADDRESS, &a1);
75     symt_get_info(sym2, TI_GET_ADDRESS, &a2);
76     return cmp_addr(a1, a2);
77 }
78
79 static inline void re_append(char** mask, unsigned* len, char ch)
80 {
81     *mask = HeapReAlloc(GetProcessHeap(), 0, *mask, ++(*len));
82     (*mask)[*len - 2] = ch;
83 }
84
85 /* transforms a dbghelp's regular expression into a POSIX one
86  * Here are the valid dbghelp reg ex characters:
87  *      *       0 or more characters
88  *      ?       a single character
89  *      []      list
90  *      #       0 or more of preceding char
91  *      +       1 or more of preceding char
92  *      escapes \ on #, ?, [, ], *, +. don't work on -
93  */
94 static void compile_regex(const char* str, regex_t* re)
95 {
96     char*       mask = HeapAlloc(GetProcessHeap(), 0, 1);
97     unsigned    len = 1;
98     BOOL        in_escape = FALSE;
99
100     re_append(&mask, &len, '^');
101     while (*str)
102     {
103         /* FIXME: this shouldn't be valid on '-' */
104         if (in_escape)
105         {
106             re_append(&mask, &len, '\\');
107             re_append(&mask, &len, *str);
108             in_escape = FALSE;
109         }
110         else switch (*str)
111         {
112         case '\\': in_escape = TRUE; break;
113         case '*':  re_append(&mask, &len, '.'); re_append(&mask, &len, '*'); break;
114         case '?':  re_append(&mask, &len, '.'); break;
115         case '#':  re_append(&mask, &len, '*'); break;
116         /* escape some valid characters in dbghelp reg exp:s */
117         case '$':  re_append(&mask, &len, '\\'); re_append(&mask, &len, '$'); break;
118         /* +, [, ], - are the same in dbghelp & POSIX, use them as any other char */
119         default:   re_append(&mask, &len, *str); break;
120         }
121         str++;
122     }
123     if (in_escape)
124     {
125         re_append(&mask, &len, '\\');
126         re_append(&mask, &len, '\\');
127     }
128     re_append(&mask, &len, '$');
129     mask[len - 1] = '\0';
130     regcomp(re, mask, REG_NOSUB);
131     HeapFree(GetProcessHeap(), 0, mask);
132 }
133
134 struct symt_compiland* symt_new_compiland(struct module* module, const char* name)
135 {
136     struct symt_compiland*    sym;
137
138     TRACE_(dbghelp_symt)("Adding compiland symbol %s:%s\n", 
139                          module->module.ModuleName, name);
140     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
141     {
142         sym->symt.tag = SymTagCompiland;
143         sym->source   = source_new(module, name);
144         vector_init(&sym->vchildren, sizeof(struct symt*), 32);
145     }
146     return sym;
147 }
148
149 struct symt_public* symt_new_public(struct module* module, 
150                                     struct symt_compiland* compiland,
151                                     const char* name,
152                                     unsigned long address, unsigned size,
153                                     BOOL in_code, BOOL is_func)
154 {
155     struct symt_public* sym;
156     struct symt**       p;
157
158     TRACE_(dbghelp_symt)("Adding public symbol %s:%s @%lx\n", 
159                          module->module.ModuleName, name, address);
160     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
161     {
162         sym->symt.tag      = SymTagPublicSymbol;
163         sym->hash_elt.name = pool_strdup(&module->pool, name);
164         hash_table_add(&module->ht_symbols, &sym->hash_elt);
165         module->sortlist_valid = FALSE;
166         sym->container     = compiland ? &compiland->symt : NULL;
167         sym->address       = address;
168         sym->size          = size;
169         sym->in_code       = in_code;
170         sym->is_function   = is_func;
171         if (compiland)
172         {
173             p = vector_add(&compiland->vchildren, &module->pool);
174             *p = &sym->symt;
175         }
176     }
177     return sym;
178 }
179
180 struct symt_data* symt_new_global_variable(struct module* module, 
181                                            struct symt_compiland* compiland, 
182                                            const char* name, unsigned is_static,
183                                            unsigned long addr, unsigned long size,
184                                            struct symt* type)
185 {
186     struct symt_data*   sym;
187     struct symt**       p;
188
189     TRACE_(dbghelp_symt)("Adding global symbol %s:%s @%lx %p\n", 
190                          module->module.ModuleName, name, addr, type);
191     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
192     {
193         sym->symt.tag      = SymTagData;
194         sym->hash_elt.name = pool_strdup(&module->pool, name);
195         hash_table_add(&module->ht_symbols, &sym->hash_elt);
196         module->sortlist_valid = FALSE;
197         sym->kind          = is_static ? DataIsFileStatic : DataIsGlobal;
198         sym->container     = compiland ? &compiland->symt : NULL;
199         sym->type          = type;
200         sym->u.address     = addr;
201         if (compiland)
202         {
203             p = vector_add(&compiland->vchildren, &module->pool);
204             *p = &sym->symt;
205         }
206     }
207     return sym;
208 }
209
210 struct symt_function* symt_new_function(struct module* module, 
211                                         struct symt_compiland* compiland, 
212                                         const char* name,
213                                         unsigned long addr, unsigned long size,
214                                         struct symt* sig_type)
215 {
216     struct symt_function*       sym;
217     struct symt**               p;
218
219     TRACE_(dbghelp_symt)("Adding global function %s:%s @%lx-%lx\n", 
220                          module->module.ModuleName, name, addr, addr + size - 1);
221
222     assert(!sig_type || sig_type->tag == SymTagFunctionType);
223     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
224     {
225         sym->symt.tag  = SymTagFunction;
226         sym->hash_elt.name = pool_strdup(&module->pool, name);
227         hash_table_add(&module->ht_symbols, &sym->hash_elt);
228         module->sortlist_valid = FALSE;
229         sym->container = &compiland->symt;
230         sym->addr      = addr;
231         sym->type      = sig_type;
232         sym->size      = size;
233         sym->addr      = addr;
234         vector_init(&sym->vlines,  sizeof(struct line_info), 64);
235         vector_init(&sym->vchildren, sizeof(struct symt*), 8);
236         if (compiland)
237         {
238             p = vector_add(&compiland->vchildren, &module->pool);
239             *p = &sym->symt;
240         }
241     }
242     return sym;
243 }
244
245 void symt_add_func_line(struct module* module, struct symt_function* func,
246                         unsigned source_idx, int line_num, unsigned long offset)
247 {
248     struct line_info*   dli;
249     BOOL                last_matches = FALSE;
250
251     if (func == NULL || !(dbghelp_options & SYMOPT_LOAD_LINES)) return;
252
253     TRACE_(dbghelp_symt)("(%p)%s:%lx %s:%u\n", 
254                          func, func->hash_elt.name, offset, 
255                          source_get(module, source_idx), line_num);
256
257     assert(func->symt.tag == SymTagFunction);
258
259     dli = NULL;
260     while ((dli = vector_iter_down(&func->vlines, dli)))
261     {
262         if (dli->is_source_file)
263         {
264             last_matches = (source_idx == dli->u.source_file);
265             break;
266         }
267     }
268
269     if (!last_matches)
270     {
271         /* we shouldn't have line changes on first line of function */
272         dli = vector_add(&func->vlines, &module->pool);
273         dli->is_source_file = 1;
274         dli->is_first       = dli->is_last = 0;
275         dli->line_number    = 0;
276         dli->u.source_file  = source_idx;
277     }
278     dli = vector_add(&func->vlines, &module->pool);
279     dli->is_source_file = 0;
280     dli->is_first       = dli->is_last = 0;
281     dli->line_number    = line_num;
282     dli->u.pc_offset    = func->addr + offset;
283 }
284
285 struct symt_data* symt_add_func_local(struct module* module, 
286                                       struct symt_function* func, 
287                                       int regno, int offset, 
288                                       struct symt_block* block, 
289                                       struct symt* type, const char* name)
290 {
291     struct symt_data*   locsym;
292     struct symt**       p;
293
294     assert(func);
295     assert(func->symt.tag == SymTagFunction);
296
297     TRACE_(dbghelp_symt)("Adding local symbol (%s:%s): %s %p\n", 
298                          module->module.ModuleName, func->hash_elt.name, 
299                          name, type);
300     locsym = pool_alloc(&module->pool, sizeof(*locsym));
301     locsym->symt.tag      = SymTagData;
302     locsym->hash_elt.name = pool_strdup(&module->pool, name);
303     locsym->hash_elt.next = NULL;
304     locsym->kind          = (offset < 0) ? DataIsParam : DataIsLocal;
305     locsym->container     = &block->symt;
306     locsym->type          = type;
307     if (regno)
308     {
309         locsym->u.s.reg_id = regno;
310         locsym->u.s.offset = 0;
311         locsym->u.s.length = 0;
312     }
313     else
314     {
315         locsym->u.s.reg_id = 0;
316         locsym->u.s.offset = offset * 8;
317         locsym->u.s.length = 0;
318     }
319     if (block)
320         p = vector_add(&block->vchildren, &module->pool);
321     else
322         p = vector_add(&func->vchildren, &module->pool);
323     *p = &locsym->symt;
324     return locsym;
325 }
326
327 struct symt_block* symt_open_func_block(struct module* module, 
328                                         struct symt_function* func,
329                                         struct symt_block* parent_block, 
330                                         unsigned pc, unsigned len)
331 {
332     struct symt_block*  block;
333     struct symt**       p;
334
335     assert(func);
336     assert(func->symt.tag == SymTagFunction);
337
338     assert(!parent_block || parent_block->symt.tag == SymTagBlock);
339     block = pool_alloc(&module->pool, sizeof(*block));
340     block->symt.tag = SymTagBlock;
341     block->address  = func->addr + pc;
342     block->size     = len;
343     block->container = parent_block ? &parent_block->symt : &func->symt;
344     vector_init(&block->vchildren, sizeof(struct symt*), 4);
345     if (parent_block)
346         p = vector_add(&parent_block->vchildren, &module->pool);
347     else
348         p = vector_add(&func->vchildren, &module->pool);
349     *p = &block->symt;
350
351     return block;
352 }
353
354 struct symt_block* symt_close_func_block(struct module* module, 
355                                          struct symt_function* func,
356                                          struct symt_block* block, unsigned pc)
357 {
358     assert(func->symt.tag == SymTagFunction);
359
360     if (pc) block->size = func->addr + pc - block->address;
361     return (block->container->tag == SymTagBlock) ? 
362         GET_ENTRY(block->container, struct symt_block, symt) : NULL;
363 }
364
365 struct symt_function_point* symt_add_function_point(struct module* module, 
366                                                     struct symt_function* func,
367                                                     enum SymTagEnum point, 
368                                                     unsigned offset, const char* name)
369 {
370     struct symt_function_point* sym;
371     struct symt**               p;
372
373     if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
374     {
375         sym->symt.tag = point;
376         sym->parent   = func;
377         sym->offset   = offset;
378         sym->name     = name ? pool_strdup(&module->pool, name) : NULL;
379         p = vector_add(&func->vchildren, &module->pool);
380         *p = &sym->symt;
381     }
382     return sym;
383 }
384
385 BOOL symt_normalize_function(struct module* module, struct symt_function* func)
386 {
387     unsigned            len;
388     struct line_info*   dli;
389
390     assert(func);
391     /* We aren't adding any more locals or line numbers to this function.
392      * Free any spare memory that we might have allocated.
393      */
394     assert(func->symt.tag == SymTagFunction);
395
396 /* EPP     vector_pool_normalize(&func->vlines,    &module->pool); */
397 /* EPP     vector_pool_normalize(&func->vchildren, &module->pool); */
398
399     len = vector_length(&func->vlines);
400     if (len--)
401     {
402         dli = vector_at(&func->vlines,   0);  dli->is_first = 1;
403         dli = vector_at(&func->vlines, len);  dli->is_last  = 1;
404     }
405     return TRUE;
406 }
407
408 /* expect sym_info->MaxNameLen to be set before being called */
409 static void symt_fill_sym_info(const struct module* module, 
410                                const struct symt* sym, SYMBOL_INFO* sym_info)
411 {
412     const char* name;
413
414     sym_info->TypeIndex = (DWORD)sym;
415     sym_info->info = 0; /* TBD */
416     symt_get_info(sym, TI_GET_LENGTH, &sym_info->Size);
417     sym_info->ModBase = module->module.BaseOfImage;
418     sym_info->Flags = 0;
419     switch (sym->tag)
420     {
421     case SymTagData:
422         {
423             struct symt_data*  data = (struct symt_data*)sym;
424             switch (data->kind)
425             {
426             case DataIsLocal:
427             case DataIsParam:
428                 if (data->u.s.reg_id)
429                 {
430                     sym_info->Flags |= SYMFLAG_REGISTER;
431                     sym_info->Register = data->u.s.reg_id;
432                     sym_info->Address = 0;
433                 }
434                 else
435                 {
436                     if (data->u.s.offset < 0)
437                         sym_info->Flags |= SYMFLAG_LOCAL | SYMFLAG_FRAMEREL;
438                     else
439                         sym_info->Flags |= SYMFLAG_PARAMETER | SYMFLAG_FRAMEREL;
440                     sym_info->Register = CV_REG_EBP; /* FIXME: needed ? */
441                     sym_info->Address = data->u.s.offset;
442                 }
443                 break;
444             case DataIsGlobal:
445             case DataIsFileStatic:
446                 symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
447                 sym_info->Register = 0;
448                 break;
449             case DataIsConstant:
450                 sym_info->Flags |= SYMFLAG_VALUEPRESENT;
451                 switch (data->u.value.n1.n2.vt)
452                 {
453                 case VT_I4:  sym_info->Value = (ULONG)data->u.value.n1.n2.n3.lVal; break;
454                 case VT_I2:  sym_info->Value = (ULONG)(long)data->u.value.n1.n2.n3.iVal; break;
455                 case VT_I1:  sym_info->Value = (ULONG)(long)data->u.value.n1.n2.n3.cVal; break;
456                 case VT_UI4: sym_info->Value = (ULONG)data->u.value.n1.n2.n3.ulVal; break;
457                 case VT_UI2: sym_info->Value = (ULONG)data->u.value.n1.n2.n3.uiVal; break;
458                 case VT_UI1: sym_info->Value = (ULONG)data->u.value.n1.n2.n3.bVal; break;
459                 default:        
460                     FIXME("Unsupported variant type (%u)\n", data->u.value.n1.n2.vt);
461                 }
462                 break;
463             default:
464                 FIXME("Unhandled kind (%u) in sym data\n", data->kind);
465             }
466         }
467         break;
468     case SymTagPublicSymbol:
469         sym_info->Flags |= SYMFLAG_EXPORT;
470         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
471         break;
472     case SymTagFunction:
473         sym_info->Flags |= SYMFLAG_FUNCTION;
474         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
475         break;
476     default:
477         symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
478         sym_info->Register = 0;
479         break;
480     }
481     sym_info->Scope = 0; /* FIXME */
482     sym_info->Tag = sym->tag;
483     name = symt_get_name(sym);
484     sym_info->NameLen = strlen(name) + 1;
485     if (sym_info->MaxNameLen)
486     {
487         strncpy(sym_info->Name, name, min(sym_info->NameLen, sym_info->MaxNameLen));
488         sym_info->Name[sym_info->MaxNameLen - 1] = '\0';
489     }
490     TRACE_(dbghelp_symt)("%p => %s %lu %lx\n",
491                          sym, sym_info->Name, sym_info->Size, sym_info->Address);
492 }
493
494 static BOOL symt_enum_module(struct module* module, const char* mask,
495                              PSYM_ENUMERATESYMBOLS_CALLBACK cb, PVOID user)
496 {
497     char                        buffer[sizeof(SYMBOL_INFO) + 256];
498     SYMBOL_INFO*                sym_info = (SYMBOL_INFO*)buffer;
499     void*                       ptr;
500     struct symt_ht*             sym = NULL;
501     struct hash_table_iter      hti;
502     regex_t                     preg;
503
504     assert(mask);
505     assert(mask[0] != '!');
506     compile_regex(mask, &preg);
507     hash_table_iter_init(&module->ht_symbols, &hti, NULL);
508     while ((ptr = hash_table_iter_up(&hti)))
509     {
510         sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
511         /* FIXME: this is not true, we should only drop the public
512          * symbol iff no other one is found
513          */
514         if ((dbghelp_options & SYMOPT_AUTO_PUBLICS) &&
515             sym->symt.tag == SymTagPublicSymbol) continue;
516
517         if (sym->hash_elt.name &&
518             regexec(&preg, sym->hash_elt.name, 0, NULL, 0) == 0)
519         {
520             sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
521             sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
522             symt_fill_sym_info(module, &sym->symt, sym_info);
523             if (!cb(sym_info, sym_info->Size, user)) break;
524         }
525     }   
526     regfree(&preg);
527     return sym ? FALSE : TRUE;
528 }
529
530 /***********************************************************************
531  *              resort_symbols
532  *
533  * Rebuild sorted list of symbols for a module.
534  */
535 static BOOL resort_symbols(struct module* module)
536 {
537     int                         nsym = 0;
538     void*                       ptr;
539     struct symt_ht*             sym;
540     struct hash_table_iter      hti;
541
542     hash_table_iter_init(&module->ht_symbols, &hti, NULL);
543     while ((ptr = hash_table_iter_up(&hti)))
544         nsym++;
545
546     if (!(module->module.NumSyms = nsym)) return FALSE;
547     
548     if (module->addr_sorttab)
549         module->addr_sorttab = HeapReAlloc(GetProcessHeap(), 0,
550                                            module->addr_sorttab, 
551                                            nsym * sizeof(struct symt_ht*));
552     else
553         module->addr_sorttab = HeapAlloc(GetProcessHeap(), 0,
554                                          nsym * sizeof(struct symt_ht*));
555     if (!module->addr_sorttab) return FALSE;
556
557     nsym = 0;
558     hash_table_iter_init(&module->ht_symbols, &hti, NULL);
559     while ((ptr = hash_table_iter_up(&hti)))
560     {
561         sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
562         assert(sym);
563         module->addr_sorttab[nsym++] = sym;
564     }
565     
566     qsort(module->addr_sorttab, nsym, sizeof(struct symt_ht*), symt_cmp_addr);
567     return module->sortlist_valid = TRUE;
568 }
569
570 /* assume addr is in module */
571 static int symt_find_nearest(struct module* module, DWORD addr)
572 {
573     int         mid, high, low;
574
575     if (!module->sortlist_valid && !resort_symbols(module)) return -1;
576
577     /*
578      * Binary search to find closest symbol.
579      */
580     low = 0;
581     high = module->module.NumSyms;
582     
583     while (high > low + 1)
584     {
585         mid = (high + low) / 2;
586         if (cmp_sorttab_addr(module, mid, addr) < 0)
587             low = mid;
588         else
589             high = mid;
590     }
591     if (low != high && high != module->module.NumSyms && 
592         cmp_sorttab_addr(module, high, addr) <= 0)
593         low = high;
594
595     /* If found symbol is a public symbol, check if there are any other entries that
596      * might also have the same address, but would get better information
597      */
598     if (module->addr_sorttab[low]->symt.tag == SymTagPublicSymbol)
599     {   
600         DWORD   ref;
601
602         symt_get_info(&module->addr_sorttab[low]->symt, TI_GET_ADDRESS, &ref);
603         if (low > 0 &&
604             module->addr_sorttab[low - 1]->symt.tag != SymTagPublicSymbol &&
605             !cmp_sorttab_addr(module, low - 1, ref))
606             low--;
607         else if (low < module->module.NumSyms - 1 && 
608                  module->addr_sorttab[low + 1]->symt.tag != SymTagPublicSymbol &&
609                  !cmp_sorttab_addr(module, low + 1, ref))
610             low++;
611     }
612
613     return low;
614 }
615
616 static BOOL symt_enum_locals_helper(struct process* pcs, struct module* module,
617                                     regex_t* preg, PSYM_ENUMERATESYMBOLS_CALLBACK cb,
618                                     PVOID user, SYMBOL_INFO* sym_info,
619                                     struct vector* v)
620 {
621     struct symt**       plsym = NULL;
622     struct symt*        lsym = NULL;
623     DWORD               pc = pcs->ctx_frame.InstructionOffset;
624
625     while ((plsym = vector_iter_up(v, plsym)))
626     {
627         lsym = *plsym;
628         switch (lsym->tag)
629         {
630         case SymTagBlock:
631             {
632                 struct symt_block*  block = (struct symt_block*)lsym;
633                 if (pc < block->address || block->address + block->size <= pc)
634                     continue;
635                 if (!symt_enum_locals_helper(pcs, module, preg, cb, user, 
636                                              sym_info, &block->vchildren))
637                     return FALSE;
638             }
639             break;
640         case SymTagData:
641             if (regexec(preg, symt_get_name(lsym), 0, NULL, 0) == 0)
642             {
643                 symt_fill_sym_info(module, lsym, sym_info);
644                 if (!cb(sym_info, sym_info->Size, user))
645                     return FALSE;
646             }
647             break;
648         case SymTagLabel:
649         case SymTagFuncDebugStart:
650         case SymTagFuncDebugEnd:
651             break;
652         default:
653             FIXME("Unknown type: %u (%x)\n", lsym->tag, lsym->tag);
654             assert(0);
655         }
656     }
657     return TRUE;
658 }
659
660 static BOOL symt_enum_locals(struct process* pcs, const char* mask,
661                              PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
662                              PVOID UserContext)
663 {
664     struct module*      module;
665     struct symt_ht*     sym;
666     char                buffer[sizeof(SYMBOL_INFO) + 256];
667     SYMBOL_INFO*        sym_info = (SYMBOL_INFO*)buffer;
668     DWORD               pc = pcs->ctx_frame.InstructionOffset;
669     int                 idx;
670
671     sym_info->SizeOfStruct = sizeof(*sym_info);
672     sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
673
674     module = module_find_by_addr(pcs, pc, DMT_UNKNOWN);
675     if (!(module = module_get_debug(pcs, module))) return FALSE;
676     if ((idx = symt_find_nearest(module, pc)) == -1) return FALSE;
677
678     sym = module->addr_sorttab[idx];
679     if (sym->symt.tag == SymTagFunction)
680     {
681         BOOL            ret;
682         regex_t         preg;
683
684         compile_regex(mask ? mask : "*", &preg);
685         ret = symt_enum_locals_helper(pcs, module, &preg, EnumSymbolsCallback, 
686                                       UserContext, sym_info, 
687                                       &((struct symt_function*)sym)->vchildren);
688         regfree(&preg);
689         return ret;
690         
691     }
692     symt_fill_sym_info(module, &sym->symt, sym_info);
693     return EnumSymbolsCallback(sym_info, sym_info->Size, UserContext);
694 }
695
696 /******************************************************************
697  *              SymEnumSymbols (DBGHELP.@)
698  *
699  */
700 BOOL WINAPI SymEnumSymbols(HANDLE hProcess, ULONG BaseOfDll, PCSTR Mask,
701                            PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
702                            PVOID UserContext)
703 {
704     struct process*     pcs = process_find_by_handle(hProcess);
705     struct module*      module;
706
707     TRACE("(%p %08lx %s %p %p)\n", 
708           hProcess, BaseOfDll, debugstr_a(Mask), EnumSymbolsCallback, UserContext);
709
710     if (!pcs) return FALSE;
711
712     if (BaseOfDll == 0)
713     {
714         if (Mask && Mask[0] == '!')
715         {
716             if (!Mask[1])
717             {
718                 /* FIXME: is this really what's intended ??? */
719                 for (module = pcs->lmodules; module; module = module->next)
720                 {
721                     if (module->module.SymType != SymNone &&
722                         !symt_enum_module(module, "*", EnumSymbolsCallback, UserContext))
723                         break;
724                 }
725                 return TRUE;
726             }
727             module = module_find_by_name(pcs, &Mask[1], DMT_UNKNOWN);
728             Mask++;
729         }
730         else return symt_enum_locals(pcs, Mask, EnumSymbolsCallback, UserContext);
731     }
732     else
733     {
734         module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
735         if (Mask && Mask[0] == '!')
736         {
737             if (!Mask[1] ||
738                 strcmp(&Mask[1], module->module.ModuleName))
739             {
740                 FIXME("Strange call mode\n");
741                 return FALSE;
742             }
743             Mask = "*";
744         }
745         else if (!Mask) Mask = "*";
746     }
747     if ((module = module_get_debug(pcs, module)))
748         symt_enum_module(module, Mask, EnumSymbolsCallback, UserContext);
749     return TRUE;
750 }
751
752 struct sym_enumerate
753 {
754     void*                       ctx;
755     PSYM_ENUMSYMBOLS_CALLBACK   cb;
756 };
757
758 static BOOL CALLBACK sym_enumerate_cb(PSYMBOL_INFO syminfo, ULONG size, void* ctx)
759 {
760     struct sym_enumerate*       se = (struct sym_enumerate*)ctx;
761     return (se->cb)(syminfo->Name, syminfo->Address, syminfo->Size, se->ctx);
762 }
763
764 /***********************************************************************
765  *              SymEnumerateSymbols (DBGHELP.@)
766  */
767 BOOL WINAPI SymEnumerateSymbols(HANDLE hProcess, DWORD BaseOfDll,
768                                 PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback, 
769                                 PVOID UserContext)
770 {
771     struct sym_enumerate        se;
772
773     se.ctx = UserContext;
774     se.cb  = EnumSymbolsCallback;
775     
776     return SymEnumSymbols(hProcess, BaseOfDll, NULL, sym_enumerate_cb, &se);
777 }
778
779 /******************************************************************
780  *              SymFromAddr (DBGHELP.@)
781  *
782  */
783 BOOL WINAPI SymFromAddr(HANDLE hProcess, DWORD Address, 
784                         DWORD* Displacement, PSYMBOL_INFO Symbol)
785 {
786     struct process*     pcs = process_find_by_handle(hProcess);
787     struct module*      module;
788     struct symt_ht*     sym;
789     int                 idx;
790
791     if (!pcs) return FALSE;
792     module = module_find_by_addr(pcs, Address, DMT_UNKNOWN);
793     if (!(module = module_get_debug(pcs, module))) return FALSE;
794     if ((idx = symt_find_nearest(module, Address)) == -1) return FALSE;
795
796     sym = module->addr_sorttab[idx];
797
798     symt_fill_sym_info(module, &sym->symt, Symbol);
799     if (Displacement) *Displacement = Address - Symbol->Address;
800     return TRUE;
801 }
802
803 /******************************************************************
804  *              SymGetSymFromAddr (DBGHELP.@)
805  *
806  */
807 BOOL WINAPI SymGetSymFromAddr(HANDLE hProcess, DWORD Address,
808                               PDWORD Displacement, PIMAGEHLP_SYMBOL Symbol)
809 {
810     char        buffer[sizeof(SYMBOL_INFO) + 256];
811     SYMBOL_INFO*si = (SYMBOL_INFO*)buffer;
812     size_t      len;
813
814     if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
815     si->SizeOfStruct = sizeof(*si);
816     si->MaxNameLen = 256;
817     if (!SymFromAddr(hProcess, Address, Displacement, si))
818         return FALSE;
819
820     Symbol->Address = si->Address;
821     Symbol->Size    = si->Size;
822     Symbol->Flags   = si->Flags;
823     len = min(Symbol->MaxNameLength, si->MaxNameLen);
824     strncpy(Symbol->Name, si->Name, len);
825     Symbol->Name[len - 1] = '\0';
826     return TRUE;
827 }
828
829 /******************************************************************
830  *              SymFromName (DBGHELP.@)
831  *
832  */
833 BOOL WINAPI SymFromName(HANDLE hProcess, LPSTR Name, PSYMBOL_INFO Symbol)
834 {
835     struct process*             pcs = process_find_by_handle(hProcess);
836     struct module*              module;
837     struct hash_table_iter      hti;
838     void*                       ptr;
839     struct symt_ht*             sym = NULL;
840
841     TRACE("(%p, %s, %p)\n", hProcess, Name, Symbol);
842     if (!pcs) return FALSE;
843     if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
844     for (module = pcs->lmodules; module; module = module->next)
845     {
846         if (module->module.SymType != SymNone)
847         {
848             if (module->module.SymType == SymDeferred)
849             {
850                 struct module*  xmodule = module_get_debug(pcs, module);
851                 if (!xmodule) continue;
852                 module = xmodule;
853             }
854             hash_table_iter_init(&module->ht_symbols, &hti, Name);
855             while ((ptr = hash_table_iter_up(&hti)))
856             {
857                 sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
858
859                 if (!strcmp(sym->hash_elt.name, Name))
860                 {
861                     symt_fill_sym_info(module, &sym->symt, Symbol);
862                     return TRUE;
863                 }
864             }
865         }
866     }
867     return FALSE;
868 }
869
870 /***********************************************************************
871  *              SymGetSymFromName (DBGHELP.@)
872  */
873 BOOL WINAPI SymGetSymFromName(HANDLE hProcess, LPSTR Name, PIMAGEHLP_SYMBOL Symbol)
874 {
875     char        buffer[sizeof(SYMBOL_INFO) + 256];
876     SYMBOL_INFO*si = (SYMBOL_INFO*)buffer;
877     size_t      len;
878
879     if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
880     si->SizeOfStruct = sizeof(*si);
881     si->MaxNameLen = 256;
882     if (!SymFromName(hProcess, Name, si)) return FALSE;
883
884     Symbol->Address = si->Address;
885     Symbol->Size    = si->Size;
886     Symbol->Flags   = si->Flags;
887     len = min(Symbol->MaxNameLength, si->MaxNameLen);
888     strncpy(Symbol->Name, si->Name, len);
889     Symbol->Name[len - 1] = '\0';
890     return TRUE;
891 }
892
893 /******************************************************************
894  *              sym_fill_func_line_info
895  *
896  * fills information about a file
897  */
898 BOOL symt_fill_func_line_info(struct module* module, struct symt_function* func, 
899                               DWORD addr, IMAGEHLP_LINE* line)
900 {
901     struct line_info*   dli = NULL;
902     BOOL                found = FALSE;
903
904     assert(func->symt.tag == SymTagFunction);
905
906     while ((dli = vector_iter_down(&func->vlines, dli)))
907     {
908         if (!dli->is_source_file)
909         {
910             if (found || dli->u.pc_offset > addr) continue;
911             line->LineNumber = dli->line_number;
912             line->Address    = dli->u.pc_offset;
913             line->Key        = dli;
914             found = TRUE;
915             continue;
916         }
917         if (found)
918         {
919             line->FileName = (char*)source_get(module, dli->u.source_file);
920             return TRUE;
921         }
922     }
923     return FALSE;
924 }
925
926 /***********************************************************************
927  *              SymGetSymNext (DBGHELP.@)
928  */
929 BOOL WINAPI SymGetSymNext(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol)
930 {
931     /* algo:
932      * get module from Symbol.Address
933      * get index in module.addr_sorttab of Symbol.Address
934      * increment index
935      * if out of module bounds, move to next module in process address space
936      */
937     FIXME("(%p, %p): stub\n", hProcess, Symbol);
938     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
939     return FALSE;
940 }
941
942 /***********************************************************************
943  *              SymGetSymPrev (DBGHELP.@)
944  */
945
946 BOOL WINAPI SymGetSymPrev(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol)
947 {
948     FIXME("(%p, %p): stub\n", hProcess, Symbol);
949     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
950     return FALSE;
951 }
952
953 /******************************************************************
954  *              SymGetLineFromAddr (DBGHELP.@)
955  *
956  */
957 BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, 
958                                PDWORD pdwDisplacement, PIMAGEHLP_LINE Line)
959 {
960     struct process*     pcs = process_find_by_handle(hProcess);
961     struct module*      module;
962     int                 idx;
963
964     TRACE("%p %08lx %p %p\n", hProcess, dwAddr, pdwDisplacement, Line);
965
966     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
967
968     if (!pcs) return FALSE;
969     module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
970     if (!(module = module_get_debug(pcs, module))) return FALSE;
971     if ((idx = symt_find_nearest(module, dwAddr)) == -1) return FALSE;
972
973     if (module->addr_sorttab[idx]->symt.tag != SymTagFunction) return FALSE;
974     if (!symt_fill_func_line_info(module, 
975                                   (struct symt_function*)module->addr_sorttab[idx],
976                                   dwAddr, Line)) return FALSE;
977     if (pdwDisplacement) *pdwDisplacement = dwAddr - Line->Address;
978     return TRUE;
979 }
980
981 /******************************************************************
982  *              SymGetLinePrev (DBGHELP.@)
983  *
984  */
985 BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line)
986 {
987     struct process*     pcs = process_find_by_handle(hProcess);
988     struct module*      module;
989     struct line_info*   li;
990     BOOL                in_search = FALSE;
991
992     TRACE("(%p %p)\n", hProcess, Line);
993
994     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
995
996     if (!pcs) return FALSE;
997     module = module_find_by_addr(pcs, Line->Address, DMT_UNKNOWN);
998     if (!(module = module_get_debug(pcs, module))) return FALSE;
999
1000     if (Line->Key == 0) return FALSE;
1001     li = (struct line_info*)Line->Key;
1002     /* things are a bit complicated because when we encounter a DLIT_SOURCEFILE
1003      * element we have to go back until we find the prev one to get the real
1004      * source file name for the DLIT_OFFSET element just before 
1005      * the first DLIT_SOURCEFILE
1006      */
1007     while (!li->is_first)
1008     {
1009         li--;
1010         if (!li->is_source_file)
1011         {
1012             Line->LineNumber = li->line_number;
1013             Line->Address    = li->u.pc_offset;
1014             Line->Key        = li;
1015             if (!in_search) return TRUE;
1016         }
1017         else
1018         {
1019             if (in_search)
1020             {
1021                 Line->FileName = (char*)source_get(module, li->u.source_file);
1022                 return TRUE;
1023             }
1024             in_search = TRUE;
1025         }
1026     }
1027     SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */
1028     return FALSE;
1029 }
1030
1031 BOOL symt_get_func_line_next(struct module* module, PIMAGEHLP_LINE line)
1032 {
1033     struct line_info*   li;
1034
1035     if (line->Key == 0) return FALSE;
1036     li = (struct line_info*)line->Key;
1037     while (!li->is_last)
1038     {
1039         li++;
1040         if (!li->is_source_file)
1041         {
1042             line->LineNumber = li->line_number;
1043             line->Address    = li->u.pc_offset;
1044             line->Key        = li;
1045             return TRUE;
1046         }
1047         line->FileName = (char*)source_get(module, li->u.source_file);
1048     }
1049     return FALSE;
1050 }
1051
1052 /******************************************************************
1053  *              SymGetLineNext (DBGHELP.@)
1054  *
1055  */
1056 BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line)
1057 {
1058     struct process*     pcs = process_find_by_handle(hProcess);
1059     struct module*      module;
1060
1061     TRACE("(%p %p)\n", hProcess, Line);
1062
1063     if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
1064     if (!pcs) return FALSE;
1065     module = module_find_by_addr(pcs, Line->Address, DMT_UNKNOWN);
1066     if (!(module = module_get_debug(pcs, module))) return FALSE;
1067
1068     if (symt_get_func_line_next(module, Line)) return TRUE;
1069     SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */
1070     return FALSE;
1071 }
1072
1073 /***********************************************************************
1074  *              SymFunctionTableAccess (DBGHELP.@)
1075  */
1076 PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase)
1077 {
1078     FIXME("(%p, 0x%08lx): stub\n", hProcess, AddrBase);
1079     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1080     return FALSE;
1081 }
1082
1083 /***********************************************************************
1084  *              SymUnDName (DBGHELP.@)
1085  */
1086 BOOL WINAPI SymUnDName(PIMAGEHLP_SYMBOL sym, LPSTR UnDecName, DWORD UnDecNameLength)
1087 {
1088     FIXME("(%p %s %lu): stub\n", sym, UnDecName, UnDecNameLength);
1089     return UnDecorateSymbolName(sym->Name, UnDecName, UnDecNameLength, 
1090                                 UNDNAME_COMPLETE);
1091 }
1092
1093 /***********************************************************************
1094  *              UnDecorateSymbolName (DBGHELP.@)
1095  */
1096 DWORD WINAPI UnDecorateSymbolName(LPCSTR DecoratedName, LPSTR UnDecoratedName,
1097                                   DWORD UndecoratedLength, DWORD Flags)
1098 {
1099     FIXME("(%s, %p, %ld, 0x%08lx): stub\n",
1100           debugstr_a(DecoratedName), UnDecoratedName, UndecoratedLength, Flags);
1101
1102     strncpy(UnDecoratedName, DecoratedName, UndecoratedLength);
1103     UnDecoratedName[UndecoratedLength - 1] = '\0';
1104     return TRUE;
1105 }