| /* -*- tab-width: 8; c-basic-offset: 4 -*- */ |
| /* |
| * File module.c - module handling for the wine debugger |
| * |
| * Copyright (C) 1993, Eric Youngdale. |
| * 2000, Eric Pouech |
| */ |
| #include "config.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include "neexe.h" |
| #include "module.h" |
| #include "file.h" |
| #include "debugger.h" |
| #include "toolhelp.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| |
| /*********************************************************************** |
| * Creates and links a new module to the current process |
| * |
| */ |
| DBG_MODULE* DEBUG_AddModule(const char* name, int type, |
| void* mod_addr, HMODULE hmodule) |
| { |
| DBG_MODULE* wmod; |
| |
| if (!(wmod = (DBG_MODULE*)DBG_alloc(sizeof(*wmod)))) |
| return NULL; |
| |
| memset(wmod, 0, sizeof(*wmod)); |
| |
| wmod->next = DEBUG_CurrProcess->modules; |
| wmod->status = DM_STATUS_NEW; |
| wmod->type = type; |
| wmod->load_addr = mod_addr; |
| wmod->handle = hmodule; |
| wmod->dbg_index = DEBUG_CurrProcess->next_index; |
| wmod->module_name = DBG_strdup(name); |
| DEBUG_CurrProcess->modules = wmod; |
| |
| return wmod; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_FindModuleByName |
| * |
| */ |
| DBG_MODULE* DEBUG_FindModuleByName(const char* name, int type) |
| { |
| DBG_MODULE* wmod; |
| |
| for (wmod = DEBUG_CurrProcess->modules; wmod; wmod = wmod->next) { |
| if ((type == DM_TYPE_UNKNOWN || type == wmod->type) && |
| !strcasecmp(name, wmod->module_name)) break; |
| } |
| return wmod; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_FindModuleByAddr |
| * |
| * either the addr where module is loaded, or any address inside the |
| * module |
| */ |
| DBG_MODULE* DEBUG_FindModuleByAddr(void* addr, int type) |
| { |
| DBG_MODULE* wmod; |
| DBG_MODULE* res = NULL; |
| |
| for (wmod = DEBUG_CurrProcess->modules; wmod; wmod = wmod->next) { |
| if ((type == DM_TYPE_UNKNOWN || type == wmod->type) && |
| (u_long)addr >= (u_long)wmod->load_addr && |
| (!res || res->load_addr < wmod->load_addr)) |
| res = wmod; |
| } |
| return res; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_FindModuleByHandle |
| */ |
| DBG_MODULE* DEBUG_FindModuleByHandle(HANDLE handle, int type) |
| { |
| DBG_MODULE* wmod; |
| |
| for (wmod = DEBUG_CurrProcess->modules; wmod; wmod = wmod->next) { |
| if ((type == DM_TYPE_UNKNOWN || type == wmod->type) && handle == wmod->handle) break; |
| } |
| return wmod; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_GetProcessMainModule |
| */ |
| DBG_MODULE* DEBUG_GetProcessMainModule(DBG_PROCESS* process) |
| { |
| DBG_MODULE* wmod; |
| |
| if (!process) return NULL; |
| |
| /* main module is the first to be loaded on a given process, so it's the last on |
| * the list */ |
| for (wmod = process->modules; wmod && wmod->next; wmod = wmod->next); |
| return wmod; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_RegisterELFModule |
| * |
| * ELF modules are also entered into the list - this is so that we |
| * can make 'info shared' types of displays possible. |
| */ |
| DBG_MODULE* DEBUG_RegisterELFModule(u_long load_addr, const char* name) |
| { |
| DBG_MODULE* wmod = DEBUG_AddModule(name, DM_TYPE_ELF, (void*)load_addr, 0); |
| |
| if (!wmod) return NULL; |
| |
| wmod->status = DM_STATUS_LOADED; |
| DEBUG_CurrProcess->next_index++; |
| |
| return wmod; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_RegisterPEModule |
| * |
| */ |
| DBG_MODULE* DEBUG_RegisterPEModule(HMODULE hModule, u_long load_addr, const char *module_name) |
| { |
| DBG_MODULE* wmod = DEBUG_AddModule(module_name, DM_TYPE_PE, (void*)load_addr, hModule); |
| |
| if (!wmod) return NULL; |
| |
| DEBUG_CurrProcess->next_index++; |
| |
| return wmod; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_RegisterNEModule |
| * |
| */ |
| DBG_MODULE* DEBUG_RegisterNEModule(HMODULE hModule, void* load_addr, const char *module_name) |
| { |
| DBG_MODULE* wmod = DEBUG_AddModule(module_name, DM_TYPE_NE, load_addr, hModule); |
| |
| if (!wmod) return NULL; |
| |
| wmod->status = DM_STATUS_LOADED; |
| DEBUG_CurrProcess->next_index++; |
| return wmod; |
| } |
| |
| #if 0 |
| /*********************************************************************** |
| * DEBUG_GetEP16 |
| * |
| * Helper function fo DEBUG_LoadModuleEPs16: |
| * finds the address of a given entry point from a given module |
| */ |
| static BOOL DEBUG_GetEP16(char* moduleAddr, const NE_MODULE* module, |
| WORD ordinal, DBG_ADDR* addr) |
| { |
| void* idx; |
| ET_ENTRY entry; |
| ET_BUNDLE bundle; |
| SEGTABLEENTRY ste; |
| |
| bundle.next = module->entry_table; |
| do { |
| if (!bundle.next) |
| return FALSE; |
| idx = moduleAddr + bundle.next; |
| if (!DEBUG_READ_MEM_VERBOSE(idx, &bundle, sizeof(bundle))) |
| return FALSE; |
| } while ((ordinal < bundle.first + 1) || (ordinal > bundle.last)); |
| |
| if (!DEBUG_READ_MEM_VERBOSE((char*)idx + sizeof(ET_BUNDLE) + |
| (ordinal - bundle.first - 1) * sizeof(ET_ENTRY), |
| &entry, sizeof(ET_ENTRY))) |
| return FALSE; |
| |
| addr->seg = entry.segnum; |
| addr->off = entry.offs; |
| |
| if (addr->seg == 0xfe) addr->seg = 0xffff; /* constant entry */ |
| else { |
| if (!DEBUG_READ_MEM_VERBOSE(moduleAddr + module->seg_table + |
| sizeof(ste) * (addr->seg - 1), |
| &ste, sizeof(ste))) |
| return FALSE; |
| addr->seg = GlobalHandleToSel16(ste.hSeg); |
| } |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_LoadModule16 |
| * |
| * Load the entry points of a Win16 module into the hash table. |
| */ |
| static void DEBUG_LoadModule16(HMODULE hModule, NE_MODULE* module, char* moduleAddr, const char* name) |
| { |
| DBG_VALUE value; |
| BYTE buf[1 + 256 + 2]; |
| char epname[512]; |
| char* cpnt; |
| DBG_MODULE* wmod; |
| |
| wmod = DEBUG_RegisterNEModule(hModule, moduleAddr, name); |
| |
| value.type = NULL; |
| value.cookie = DV_TARGET; |
| value.addr.seg = 0; |
| value.addr.off = 0; |
| |
| cpnt = moduleAddr + module->name_table; |
| |
| /* First search the resident names */ |
| |
| /* skip module name */ |
| if (!DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) || !buf[0]) |
| return; |
| cpnt += 1 + buf[0] + sizeof(WORD); |
| |
| while (DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) && buf[0]) { |
| sprintf(epname, "%s.%.*s", name, buf[0], &buf[1]); |
| if (DEBUG_GetEP16(moduleAddr, module, *(WORD*)&buf[1 + buf[0]], &value.addr)) { |
| DEBUG_AddSymbol(epname, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| } |
| cpnt += buf[0] + 1 + sizeof(WORD); |
| } |
| |
| /* Now search the non-resident names table */ |
| if (!module->nrname_handle) return; /* No non-resident table */ |
| cpnt = (char *)GlobalLock16(module->nrname_handle); |
| while (DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) && buf[0]) { |
| sprintf(epname, "%s.%.*s", name, buf[0], &buf[1]); |
| if (DEBUG_GetEP16(moduleAddr, module, *(WORD*)&buf[1 + buf[0]], &value.addr)) { |
| DEBUG_AddSymbol(epname, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| } |
| cpnt += buf[0] + 1 + sizeof(WORD); |
| } |
| GlobalUnlock16(module->nrname_handle); |
| } |
| #endif |
| |
| /*********************************************************************** |
| * DEBUG_LoadModule32 |
| */ |
| void DEBUG_LoadModule32(const char* name, HANDLE hFile, DWORD base) |
| { |
| DBG_VALUE value; |
| char buffer[512]; |
| char bufstr[256]; |
| int i; |
| IMAGE_NT_HEADERS pe_header; |
| DWORD pe_header_ofs; |
| IMAGE_SECTION_HEADER pe_seg; |
| DWORD pe_seg_ofs; |
| IMAGE_DATA_DIRECTORY dir; |
| DWORD dir_ofs; |
| DBG_MODULE* wmod; |
| const char *prefix; |
| |
| /* FIXME: we make the assumption that hModule == base */ |
| wmod = DEBUG_RegisterPEModule((HMODULE)base, base, name); |
| |
| DEBUG_Printf(DBG_CHN_TRACE, "Registring 32bit DLL '%s' at %08lx\n", name, base); |
| |
| value.type = NULL; |
| value.cookie = DV_TARGET; |
| value.addr.seg = 0; |
| value.addr.off = 0; |
| |
| /* grab PE Header */ |
| if (!DEBUG_READ_MEM_VERBOSE((void*)(base + OFFSET_OF(IMAGE_DOS_HEADER, e_lfanew)), |
| &pe_header_ofs, sizeof(pe_header_ofs)) || |
| !DEBUG_READ_MEM_VERBOSE((void*)(base + pe_header_ofs), |
| &pe_header, sizeof(pe_header))) |
| return; |
| |
| if (wmod) { |
| DEBUG_RegisterStabsDebugInfo(wmod, hFile, &pe_header, pe_header_ofs); |
| DEBUG_RegisterMSCDebugInfo(wmod, hFile, &pe_header, pe_header_ofs); |
| } |
| |
| /* Add start of DLL */ |
| value.addr.off = base; |
| if ((prefix = strrchr( name, '\\' ))) prefix++; |
| else prefix = name; |
| |
| DEBUG_AddSymbol(prefix, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| |
| /* Add entry point */ |
| wsnprintf(buffer, sizeof(buffer), "%s.EntryPoint", prefix); |
| value.addr.off = base + pe_header.OptionalHeader.AddressOfEntryPoint; |
| DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| |
| /* Add start of sections */ |
| pe_seg_ofs = pe_header_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) + |
| pe_header.FileHeader.SizeOfOptionalHeader; |
| |
| for (i = 0; i < pe_header.FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg)) { |
| if (!DEBUG_READ_MEM_VERBOSE((void*)(base + pe_seg_ofs), &pe_seg, sizeof(pe_seg))) |
| continue; |
| wsnprintf(buffer, sizeof(buffer), "%s.%s", prefix, pe_seg.Name); |
| value.addr.off = base + pe_seg.VirtualAddress; |
| DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| } |
| |
| /* Add exported functions */ |
| dir_ofs = pe_header_ofs + |
| OFFSET_OF(IMAGE_NT_HEADERS, |
| OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]); |
| if (DEBUG_READ_MEM_VERBOSE((void*)(base + dir_ofs), &dir, sizeof(dir)) && dir.Size) { |
| IMAGE_EXPORT_DIRECTORY exports; |
| WORD* ordinals = NULL; |
| void** functions = NULL; |
| DWORD* names = NULL; |
| int j; |
| |
| if (DEBUG_READ_MEM_VERBOSE((void*)(base + dir.VirtualAddress), |
| &exports, sizeof(exports)) && |
| |
| ((functions = DBG_alloc(sizeof(functions[0]) * exports.NumberOfFunctions))) && |
| DEBUG_READ_MEM_VERBOSE((void*)(base + (DWORD)exports.AddressOfFunctions), |
| functions, sizeof(functions[0]) * exports.NumberOfFunctions) && |
| |
| ((ordinals = DBG_alloc(sizeof(ordinals[0]) * exports.NumberOfNames))) && |
| DEBUG_READ_MEM_VERBOSE((void*)(base + (DWORD)exports.AddressOfNameOrdinals), |
| ordinals, sizeof(ordinals[0]) * exports.NumberOfNames) && |
| |
| ((names = DBG_alloc(sizeof(names[0]) * exports.NumberOfNames))) && |
| DEBUG_READ_MEM_VERBOSE((void*)(base + (DWORD)exports.AddressOfNames), |
| names, sizeof(names[0]) * exports.NumberOfNames)) { |
| |
| for (i = 0; i < exports.NumberOfNames; i++) { |
| if (!names[i] || |
| !DEBUG_READ_MEM_VERBOSE((void*)(base + names[i]), bufstr, sizeof(bufstr))) |
| continue; |
| bufstr[sizeof(bufstr) - 1] = 0; |
| wsnprintf(buffer, sizeof(buffer), "%s.%s", prefix, bufstr); |
| value.addr.off = base + (DWORD)functions[ordinals[i]]; |
| DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| } |
| |
| for (i = 0; i < exports.NumberOfFunctions; i++) { |
| if (!functions[i]) continue; |
| /* Check if we already added it with a name */ |
| for (j = 0; j < exports.NumberOfNames; j++) |
| if ((ordinals[j] == i) && names[j]) break; |
| if (j < exports.NumberOfNames) continue; |
| wsnprintf(buffer, sizeof(buffer), "%s.%ld", prefix, i + exports.Base); |
| value.addr.off = base + (DWORD)functions[i]; |
| DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); |
| } |
| } |
| DBG_free(functions); |
| DBG_free(ordinals); |
| DBG_free(names); |
| } |
| } |
| |
| /*********************************************************************** |
| * DEBUG_LoadEntryPoints |
| * |
| * Load the entry points of all the modules into the hash table. |
| */ |
| int DEBUG_LoadEntryPoints(const char* pfx) |
| { |
| int first = 0; |
| /* FIXME: with address space separation in space, this is plain wrong |
| * it requires the 16 bit WOW debugging interface... |
| */ |
| #if 0 |
| MODULEENTRY entry; |
| NE_MODULE module; |
| void* moduleAddr; |
| int rowcount = 0; |
| int len; |
| |
| /* FIXME: we assume that a module is never removed from memory */ |
| /* FIXME: this is (currently plain wrong when debugger is started by |
| * attaching to an existing program => the 16 bit modules will |
| * not be shared... not much to do on debugger side... sigh |
| */ |
| if (ModuleFirst16(&entry)) do { |
| if (DEBUG_FindModuleByName(entry.szModule, DM_TYPE_UNKNOWN) || |
| !(moduleAddr = NE_GetPtr(entry.hModule)) || |
| !DEBUG_READ_MEM_VERBOSE(moduleAddr, &module, sizeof(module)) || |
| (module.flags & NE_FFLAGS_WIN32) /* NE module */) |
| continue; |
| if (!first) { |
| if (pfx) DEBUG_Printf(DBG_CHN_MESG, pfx); |
| DEBUG_Printf(DBG_CHN_MESG, " "); |
| rowcount = 3 + (pfx ? strlen(pfx) : 0); |
| first = 1; |
| } |
| |
| len = strlen(entry.szModule); |
| if ((rowcount + len) > 76) { |
| DEBUG_Printf(DBG_CHN_MESG, "\n "); |
| rowcount = 3; |
| } |
| DEBUG_Printf(DBG_CHN_MESG, " %s", entry.szModule); |
| rowcount += len + 1; |
| |
| DEBUG_LoadModule16(entry.hModule, &module, moduleAddr, entry.szModule); |
| } while (ModuleNext16(&entry)); |
| #endif |
| |
| if (first) DEBUG_Printf(DBG_CHN_MESG, "\n"); |
| return first; |
| } |
| |
| /*********************************************************************** |
| * DEBUG_InfoShare |
| * |
| * Display shared libarary information. |
| */ |
| void DEBUG_InfoShare(void) |
| { |
| DBG_MODULE* wmod; |
| const char* xtype; |
| |
| DEBUG_Printf(DBG_CHN_MESG, "Address\t\tModule\tName\n"); |
| |
| for (wmod = DEBUG_CurrProcess->modules; wmod; wmod = wmod->next) { |
| switch (wmod->type) { |
| case DM_TYPE_NE: xtype = "NE"; break; |
| case DM_TYPE_PE: xtype = "PE"; break; |
| case DM_TYPE_ELF: xtype = "ELF"; break; |
| default: xtype = "???"; break; |
| } |
| DEBUG_Printf(DBG_CHN_MESG, "0x%8.8x\t(%s)\t%s\n", (unsigned int)wmod->load_addr, |
| xtype, wmod->module_name); |
| } |
| } |
| |
| static const char* DEBUG_GetModuleType(int type) |
| { |
| switch (type) { |
| case DM_TYPE_NE: return "NE"; |
| case DM_TYPE_PE: return "PE"; |
| case DM_TYPE_ELF: return "ELF"; |
| default: return "???";; |
| } |
| } |
| |
| static const char* DEBUG_GetModuleStatus(int status) |
| { |
| switch (status) { |
| case DM_STATUS_NEW: return "deferred"; |
| case DM_STATUS_LOADED: return "ok"; |
| case DM_STATUS_ERROR: return "error"; |
| default: return "???"; |
| } |
| } |
| |
| /*********************************************************************** |
| * DEBUG_ |
| * Display information about a given module (DLL or EXE) |
| */ |
| void DEBUG_DumpModule(DWORD mod) |
| { |
| DBG_MODULE* wmod; |
| |
| if (!(wmod = DEBUG_FindModuleByHandle(mod, DM_TYPE_UNKNOWN)) && |
| !(wmod = DEBUG_FindModuleByAddr((void*)mod, DM_TYPE_UNKNOWN))) { |
| DEBUG_Printf(DBG_CHN_MESG, "'0x%08lx' is not a valid module handle or address\n", mod); |
| return; |
| } |
| |
| DEBUG_Printf(DBG_CHN_MESG, "Module '%s' (handle=0x%08x) at 0x%8.8x (%s/%s)\n", |
| wmod->module_name, wmod->handle, (unsigned int)wmod->load_addr, |
| DEBUG_GetModuleType(wmod->type), DEBUG_GetModuleStatus(wmod->status)); |
| } |
| |
| /*********************************************************************** |
| * DEBUG_WalkModules |
| * |
| * Display information about all modules (DLLs and EXEs) |
| */ |
| void DEBUG_WalkModules(void) |
| { |
| DBG_MODULE* wmod; |
| const char* xtype; |
| |
| DEBUG_Printf(DBG_CHN_MESG, "Address\t\tModule\tName\n"); |
| |
| for (wmod = DEBUG_CurrProcess->modules; wmod; wmod = wmod->next) { |
| switch (wmod->type) { |
| case DM_TYPE_NE: xtype = "NE"; break; |
| case DM_TYPE_PE: xtype = "PE"; break; |
| case DM_TYPE_ELF: continue; |
| default: xtype = "???"; break; |
| } |
| |
| DEBUG_Printf(DBG_CHN_MESG, "0x%8.8x\t(%s)\t%s\n", |
| (unsigned int)wmod->load_addr, DEBUG_GetModuleType(wmod->type), |
| wmod->module_name); |
| } |
| } |
| |