218 lines
7.4 KiB
C++
218 lines
7.4 KiB
C++
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// thread.cpp - Extension to select a Singularity thread.
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
#include "singx86.h"
|
||
|
|
||
|
struct GDTE
|
||
|
{
|
||
|
USHORT limit;
|
||
|
USHORT base0_15;
|
||
|
BYTE base16_23;
|
||
|
BYTE access;
|
||
|
BYTE granularity;
|
||
|
BYTE base24_31;
|
||
|
};
|
||
|
|
||
|
static void Usage()
|
||
|
{
|
||
|
ExtOut("Usage:\n"
|
||
|
" !thread {options} thread_addr\n"
|
||
|
"Options:\n"
|
||
|
" -p show stack prior to interrupt or exception (if any)\n"
|
||
|
" -t show top stack (i.e. interrupt or exception (if any))\n"
|
||
|
"Notes:\n"
|
||
|
" There are potentially three contexts associated with a thread:\n"
|
||
|
" the scheduler context, the interrupt context, and the exception\n"
|
||
|
" context. The -p flag is only useful if an exception or interrupt\n"
|
||
|
" has occurred. Using -p -p always gets the scheduler context.");
|
||
|
}
|
||
|
|
||
|
static PCSTR ParseArguments(PCSTR args,
|
||
|
OUT PLONG plPriorStack,
|
||
|
OUT PLONG plTopStack)
|
||
|
{
|
||
|
*plPriorStack = 0;
|
||
|
*plTopStack = 0;
|
||
|
while (*args != '\0') {
|
||
|
while (*args == ' ' || *args == '\t') {
|
||
|
args++;
|
||
|
}
|
||
|
if (*args != '-' && *args != '/') {
|
||
|
break;
|
||
|
}
|
||
|
args++;
|
||
|
switch (*args++) {
|
||
|
case 'p':
|
||
|
*plPriorStack = *plPriorStack + 1;
|
||
|
break;
|
||
|
case 't':
|
||
|
*plTopStack = 1;
|
||
|
break;
|
||
|
default:
|
||
|
Usage();
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
return args;
|
||
|
}
|
||
|
|
||
|
EXT_DECL(thread) // Defines: PDEBUG_CLIENT Client, PCSTR args
|
||
|
{
|
||
|
EXT_ENTER(); // Defines: HRESULT status = S_OK;
|
||
|
|
||
|
Thread thread;
|
||
|
ThreadContext tc;
|
||
|
|
||
|
LONG priorStack = 0;
|
||
|
LONG topStack = 0;
|
||
|
ULONG64 address = 0;
|
||
|
PVOID extendedRegs = NULL;
|
||
|
PVOID floatingRegs = NULL;
|
||
|
|
||
|
if ((args = ParseArguments(args, &priorStack, &topStack)) == NULL) {
|
||
|
status = S_FALSE;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
status = ExtEvalU64(&args, &address);
|
||
|
if (status != S_OK) {
|
||
|
ULONG indexGdtr = 0;
|
||
|
ULONG indexGdtl = 0;
|
||
|
ULONG indexFs = 0;
|
||
|
EXT_CHECK(g_ExtRegisters->GetIndexByName("gdtr", &indexGdtr));
|
||
|
EXT_CHECK(g_ExtRegisters->GetIndexByName("gdtl", &indexGdtl));
|
||
|
EXT_CHECK(g_ExtRegisters->GetIndexByName("fs", &indexFs));
|
||
|
|
||
|
ExtVerb("gdtr: %04x\n", indexGdtr);
|
||
|
ExtVerb("gdtl: %04x\n", indexGdtl);
|
||
|
ExtVerb("fs: %04x\n", indexFs);
|
||
|
|
||
|
ULONG64 gdtr;
|
||
|
ULONG64 gdtl;
|
||
|
ULONG64 fs;
|
||
|
|
||
|
DEBUG_VALUE v;
|
||
|
EXT_CHECK(g_ExtRegisters->GetValue(indexGdtr, &v));
|
||
|
ExtVerb("gdtr: %04x -> %p\n", indexGdtr, v.I64);
|
||
|
gdtr = v.I64;
|
||
|
EXT_CHECK(g_ExtRegisters->GetValue(indexGdtl, &v));
|
||
|
gdtl = v.I64;
|
||
|
ExtVerb("gdtl: %04x -> %p\n", indexGdtl, v.I64);
|
||
|
EXT_CHECK(g_ExtRegisters->GetValue(indexFs, &v));
|
||
|
fs = v.I64;
|
||
|
ExtVerb("fs: %04x -> %p\n", indexFs, v.I64);
|
||
|
|
||
|
GDTE fse;
|
||
|
ZeroMemory(&fse, sizeof(fse));
|
||
|
EXT_CHECK(g_ExtData->ReadVirtual(gdtr + fs, &fse, sizeof(fse), NULL));
|
||
|
ULONG64 fs0 = ((ULONG64)fse.base0_15 +
|
||
|
((ULONG64)fse.base16_23 << 16) |
|
||
|
((ULONG64)fse.base24_31 << 24));
|
||
|
ExtVerb("fs[0] = %p\n", fs0);
|
||
|
|
||
|
ProcessorContext pc;
|
||
|
EXT_CHECK(ProcessorContextStruct.Read(fs0, &pc));
|
||
|
|
||
|
ExtVerb(" threadContext: %p\n", pc.threadContext);
|
||
|
ExtVerb(" _processor: %p\n", pc._processor);
|
||
|
ExtVerb(" exception: %p\n", pc.exception);
|
||
|
ExtVerb(" cpuId: %p\n", pc.cpuId);
|
||
|
ExtOut("%d: Stacks: Int: %p..%p, Exc: %p..%p, Sch: %p..%p\n",
|
||
|
(int)pc.cpuId,
|
||
|
pc.interruptStackLimit, pc.interruptStackBegin - 1,
|
||
|
pc.exceptionStackLimit, pc.exceptionStackBegin - 1,
|
||
|
pc.schedulerStackLimit, pc.schedulerStackBegin - 1);
|
||
|
|
||
|
ThreadContext tc;
|
||
|
EXT_CHECK(ThreadContextStruct.Read(pc.threadContext, &tc));
|
||
|
|
||
|
address = tc._thread;
|
||
|
}
|
||
|
|
||
|
if (address == 0) {
|
||
|
ExtErr("Null is invalid thread address.\n");
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
EXT_CHECK(ThreadStruct.Read(address, &thread));
|
||
|
|
||
|
ULONG64 caddress[3];
|
||
|
int currentStack = 0;
|
||
|
caddress[0] = address + ThreadFields[9].offset;
|
||
|
|
||
|
if (thread.context.next) {
|
||
|
caddress[1] = thread.context.next;
|
||
|
currentStack++;
|
||
|
EXT_CHECK(ThreadContextStruct.Read(caddress[1], &tc));
|
||
|
if (tc.next) {
|
||
|
caddress[2] = tc.next;
|
||
|
EXT_CHECK(ThreadContextStruct.Read(caddress[2], &tc));
|
||
|
currentStack++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (topStack == 0) {
|
||
|
currentStack = 0;
|
||
|
}
|
||
|
else {
|
||
|
if (priorStack != 0) {
|
||
|
currentStack -= priorStack;
|
||
|
if (currentStack < 0) {
|
||
|
currentStack = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EXT_CHECK(ThreadContextStruct.Read(caddress[currentStack], &tc));
|
||
|
EXT_CHECK(ThreadContextStruct.RawAccess(ThreadContextFields[0].offset, &floatingRegs));
|
||
|
EXT_CHECK(ThreadContextStruct.RawAccess(ThreadContextFields[8].offset, &extendedRegs));
|
||
|
|
||
|
ExtOut(" %p { tid=%03x pid=%03x eip=%p, ebp=%p, esp=%p %02x }\n",
|
||
|
address,
|
||
|
(ULONG)(tc.threadIndex == 0xffff ? 0xfff : tc.threadIndex),
|
||
|
(ULONG)(tc.processId == 0xffff ? 0xfff : tc.processId),
|
||
|
tc.eip,
|
||
|
tc.ebp,
|
||
|
tc.esp,
|
||
|
(ULONG)tc.gcStates);
|
||
|
|
||
|
CONTEXT context;
|
||
|
|
||
|
EXT_CHECK(g_ExtSymbols->GetScope(NULL, NULL, &context, sizeof(context)));
|
||
|
context.Eip = (ULONG)tc.eip;
|
||
|
context.Esp = (ULONG)tc.esp;
|
||
|
context.Ebp = (ULONG)tc.ebp;
|
||
|
context.Eax = (ULONG)tc.eax;
|
||
|
context.Ebx = (ULONG)tc.ebx;
|
||
|
context.Ecx = (ULONG)tc.ecx;
|
||
|
context.Edx = (ULONG)tc.edx;
|
||
|
context.Esi = (ULONG)tc.esi;
|
||
|
context.Edi = (ULONG)tc.edi;
|
||
|
context.EFlags = (ULONG)tc.efl;
|
||
|
|
||
|
// CONTEXT_FLOATING_POINT
|
||
|
context.FloatSave.ControlWord = (ULONG)tc.mmx.fcw;
|
||
|
context.FloatSave.StatusWord = (ULONG)tc.mmx.fsw;
|
||
|
context.FloatSave.TagWord = (ULONG)tc.mmx.ftw;
|
||
|
context.FloatSave.ErrorOffset = (ULONG)tc.mmx.cs;
|
||
|
context.FloatSave.ErrorSelector = (ULONG)tc.mmx.eip;
|
||
|
context.FloatSave.DataOffset = (ULONG)tc.mmx.ds;
|
||
|
context.FloatSave.DataSelector = (ULONG)tc.mmx.dp;
|
||
|
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+0, (PBYTE)floatingRegs + 0x00, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+10, (PBYTE)floatingRegs + 0x10, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+20, (PBYTE)floatingRegs + 0x20, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+30, (PBYTE)floatingRegs + 0x30, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+40, (PBYTE)floatingRegs + 0x40, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+50, (PBYTE)floatingRegs + 0x50, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+60, (PBYTE)floatingRegs + 0x60, 10);
|
||
|
memcpy((PBYTE)context.FloatSave.RegisterArea+70, (PBYTE)floatingRegs + 0x70, 10);
|
||
|
memcpy(context.ExtendedRegisters, extendedRegs, 512);
|
||
|
|
||
|
EXT_CHECK(g_ExtSymbols->SetScope(0, NULL, &context, sizeof(context)));
|
||
|
EXT_LEAVE(); // Macro includes: return status;
|
||
|
}
|