/* * File pe.c - handle PE module information * * Copyright (C) 1996, Eric Youngdale. * Copyright (C) 1999, 2000, Ulrich Weigand. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "config.h" #include "wine/port.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #include "wine/exception.h" #include "wine/debug.h" #include "excpt.h" #include "debugger.h" WINE_DEFAULT_DEBUG_CHANNEL(winedbg); #define MAX_PATHNAME_LEN 1024 typedef struct { DWORD from; DWORD to; } OMAP_DATA; typedef struct tagMSC_DBG_INFO { int nsect; PIMAGE_SECTION_HEADER sectp; int nomap; OMAP_DATA* omapp; } MSC_DBG_INFO; /*********************************************************************** * DEBUG_LocateDebugInfoFile * * NOTE: dbg_filename must be at least MAX_PATHNAME_LEN bytes in size */ static void DEBUG_LocateDebugInfoFile(const char* filename, char* dbg_filename) { char* str1 = DBG_alloc(MAX_PATHNAME_LEN); char* str2 = DBG_alloc(MAX_PATHNAME_LEN*10); const char* file; char* name_part; file = strrchr(filename, '\\'); if (file == NULL) file = filename; else file++; if ((GetEnvironmentVariable("_NT_SYMBOL_PATH", str1, MAX_PATHNAME_LEN) && (SearchPath(str1, file, NULL, MAX_PATHNAME_LEN*10, str2, &name_part))) || (GetEnvironmentVariable("_NT_ALT_SYMBOL_PATH", str1, MAX_PATHNAME_LEN) && (SearchPath(str1, file, NULL, MAX_PATHNAME_LEN*10, str2, &name_part))) || (SearchPath(NULL, file, NULL, MAX_PATHNAME_LEN*10, str2, &name_part))) lstrcpyn(dbg_filename, str2, MAX_PATHNAME_LEN); else lstrcpyn(dbg_filename, filename, MAX_PATHNAME_LEN); DBG_free(str1); DBG_free(str2); } /*********************************************************************** * DEBUG_MapDebugInfoFile */ void* DEBUG_MapDebugInfoFile(const char* name, DWORD offset, DWORD size, HANDLE* hFile, HANDLE* hMap) { DWORD g_offset; /* offset aligned on map granuality */ DWORD g_size; /* size to map, with offset aligned */ char* ret; *hMap = 0; if (name != NULL) { char filename[MAX_PATHNAME_LEN]; DEBUG_LocateDebugInfoFile(name, filename); if ((*hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) return NULL; } if (!size) { DWORD file_size = GetFileSize(*hFile, NULL); if (file_size == (DWORD)-1) return NULL; size = file_size - offset; } g_offset = offset & ~0xFFFF; /* FIXME: is granularity portable ? */ g_size = offset + size - g_offset; if ((*hMap = CreateFileMapping(*hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == 0) return NULL; if ((ret = MapViewOfFile(*hMap, FILE_MAP_READ, 0, g_offset, g_size)) != NULL) ret += offset - g_offset; return ret; } /*********************************************************************** * DEBUG_UnmapDebugInfoFile */ void DEBUG_UnmapDebugInfoFile(HANDLE hFile, HANDLE hMap, const void* addr) { if (addr) UnmapViewOfFile((void*)addr); if (hMap) CloseHandle(hMap); if (hFile!=INVALID_HANDLE_VALUE) CloseHandle(hFile); } /*======================================================================== * Process DBG file. */ static enum DbgInfoLoad DEBUG_ProcessDBGFile(DBG_MODULE* module, const char* filename, DWORD timestamp) { enum DbgInfoLoad dil = DIL_ERROR; HANDLE hFile = INVALID_HANDLE_VALUE, hMap = 0; const BYTE* file_map = NULL; PIMAGE_SEPARATE_DEBUG_HEADER hdr; PIMAGE_DEBUG_DIRECTORY dbg; int nDbg; WINE_TRACE("Processing DBG file %s\n", filename); file_map = DEBUG_MapDebugInfoFile(filename, 0, 0, &hFile, &hMap); if (!file_map) { WINE_ERR("-Unable to peruse .DBG file %s\n", filename); goto leave; } hdr = (PIMAGE_SEPARATE_DEBUG_HEADER) file_map; if (hdr->TimeDateStamp != timestamp) { WINE_ERR("Warning - %s has incorrect internal timestamp\n", filename); /* * Well, sometimes this happens to DBG files which ARE REALLY the right .DBG * files but nonetheless this check fails. Anyway, WINDBG (debugger for * Windows by Microsoft) loads debug symbols which have incorrect timestamps. */ } dbg = (PIMAGE_DEBUG_DIRECTORY) (file_map + sizeof(*hdr) + hdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + hdr->ExportedNamesSize); nDbg = hdr->DebugDirectorySize / sizeof(*dbg); dil = DEBUG_ProcessDebugDirectory(module, file_map, dbg, nDbg); leave: DEBUG_UnmapDebugInfoFile(hFile, hMap, file_map); return dil; } /*======================================================================== * Process MSC debug information in PE file. */ enum DbgInfoLoad DEBUG_RegisterMSCDebugInfo(DBG_MODULE* module, HANDLE hFile, void* _nth, unsigned long nth_ofs) { enum DbgInfoLoad dil = DIL_ERROR; PIMAGE_NT_HEADERS nth = (PIMAGE_NT_HEADERS)_nth; PIMAGE_DATA_DIRECTORY dir = nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DEBUG; PIMAGE_DEBUG_DIRECTORY dbg = NULL; int nDbg; MSC_DBG_INFO extra_info = { 0, NULL, 0, NULL }; HANDLE hMap = 0; const BYTE* file_map = NULL; /* Read in section data */ module->msc_dbg_info = &extra_info; extra_info.nsect = nth->FileHeader.NumberOfSections; extra_info.sectp = DBG_alloc(extra_info.nsect * sizeof(IMAGE_SECTION_HEADER)); if (!extra_info.sectp) goto leave; if (!DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) + nth->FileHeader.SizeOfOptionalHeader, extra_info.sectp, extra_info.nsect * sizeof(IMAGE_SECTION_HEADER))) goto leave; /* Read in debug directory */ nDbg = dir->Size / sizeof(IMAGE_DEBUG_DIRECTORY); if (!nDbg) goto leave; dbg = (PIMAGE_DEBUG_DIRECTORY) DBG_alloc(nDbg * sizeof(IMAGE_DEBUG_DIRECTORY)); if (!dbg) goto leave; if (!DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + dir->VirtualAddress, dbg, nDbg * sizeof(IMAGE_DEBUG_DIRECTORY))) goto leave; /* Map in PE file */ file_map = DEBUG_MapDebugInfoFile(NULL, 0, 0, &hFile, &hMap); if (!file_map) goto leave; /* Parse debug directory */ if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) { /* Debug info is stripped to .DBG file */ PIMAGE_DEBUG_MISC misc = (PIMAGE_DEBUG_MISC)(file_map + dbg->PointerToRawData); if (nDbg != 1 || dbg->Type != IMAGE_DEBUG_TYPE_MISC || misc->DataType != IMAGE_DEBUG_MISC_EXENAME) { WINE_ERR("-Debug info stripped, but no .DBG file in module %s\n", module->module_name); goto leave; } dil = DEBUG_ProcessDBGFile(module, misc->Data, nth->FileHeader.TimeDateStamp); } else { /* Debug info is embedded into PE module */ /* FIXME: the nDBG information we're manipulating comes from the debuggee * address space. However, the following code will be made against the * version mapped in the debugger address space. There are cases (for example * when the PE sections are compressed in the file and become decompressed * in the debuggee address space) where the two don't match. * Therefore, redo the DBG information lookup with the mapped data */ PIMAGE_NT_HEADERS mpd_nth = (PIMAGE_NT_HEADERS)(file_map + nth_ofs); PIMAGE_DATA_DIRECTORY mpd_dir; PIMAGE_DEBUG_DIRECTORY mpd_dbg = NULL; /* sanity checks */ if (mpd_nth->Signature != IMAGE_NT_SIGNATURE || mpd_nth->FileHeader.NumberOfSections != nth->FileHeader.NumberOfSections || (mpd_nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) != 0) goto leave; mpd_dir = mpd_nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DEBUG; if ((mpd_dir->Size / sizeof(IMAGE_DEBUG_DIRECTORY)) != nDbg) goto leave; mpd_dbg = (PIMAGE_DEBUG_DIRECTORY)(file_map + mpd_dir->VirtualAddress); dil = DEBUG_ProcessDebugDirectory(module, file_map, mpd_dbg, nDbg); } leave: module->msc_dbg_info = NULL; DEBUG_UnmapDebugInfoFile(0, hMap, file_map); if (extra_info.sectp) DBG_free(extra_info.sectp); if (dbg) DBG_free(dbg); return dil; } /*======================================================================== * look for stabs information in PE header (it's how mingw compiler provides its * debugging information), and also wine PE <-> ELF linking through .wsolnk sections */ enum DbgInfoLoad DEBUG_RegisterStabsDebugInfo(DBG_MODULE* module, HANDLE hFile, void* _nth, unsigned long nth_ofs) { IMAGE_SECTION_HEADER pe_seg; unsigned long pe_seg_ofs; int i, stabsize = 0, stabstrsize = 0; unsigned int stabs = 0, stabstr = 0; PIMAGE_NT_HEADERS nth = (PIMAGE_NT_HEADERS)_nth; enum DbgInfoLoad dil = DIL_ERROR; pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) + nth->FileHeader.SizeOfOptionalHeader; for (i = 0; i < nth->FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg)) { if (!DEBUG_READ_MEM_VERBOSE((void*)((char*)module->load_addr + pe_seg_ofs), &pe_seg, sizeof(pe_seg))) continue; if (!strcasecmp(pe_seg.Name, ".stab")) { stabs = pe_seg.VirtualAddress; stabsize = pe_seg.SizeOfRawData; } else if (!strncasecmp(pe_seg.Name, ".stabstr", 8)) { stabstr = pe_seg.VirtualAddress; stabstrsize = pe_seg.SizeOfRawData; } } if (stabstrsize && stabsize) { char* s1 = DBG_alloc(stabsize+stabstrsize); if (s1) { if (DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + stabs, s1, stabsize) && DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + stabstr, s1 + stabsize, stabstrsize)) { dil = DEBUG_ParseStabs(s1, 0, 0, stabsize, stabsize, stabstrsize); } else { DEBUG_Printf("couldn't read data block\n"); } DBG_free(s1); } else { DEBUG_Printf("couldn't alloc %d bytes\n", stabsize + stabstrsize); } } else { dil = DIL_NOINFO; } return dil; } /*********************************************************************** * DEBUG_RegisterPEDebugInfo */ enum DbgInfoLoad DEBUG_RegisterPEDebugInfo(DBG_MODULE* wmod, HANDLE hFile, void* _nth, unsigned long nth_ofs) { DBG_VALUE value; char buffer[512]; char bufstr[256]; unsigned int i; IMAGE_SECTION_HEADER pe_seg; DWORD pe_seg_ofs; IMAGE_DATA_DIRECTORY dir; DWORD dir_ofs; const char* prefix; IMAGE_NT_HEADERS* nth = (PIMAGE_NT_HEADERS)_nth; void* base = wmod->load_addr; value.type = NULL; value.cookie = DV_TARGET; value.addr.seg = 0; value.addr.off = 0; /* Add start of DLL */ value.addr.off = (unsigned long)base; if ((prefix = strrchr(wmod->module_name, '\\'))) prefix++; else prefix = wmod->module_name; DEBUG_AddSymbol(prefix, &value, NULL, SYM_WIN32 | SYM_FUNC); /* Add entry point */ snprintf(buffer, sizeof(buffer), "%s.EntryPoint", prefix); value.addr.off = (unsigned long)base + nth->OptionalHeader.AddressOfEntryPoint; DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); /* Add start of sections */ pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) + nth->FileHeader.SizeOfOptionalHeader; for (i = 0; i < nth->FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg)) { if (!DEBUG_READ_MEM_VERBOSE((char*)base + pe_seg_ofs, &pe_seg, sizeof(pe_seg))) continue; snprintf(buffer, sizeof(buffer), "%s.%s", prefix, pe_seg.Name); value.addr.off = (unsigned long)base + pe_seg.VirtualAddress; DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); } /* Add exported functions */ dir_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]); if (DEBUG_READ_MEM_VERBOSE((char*)base + dir_ofs, &dir, sizeof(dir)) && dir.Size) { IMAGE_EXPORT_DIRECTORY exports; WORD* ordinals = NULL; void** functions = NULL; DWORD* names = NULL; unsigned int j; if (DEBUG_READ_MEM_VERBOSE((char*)base + dir.VirtualAddress, &exports, sizeof(exports)) && ((functions = DBG_alloc(sizeof(functions[0]) * exports.NumberOfFunctions))) && DEBUG_READ_MEM_VERBOSE((char*)base + exports.AddressOfFunctions, functions, sizeof(functions[0]) * exports.NumberOfFunctions) && ((ordinals = DBG_alloc(sizeof(ordinals[0]) * exports.NumberOfNames))) && DEBUG_READ_MEM_VERBOSE((char*)base + (DWORD)exports.AddressOfNameOrdinals, ordinals, sizeof(ordinals[0]) * exports.NumberOfNames) && ((names = DBG_alloc(sizeof(names[0]) * exports.NumberOfNames))) && DEBUG_READ_MEM_VERBOSE((char*)base + (DWORD)exports.AddressOfNames, names, sizeof(names[0]) * exports.NumberOfNames)) { for (i = 0; i < exports.NumberOfNames; i++) { if (!names[i] || !DEBUG_READ_MEM_VERBOSE((char*)base + names[i], bufstr, sizeof(bufstr))) continue; bufstr[sizeof(bufstr) - 1] = 0; snprintf(buffer, sizeof(buffer), "%s.%s", prefix, bufstr); value.addr.off = (unsigned long)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; snprintf(buffer, sizeof(buffer), "%s.%ld", prefix, i + exports.Base); value.addr.off = (unsigned long)base + (DWORD)functions[i]; DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC); } } DBG_free(functions); DBG_free(ordinals); DBG_free(names); } /* no real debug info, only entry points */ return DIL_NOINFO; } /*********************************************************************** * DEBUG_LoadPEModule */ void DEBUG_LoadPEModule(const char* name, HANDLE hFile, void* base) { IMAGE_NT_HEADERS pe_header; DWORD nth_ofs; DBG_MODULE* wmod = NULL; int i; IMAGE_SECTION_HEADER pe_seg; DWORD pe_seg_ofs; DWORD size = 0; enum DbgInfoLoad dil = DIL_ERROR; /* grab PE Header */ if (!DEBUG_READ_MEM_VERBOSE((char*)base + OFFSET_OF(IMAGE_DOS_HEADER, e_lfanew), &nth_ofs, sizeof(nth_ofs)) || !DEBUG_READ_MEM_VERBOSE((char*)base + nth_ofs, &pe_header, sizeof(pe_header))) return; pe_seg_ofs = nth_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((char*)base + pe_seg_ofs, &pe_seg, sizeof(pe_seg))) continue; if (size < pe_seg.VirtualAddress + pe_seg.SizeOfRawData) size = pe_seg.VirtualAddress + pe_seg.SizeOfRawData; } /* FIXME: we make the assumption that hModule == base */ wmod = DEBUG_AddModule(name, DMT_PE, base, size, (HMODULE)base); if (wmod) { dil = DEBUG_RegisterStabsDebugInfo(wmod, hFile, &pe_header, nth_ofs); if (dil != DIL_LOADED) dil = DEBUG_RegisterMSCDebugInfo(wmod, hFile, &pe_header, nth_ofs); if (dil != DIL_LOADED) dil = DEBUG_RegisterPEDebugInfo(wmod, hFile, &pe_header, nth_ofs); wmod->dil = dil; } DEBUG_ReportDIL(dil, "32bit DLL", name, base); }