//---------------------------------------------------------------------------- // // General utility functions. // // Copyright (C) Microsoft Corporation, 1997-2001. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" #include #include #define DBG_MOD_LIST 0 PCSTR g_DefaultLogFileName = "dbgeng.log"; char g_OpenLogFileName[MAX_PATH + 1]; BOOL g_OpenLogFileAppended; int g_LogFile = -1; BOOL g_DisableErrorPrint; ULONG CheckUserInterrupt(void) { if (g_EngStatus & ENG_STATUS_USER_INTERRUPT) { g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT; return TRUE; } return FALSE; } LONG MappingExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) { static ULONG s_InPageErrors = 0; ULONG Code = ExceptionInfo->ExceptionRecord->ExceptionCode; if (Code == STATUS_IN_PAGE_ERROR) { if (++s_InPageErrors < 10) { if (s_InPageErrors % 2) { WarnOut("Ignore in-page I/O error\n"); FlushCallbacks(); } Sleep(500); return EXCEPTION_CONTINUE_EXECUTION; } } s_InPageErrors = 0; ErrOut("Exception 0x%08x while accessing mapping\n", Code); FlushCallbacks(); return EXCEPTION_EXECUTE_HANDLER; } void RemoveDelChar(PSTR Buffer) { PSTR BufferOld = Buffer; PSTR BufferNew = Buffer; while (*BufferOld) { if (*BufferOld == 0x7f) { if (BufferNew > Buffer) { BufferNew--; } } else if (*BufferOld == '\r' || *BufferOld == '\n') { *BufferNew++ = ' '; } else { *BufferNew++ = *BufferOld; } BufferOld++; } *BufferNew = '\0'; } /*** error - error reporting and recovery * * Purpose: * To output an error message with a location indicator * of the problem. Once output, the command line is reset * and the command processor is restarted. * * Input: * errorcode - number of error to output * * Output: * None. * * Exceptions: * Return is made via exception to start of command processor. * *************************************************************************/ static char g_Blanks[] = " " " " " " " ^ "; void ReportError( ULONG errcode, PCSTR* DescPtr ) { ULONG Count = g_PromptLength + 1; PSTR Temp = g_CommandStart; PCSTR Desc; if (DescPtr != NULL) { // Clear out description so it doesn't get reused. Desc = *DescPtr; *DescPtr = NULL; } else { Desc = NULL; } if (g_DisableErrorPrint || (g_CommandStart > g_CurCmd) || (g_CommandStart + MAX_COMMAND < g_CurCmd)) { goto SkipErrorPrint; } while (Temp < g_CurCmd) { if (*Temp++ == '\t') { Count = (Count + 7) & ~7; } else { Count++; } } ErrOut(&g_Blanks[sizeof(g_Blanks) - (Count + 1)]); if (Desc != NULL) { ErrOut("%s '%s'\n", Desc, g_CommandStart); goto SkipErrorPrint; } switch (errcode) { case OVERFLOW: ErrOut("Overflow"); break; case SYNTAX: ErrOut("Syntax"); break; case BADRANGE: ErrOut("Range"); break; case VARDEF: ErrOut("Couldn't resolve"); break; case EXTRACHARS: ErrOut("Extra character"); break; case LISTSIZE: ErrOut("List size"); break; case STRINGSIZE: ErrOut("String size"); break; case MEMORY: ErrOut("Memory access"); break; case BADREG: ErrOut("Bad register"); break; case BADOPCODE: ErrOut("Bad opcode"); break; case SUFFIX: ErrOut("Opcode suffix"); break; case OPERAND: ErrOut("Operand"); break; case ALIGNMENT: ErrOut("Alignment"); break; case PREFIX: ErrOut("Opcode prefix"); break; case DISPLACEMENT: ErrOut("Displacement"); break; case BPLISTFULL: ErrOut("No breakpoint available"); break; case BPDUPLICATE: ErrOut("Duplicate breakpoint"); break; case UNIMPLEMENT: ErrOut("Unimplemented"); break; case AMBIGUOUS: ErrOut("Ambiguous symbol"); break; case BADTHREAD: ErrOut("Illegal thread"); break; case BADPROCESS: ErrOut("Illegal process"); break; case FILEREAD: ErrOut("File read"); break; case LINENUMBER: ErrOut("Line number"); break; case BADSEL: ErrOut("Bad selector"); break; case BADSEG: ErrOut("Bad segment"); break; case SYMTOOSMALL: ErrOut("Symbol only 1 character"); break; case BPIONOTSUP: ErrOut("I/O breakpoints not supported"); break; case NOTFOUND: ErrOut("No information found"); break; case SESSIONNOTSUP: ErrOut("Operation not supported in current debug session"); break; default: ErrOut("Unknown"); break; } if (errcode != VARDEF && errcode != SESSIONNOTSUP) { ErrOut(" error in '%s'\n", g_CommandStart); } else { ErrOut(" '%s'\n", g_CommandStart); } SkipErrorPrint: RaiseException(COMMAND_EXCEPTION_BASE + errcode, 0, 0, NULL); } /*** HexList - parse list of hex values * * Purpose: * With the current location of the command string in * g_CurCmd, attempt to parse hex number of byte size * bytesize as bytes into the character array pointed by * parray. The value pointed by *pcount contains the bytes * of the array filled. * * Input: * g_CurCmd - start of command string * bytesize - size of items in bytes * * Output: * parray - pointer to byte array to fill * pcount - pointer to value of bytes filled * * Exceptions: * error exit: * LISTSIZE: too many items in list * SYNTAX: illegal separator of list items * OVERFLOW: item value too large * *************************************************************************/ void HexList (PUCHAR parray, ULONG *pcount, ULONG bytesize) { UCHAR ch; ULONG64 value; ULONG count = 0; ULONG i; while ((ch = PeekChar()) != '\0' && ch != ';') { if (count == STRLISTSIZE) { error(LISTSIZE); } value = HexValue(bytesize); for (i = 0; i < bytesize; i++) { *parray++ = (char) value; value >>= 8; } count += bytesize; } *pcount = count; } ULONGLONG HexValue ( ULONG ByteSize ) { ULONGLONG Value; Value = GetExpression(); // Reverse sign extension done by expression evaluator. if (ByteSize == 4 && !NeedUpper(Value)) { Value = (ULONG)Value; } if (Value > (0xffffffffffffffffUI64 >> (8 * (8 - ByteSize)))) { error(OVERFLOW); } return Value; } /*** AsciiList - process string for ascii list * * Purpose: * With the current location of the command string in * g_CurCmd, attempt to parse ascii characters into the * character array pointed by parray. The value pointed * by pcount contains the bytes of the array filled. The * string must start with a double quote. The string must * end with a quote or be at the end of the input line. * * Input: * g_CurCmd - start of command string * * Output: * parray - pointer to byte array to fill * pcount - pointer to value of bytes filled * * Exceptions: * error exit: * STRINGSIZE: string length too long * SYNTAX: no leading double quote * *************************************************************************/ void AsciiList(PSTR parray, ULONG *pcount) { UCHAR ch; ULONG count = 0; if (PeekChar() != '"') { error(SYNTAX); } g_CurCmd++; do { ch = *g_CurCmd++; if (ch == '"') { count++; *parray++ = 0; break; } if (ch == '\0' || ch == ';') { g_CurCmd--; break; } if (count == STRLISTSIZE) { error(STRINGSIZE); } count++; *parray++ = ch; } while (1); *pcount = count; } PSTR GetEscapedChar(PSTR Str, PCHAR Raw) { switch(*Str) { case 0: error(SYNTAX); case '0': // Octal char value. *Raw = (char)strtoul(Str + 1, &Str, 8); break; case 'b': *Raw = '\b'; Str++; break; case 'n': *Raw = '\n'; Str++; break; case 'r': *Raw = '\r'; Str++; break; case 't': *Raw = '\t'; Str++; break; case 'x': // Hex char value. *Raw = (char)strtoul(Str + 1, &Str, 16); break; default: // Verbatim escape. *Raw = *Str; Str++; break; } return Str; } PSTR BufferStringValue(PSTR* Buf, ULONG Flags, PCHAR Save) { BOOL Quoted; PSTR Str; BOOL Escapes = FALSE; while (isspace(*(*Buf))) { (*Buf)++; } if (*(*Buf) == '"') { Quoted = TRUE; Str = ++(*Buf); // If the string is quoted it can always contain spaces. Flags &= ~STRV_SPACE_IS_SEPARATOR; } else { Quoted = FALSE; Str = *Buf; // Escaped characters can only be present in quoted strings. Flags &= ~STRV_ALLOW_ESCAPED_CHARACTERS; } while (*(*Buf) && (!(Flags & STRV_SPACE_IS_SEPARATOR) || !isspace(*(*Buf))) && (Quoted || *(*Buf) != ';') && (!Quoted || *(*Buf) != '"')) { if (Flags & STRV_ALLOW_ESCAPED_CHARACTERS) { if (*(*Buf) == '\\') { char Raw; *Buf = GetEscapedChar((*Buf) + 1, &Raw); Escapes = TRUE; } else { (*Buf)++; } } else { (*Buf)++; } } if (Quoted && *(*Buf) != '"') { return NULL; } if (Flags & STRV_TRIM_TRAILING_SPACE) { PSTR Trim = *Buf; while (Trim > Str) { if (isspace(*--Trim)) { *Trim = 0; } else { break; } } } if (Quoted && *(*Buf) == '"') { // Null the quote and advance beyond it // so that the buffer pointer is always pointing // beyond the string on exit. *(*Buf)++ = 0; // Require some kind of separator after the // string to keep things symmetric with the // non-quoted case. if (!isspace(*(*Buf)) && *(*Buf) != ';' && *(*Buf) != 0) { *((*Buf) - 1) = '"'; return NULL; } } *Save = *(*Buf); *(*Buf) = 0; if (Escapes && (Flags & STRV_COMPRESS_ESCAPED_CHARACTERS)) { CompressEscapes(Str); } return Str; } PSTR StringValue(ULONG Flags, PCHAR Save) { PSTR Str = BufferStringValue((PSTR*)&g_CurCmd, Flags, Save); if (Str == NULL) { error(SYNTAX); } return Str; } void CompressEscapes(PSTR Str) { // Scan through a string for character escapes and // convert them to their escape value, packing // the rest of the string down. This allows for // in-place conversion of strings with escapes // inside the command buffer. while (*Str) { if (*Str == '\\') { char Raw; PSTR Slash = Str; Str = GetEscapedChar(Slash + 1, &Raw); // Copy raw value over backslash and pack down // trailing characters. *Slash = Raw; ULONG Len = strlen(Str) + 1; memmove(Slash + 1, Str, Len); Str = Slash + 1; } else { Str++; } } } void OpenLogFile(PCSTR File, BOOL Append) { // close existing opened log file fnLogClose(); if (Append) { g_LogFile = _open(File, O_APPEND | O_CREAT | O_RDWR, S_IREAD | S_IWRITE); } else { g_LogFile = _open(File, O_APPEND | O_CREAT | O_TRUNC | O_RDWR, S_IREAD | S_IWRITE); } if (g_LogFile != -1) { dprintf("Opened log file '%s'\n", File); strncat(g_OpenLogFileName, File, sizeof(g_OpenLogFileName) - 1); g_OpenLogFileAppended = Append; NotifyChangeEngineState(DEBUG_CES_LOG_FILE, TRUE, TRUE); } else { ErrOut("log file could not be opened\n"); } } void fnLogOpen(BOOL Append) { PSTR FileName; CHAR Save; if (PeekChar() && *g_CurCmd != ';') { FileName = StringValue(STRV_SPACE_IS_SEPARATOR | STRV_TRIM_TRAILING_SPACE, &Save); } else { FileName = (PSTR)g_DefaultLogFileName; } OpenLogFile(FileName, Append); *g_CurCmd = Save; } /*** fnLogClose - close log file * * Purpose: * Closes any open log file. * * Input: * g_LogFile - file handle of open log file * * Output: * None. * *************************************************************************/ void fnLogClose (void) { if (g_LogFile != -1) { dprintf("closing open log file\n"); _close(g_LogFile); g_LogFile = -1; g_OpenLogFileName[0] = 0; g_OpenLogFileAppended = FALSE; NotifyChangeEngineState(DEBUG_CES_LOG_FILE, FALSE, TRUE); } } /*** lprintf - log file print * * Purpose: * Print line only to log file. Used to echo input line that * is already printed on standard output by gets() function. * * Input: * g_LogFile - file handle variable of log file * * Output: * None. * *************************************************************************/ void lprintf ( PCSTR string ) { if (g_LogFile != -1) { _write(g_LogFile, string, strlen(string)); } } void RestrictModNameChars(PCSTR ModName, PSTR RewriteBuffer) { PCSTR Scan = ModName; PSTR Write = RewriteBuffer; while (*Scan) { if ((*Scan < 'a' || *Scan > 'z') && (*Scan < 'A' || *Scan > 'Z') && (*Scan < '0' || *Scan > '9') && *Scan != '_') { *Write++ = '_'; } else { *Write++ = *Scan; } Scan++; } *Write = 0; } PSTR PrepareImagePath(PSTR ImagePath) { // dbghelp will sometimes scan the path given to // SymLoadModule64 for the image itself. There // can be cases where the scan uses fuzzy matching, // so we want to be careful to only pass in a path // for dbghelp to use when the path can safely be // used. if ((IS_LIVE_USER_TARGET() && !IS_REMOTE_USER_TARGET()) || IS_LOCAL_KERNEL_TARGET()) { return ImagePath; } else { return (PSTR)PathTail(ImagePath); } } #define CSRSS_IMAGE_NAME "csrss.exe" #define LSASS_IMAGE_NAME "lsass.exe" #define SERVICES_IMAGE_NAME "services.exe" CHAR * AddImage( PMODULE_INFO_ENTRY ModEntry, BOOL ForceSymbolLoad ) { PDEBUG_IMAGE_INFO ImageNew; PDEBUG_IMAGE_INFO *pp; IMAGEHLP_MODULE64 mi; ULONG64 LoadAddress; BOOL LoadSymbols = TRUE; BOOL ReusedExisting = FALSE; PCSTR ImagePathTail; MODLOAD_DATA mld; #if DBG_MOD_LIST dprintf("AddImage:\n" " ImagePath %s\n" " File %I64x\n" " BaseOfImage %I64x\n" " SizeOfImage %x\n" " CheckSum %x\n" " ModuleName %s\n" " ForceSymbolLoad %d\n", ModEntry->NamePtr, (ULONG64)(ULONG_PTR)File, ModEntry->Base, ModEntry->Size, ModEntry->CheckSum, ModEntry->ModuleName, ForceSymbolLoad); #endif if (ModEntry->NamePtr == NULL) { WarnOut("AddImage(NULL) fails\n"); return NULL; } // // Search for existing image with same checksum at same base address. // If found, remove symbols, but leave image structure intact. // pp = &g_CurrentProcess->ImageHead; while (ImageNew = *pp) { if (ImageNew->BaseOfImage == ModEntry->Base) { VerbOut("Force unload of %s\n", ImageNew->ImagePath); SymUnloadModule64( g_CurrentProcess->Handle, ImageNew->BaseOfImage ); ClearStoredTypes(ImageNew->BaseOfImage); if (IS_DUMP_WITH_MAPPED_IMAGES()) { // Unmap the memory for this image. UnloadExecutableImageMemory(ImageNew); } ReusedExisting = TRUE; break; } else if (ImageNew->BaseOfImage > ModEntry->Base) { break; } pp = &ImageNew->Next; } // // If we didn't reuse an existing image, allocate // a new one. // if (!ReusedExisting) { ImageNew = (PDEBUG_IMAGE_INFO)calloc(sizeof(*ImageNew), 1); if (ImageNew == NULL) { ErrOut("Unable to allocate memory for %s symbols.\n", ModEntry->NamePtr); return NULL; } strcpy(ImageNew->ImagePath, ModEntry->NamePtr); // Module name is set later after possible renaming. ImageNew->File = ModEntry->File; ImageNew->BaseOfImage = ModEntry->Base; ImageNew->CheckSum = ModEntry->CheckSum; ImageNew->TimeDateStamp = ModEntry->TimeDateStamp; ImageNew->GoodCheckSum = TRUE; } // Always update the entry size as this allows users // to update explicit entries (.reload image.ext=base,size) // without having to unload them first. ImageNew->SizeOfImage = ModEntry->Size; ImagePathTail = PathTail(ImageNew->ImagePath); // // If we are handling a mini dump, we need to try to add the // executable's memory as well as the symbol file. We // do this right away so the image is mapped for the // below GetModnameFromImage calls. // if (IS_DUMP_WITH_MAPPED_IMAGES()) { if (!LoadExecutableImageMemory(ImageNew)) { // If the caller has requested that we fail on // incomplete information fail the module load. // We don't do this if we're reusing an existing // module under the assumption that it's better // to continue and try to complete the reused // image rather than deleting it. if ((g_EngOptions & DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION) && !ReusedExisting) { free(ImageNew); return NULL; } } } if (IS_KERNEL_TARGET()) { CHAR Buf[MAX_IMAGE_PATH]; // // Determine the actual image name for kernel images which // are known to have multiple identities. // if ((ModEntry->ModuleName && _stricmp(ModEntry->ModuleName, KERNEL_MODULE_NAME) == 0) || ModEntry->Base == KdDebuggerData.KernBase) { if (GetModnameFromImage(ModEntry->Base, NULL, Buf, sizeof(Buf))) { strcpy(ImageNew->ImagePath, Buf); } ModEntry->ModuleName = KERNEL_MODULE_NAME; } else if (_stricmp(ImagePathTail, HAL_IMAGE_FILE_NAME) == 0) { if (GetModnameFromImage(ModEntry->Base, NULL, Buf, sizeof(Buf))) { strcpy(ImageNew->ImagePath, Buf); } ModEntry->ModuleName = HAL_MODULE_NAME; } else if (_stricmp(ImagePathTail, KDHWEXT_IMAGE_FILE_NAME) == 0) { if (GetModnameFromImage(ModEntry->Base, NULL, Buf, sizeof(Buf))) { strcpy(ImageNew->ImagePath, Buf); } ModEntry->ModuleName = KDHWEXT_MODULE_NAME; } else if (ImageNew->SizeOfImage == 0 && ((_stricmp(ImagePathTail, NTLDR_IMAGE_NAME) == 0) || (_stricmp(ImagePathTail, NTLDR_IMAGE_NAME ".exe") == 0) || (_stricmp(ImagePathTail, OSLOADER_IMAGE_NAME) == 0) || (_stricmp(ImagePathTail, OSLOADER_IMAGE_NAME ".exe") == 0) || (_stricmp(ImagePathTail, SETUPLDR_IMAGE_NAME) == 0) || (_stricmp(ImagePathTail, SETUPLDR_IMAGE_NAME ".exe") == 0))) { ImageNew->SizeOfImage = LDR_IMAGE_SIZE; } } else if (!IS_DUMP_TARGET()) { // // When debugging CSR, LSA or Services.exe, force the use of local-only // symbols. Otherwise we can deadlock the entire machine when trying // to load the symbol file from the network. // if (IS_LOCAL_USER_TARGET() && (_stricmp(ImagePathTail, CSRSS_IMAGE_NAME) == 0 || _stricmp(ImagePathTail, LSASS_IMAGE_NAME) == 0 || _stricmp(ImagePathTail, SERVICES_IMAGE_NAME) == 0)) { if (g_EngOptions & DEBUG_ENGOPT_ALLOW_NETWORK_PATHS) { // // Since the user has chambered a round and pointed the barrel // of the gun at his head, we may as well tell him that it's // going to hurt if he pulls the trigger. // WarnOut("WARNING: Using network symbols with %s\n", ImagePathTail); WarnOut("WARNING: You may deadlock your machine.\n"); } else { g_EngOptions |= DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS; } } if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS) { DWORD NetPath; NetPath = NetworkPathCheck(g_SymbolSearchPath); if (NetPath == ERROR_FILE_OFFLINE) { ErrOut("ERROR: sympath contained network references but " "they were not allowed.\n"); ErrOut("Symbols not loaded for %s\n", ImagePathTail); LoadSymbols = FALSE; } else if (NetPath == ERROR_BAD_PATHNAME) { VerbOut("WARNING: sympath contains invalid references.\n"); } } } if (ModEntry->ModuleName == NULL) { CreateModuleNameFromPath(ImageNew->ImagePath, ImageNew->ModuleName); RestrictModNameChars(ImageNew->ModuleName, ImageNew->ModuleName); strcpy(ImageNew->OriginalModuleName, ImageNew->ModuleName); } else { RestrictModNameChars(ModEntry->ModuleName, ImageNew->ModuleName); // // We have an alias, so keep original module name from path // CreateModuleNameFromPath(ImageNew->ImagePath, ImageNew->OriginalModuleName); RestrictModNameChars(ImageNew->OriginalModuleName, ImageNew->OriginalModuleName); } // // Check for duplicate module names. // PDEBUG_IMAGE_INFO Check; for (Check = g_CurrentProcess->ImageHead; Check != NULL; Check = Check->Next) { if (Check != ImageNew && !_strcmpi(ImageNew->ModuleName, Check->ModuleName)) { int Len = strlen(ImageNew->ModuleName); // Module names match, so qualify with the base address. // Resulting name must be unique since base addresses are. if (g_TargetMachine->m_Ptr64) { if (Len >= MAX_MODULE - 17) { Len = MAX_MODULE - 18; } sprintf(ImageNew->ModuleName + Len, "_%I64x", ModEntry->Base); } else { if (Len >= MAX_MODULE - 9) { Len = MAX_MODULE - 10; } sprintf(ImageNew->ModuleName + Len, "_%x", (ULONG)ModEntry->Base); } break; } } // // If a new image structure was allocated, add it // into the image list. // if (!ReusedExisting) { ImageNew->Next = *pp; *pp = ImageNew; g_CurrentProcess->NumberImages++; if (g_CurrentProcess->ExecutableImage == NULL) { // Try and locate the executable image entry for // the process to use as the process's name. ULONG NameLen = strlen(ImageNew->ImagePath); if (NameLen > 4 && !_stricmp(ImageNew->ImagePath + NameLen - 4, ".exe")) { g_CurrentProcess->ExecutableImage = ImageNew; } } } // // If we do not want to load symbolic information, just return here. // if (!LoadSymbols) { return ImageNew->ImagePath; } if (ModEntry->DebugHeader) { mld.ssize = sizeof(MODLOAD_DATA); mld.ssig = DBHHEADER_DEBUGDIRS; mld.data = ModEntry->DebugHeader; mld.size = ModEntry->SizeOfDebugHeader; mld.flags = 0;; } LoadAddress = SymLoadModuleEx(g_CurrentProcess->Handle, ImageNew->File, PrepareImagePath(ImageNew->ImagePath), ImageNew->ModuleName, ImageNew->BaseOfImage, ImageNew->SizeOfImage, ModEntry->DebugHeader ? &mld : NULL, 0); if (!LoadAddress) { VerbOut("SymLoadModule(%N, %N, \"%s\", \"%s\", %s, %x) fails\n", g_CurrentProcess->Handle, ImageNew->File, ImageNew->ImagePath, ImageNew->ModuleName, FormatAddr64(ImageNew->BaseOfImage), ImageNew->SizeOfImage); // We don't want DelImage to notify of a symbol change // if this is a partially newly created image. if (!ReusedExisting) { g_EngNotify++; } DelImageByBase(g_CurrentProcess, ImageNew->BaseOfImage); if (!ReusedExisting) { g_EngNotify--; } return NULL; } if (!ImageNew->BaseOfImage) { ImageNew->BaseOfImage = LoadAddress; } if (ForceSymbolLoad) { SymLoadModule64(g_CurrentProcess->Handle, NULL, NULL, NULL, ImageNew->BaseOfImage, 0); } mi.SizeOfStruct = sizeof(mi); if (SymGetModuleInfo64( g_CurrentProcess->Handle, ImageNew->BaseOfImage, &mi )) { ImageNew->SizeOfImage = mi.ImageSize; } else { VerbOut("SymGetModuleInfo(%N, %s) fails\n", g_CurrentProcess->Handle, FormatAddr64(ImageNew->BaseOfImage)); // We don't want DelImage to notify of a symbol change // if this is a partially newly created image. if (!ReusedExisting) { g_EngNotify++; } DelImageByBase(g_CurrentProcess, ImageNew->BaseOfImage); if (!ReusedExisting) { g_EngNotify--; } return NULL; } StartOutLine(DEBUG_OUTPUT_VERBOSE, OUT_LINE_NO_PREFIX); VerbOut( "ModLoad: %s %s %-8s\n", FormatAddr64(ImageNew->BaseOfImage), FormatAddr64(ImageNew->BaseOfImage + ImageNew->SizeOfImage), ImageNew->ImagePath); NotifyChangeSymbolState(DEBUG_CSS_LOADS, ImageNew->BaseOfImage, g_CurrentProcess); return ImageNew->ImagePath; } void DelImage( PPROCESS_INFO Process, PDEBUG_IMAGE_INFO Image ) { ULONG64 ImageBase = Image->BaseOfImage; #if DBG_MOD_LIST dprintf("DelImage:\n" " ImagePath %s\n" " BaseOfImage %I64x\n" " SizeOfImage %x\n", Image->ImagePath, Image->BaseOfImage, Image->SizeOfImage); #endif if (IS_DUMP_WITH_MAPPED_IMAGES()) { // Unmap the memory for this image. UnloadExecutableImageMemory(Image); } SymUnloadModule64( Process->Handle, Image->BaseOfImage ); ClearStoredTypes(Image->BaseOfImage); if (Image->File) { CloseHandle( Image->File ); } if (Process->ExecutableImage == Image) { Process->ExecutableImage = NULL; } free(Image); Process->NumberImages--; // Notify with this process in order to mark any resulting // defered breakpoints due to this mod unload NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, ImageBase, Process); } BOOL DelImageByName(PPROCESS_INFO Process, PCSTR Name, INAME Which) { PDEBUG_IMAGE_INFO Image, *pp; pp = &Process->ImageHead; while (Image = *pp) { PCSTR WhichStr; switch(Which) { case INAME_IMAGE_PATH: WhichStr = Image->ImagePath; break; case INAME_IMAGE_PATH_TAIL: WhichStr = PathTail(Image->ImagePath); break; case INAME_MODULE: WhichStr = Image->ModuleName; break; } if (!_stricmp(WhichStr, Name)) { *pp = Image->Next; DelImage(Process, Image); return TRUE; } else { pp = &Image->Next; } } return FALSE; } BOOL DelImageByBase( PPROCESS_INFO pProcess, ULONG64 BaseOfImage ) { PDEBUG_IMAGE_INFO Image, *pp; pp = &pProcess->ImageHead; while (Image = *pp) { if (Image->BaseOfImage == BaseOfImage) { *pp = Image->Next; DelImage(pProcess, Image); return TRUE; } else { pp = &Image->Next; } } return FALSE; } void DelImages(PPROCESS_INFO Process) { PDEBUG_IMAGE_INFO Image, NextImage; // Suppress notifications until all images are deleted. g_EngNotify++; NextImage = Process->ImageHead; Process->ImageHead = NULL; while (NextImage) { Image = NextImage; NextImage = Image->Next; DelImage(Process, Image); } g_EngNotify--; NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, 0, Process); } void OutputSymAddr ( ULONG64 Offset, ULONG Flags ) { CHAR AddrBuffer[MAX_SYMBOL_LEN]; ULONG64 Displacement; GetSymbolStdCall(Offset, AddrBuffer, sizeof(AddrBuffer), &Displacement, NULL); if ((!Displacement || (Flags & SYMADDR_FORCE)) && AddrBuffer[0]) { dprintf("%s", AddrBuffer); if (Displacement) { dprintf("+%s", FormatDisp64(Displacement)); } if (Flags & SYMADDR_SOURCE) { OutputLineAddr(Offset, " [%s @ %d]"); } if (Flags & SYMADDR_LABEL) { dprintf(":\n"); } else { dprintf(" "); } } } void OutputLineAddr( ULONG64 Offset, PCSTR Format ) { if ((g_SymOptions & SYMOPT_LOAD_LINES) == 0) { return; } IMAGEHLP_LINE Line; DWORD LineDisp; Line.SizeOfStruct = sizeof(Line); if (SymGetLineFromAddr(g_CurrentProcess->Handle, Offset, &LineDisp, &Line)) { dprintf(Format, Line.FileName, Line.LineNumber); } } /*** OutCurInfo - Display selected information about the current register * state. * * Purpose: * Source file lines may be shown. * Source line information may be shown. * Symbol information may be shown. * The current register set may be shown. * The instruction at the current program current may be disassembled * with any effective address displayed. * * Input: * None. * * Output: * None. * * Notes: * If the disassembly is of a delayed control instruction, the * delay slot instruction is also output. * *************************************************************************/ void OutCurInfo(ULONG Flags, ULONG AllMask, ULONG RegMask) { ADDR PcValue; ADDR DisasmAddr; CHAR Buffer[MAX_DISASM_LEN]; BOOL EA; if (g_CurrentProcess == NULL || g_CurrentProcess->CurrentThread == NULL) { WarnOut("WARNING: The debugger does not have a current " "process or thread\n"); WarnOut("WARNING: Many commands will not work\n"); } if (!IS_MACHINE_SET() || g_CurrentProcess == NULL || g_CurrentProcess->CurrentThread == NULL || IS_LOCAL_KERNEL_TARGET() || ((Flags & OCI_IGNORE_STATE) == 0 && IS_RUNNING(g_CmdState)) || ((IS_KERNEL_FULL_DUMP() || IS_KERNEL_SUMMARY_DUMP()) && KdDebuggerData.KiProcessorBlock == 0)) { // State is not available right now. return; } g_Machine->GetPC(&PcValue); if ((Flags & (OCI_FORCE_ALL | OCI_FORCE_REG)) || ((g_SrcOptions & SRCOPT_LIST_SOURCE_ONLY) == 0 && (Flags & OCI_ALLOW_REG) && g_OciOutputRegs)) { g_Machine->OutputAll(AllMask, RegMask); } // Output g_PrevRelatedPc address if (Flat(g_PrevRelatedPc) && !AddrEqu(g_PrevRelatedPc, PcValue)) { if (Flags & (OCI_FORCE_ALL | OCI_SYMBOL)) { OutputSymAddr(Flat(g_PrevRelatedPc), SYMADDR_FORCE); dprintf("(%s)", FormatAddr64(Flat(g_PrevRelatedPc))); } else { dprintf("%s", FormatAddr64(Flat(g_PrevRelatedPc))); } dprintf("\n -> "); } // Deliberately does not force source with force-all so that source line // support has no effect on default operation. if (Flags & (OCI_FORCE_ALL | OCI_FORCE_SOURCE | OCI_ALLOW_SOURCE)) { if (g_SrcOptions & SRCOPT_LIST_SOURCE) { if (OutputSrcLinesAroundAddr(Flat(PcValue), g_OciSrcBefore, g_OciSrcAfter) && (Flags & OCI_FORCE_ALL) == 0 && (g_SrcOptions & SRCOPT_LIST_SOURCE_ONLY)) { return; } } else if ((g_SrcOptions & SRCOPT_LIST_LINE) || (Flags & OCI_FORCE_SOURCE)) { OutputLineAddr(Flat(PcValue)); } } if (Flags & (OCI_FORCE_ALL | OCI_SYMBOL)) { OutputSymAddr(Flat(PcValue), SYMADDR_FORCE | SYMADDR_LABEL); } if (Flags & (OCI_FORCE_ALL | OCI_DISASM)) { if (Flags & (OCI_FORCE_ALL | OCI_FORCE_EA)) { EA = TRUE; } else if (Flags & OCI_ALLOW_EA) { if (IS_DUMP_TARGET() || IS_USER_TARGET()) { // Always show the EA info. EA = TRUE; } else { // Only show the EA information if registers were shown. EA = g_OciOutputRegs; } } else { EA = FALSE; } DisasmAddr = PcValue; g_Machine->Disassemble(&DisasmAddr, Buffer, EA); dprintf("%s", Buffer); if (g_Machine->IsDelayInstruction(&PcValue)) { g_Machine->Disassemble(&DisasmAddr, Buffer, EA); dprintf("%s", Buffer); } } } #define MAX_FORMAT_STRINGS 8 LPSTR FormatMachineAddr64( MachineInfo* Machine, ULONG64 Addr ) /*++ Routine Description: Format a 64 bit address, showing the high bits or not according to various flags. An array of static string buffers is used, returning a different buffer for each successive call so that it may be used multiple times in the same printf. Arguments: Addr - Supplies the value to format Return Value: A pointer to the string buffer containing the formatted number --*/ { static CHAR s_Strings[MAX_FORMAT_STRINGS][18]; static int s_Next = 0; LPSTR String; String = s_Strings[s_Next]; ++s_Next; if (s_Next >= MAX_FORMAT_STRINGS) { s_Next = 0; } if (Machine->m_Ptr64) { sprintf(String, "%08x`%08x", (ULONG)(Addr >> 32), (ULONG)Addr); } else { sprintf(String, "%08x", (ULONG)Addr); } return String; } LPSTR FormatDisp64( ULONG64 addr ) /*++ Routine Description: Format a 64 bit address, showing the high bits or not according to various flags. This version does not print leading 0's. An array of static string buffers is used, returning a different buffer for each successive call so that it may be used multiple times in the same printf. Arguments: addr - Supplies the value to format Return Value: A pointer to the string buffer containing the formatted number --*/ { static CHAR strings[MAX_FORMAT_STRINGS][18]; static int next = 0; LPSTR string; string = strings[next]; ++next; if (next >= MAX_FORMAT_STRINGS) { next = 0; } if ((addr >> 32) != 0) { sprintf(string, "%x`%08x", (ULONG)(addr>>32), (ULONG)addr); } else { sprintf(string, "%x", (ULONG)addr); } return string; } DWORD NetworkPathCheck( LPCSTR PathList ) /*++ Routine Description: Checks if any members of the PathList are network paths. Arguments: PathList - A list of paths separated by ';' characters. Return Values: ERROR_SUCCESS - The path list contained no network or invalid paths. ERROR_BAD_PATHNAME - The path list contained one or more invalid paths, but no network paths. ERROR_FILE_OFFLINE - The path list contained one or more network paths. Bugs: Any path containing the ';' character will totally confuse this function. --*/ { CHAR EndPath0; CHAR EndPath1; LPSTR EndPath; LPSTR StartPath; DWORD DriveType; LPSTR Buffer = NULL; DWORD ret = ERROR_SUCCESS; BOOL AddedTrailingSlash = FALSE; if (PathList == NULL || *PathList == '\000') { return FALSE; } Buffer = (LPSTR) malloc ( strlen (PathList) + 3); if (!Buffer) { return ERROR_BAD_PATHNAME; } strcpy (Buffer, PathList); StartPath = Buffer; do { if (StartPath [0] == '\\' && StartPath [1] == '\\') { ret = ERROR_FILE_OFFLINE; break; } EndPath = strchr (StartPath, ';'); if (EndPath == NULL) { EndPath = StartPath + strlen (StartPath); EndPath0 = *EndPath; } else { EndPath0 = *EndPath; *EndPath = '\000'; } if (EndPath [-1] != '\\') { EndPath [0] = '\\'; EndPath1 = EndPath [1]; EndPath [1] = '\000'; AddedTrailingSlash = TRUE; } DriveType = GetDriveType (StartPath); if (DriveType == DRIVE_REMOTE) { ret = ERROR_FILE_OFFLINE; break; } else if (DriveType == DRIVE_UNKNOWN || DriveType == DRIVE_NO_ROOT_DIR) { // // This is not necessarily an error, but it may merit // investigation. // if (ret == ERROR_SUCCESS) { ret = ERROR_BAD_PATHNAME; } } EndPath [0] = EndPath0; if (AddedTrailingSlash) { EndPath [1] = EndPath1; } AddedTrailingSlash = FALSE; if (EndPath [ 0 ] == '\000') { StartPath = NULL; } else { StartPath = &EndPath [ 1 ]; } } while (StartPath && *StartPath != '\000'); if (Buffer) { free ( Buffer ); Buffer = NULL; } return ret; } //---------------------------------------------------------------------------- // // Returns either an ID value or ALL_ID_LIST. In theory // this routine could be expanded to pass back true intervals // so a full list could be specified. // // Originally built up a mask for the multi-ID case but that // was changed to return a real ID when 32 bits became // constraining. // //---------------------------------------------------------------------------- ULONG GetIdList (void) { ULONG Value = 0; CHAR ch; CHAR Digits[5]; // allow up to four digits int i; // // Change to allow more than 32 break points to be set. Use // break point numbers instead of masks. // if ((ch = PeekChar()) == '*') { Value = ALL_ID_LIST; g_CurCmd++; } else if (ch == '[') { Value = (ULONG)GetTermExprDesc("Breakpoint ID missing from"); } else { for (i = 0; i < sizeof(Digits) - 1; i++) { if (ch >= '0' && ch <= '9') { Digits[i] = ch; ch = *++g_CurCmd; } else { break; } } Digits[i] = '\0'; if (ch == '\0' || ch == ';' || ch == ' ' || ch == '\t') { Value = strtol(Digits, NULL, 10); } else { error (SYNTAX); } } return Value; } // // Sets or appends to a semicolon-delimited path. // HRESULT ChangePath(PSTR* Path, PCSTR New, BOOL Append, ULONG SymNotify) { ULONG NewLen, CurLen, TotLen; PSTR NewPath; if (New != NULL && *New != 0) { NewLen = strlen(New) + 1; } else if (Append) { // Nothing to append. return S_OK; } else { NewLen = 0; } if (*Path == NULL || **Path == 0) { // Nothing to append to. Append = FALSE; } if (Append) { CurLen = strlen(*Path) + 1; } else { CurLen = 0; } TotLen = CurLen + NewLen; if (TotLen > 0) { NewPath = (PSTR)malloc(TotLen); if (NewPath == NULL) { ErrOut("Unable to allocate memory for path\n"); return E_OUTOFMEMORY; } } else { NewPath = NULL; } PSTR Cat = NewPath; if (CurLen > 0) { memcpy(Cat, *Path, CurLen); Cat[CurLen - 1] = ';'; Cat += CurLen; } if (NewLen > 0) { memcpy(Cat, New, NewLen); } if (*Path != NULL) { free(*Path); } *Path = NewPath; if (SymNotify != 0) { NotifyChangeSymbolState(SymNotify, 0, g_CurrentProcess); } return S_OK; } PSTR FindPathElement(PSTR Path, ULONG Element, PSTR* EltEnd) { PSTR Elt, Sep; if (Path == NULL) { return NULL; } Elt = Path; for (;;) { Sep = strchr(Elt, ';'); if (Sep == NULL) { Sep = Elt + strlen(Elt); } if (Element == 0) { break; } if (*Sep == 0) { // No more elements. return NULL; } Elt = Sep + 1; Element--; } *EltEnd = Sep; return Elt; } void CheckPath(PCSTR Path) { PCSTR EltStart; PCSTR Scan; BOOL Space; if (Path == NULL) { return; } for (;;) { BOOL Warned = FALSE; EltStart = Path; Scan = EltStart; while (isspace(*Scan)) { Scan++; } if (Scan != EltStart) { WarnOut("WARNING: Whitespace at start of path element\n"); Warned = TRUE; } // Find the end of the element. Space = FALSE; while (*Scan && *Scan != ';') { Space = isspace(*Scan); Scan++; } if (Space) { WarnOut("WARNING: Whitespace at end of path element\n"); Warned = TRUE; } if (Scan - EltStart >= MAX_PATH) { WarnOut("WARNING: Path element is longer than MAX_PATH\n"); Warned = TRUE; } if (!Warned) { char Elt[MAX_PATH]; memcpy(Elt, EltStart, Scan - EltStart); Elt[Scan - EltStart] = 0; if (!ValidatePathComponent(Elt)) { WarnOut("WARNING: %s is not accessible\n", Elt); Warned = TRUE; } } if (!*Scan) { break; } Path = Scan + 1; } } HRESULT ChangeString(PSTR* Str, PULONG StrLen, PCSTR New) { ULONG Len; PSTR Buf; if (New != NULL) { Len = strlen(New) + 1; Buf = new char[Len]; if (Buf == NULL) { return E_OUTOFMEMORY; } } else { Buf = NULL; Len = 0; } delete [] *Str; *Str = Buf; if (New != NULL) { memcpy(Buf, New, Len); } if (StrLen != NULL) { *StrLen = Len; } return S_OK; } typedef struct _FIND_MODULE_DATA { ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; PVOID FileMapping; HANDLE FileHandle; } FIND_MODULE_DATA, *PFIND_MODULE_DATA; PCSTR KernelAliasList [] = { "ntoskrnl.exe", "ntkrnlpa.exe", "ntkrnlmp.exe", "ntkrpamp.exe" }; PCSTR ScsiAlias = "diskdump.sys"; PCSTR HalAliasList [] = { "hal.dll", "hal486c.dll", "halaacpi.dll", "halacpi.dll", "halapic.dll", "halborg.dll" "halmacpi.dll", "halmps.dll", "halsp.dll" }; PCSTR KdAliasList [] = { "kdcom.dll", "kd1394.dll" }; #define MAX_ALIAS_COUNT (DIMA(HalAliasList)) PVOID OpenMapping( IN PCSTR FilePath, OUT HANDLE* FileHandle ) { HANDLE File; HANDLE Mapping; PVOID View; ULONG OldMode; *FileHandle = NULL; if (g_SymOptions & SYMOPT_FAIL_CRITICAL_ERRORS) { OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); } File = CreateFile( FilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (g_SymOptions & SYMOPT_FAIL_CRITICAL_ERRORS) { SetErrorMode(OldMode); } if ( File == INVALID_HANDLE_VALUE ) { return NULL; } Mapping = CreateFileMapping ( File, NULL, PAGE_READONLY, 0, 0, NULL ); if ( !Mapping ) { CloseHandle ( File ); return FALSE; } View = MapViewOfFile ( Mapping, FILE_MAP_READ, 0, 0, 0 ); CloseHandle (Mapping); *FileHandle = File; return View; } PVOID MapImageFile( PCSTR FilePath, ULONG SizeOfImage, ULONG CheckSum, ULONG TimeDateStamp, HANDLE* FileHandle ) { PVOID FileMapping; PIMAGE_NT_HEADERS NtHeader; FileMapping = OpenMapping(FilePath, FileHandle); NtHeader = ImageNtHeader(FileMapping); if ((NtHeader == NULL) || (CheckSum && NtHeader->OptionalHeader.CheckSum && (NtHeader->OptionalHeader.CheckSum != CheckSum)) || (SizeOfImage != 0 && NtHeader->OptionalHeader.SizeOfImage != SizeOfImage) || (TimeDateStamp != 0 && NtHeader->FileHeader.TimeDateStamp != TimeDateStamp)) { // // The image data does not match the request. // if (g_SymOptions & SYMOPT_DEBUG) { ErrOut("DBGENG: %s image header does not match memory " "image header\n", FilePath); } UnmapViewOfFile(FileMapping); CloseHandle(*FileHandle); *FileHandle = NULL; return NULL; } return FileMapping; } BOOL CALLBACK FindFileInPathCallback(PSTR FileName, PVOID CallerData) { PFIND_MODULE_DATA FindModuleData = (PFIND_MODULE_DATA)CallerData; FindModuleData->FileMapping = MapImageFile(FileName, FindModuleData->SizeOfImage, (g_SymOptions & SYMOPT_EXACT_SYMBOLS) ? FindModuleData->CheckSum : 0, FindModuleData->TimeDateStamp, &FindModuleData->FileHandle); // The search stops when FALSE is returned, so // return FALSE when we've found a match. return FindModuleData->FileMapping == NULL; } BOOL FindExecutableCallback( HANDLE File, PSTR FileName, PVOID CallerData ) { PFIND_MODULE_DATA FindModuleData; DBG_ASSERT ( CallerData ); FindModuleData = (PFIND_MODULE_DATA) CallerData; FindModuleData->FileMapping = MapImageFile(FileName, FindModuleData->SizeOfImage, (g_SymOptions & SYMOPT_EXACT_SYMBOLS) ? FindModuleData->CheckSum : 0, FindModuleData->TimeDateStamp, &FindModuleData->FileHandle); return FindModuleData->FileMapping != NULL; } PVOID FindImageFile( IN PCSTR ImagePath, IN ULONG SizeOfImage, IN ULONG CheckSum, IN ULONG TimeDateStamp, OUT HANDLE* FileHandle, OUT PSTR MappedImagePath ) /*++ Routine Description: Find the executable image on the SymbolPath that matches ModuleName, CheckSum. This function takes care of things like renamed kernels and hals, and multiple images with the same name on the path. Return Values: File mapping or NULL. --*/ { ULONG i; ULONG j = 0; HANDLE File; ULONG AliasCount = 0; PCSTR AliasList[MAX_ALIAS_COUNT + 3]; FIND_MODULE_DATA FindModuleData; C_ASSERT (MAX_ALIAS_COUNT >= DIMA (KernelAliasList)); C_ASSERT (MAX_ALIAS_COUNT >= DIMA (HalAliasList)); C_ASSERT (MAX_ALIAS_COUNT >= DIMA (KdAliasList)); DBG_ASSERT ( ImagePath != NULL && ImagePath[0] != 0 ); PCSTR ModuleName = PathTail(ImagePath); // // Build an alias list. For normal modules, modules that are not the // kernel, the hal or a dump driver, this list will contain exactly one // entry with the module name. For kernel, hal and dump drivers, the // list will contain any number of known aliases for the specific file. // for (i = 0; i < DIMA(KernelAliasList); i++) { if (!_strcmpi(ModuleName, KernelAliasList[i])) { // // found a kernel alias. // AliasList[AliasCount++] = ModuleName; while (j < DIMA(KernelAliasList)) { AliasList[AliasCount++] = KernelAliasList[j++]; } break; } } if (!AliasCount) { for (i = 0; i < DIMA(HalAliasList); i++) { if (!_strcmpi(ModuleName, HalAliasList[i])) { // // found a HAL alias. // AliasList[AliasCount++] = ModuleName; while (j < DIMA(HalAliasList)) { AliasList[AliasCount++] = HalAliasList[j++]; } break; } } } if (!AliasCount) { for (i = 0; i < DIMA(KdAliasList); i++) { if (!_strcmpi(ModuleName, KdAliasList[i])) { // // found a HAL alias. // AliasList[AliasCount++] = ModuleName; while (j < DIMA(KdAliasList)) { AliasList[AliasCount++] = KdAliasList[j++]; } break; } } } if (!AliasCount) { if ( _strnicmp (ModuleName, "dump_scsiport", 11) == 00 ) { AliasList[0] = ScsiAlias; AliasCount = 1; } else if ( _strnicmp (ModuleName, "dump_", 5) == 00 ) { // // Setup dump driver alias list // AliasList[0] = &ModuleName[5]; AliasList[1] = ModuleName; AliasCount = 2; } else { AliasList[0] = ModuleName; AliasCount = 1; } } // // First try to find it in a symbol server or // directly on the image path. // for (i = 0; i < AliasCount; i++) { FindModuleData.SizeOfImage = SizeOfImage; FindModuleData.CheckSum = CheckSum; FindModuleData.TimeDateStamp = TimeDateStamp; FindModuleData.FileMapping = NULL; FindModuleData.FileHandle = NULL; if (SymFindFileInPath(g_CurrentProcess->Handle, g_ExecutableImageSearchPath, (PSTR)AliasList[i], UlongToPtr(TimeDateStamp), SizeOfImage, 0, SSRVOPT_DWORD, MappedImagePath, FindFileInPathCallback, &FindModuleData)) { if (FileHandle) { *FileHandle = FindModuleData.FileHandle; } return FindModuleData.FileMapping; } } // // Initial search didn't work so do a full tree search. // for (i = 0; i < AliasCount; i++) { FindModuleData.SizeOfImage = SizeOfImage; FindModuleData.CheckSum = CheckSum; FindModuleData.TimeDateStamp = TimeDateStamp; FindModuleData.FileMapping = NULL; FindModuleData.FileHandle = NULL; File = FindExecutableImageEx ((PSTR)AliasList[i], g_ExecutableImageSearchPath, MappedImagePath, FindExecutableCallback, &FindModuleData); if ( File != NULL && File != INVALID_HANDLE_VALUE ) { CloseHandle (File); } if ( FindModuleData.FileMapping != NULL ) { if (FileHandle) { *FileHandle = FindModuleData.FileHandle; } return FindModuleData.FileMapping; } } // // No path searches found the image so just try // the given path as a last-ditch check. // strcpy(MappedImagePath, ImagePath); FindModuleData.FileMapping = MapImageFile(ImagePath, SizeOfImage, CheckSum, TimeDateStamp, FileHandle); if (FindModuleData.FileMapping == NULL) { MappedImagePath[0] = 0; } return FindModuleData.FileMapping; } // User-mode minidump can be created with data segments // embedded in the dump. If that's the case, don't map // such sections. #define IS_MINI_DATA_SECTION(SecHeader) \ (IS_USER_MINI_DUMP() && \ ((SecHeader)->Characteristics & IMAGE_SCN_MEM_WRITE) && \ ((SecHeader)->Characteristics & IMAGE_SCN_MEM_READ) && \ (((SecHeader)->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) || \ ((SecHeader)->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA))) #if 0 #define DBG_IMAGE_MAP #endif BOOL LoadExecutableImageMemory(PDEBUG_IMAGE_INFO Image) { PVOID FileMapping; HRESULT Status; DBG_ASSERT(Image->File == NULL); FileMapping = FindImageFile(Image->ImagePath, Image->SizeOfImage, Image->CheckSum, Image->TimeDateStamp, &Image->File, Image->MappedImagePath); if (FileMapping == NULL) { ErrOut("Unable to load image %s\n", Image->ImagePath); return FALSE; } PIMAGE_NT_HEADERS Header = ImageNtHeader(FileMapping); // Header was already validated in MapImageFile. DBG_ASSERT(Header != NULL); // Map the header so we have it later. // Mark it with the image structure that this mapping is for. if (MemoryMap_AddRegion(Image->BaseOfImage, Header->OptionalHeader.SizeOfHeaders, FileMapping, Image, FALSE) != S_OK) { UnmapViewOfFile(FileMapping); if (Image->File != NULL) { CloseHandle(Image->File); Image->File = NULL; } Image->MappedImagePath[0] = 0; ErrOut("Unable to map image header memory for %s\n", Image->ImagePath); return FALSE; } PIMAGE_DATA_DIRECTORY DebugDataDir; IMAGE_DEBUG_DIRECTORY UNALIGNED * DebugDir = NULL; // Due to a linker bug, some images have debug data that is not // included as part of a section. Scan the debug data directory // and map anything that isn't already mapped. switch(Header->OptionalHeader.Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: DebugDataDir = &((PIMAGE_NT_HEADERS32)Header)->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; break; case IMAGE_NT_OPTIONAL_HDR64_MAGIC: DebugDataDir = &((PIMAGE_NT_HEADERS64)Header)->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; break; default: DebugDataDir = NULL; break; } // // Map all the sections in the image at their // appropriate offsets from the base address. // ULONG i; #ifdef DBG_IMAGE_MAP dprintf("Map %s: base %s, size %x, %d sections, mapping %p\n", Image->ImagePath, FormatAddr64(Image->BaseOfImage), Image->SizeOfImage, Header->FileHeader.NumberOfSections, FileMapping); #endif PIMAGE_SECTION_HEADER SecHeader = IMAGE_FIRST_SECTION(Header); for (i = 0; i < Header->FileHeader.NumberOfSections; i++) { BOOL AllowOverlap; #ifdef DBG_IMAGE_MAP dprintf(" %2d: %8.8s v %08x s %08x p %08x char %X\n", i, SecHeader->Name, SecHeader->VirtualAddress, SecHeader->SizeOfRawData, SecHeader->PointerToRawData, SecHeader->Characteristics); #endif if (SecHeader->SizeOfRawData == 0) { // Probably a BSS section that describes // a zero-filled data region and so is not // present in the executable. This should really // map to the appropriate page full of zeroes but // for now just ignore it. SecHeader++; continue; } if (DebugDataDir != NULL && DebugDataDir->VirtualAddress >= SecHeader->VirtualAddress && DebugDataDir->VirtualAddress < SecHeader->VirtualAddress + SecHeader->SizeOfRawData) { #ifdef DBG_IMAGE_MAP dprintf(" DebugDataDir found in sec %d at %X (%X)\n", i, DebugDataDir->VirtualAddress, DebugDataDir->VirtualAddress - SecHeader->VirtualAddress); #endif DebugDir = (PIMAGE_DEBUG_DIRECTORY) ((PUCHAR)FileMapping + (DebugDataDir->VirtualAddress - SecHeader->VirtualAddress + SecHeader->PointerToRawData)); } // As a sanity check make sure that the mapped region will // fall within the overall image bounds. if (SecHeader->VirtualAddress >= Image->SizeOfImage || SecHeader->VirtualAddress + SecHeader->SizeOfRawData > Image->SizeOfImage) { WarnOut("WARNING: Image %s section %d extends " "outside of image bounds\n", Image->ImagePath, i); } if (IS_MINI_DATA_SECTION(SecHeader)) { ULONG64 DataBase; ULONG DataSize; // Dumps can have explicit memory regions for // data sections to reflect changes to globals // and so on. If the current data section already // is completely mapped just ignore the image data. // Otherwise map but allow overlap. if (MemoryMap_GetRegionInfo(Image->BaseOfImage + SecHeader->VirtualAddress, &DataBase, &DataSize, NULL, NULL) && DataBase + DataSize >= Image->BaseOfImage + SecHeader->VirtualAddress + SecHeader->SizeOfRawData) { SecHeader++; continue; } AllowOverlap = TRUE; } else { AllowOverlap = FALSE; } // Mark the region with the image structure to identify the // region as an image area. if ((Status = MemoryMap_AddRegion(Image->BaseOfImage + SecHeader->VirtualAddress, SecHeader->SizeOfRawData, (PUCHAR)FileMapping + SecHeader->PointerToRawData, Image, AllowOverlap)) != S_OK) { ErrOut("Unable to map %s section %d at %s, %s\n", Image->ImagePath, i, FormatAddr64(Image->BaseOfImage + SecHeader->VirtualAddress), FormatStatusCode(Status)); // Conflicting region data is not a critical failure // unless the incomplete information flag is set. if (Status != HR_REGION_CONFLICT || (g_EngOptions & DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION)) { if (!UnloadExecutableImageMemory(Image)) { UnmapViewOfFile(FileMapping); if (Image->File != NULL) { CloseHandle(Image->File); Image->File = NULL; } Image->MappedImagePath[0] = 0; } return FALSE; } } SecHeader++; } if (DebugDir != NULL) { i = DebugDataDir->Size / sizeof(*DebugDir); #ifdef DBG_IMAGE_MAP dprintf(" %d debug dirs\n", i); #endif while (i-- > 0) { #ifdef DBG_IMAGE_MAP dprintf(" Dir %d at %p\n", i, DebugDir); #endif // If this debug directory's data is past the size // of the image it's a good indicator of the problem. if (DebugDir->AddressOfRawData != 0 && DebugDir->PointerToRawData >= Image->SizeOfImage && !MemoryMap_GetRegionInfo(Image->BaseOfImage + DebugDir->AddressOfRawData, NULL, NULL, NULL, NULL)) { #ifdef DBG_IMAGE_MAP dprintf(" Mapped hidden debug data at RVA %08x, " "size %x, ptr %08x\n", DebugDir->AddressOfRawData, DebugDir->SizeOfData, DebugDir->PointerToRawData); #endif if (MemoryMap_AddRegion(Image->BaseOfImage + DebugDir->AddressOfRawData, DebugDir->SizeOfData, (PUCHAR)FileMapping + DebugDir->PointerToRawData, Image, FALSE) != S_OK) { ErrOut("Unable to map extended debug data at %s\n", FormatAddr64(Image->BaseOfImage + DebugDir->AddressOfRawData)); } } DebugDir++; } } Image->MappedImageBase = FileMapping; return TRUE; } BOOL UnloadExecutableImageMemory(PDEBUG_IMAGE_INFO Image) { ULONG64 RegBase; ULONG RegSize; PVOID RegImage; ULONG i; // Look up the header region. if (!MemoryMap_GetRegionInfo(Image->BaseOfImage, &RegBase, &RegSize, NULL, &RegImage)) { // This is expected for images which couldn't be located. return FALSE; } if (RegImage != Image) { ErrOut("UnloadEIM: Header at %s isn't owned by %s\n", FormatAddr64(Image->BaseOfImage), Image->ImagePath); return FALSE; } PIMAGE_NT_HEADERS Header = ImageNtHeader(Image->MappedImageBase); if (Header == NULL || Header->OptionalHeader.SizeOfHeaders != RegSize) { ErrOut("UnloadEIM %s: Unable to get image header at %s\n", Image->ImagePath, FormatAddr64(Image->BaseOfImage)); return FALSE; } #ifdef DBG_IMAGE_MAP dprintf("Unmap %s: base %s, size %x, %d sections\n", Image->ImagePath, FormatAddr64(Image->BaseOfImage), Image->SizeOfImage, Header->FileHeader.NumberOfSections); #endif PIMAGE_DATA_DIRECTORY DebugDataDir; IMAGE_DEBUG_DIRECTORY UNALIGNED * DebugDir = NULL; // Due to a linker bug, some images have debug data that is not // included as part of a section. Scan the debug data directory // and map anything that isn't already mapped. switch(Header->OptionalHeader.Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: DebugDataDir = &((PIMAGE_NT_HEADERS32)Header)->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; break; case IMAGE_NT_OPTIONAL_HDR64_MAGIC: DebugDataDir = &((PIMAGE_NT_HEADERS64)Header)->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; break; default: DebugDataDir = NULL; break; } // // Unmap sections. // PIMAGE_SECTION_HEADER SecHeader = IMAGE_FIRST_SECTION(Header); for (i = 0; i < Header->FileHeader.NumberOfSections; i++) { if (SecHeader->SizeOfRawData == 0) { SecHeader++; continue; } if (DebugDataDir != NULL && DebugDataDir->VirtualAddress >= SecHeader->VirtualAddress && DebugDataDir->VirtualAddress < SecHeader->VirtualAddress + SecHeader->SizeOfRawData) { ULONG Dir; DebugDir = (PIMAGE_DEBUG_DIRECTORY) ((PUCHAR)Image->MappedImageBase + (DebugDataDir->VirtualAddress - SecHeader->VirtualAddress + SecHeader->PointerToRawData)); Dir = DebugDataDir->Size / sizeof(*DebugDir); while (Dir-- > 0) { if (!IsBadReadPtr(DebugDir, sizeof(*DebugDir)) && DebugDir->AddressOfRawData != 0 && DebugDir->PointerToRawData >= Image->SizeOfImage && MemoryMap_GetRegionInfo(Image->BaseOfImage + DebugDir->AddressOfRawData, &RegBase, &RegSize, NULL, &RegImage) && RegImage == Image) { #ifdef DBG_IMAGE_MAP dprintf(" Unmap hidden debug data at RVA %08x, " "size %x\n", DebugDir->AddressOfRawData, DebugDir->SizeOfData); #endif MemoryMap_RemoveRegion(RegBase, RegSize); } DebugDir++; } } // Code segments may be fragmented by the overlap handling // splitting them up when they overlap with code memory // areas recorded in the dump. Iterate over the sections // and clean up everything that's tagged as coming from this // image. ULONG64 SecBase = Image->BaseOfImage + SecHeader->VirtualAddress; ULONG SecSize = SecHeader->SizeOfRawData; while (SecSize > 0) { if (!MemoryMap_GetRegionInfo(SecBase, &RegBase, &RegSize, NULL, &RegImage)) { ErrOut("UnloadEIM %s: Unable to get section %d " "fragment at %s\n", Image->ImagePath, i, FormatAddr64(SecBase)); break; } if (RegImage == Image) { MemoryMap_RemoveRegion(RegBase, RegSize); } SecBase += RegSize; if (RegSize <= SecSize) { SecSize -= RegSize; } else { break; } } SecHeader++; } MemoryMap_RemoveRegion(Image->BaseOfImage, Header->OptionalHeader.SizeOfHeaders); UnmapViewOfFile(Image->MappedImageBase); Image->MappedImageBase = NULL; CloseHandle(Image->File); Image->File = NULL; Image->MappedImagePath[0] = 0; return TRUE; } #if DBG void DbgAssertionFailed(PCSTR File, int Line, PCSTR Str) { char Text[512]; _snprintf(Text, sizeof(Text), "Assertion failed: %s(%d)\n %s\n", File, Line, Str); OutputDebugStringA(Text); if (getenv("DBGENG_ASSERT_BREAK")) { DebugBreak(); } else { ErrOut("%s", Text); FlushCallbacks(); } } #endif // #if DBG void ExceptionRecordTo64(PEXCEPTION_RECORD Rec, PEXCEPTION_RECORD64 Rec64) { ULONG i; Rec64->ExceptionCode = Rec->ExceptionCode; Rec64->ExceptionFlags = Rec->ExceptionFlags; Rec64->ExceptionRecord = (ULONG64)Rec->ExceptionRecord; Rec64->ExceptionAddress = (ULONG64)Rec->ExceptionAddress; Rec64->NumberParameters = Rec->NumberParameters; for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++) { Rec64->ExceptionInformation[i] = Rec->ExceptionInformation[i]; } } void ExceptionRecord64To(PEXCEPTION_RECORD64 Rec64, PEXCEPTION_RECORD Rec) { ULONG i; Rec->ExceptionCode = Rec64->ExceptionCode; Rec->ExceptionFlags = Rec64->ExceptionFlags; Rec->ExceptionRecord = (PEXCEPTION_RECORD)(ULONG_PTR) Rec64->ExceptionRecord; Rec->ExceptionAddress = (PVOID)(ULONG_PTR) Rec64->ExceptionAddress; Rec->NumberParameters = Rec64->NumberParameters; for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++) { Rec->ExceptionInformation[i] = (ULONG_PTR) Rec64->ExceptionInformation[i]; } } void MemoryBasicInformationTo64(PMEMORY_BASIC_INFORMATION Mbi, PMEMORY_BASIC_INFORMATION64 Mbi64) { #ifdef _WIN64 memcpy(Mbi64, Mbi, sizeof(*Mbi64)); #else Mbi64->BaseAddress = (ULONG64) Mbi->BaseAddress; Mbi64->AllocationBase = (ULONG64) Mbi->AllocationBase; Mbi64->AllocationProtect = Mbi->AllocationProtect; Mbi64->__alignment1 = 0; Mbi64->RegionSize = Mbi->RegionSize; Mbi64->State = Mbi->State; Mbi64->Protect = Mbi->Protect; Mbi64->Type = Mbi->Type; Mbi64->__alignment2 = 0; #endif } void MemoryBasicInformation32To64(PMEMORY_BASIC_INFORMATION32 Mbi32, PMEMORY_BASIC_INFORMATION64 Mbi64) { Mbi64->BaseAddress = EXTEND64(Mbi32->BaseAddress); Mbi64->AllocationBase = EXTEND64(Mbi32->AllocationBase); Mbi64->AllocationProtect = Mbi32->AllocationProtect; Mbi64->__alignment1 = 0; Mbi64->RegionSize = Mbi32->RegionSize; Mbi64->State = Mbi32->State; Mbi64->Protect = Mbi32->Protect; Mbi64->Type = Mbi32->Type; Mbi64->__alignment2 = 0; } void DebugEvent32To64(LPDEBUG_EVENT32 Event32, LPDEBUG_EVENT64 Event64) { Event64->dwDebugEventCode = Event32->dwDebugEventCode; Event64->dwProcessId = Event32->dwProcessId; Event64->dwThreadId = Event32->dwThreadId; Event64->__alignment = 0; switch(Event32->dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: ExceptionRecord32To64(&Event32->u.Exception.ExceptionRecord, &Event64->u.Exception.ExceptionRecord); Event64->u.Exception.dwFirstChance = Event32->u.Exception.dwFirstChance; break; case CREATE_THREAD_DEBUG_EVENT: Event64->u.CreateThread.hThread = EXTEND64(Event32->u.CreateThread.hThread); Event64->u.CreateThread.lpThreadLocalBase = EXTEND64(Event32->u.CreateThread.lpThreadLocalBase); Event64->u.CreateThread.lpStartAddress = EXTEND64(Event32->u.CreateThread.lpStartAddress); break; case CREATE_PROCESS_DEBUG_EVENT: Event64->u.CreateProcessInfo.hFile = EXTEND64(Event32->u.CreateProcessInfo.hFile); Event64->u.CreateProcessInfo.hProcess = EXTEND64(Event32->u.CreateProcessInfo.hProcess); Event64->u.CreateProcessInfo.hThread = EXTEND64(Event32->u.CreateProcessInfo.hThread); Event64->u.CreateProcessInfo.lpBaseOfImage = EXTEND64(Event32->u.CreateProcessInfo.lpBaseOfImage); Event64->u.CreateProcessInfo.dwDebugInfoFileOffset = Event32->u.CreateProcessInfo.dwDebugInfoFileOffset; Event64->u.CreateProcessInfo.nDebugInfoSize = Event32->u.CreateProcessInfo.nDebugInfoSize; Event64->u.CreateProcessInfo.lpThreadLocalBase = EXTEND64(Event32->u.CreateProcessInfo.lpThreadLocalBase); Event64->u.CreateProcessInfo.lpStartAddress = EXTEND64(Event32->u.CreateProcessInfo.lpStartAddress); Event64->u.CreateProcessInfo.lpImageName = EXTEND64(Event32->u.CreateProcessInfo.lpImageName); Event64->u.CreateProcessInfo.fUnicode = Event32->u.CreateProcessInfo.fUnicode; break; case EXIT_THREAD_DEBUG_EVENT: Event64->u.ExitThread.dwExitCode = Event32->u.ExitThread.dwExitCode; break; case EXIT_PROCESS_DEBUG_EVENT: Event64->u.ExitProcess.dwExitCode = Event32->u.ExitProcess.dwExitCode; break; case LOAD_DLL_DEBUG_EVENT: Event64->u.LoadDll.hFile = EXTEND64(Event32->u.LoadDll.hFile); Event64->u.LoadDll.lpBaseOfDll = EXTEND64(Event32->u.LoadDll.lpBaseOfDll); Event64->u.LoadDll.dwDebugInfoFileOffset = Event32->u.LoadDll.dwDebugInfoFileOffset; Event64->u.LoadDll.nDebugInfoSize = Event32->u.LoadDll.nDebugInfoSize; Event64->u.LoadDll.lpImageName = EXTEND64(Event32->u.LoadDll.lpImageName); Event64->u.LoadDll.fUnicode = Event32->u.LoadDll.fUnicode; break; case UNLOAD_DLL_DEBUG_EVENT: Event64->u.UnloadDll.lpBaseOfDll = EXTEND64(Event32->u.UnloadDll.lpBaseOfDll); break; case OUTPUT_DEBUG_STRING_EVENT: Event64->u.DebugString.lpDebugStringData = EXTEND64(Event32->u.DebugString.lpDebugStringData); Event64->u.DebugString.fUnicode = Event32->u.DebugString.fUnicode; Event64->u.DebugString.nDebugStringLength = Event32->u.DebugString.nDebugStringLength; break; case RIP_EVENT: Event64->u.RipInfo.dwError = Event32->u.RipInfo.dwError; Event64->u.RipInfo.dwType = Event32->u.RipInfo.dwType; break; } } LPSTR TimeToStr( ULONG TimeDateStamp ) { LPSTR TimeDateStr; // Handle invalid \ page out timestamps, since ctime blows up on // this number if ((TimeDateStamp == 0) || (TimeDateStamp == UNKNOWN_TIMESTAMP)) { return "unavailable"; } else if (IS_LIVE_KERNEL_TARGET() && TimeDateStamp == 0x49ef6f00) { // At boot time the shared memory data area is not // yet initialized. The above value seems to be // the random garbage that's there so detect it and // ignore it. This is highly fragile but people // keep asking about the garbage value. return "unavailable until booted"; } else { // TimeDateStamp is always a 32 bit quantity on the target, // and we need to sign extend for 64 bit host since time_t // has been extended to 64 bits. time_t TDStamp = (time_t) (LONG) TimeDateStamp; TimeDateStr = ctime((time_t *)&TDStamp); if (TimeDateStr) { TimeDateStr[strlen(TimeDateStr) - 1] = 0; } else { TimeDateStr = "***** Invalid"; } } return TimeDateStr; } PCSTR PathTail(PCSTR Path) { PCSTR Tail = Path + strlen(Path); while (--Tail >= Path) { if (*Tail == '\\' || *Tail == '/' || *Tail == ':') { break; } } return Tail + 1; } BOOL MatchPathTails(PCSTR Path1, PCSTR Path2) { return _stricmp(PathTail(Path1), PathTail(Path2)) == 0; }