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