511 lines
12 KiB
C
511 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
segment.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the debugging support needed to track
|
|
16-bit VDM segment notifications.
|
|
|
|
Author:
|
|
|
|
Neil Sandlin (neilsa) 1-Mar-1997 Rewrote it
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
#include <stdio.h>
|
|
|
|
SEGENTRY SegListHead = {0};
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// VDMGetSegtablePointer
|
|
//
|
|
// This is an undocumented entry point that allows VDMEXTS to dump the
|
|
// segment list
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
PSEGENTRY
|
|
WINAPI
|
|
VDMGetSegtablePointer(
|
|
VOID
|
|
)
|
|
{
|
|
return SegListHead.Next;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// VDMIsModuleLoaded
|
|
//
|
|
// Given the path parameter, this routine determines if there are any
|
|
// segments in the segment list from the specified executable.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
VDMIsModuleLoaded(
|
|
LPSTR szPath
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
|
|
if (!*szPath) {
|
|
return FALSE;
|
|
}
|
|
|
|
while (pSegEntry) {
|
|
if ( _stricmp(pSegEntry->szExePath, szPath) == 0 ) {
|
|
return TRUE;
|
|
}
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// SegmentLoad
|
|
//
|
|
// This routine adds an entry to the segment list based on the parameters
|
|
// of a client SegmentLoad notification.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
SegmentLoad(
|
|
WORD selector,
|
|
WORD segment,
|
|
LPSTR szExePath
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry;
|
|
|
|
if (strlen(szExePath) >= MAX_PATH16) {
|
|
return FALSE;
|
|
}
|
|
pSegEntry = MALLOC(sizeof(SEGENTRY));
|
|
if (pSegEntry == NULL) {
|
|
return FALSE;
|
|
}
|
|
pSegEntry->Next = SegListHead.Next;
|
|
SegListHead.Next = pSegEntry;
|
|
|
|
pSegEntry->selector = selector;
|
|
pSegEntry->segment = segment;
|
|
pSegEntry->type = SEGTYPE_PROT;
|
|
strcpy( pSegEntry->szExePath, szExePath );
|
|
ParseModuleName(pSegEntry->szModule, szExePath);
|
|
pSegEntry->length = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// SegmentFree
|
|
//
|
|
// This routine removes the entry from the segment list that matches the
|
|
// pass selector.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
SegmentFree(
|
|
WORD selector
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
PSEGENTRY pSegPrev = &SegListHead;
|
|
PSEGENTRY pSegTmp;
|
|
BOOL fResult = FALSE;
|
|
|
|
while (pSegEntry) {
|
|
if ((pSegEntry->type == SEGTYPE_PROT) &&
|
|
(pSegEntry->selector == selector)) {
|
|
|
|
pSegPrev->Next = pSegEntry->Next;
|
|
pSegTmp = pSegEntry;
|
|
pSegEntry = pSegTmp->Next;
|
|
FREE(pSegTmp);
|
|
fResult = TRUE;
|
|
|
|
} else {
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ModuleLoad
|
|
//
|
|
// This routine adds an entry to the segment list based on the parameters
|
|
// of a client ModuleLoad notification.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
ModuleLoad(
|
|
WORD selector,
|
|
WORD segment,
|
|
DWORD length,
|
|
LPSTR szExePath
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry;
|
|
|
|
if (strlen(szExePath) >= MAX_PATH16) {
|
|
return FALSE;
|
|
}
|
|
pSegEntry = MALLOC(sizeof(SEGENTRY));
|
|
if (pSegEntry == NULL) {
|
|
return FALSE;
|
|
}
|
|
pSegEntry->Next = SegListHead.Next;
|
|
SegListHead.Next = pSegEntry;
|
|
|
|
pSegEntry->selector = selector;
|
|
pSegEntry->segment = segment;
|
|
pSegEntry->type = SEGTYPE_V86;
|
|
strcpy( pSegEntry->szExePath, szExePath );
|
|
ParseModuleName(pSegEntry->szModule, szExePath);
|
|
pSegEntry->length = length;
|
|
return TRUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ModuleFree
|
|
//
|
|
// This routine removes all entries from the segment list that contain
|
|
// the specified path name.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
ModuleFree(
|
|
LPSTR szExePath
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
PSEGENTRY pSegPrev = &SegListHead;
|
|
PSEGENTRY pSegTmp;
|
|
BOOL fResult = FALSE;
|
|
|
|
while (pSegEntry) {
|
|
if ( _stricmp(pSegEntry->szExePath, szExePath) == 0 ) {
|
|
|
|
pSegPrev->Next = pSegEntry->Next;
|
|
pSegTmp = pSegEntry;
|
|
pSegEntry = pSegTmp->Next;
|
|
FREE(pSegTmp);
|
|
fResult = TRUE;
|
|
|
|
} else {
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
BOOL
|
|
V86SegmentMove(
|
|
WORD Selector,
|
|
WORD segment,
|
|
DWORD length,
|
|
LPSTR szExePath
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
PSEGENTRY pSegPrev = &SegListHead;
|
|
|
|
//
|
|
// first see if one exists already
|
|
//
|
|
pSegEntry = SegListHead.Next;
|
|
while (pSegEntry) {
|
|
if ((pSegEntry->type == SEGTYPE_V86) &&
|
|
(pSegEntry->segment == segment)) {
|
|
// Normal segmove, just update selector
|
|
pSegEntry->selector = Selector;
|
|
return TRUE;
|
|
}
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
|
|
//
|
|
// An entry for this segment doesn't exist, so create one
|
|
//
|
|
|
|
ModuleLoad(Selector, segment, length, szExePath);
|
|
|
|
//
|
|
// Now delete segment zero for this module. This prevents
|
|
// confusion in the symbol routines
|
|
//
|
|
|
|
pSegEntry = SegListHead.Next;
|
|
pSegPrev = &SegListHead;
|
|
while (pSegEntry) {
|
|
if ((pSegEntry->type == SEGTYPE_V86) &&
|
|
( _stricmp(pSegEntry->szExePath, szExePath) == 0 ) &&
|
|
(pSegEntry->segment == 0)) {
|
|
|
|
// Unlink and free it
|
|
pSegPrev->Next = pSegEntry->Next;
|
|
FREE(pSegEntry);
|
|
|
|
break;
|
|
}
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
PMSegmentMove(
|
|
WORD Selector1,
|
|
WORD Selector2
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry;
|
|
|
|
if (!Selector2) {
|
|
return (SegmentFree(Selector1));
|
|
}
|
|
|
|
// Look for the segment entry
|
|
pSegEntry = SegListHead.Next;
|
|
while (pSegEntry) {
|
|
if ((pSegEntry->type == SEGTYPE_PROT) &&
|
|
(pSegEntry->selector == Selector1)) {
|
|
// Normal segmove, just update selector
|
|
pSegEntry->selector = Selector2;
|
|
return TRUE;
|
|
}
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ProcessSegmentNotification
|
|
//
|
|
// This routine is the main entry point for the following debugger
|
|
// notifications:
|
|
// DBG_SEGLOAD
|
|
// DBG_SEGFREE
|
|
// DBG_SEGMOVE
|
|
// DBG_MODLOAD
|
|
// DBG_MODFREE
|
|
//
|
|
// It is called from VDMProcessException.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
ProcessSegmentNotification(
|
|
LPDEBUG_EVENT lpDebugEvent
|
|
)
|
|
{
|
|
BOOL b;
|
|
DWORD lpNumberOfBytesRead;
|
|
LPDWORD lpdw;
|
|
SEGMENT_NOTE se;
|
|
HANDLE hProcess;
|
|
PSEGENTRY pSegEntry, pSegPrev;
|
|
|
|
lpdw = &(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]);
|
|
hProcess = OpenProcess( PROCESS_VM_READ, FALSE, lpDebugEvent->dwProcessId );
|
|
|
|
if ( hProcess == HANDLE_NULL ) {
|
|
return;
|
|
}
|
|
|
|
b = ReadProcessMemory(hProcess,
|
|
(LPVOID)lpdw[2],
|
|
&se,
|
|
sizeof(se),
|
|
&lpNumberOfBytesRead );
|
|
|
|
if ( !b || lpNumberOfBytesRead != sizeof(se) ) {
|
|
return;
|
|
}
|
|
|
|
switch(LOWORD(lpdw[0])) {
|
|
|
|
case DBG_SEGLOAD:
|
|
|
|
SegmentLoad(se.Selector1, se.Segment, se.FileName);
|
|
break;
|
|
|
|
case DBG_SEGMOVE:
|
|
|
|
if (se.Type == SN_V86) {
|
|
V86SegmentMove(se.Selector2, se.Segment, se.Length, se.FileName);
|
|
} else {
|
|
PMSegmentMove(se.Selector1, se.Selector2);
|
|
}
|
|
break;
|
|
|
|
case DBG_SEGFREE:
|
|
|
|
// Here, se.Type is a boolean to tell whether to restore
|
|
// any breakpoints in the segment. That was done in the api
|
|
// because wdeb386 didn't know how to move the breakpoint
|
|
// definitions during a SEGMOVE. Currently, we ignore it, but
|
|
// it would be nice to either support the flag, or better to
|
|
// have ntsd update the breakpoints based on it.
|
|
|
|
SegmentFree(se.Selector1);
|
|
break;
|
|
|
|
case DBG_MODFREE:
|
|
ModuleFree(se.FileName);
|
|
break;
|
|
|
|
case DBG_MODLOAD:
|
|
ModuleLoad(se.Selector1, 0, se.Length, se.FileName);
|
|
break;
|
|
|
|
}
|
|
|
|
CloseHandle( hProcess );
|
|
}
|
|
|
|
|
|
void
|
|
CopySegmentInfo(
|
|
VDM_SEGINFO *si,
|
|
PSEGENTRY pSegEntry
|
|
)
|
|
{
|
|
si->Selector = pSegEntry->selector;
|
|
si->SegNumber = pSegEntry->segment;
|
|
si->Length = pSegEntry->length;
|
|
si->Type = (pSegEntry->type == SEGTYPE_V86) ? 0 : 1;
|
|
strcpy(si->ModuleName, pSegEntry->szModule);
|
|
strcpy(si->FileName, pSegEntry->szExePath);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// VDMGetSegmentInfo
|
|
//
|
|
// This routine fills in a VDM_SEGINFO structure for the segment that matches
|
|
// the specified parameters.
|
|
// notifications:
|
|
// DBG_SEGLOAD
|
|
// DBG_SEGFREE
|
|
// DBG_SEGMOVE
|
|
// DBG_MODLOAD
|
|
// DBG_MODFREE
|
|
//
|
|
// It is called from VDMProcessException.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
VDMGetSegmentInfo(
|
|
WORD Selector,
|
|
ULONG Offset,
|
|
BOOL bProtectMode,
|
|
VDM_SEGINFO *si
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
PSEGENTRY pSegPrev = &SegListHead;
|
|
int mode = bProtectMode ? SEGTYPE_PROT : SEGTYPE_V86;
|
|
ULONG Base, BaseEnd, Target;
|
|
|
|
while (pSegEntry) {
|
|
if (pSegEntry->type == mode) {
|
|
switch(mode) {
|
|
|
|
case SEGTYPE_PROT:
|
|
if (pSegEntry->selector == Selector) {
|
|
CopySegmentInfo(si, pSegEntry);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SEGTYPE_V86:
|
|
Base = pSegEntry->selector << 4;
|
|
BaseEnd = Base + pSegEntry->length;
|
|
Target = (Selector << 4) + Offset;
|
|
if ((Target >= Base) && (Target < BaseEnd)) {
|
|
CopySegmentInfo(si, pSegEntry);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
GetInfoBySegmentNumber(
|
|
LPSTR szModule,
|
|
WORD SegNumber,
|
|
VDM_SEGINFO *si
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
PSEGENTRY pSegPrev = &SegListHead;
|
|
ULONG Base, BaseEnd, Target;
|
|
|
|
while (pSegEntry) {
|
|
|
|
if (_stricmp(szModule, pSegEntry->szModule) == 0) {
|
|
|
|
if (pSegEntry->segment == 0 || pSegEntry->segment == SegNumber) {
|
|
CopySegmentInfo(si, pSegEntry);
|
|
return TRUE;
|
|
}
|
|
}
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
EnumerateModulesForValue(
|
|
BOOL (WINAPI *pfnEnumModuleProc)(LPSTR,LPSTR,PWORD,PDWORD,PWORD),
|
|
LPSTR szSymbol,
|
|
PWORD pSelector,
|
|
PDWORD pOffset,
|
|
PWORD pType
|
|
)
|
|
{
|
|
PSEGENTRY pSegEntry = SegListHead.Next;
|
|
PSEGENTRY pSegPrev = &SegListHead;
|
|
ULONG Base, BaseEnd, Target;
|
|
|
|
while (pSegEntry) {
|
|
|
|
if (pSegEntry->szModule) {
|
|
//
|
|
// BUGBUG should optimize this so that it only calls
|
|
// the enum proc once per module, instead of once per
|
|
// segment
|
|
//
|
|
|
|
if ((*pfnEnumModuleProc)(pSegEntry->szModule,
|
|
szSymbol,
|
|
pSelector,
|
|
pOffset,
|
|
pType)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
pSegEntry = pSegEntry->Next;
|
|
}
|
|
return FALSE;
|
|
}
|