Skip directories when loading debug symbols.
[wine] / debugger / module.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * File module.c - module handling for the wine debugger
4  *
5  * Copyright (C) 1993, Eric Youngdale.
6  *               2000, Eric Pouech
7  */
8 #include "config.h"
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include "debugger.h"
13 #include "toolhelp.h"
14 #include "wingdi.h"
15 #include "winuser.h"
16
17 /***********************************************************************
18  * Creates and links a new module to the current process 
19  *
20  */
21 DBG_MODULE*     DEBUG_AddModule(const char* name, enum DbgModuleType type, 
22                                 void* mod_addr, u_long size, HMODULE hmodule)
23 {
24     DBG_MODULE* wmod;
25
26     if (!(wmod = (DBG_MODULE*)DBG_alloc(sizeof(*wmod))))
27         return NULL;
28
29     memset(wmod, 0, sizeof(*wmod));
30
31     wmod->dil = DIL_DEFERRED;
32     wmod->main = (DEBUG_CurrProcess->num_modules == 0);
33     wmod->type = type;
34     wmod->load_addr = mod_addr;
35     wmod->size = size;
36     wmod->handle = hmodule;
37     wmod->dbg_index = DEBUG_CurrProcess->next_index;
38     wmod->module_name = DBG_strdup(name);
39
40     DEBUG_CurrProcess->modules = DBG_realloc(DEBUG_CurrProcess->modules,
41                                              ++DEBUG_CurrProcess->num_modules * sizeof(DBG_MODULE*));
42     DEBUG_CurrProcess->modules[DEBUG_CurrProcess->num_modules - 1] = wmod;
43
44     return wmod;
45 }
46
47 /***********************************************************************
48  *      DEBUG_FindModuleByName
49  *
50  */
51 DBG_MODULE*     DEBUG_FindModuleByName(const char* name, enum DbgModuleType type)
52 {
53      int                i;
54      DBG_MODULE**       amod = DEBUG_CurrProcess->modules;
55      
56      for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
57          if ((type == DMT_UNKNOWN || type == amod[i]->type) &&
58              !strcasecmp(name, amod[i]->module_name)) 
59              return amod[i];
60      }
61      return NULL;
62 }
63
64 /***********************************************************************
65  *      DEBUG_FindModuleByAddr
66  *
67  * either the addr where module is loaded, or any address inside the 
68  * module
69  */
70 DBG_MODULE*     DEBUG_FindModuleByAddr(void* addr, enum DbgModuleType type)
71 {
72      int                i;
73      DBG_MODULE**       amod = DEBUG_CurrProcess->modules;
74      DBG_MODULE*        res = NULL;
75     
76      for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
77          if ((type == DMT_UNKNOWN || type == amod[i]->type) &&
78              (u_long)addr >= (u_long)amod[i]->load_addr &&
79              (!res || res->load_addr < amod[i]->load_addr))
80              res = amod[i];
81      }
82     return res;
83 }
84
85 /***********************************************************************
86  *              DEBUG_FindModuleByHandle
87  */
88 DBG_MODULE*     DEBUG_FindModuleByHandle(HANDLE handle, enum DbgModuleType type)
89 {
90      int                i;
91      DBG_MODULE**       amod = DEBUG_CurrProcess->modules;
92      
93      for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
94          if ((type == DMT_UNKNOWN || type == amod[i]->type) && 
95              handle == amod[i]->handle) 
96              return amod[i];
97      }
98      return NULL;
99 }
100
101 /***********************************************************************
102  *              DEBUG_GetProcessMainModule
103  */
104 DBG_MODULE*     DEBUG_GetProcessMainModule(DBG_PROCESS* process)
105 {
106     if (!process || !process->num_modules)      return NULL;
107  
108     /* main module is the first to be loaded on a given process, so it's the first
109      * in the array */
110     assert(process->modules[0]->main);
111     return process->modules[0];
112 }
113
114 /***********************************************************************
115  *                      DEBUG_RegisterELFModule
116  *
117  * ELF modules are also entered into the list - this is so that we
118  * can make 'info shared' types of displays possible.
119  */
120 DBG_MODULE* DEBUG_RegisterELFModule(u_long load_addr, u_long size, const char* name)
121 {
122     DBG_MODULE* wmod = DEBUG_AddModule(name, DMT_ELF, (void*)load_addr, size, 0);
123
124     if (!wmod) return NULL;
125
126     DEBUG_CurrProcess->next_index++;
127
128     return wmod;
129 }
130
131 /***********************************************************************
132  *                      DEBUG_RegisterPEModule
133  *
134  */
135 DBG_MODULE* DEBUG_RegisterPEModule(HMODULE hModule, u_long load_addr, u_long size, const char *module_name)
136 {
137     DBG_MODULE* wmod = DEBUG_AddModule(module_name, DMT_PE, (void*)load_addr, size, hModule);
138
139     if (!wmod) return NULL;
140
141     DEBUG_CurrProcess->next_index++;
142
143     return wmod;
144 }
145
146 /***********************************************************************
147  *                      DEBUG_RegisterNEModule
148  *
149  */
150 DBG_MODULE* DEBUG_RegisterNEModule(HMODULE hModule, void* load_addr, u_long size, const char *module_name)
151 {
152     DBG_MODULE* wmod = DEBUG_AddModule(module_name, DMT_NE, load_addr, size, hModule);
153
154     if (!wmod) return NULL;
155
156     DEBUG_CurrProcess->next_index++;
157     return wmod;
158 }
159
160 #if 0
161 /***********************************************************************
162  *           DEBUG_GetEP16
163  *
164  * Helper function fo DEBUG_LoadModuleEPs16:
165  *      finds the address of a given entry point from a given module
166  */
167 static BOOL DEBUG_GetEP16(char* moduleAddr, const NE_MODULE* module, 
168                           WORD ordinal, DBG_ADDR* addr)
169 {
170     void*               idx;
171     ET_ENTRY            entry;
172     ET_BUNDLE           bundle;
173     SEGTABLEENTRY       ste;
174
175     bundle.next = module->entry_table;
176     do {
177         if (!bundle.next)
178             return FALSE;
179         idx = moduleAddr + bundle.next;
180         if (!DEBUG_READ_MEM_VERBOSE(idx, &bundle, sizeof(bundle)))
181             return FALSE;
182     } while ((ordinal < bundle.first + 1) || (ordinal > bundle.last));
183     
184     if (!DEBUG_READ_MEM_VERBOSE((char*)idx + sizeof(ET_BUNDLE) + 
185                                 (ordinal - bundle.first - 1) * sizeof(ET_ENTRY), 
186                                 &entry, sizeof(ET_ENTRY)))
187         return FALSE;
188     
189     addr->seg = entry.segnum;
190     addr->off = entry.offs;
191     
192     if (addr->seg == 0xfe) addr->seg = 0xffff;  /* constant entry */
193     else {
194         if (!DEBUG_READ_MEM_VERBOSE(moduleAddr + module->seg_table + 
195                                     sizeof(ste) * (addr->seg - 1),
196                                     &ste, sizeof(ste)))
197             return FALSE;
198         addr->seg = GlobalHandleToSel16(ste.hSeg);
199     }
200     return TRUE;
201 }
202
203 /***********************************************************************
204  *           DEBUG_LoadModule16
205  *
206  * Load the entry points of a Win16 module into the hash table.
207  */
208 static void DEBUG_LoadModule16(HMODULE hModule, NE_MODULE* module, char* moduleAddr, const char* name)
209 {
210     DBG_VALUE   value;
211     BYTE        buf[1 + 256 + 2];
212     char        epname[512];
213     char*       cpnt;
214     DBG_MODULE* wmod;
215
216     wmod = DEBUG_RegisterNEModule(hModule, moduleAddr, name);
217
218     value.type = NULL;
219     value.cookie = DV_TARGET;
220     value.addr.seg = 0;
221     value.addr.off = 0;
222     
223     cpnt = moduleAddr + module->name_table;
224     
225     /* First search the resident names */
226     
227     /* skip module name */
228     if (!DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) || !buf[0])
229         return;
230     cpnt += 1 + buf[0] + sizeof(WORD);
231     
232     while (DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) && buf[0]) {
233         sprintf(epname, "%s.%.*s", name, buf[0], &buf[1]);
234         if (DEBUG_GetEP16(moduleAddr, module, *(WORD*)&buf[1 + buf[0]], &value.addr)) {
235             DEBUG_AddSymbol(epname, &value, NULL, SYM_WIN32 | SYM_FUNC);
236         }
237         cpnt += buf[0] + 1 + sizeof(WORD);
238     }
239     
240     /* Now search the non-resident names table */
241     if (!module->nrname_handle) return;  /* No non-resident table */
242     cpnt = (char *)GlobalLock16(module->nrname_handle);
243     while (DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) && buf[0]) {
244         sprintf(epname, "%s.%.*s", name, buf[0], &buf[1]);
245         if (DEBUG_GetEP16(moduleAddr, module, *(WORD*)&buf[1 + buf[0]], &value.addr)) {
246             DEBUG_AddSymbol(epname, &value, NULL, SYM_WIN32 | SYM_FUNC);
247         }
248         cpnt += buf[0] + 1 + sizeof(WORD);
249     }
250     GlobalUnlock16(module->nrname_handle);
251 }
252 #endif
253
254 /***********************************************************************
255  *                      DEBUG_LoadModule32
256  */
257 void    DEBUG_LoadModule32(const char* name, HANDLE hFile, DWORD base)
258 {
259      IMAGE_NT_HEADERS           pe_header;
260      DWORD                      nth_ofs;
261      DBG_MODULE*                wmod = NULL;
262      int                        i;
263      IMAGE_SECTION_HEADER       pe_seg;
264      DWORD                      pe_seg_ofs;
265      DWORD                      size = 0;
266      enum DbgInfoLoad           dil = DIL_ERROR;
267
268      /* grab PE Header */
269      if (!DEBUG_READ_MEM_VERBOSE((void*)(base + OFFSET_OF(IMAGE_DOS_HEADER, e_lfanew)),
270                                  &nth_ofs, sizeof(nth_ofs)) ||
271          !DEBUG_READ_MEM_VERBOSE((void*)(base + nth_ofs), &pe_header, sizeof(pe_header)))
272          return;
273      
274      pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) +
275          pe_header.FileHeader.SizeOfOptionalHeader;
276      
277      for (i = 0; i < pe_header.FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg)) {
278          if (!DEBUG_READ_MEM_VERBOSE((void*)(base + pe_seg_ofs), &pe_seg, sizeof(pe_seg)))
279              continue;
280          if (size < pe_seg.VirtualAddress + pe_seg.SizeOfRawData)
281              size = pe_seg.VirtualAddress + pe_seg.SizeOfRawData;
282      }
283      
284      /* FIXME: we make the assumption that hModule == base */
285      wmod = DEBUG_RegisterPEModule((HMODULE)base, base, size, name);
286      if (wmod) {
287          dil = DEBUG_RegisterStabsDebugInfo(wmod, hFile, &pe_header, nth_ofs);
288          if (dil != DIL_LOADED)
289              dil = DEBUG_RegisterMSCDebugInfo(wmod, hFile, &pe_header, nth_ofs);
290          if (dil != DIL_LOADED)
291              dil = DEBUG_RegisterPEDebugInfo(wmod, hFile, &pe_header, nth_ofs); 
292      }
293
294      if (wmod) wmod->dil = dil;
295      DEBUG_ReportDIL(dil, "32bit DLL", name, base);
296 }
297
298 /***********************************************************************
299  *                      DEBUG_RegisterPEDebugInfo
300  */
301 enum DbgInfoLoad        DEBUG_RegisterPEDebugInfo(DBG_MODULE* wmod, HANDLE hFile,
302                                                   void* _nth, unsigned long nth_ofs)
303 {
304     DBG_VALUE                   value;
305     char                        buffer[512];
306     char                        bufstr[256];
307     int                         i;
308     IMAGE_SECTION_HEADER        pe_seg;
309     DWORD                       pe_seg_ofs;
310     IMAGE_DATA_DIRECTORY        dir;
311     DWORD                       dir_ofs;
312     const char*                 prefix;
313     IMAGE_NT_HEADERS*           nth = (PIMAGE_NT_HEADERS)_nth;
314     DWORD                       base = (u_long)wmod->load_addr;
315
316     value.type = NULL;
317     value.cookie = DV_TARGET;
318     value.addr.seg = 0;
319     value.addr.off = 0;
320     
321     /* Add start of DLL */
322     value.addr.off = base;
323     if ((prefix = strrchr(wmod->module_name, '\\' ))) prefix++;
324     else prefix = wmod->module_name;
325
326     DEBUG_AddSymbol(prefix, &value, NULL, SYM_WIN32 | SYM_FUNC);
327     
328     /* Add entry point */
329     wsnprintf(buffer, sizeof(buffer), "%s.EntryPoint", prefix);
330     value.addr.off = base + nth->OptionalHeader.AddressOfEntryPoint;
331     DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
332
333     /* Add start of sections */
334     pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) +
335         nth->FileHeader.SizeOfOptionalHeader;
336     
337     for (i = 0; i < nth->FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg)) {
338         if (!DEBUG_READ_MEM_VERBOSE((void*)(base + pe_seg_ofs), &pe_seg, sizeof(pe_seg)))
339             continue;
340         wsnprintf(buffer, sizeof(buffer), "%s.%s", prefix, pe_seg.Name);
341         value.addr.off = base + pe_seg.VirtualAddress;
342         DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
343     }
344     
345     /* Add exported functions */
346     dir_ofs = nth_ofs + 
347         OFFSET_OF(IMAGE_NT_HEADERS, 
348                   OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
349     if (DEBUG_READ_MEM_VERBOSE((void*)(base + dir_ofs), &dir, sizeof(dir)) && dir.Size) {
350         IMAGE_EXPORT_DIRECTORY  exports;
351         WORD*                   ordinals = NULL;
352         void**                  functions = NULL;
353         DWORD*                  names = NULL;
354         int                     j;
355         
356         if (DEBUG_READ_MEM_VERBOSE((void*)(base + dir.VirtualAddress), 
357                                    &exports, sizeof(exports)) &&
358             
359             ((functions = DBG_alloc(sizeof(functions[0]) * exports.NumberOfFunctions))) &&
360             DEBUG_READ_MEM_VERBOSE((void*)(base + (DWORD)exports.AddressOfFunctions),
361                                    functions, sizeof(functions[0]) * exports.NumberOfFunctions) &&
362             
363             ((ordinals = DBG_alloc(sizeof(ordinals[0]) * exports.NumberOfNames))) &&
364             DEBUG_READ_MEM_VERBOSE((void*)(base + (DWORD)exports.AddressOfNameOrdinals),
365                                    ordinals, sizeof(ordinals[0]) * exports.NumberOfNames) &&
366             
367             ((names = DBG_alloc(sizeof(names[0]) * exports.NumberOfNames))) &&
368             DEBUG_READ_MEM_VERBOSE((void*)(base + (DWORD)exports.AddressOfNames),
369                                    names, sizeof(names[0]) * exports.NumberOfNames)) {
370             
371             for (i = 0; i < exports.NumberOfNames; i++) {
372                 if (!names[i] ||
373                     !DEBUG_READ_MEM_VERBOSE((void*)(base + names[i]), bufstr, sizeof(bufstr)))
374                     continue;
375                 bufstr[sizeof(bufstr) - 1] = 0;
376                 wsnprintf(buffer, sizeof(buffer), "%s.%s", prefix, bufstr);
377                 value.addr.off = base + (DWORD)functions[ordinals[i]];
378                 DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
379             }
380             
381             for (i = 0; i < exports.NumberOfFunctions; i++) {
382                 if (!functions[i]) continue;
383                 /* Check if we already added it with a name */
384                 for (j = 0; j < exports.NumberOfNames; j++)
385                     if ((ordinals[j] == i) && names[j]) break;
386                 if (j < exports.NumberOfNames) continue;
387                 wsnprintf(buffer, sizeof(buffer), "%s.%ld", prefix, i + exports.Base);
388                 value.addr.off = base + (DWORD)functions[i];
389                 DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
390             }
391         }
392         DBG_free(functions);
393         DBG_free(ordinals);
394         DBG_free(names);
395     }
396     /* no real debug info, only entry points */
397     return DIL_NOINFO;
398 }
399
400 /***********************************************************************
401  *              DEBUG_LoadEntryPoints
402  *
403  * Load the entry points of all the modules into the hash table.
404  */
405 int DEBUG_LoadEntryPoints(const char* pfx)
406 {
407     int         first = 0;
408     /* FIXME: with address space separation in space, this is plain wrong
409      *        it requires the 16 bit WOW debugging interface...
410      */
411 #if 0
412     MODULEENTRY entry;
413     NE_MODULE   module;
414     void*       moduleAddr;
415     int         rowcount = 0;
416     int         len;
417
418     /* FIXME: we assume that a module is never removed from memory */
419     /* FIXME: this is (currently plain wrong when debugger is started by
420      *        attaching to an existing program => the 16 bit modules will
421      *        not be shared... not much to do on debugger side... sigh
422      */
423     if (ModuleFirst16(&entry)) do {
424         if (DEBUG_FindModuleByName(entry.szModule, DM_TYPE_UNKNOWN) ||
425             !(moduleAddr = NE_GetPtr(entry.hModule)) ||
426             !DEBUG_READ_MEM_VERBOSE(moduleAddr, &module, sizeof(module)) ||
427             (module.flags & NE_FFLAGS_WIN32) /* NE module */)
428             continue;
429         if (!first) {
430             if (pfx) DEBUG_Printf(DBG_CHN_MESG, pfx);
431             DEBUG_Printf(DBG_CHN_MESG, "   ");
432             rowcount = 3 + (pfx ? strlen(pfx) : 0);
433             first = 1;
434         }
435         
436         len = strlen(entry.szModule);
437         if ((rowcount + len) > 76) {
438             DEBUG_Printf(DBG_CHN_MESG, "\n   ");
439             rowcount = 3;
440         }
441         DEBUG_Printf(DBG_CHN_MESG, " %s", entry.szModule);
442         rowcount += len + 1;
443         
444         DEBUG_LoadModule16(entry.hModule, &module, moduleAddr, entry.szModule);
445     } while (ModuleNext16(&entry));
446 #endif
447     
448     if (first) DEBUG_Printf(DBG_CHN_MESG, "\n"); 
449     return first;
450 }
451
452 void    DEBUG_ReportDIL(enum DbgInfoLoad dil, const char* pfx, const char* filename, DWORD load_addr)
453 {
454     const char* fmt;
455
456     switch (dil) {
457     case DIL_DEFERRED:  
458         fmt = "Deferring debug information loading for %s '%s' (0x%08x)\n";
459         break;
460     case DIL_LOADED:    
461         fmt = "Loaded debug information from %s '%s' (0x%08x)\n";       
462         break;
463     case DIL_NOINFO:    
464         fmt = "No debug information in %s '%s' (0x%08x)\n";             
465         break;
466     case DIL_ERROR:     
467         fmt = "Can't find file for %s '%s' (0x%08x)\n";                 
468         break;
469     default: 
470         DEBUG_Printf(DBG_CHN_ERR, "Oooocch (%d)\n", dil); 
471         return;
472     }
473    
474     DEBUG_Printf(DBG_CHN_MESG, fmt, pfx, filename, load_addr);
475 }
476
477 static const char*      DEBUG_GetModuleType(enum DbgModuleType type)
478 {
479     switch (type) {
480     case DMT_NE:    return "NE";
481     case DMT_PE:    return "PE";
482     case DMT_ELF:   return "ELF";
483     default:        return "???";;
484     }
485 }
486
487 static const char*      DEBUG_GetDbgInfo(enum DbgInfoLoad dil)
488 {
489     switch (dil) {
490     case DIL_LOADED:    return "loaded";
491     case DIL_DEFERRED:  return "deferred";
492     case DIL_NOINFO:    return "none";
493     case DIL_ERROR:     return "error";
494     default:            return "?";
495     }
496 }
497
498 /***********************************************************************
499  *           DEBUG_ModuleCompare
500  *
501  * returns -1 is p1 < p2, 0 is p1 == p2, +1 if p1 > p2
502  * order used is order on load_addr of a module
503  */
504 static int      DEBUG_ModuleCompare(const void* p1, const void* p2)
505 {
506     return (*((const DBG_MODULE**)p1))->load_addr - 
507            (*((const DBG_MODULE**)p2))->load_addr;
508 }
509
510 /***********************************************************************
511  *           DEBUG_IsContainer
512  *
513  * returns TRUE is wmod_child is contained (inside bounds) of wmod_cntnr
514  */
515 static BOOL inline DEBUG_IsContainer(const DBG_MODULE* wmod_cntnr, 
516                                      const DBG_MODULE* wmod_child)
517 {
518     return wmod_cntnr->load_addr < wmod_child->load_addr &&
519         (DWORD)wmod_cntnr->load_addr + wmod_cntnr->size > 
520         (DWORD)wmod_child->load_addr + wmod_child->size;
521 }
522
523 static void     DEBUG_InfoShareModule(const DBG_MODULE* module, int ident)
524 {
525     if (ident) DEBUG_Printf(DBG_CHN_MESG, "  \\-");
526     DEBUG_Printf(DBG_CHN_MESG, "%s\t0x%08lx-%08lx\t%s\n", 
527                  DEBUG_GetModuleType(module->type),
528                  (DWORD)module->load_addr, (DWORD)module->load_addr + module->size,
529                  module->module_name);
530 }       
531
532 /***********************************************************************
533  *           DEBUG_InfoShare
534  *
535  * Display shared libarary information.
536  */
537 void DEBUG_InfoShare(void)
538 {
539     DBG_MODULE**        ref;
540     int                 i, j;
541     
542     ref = DBG_alloc(sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
543     if (!ref) return;
544     
545     DEBUG_Printf(DBG_CHN_MESG, "Module\tAddress\t\t\tName\t%d modules\n", 
546                  DEBUG_CurrProcess->num_modules);
547     
548     memcpy(ref, DEBUG_CurrProcess->modules, 
549            sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
550     qsort(ref, DEBUG_CurrProcess->num_modules, sizeof(DBG_MODULE*), 
551           DEBUG_ModuleCompare);
552     for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
553         switch (ref[i]->type) {
554         case DMT_ELF:
555             DEBUG_InfoShareModule(ref[i], 0);
556             for (j = 0; j < DEBUG_CurrProcess->num_modules; j++) {
557                 if (ref[j]->type != DMT_ELF && DEBUG_IsContainer(ref[i], ref[j]))
558                     DEBUG_InfoShareModule(ref[j], 1);
559             }
560             break;
561         case DMT_NE:
562         case DMT_PE:
563             /* check module is not in ELF */
564             for (j = 0; j < DEBUG_CurrProcess->num_modules; j++) {
565                 if (ref[j]->type == DMT_ELF &&
566                     DEBUG_IsContainer(ref[j], ref[i]))
567                     break;
568             }
569             if (j >= DEBUG_CurrProcess->num_modules) 
570                 DEBUG_InfoShareModule(ref[i], 0);
571             break;
572         default:
573             DEBUG_Printf(DBG_CHN_ERR, "Unknown type (%d)\n", ref[i]->type);
574         }
575     }
576     DBG_free(ref);
577 }
578
579 /***********************************************************************
580  *           DEBUG_DumpModule
581  * Display information about a given module (DLL or EXE)
582  */
583 void DEBUG_DumpModule(DWORD mod)
584 {
585     DBG_MODULE* wmod;
586
587     if (!(wmod = DEBUG_FindModuleByHandle(mod, DMT_UNKNOWN)) &&
588         !(wmod = DEBUG_FindModuleByAddr((void*)mod, DMT_UNKNOWN))) {
589         DEBUG_Printf(DBG_CHN_MESG, "'0x%08lx' is not a valid module handle or address\n", mod);
590         return;
591     }
592
593     DEBUG_Printf(DBG_CHN_MESG, "Module '%s' (handle=0x%08x) 0x%08lx-0x%08lx (%s, debug info %s)\n",
594                  wmod->module_name, wmod->handle, (DWORD)wmod->load_addr,
595                  (DWORD)wmod->load_addr + wmod->size,
596                  DEBUG_GetModuleType(wmod->type), DEBUG_GetDbgInfo(wmod->dil));
597 }
598
599 /***********************************************************************
600  *           DEBUG_WalkModules
601  *
602  * Display information about all modules (DLLs and EXEs)
603  */
604 void DEBUG_WalkModules(void)
605 {
606     DBG_MODULE**        amod;
607     int                 i;
608     
609     DEBUG_Printf(DBG_CHN_MESG, "Address\t\t\tModule\tName\n");
610     
611     amod = DBG_alloc(sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
612     if (!amod) return;
613     
614     memcpy(amod, DEBUG_CurrProcess->modules, 
615            sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
616     qsort(amod, DEBUG_CurrProcess->num_modules, sizeof(DBG_MODULE*), 
617           DEBUG_ModuleCompare);
618     for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
619         if (amod[i]->type == DMT_ELF)   continue;
620         
621         DEBUG_Printf(DBG_CHN_MESG, "0x%08lx-%08lx\t(%s)\t%s\n", 
622                      (DWORD)amod[i]->load_addr, 
623                      (DWORD)amod[i]->load_addr + amod[i]->size,
624                      DEBUG_GetModuleType(amod[i]->type), amod[i]->module_name);
625     }
626     DBG_free(amod);
627 }
628