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