| /* |
| * 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 <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #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); |
| } |