reactos/ntoskrnl/kdbg/kdb_expr.c

1197 lines
34 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2005 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/kdbg/kdb_expr.c
* PURPOSE: Kernel debugger expression evaluation
* PROGRAMMER: Gregor Anich (blight@blight.eu.org)
* UPDATE HISTORY:
* Created 15/01/2005
*/
/* Note:
*
* The given expression is parsed and stored in reverse polish notation,
* then it is evaluated and the result is returned.
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* TYPES *********************************************************************/
typedef enum _RPN_OP_TYPE
{
RpnOpNop,
RpnOpBinaryOperator,
RpnOpUnaryOperator,
RpnOpImmediate,
RpnOpRegister,
RpnOpDereference
} RPN_OP_TYPE;
typedef ULONGLONG (*RPN_BINARY_OPERATOR)(ULONGLONG a, ULONGLONG b);
typedef struct _RPN_OP
{
RPN_OP_TYPE Type;
ULONG CharacterOffset;
union
{
/* RpnOpBinaryOperator */
RPN_BINARY_OPERATOR BinaryOperator;
/* RpnOpImmediate */
ULONGLONG Immediate;
/* RpnOpRegister */
UCHAR Register;
/* RpnOpDereference */
UCHAR DerefMemorySize;
}
Data;
}
RPN_OP, *PRPN_OP;
typedef struct _RPN_STACK
{
ULONG Size; /* Number of RPN_OPs on Ops */
ULONG Sp; /* Stack pointer */
RPN_OP Ops[1]; /* Array of RPN_OPs */
}
RPN_STACK, *PRPN_STACK;
/* DEFINES *******************************************************************/
#define stricmp _stricmp
#ifndef RTL_FIELD_SIZE
# define RTL_FIELD_SIZE(type, field) (sizeof(((type *)0)->field))
#endif
#define CONST_STRCPY(dst, src) \
do { if ((dst)) { memcpy(dst, src, sizeof(src)); } } while (0);
#define RPN_OP_STACK_SIZE 256
#define RPN_VALUE_STACK_SIZE 256
/* GLOBALS *******************************************************************/
static struct
{
ULONG Size;
ULONG Sp;
RPN_OP Ops[RPN_OP_STACK_SIZE];
}
RpnStack =
{
RPN_OP_STACK_SIZE,
0
};
static const struct
{
PCHAR Name;
UCHAR Offset;
UCHAR Size;
}
RegisterToTrapFrame[] =
{
{"eip", FIELD_OFFSET(KDB_KTRAP_FRAME, Eip), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Eip)},
{"eflags", FIELD_OFFSET(KDB_KTRAP_FRAME, EFlags), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, EFlags)},
{"eax", FIELD_OFFSET(KDB_KTRAP_FRAME, Eax), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Eax)},
{"ebx", FIELD_OFFSET(KDB_KTRAP_FRAME, Ebx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Ebx)},
{"ecx", FIELD_OFFSET(KDB_KTRAP_FRAME, Ecx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Ecx)},
{"edx", FIELD_OFFSET(KDB_KTRAP_FRAME, Edx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Edx)},
{"esi", FIELD_OFFSET(KDB_KTRAP_FRAME, Esi), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Esi)},
{"edi", FIELD_OFFSET(KDB_KTRAP_FRAME, Edi), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Edi)},
{"esp", FIELD_OFFSET(KDB_KTRAP_FRAME, Esp), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Esp)},
{"ebp", FIELD_OFFSET(KDB_KTRAP_FRAME, Ebp), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Ebp)},
{"cs", FIELD_OFFSET(KDB_KTRAP_FRAME, SegCs), 2 }, /* Use only the lower 2 bytes */
{"ds", FIELD_OFFSET(KDB_KTRAP_FRAME, SegDs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, SegDs)},
{"es", FIELD_OFFSET(KDB_KTRAP_FRAME, SegEs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, SegEs)},
{"fs", FIELD_OFFSET(KDB_KTRAP_FRAME, SegFs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, SegFs)},
{"gs", FIELD_OFFSET(KDB_KTRAP_FRAME, SegGs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, SegGs)},
{"ss", FIELD_OFFSET(KDB_KTRAP_FRAME, SegSs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, SegSs)},
{"dr0", FIELD_OFFSET(KDB_KTRAP_FRAME, Dr0), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Dr0)},
{"dr1", FIELD_OFFSET(KDB_KTRAP_FRAME, Dr1), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Dr1)},
{"dr2", FIELD_OFFSET(KDB_KTRAP_FRAME, Dr2), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Dr2)},
{"dr3", FIELD_OFFSET(KDB_KTRAP_FRAME, Dr3), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Dr3)},
{"dr6", FIELD_OFFSET(KDB_KTRAP_FRAME, Dr6), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Dr6)},
{"dr7", FIELD_OFFSET(KDB_KTRAP_FRAME, Dr7), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Dr7)}
};
static const INT RegisterToTrapFrameCount = sizeof (RegisterToTrapFrame) / sizeof (RegisterToTrapFrame[0]);
/* FUNCTIONS *****************************************************************/
ULONGLONG
RpnBinaryOperatorAdd(
ULONGLONG a,
ULONGLONG b)
{
return a + b;
}
ULONGLONG
RpnBinaryOperatorSub(
ULONGLONG a,
ULONGLONG b)
{
return a - b;
}
ULONGLONG
RpnBinaryOperatorMul(
ULONGLONG a,
ULONGLONG b)
{
return a * b;
}
ULONGLONG
RpnBinaryOperatorDiv(
ULONGLONG a,
ULONGLONG b)
{
return a / b;
}
ULONGLONG
RpnBinaryOperatorMod(
ULONGLONG a,
ULONGLONG b)
{
return a % b;
}
ULONGLONG
RpnBinaryOperatorEquals(
ULONGLONG a,
ULONGLONG b)
{
return (a == b);
}
ULONGLONG
RpnBinaryOperatorNotEquals(
ULONGLONG a,
ULONGLONG b)
{
return (a != b);
}
ULONGLONG
RpnBinaryOperatorLessThan(
ULONGLONG a,
ULONGLONG b)
{
return (a < b);
}
ULONGLONG
RpnBinaryOperatorLessThanOrEquals(
ULONGLONG a,
ULONGLONG b)
{
return (a <= b);
}
ULONGLONG
RpnBinaryOperatorGreaterThan(
ULONGLONG a,
ULONGLONG b)
{
return (a > b);
}
ULONGLONG
RpnBinaryOperatorGreaterThanOrEquals(
ULONGLONG a,
ULONGLONG b)
{
return (a >= b);
}
/*!\brief Dumps the given RPN stack content
*
* \param Stack Pointer to a RPN_STACK structure.
*/
VOID
RpnpDumpStack(
IN PRPN_STACK Stack)
{
ULONG ul;
ASSERT(Stack);
DbgPrint("\nStack size: %ld\n", Stack->Sp);
for (ul = 0; ul < Stack->Sp; ul++)
{
PRPN_OP Op = Stack->Ops + ul;
switch (Op->Type)
{
case RpnOpNop:
DbgPrint("NOP,");
break;
case RpnOpImmediate:
DbgPrint("0x%I64x,", Op->Data.Immediate);
break;
case RpnOpBinaryOperator:
if (Op->Data.BinaryOperator == RpnBinaryOperatorAdd)
DbgPrint("+,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorSub)
DbgPrint("-,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorMul)
DbgPrint("*,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorDiv)
DbgPrint("/,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorMod)
DbgPrint("%%,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorEquals)
DbgPrint("==,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorNotEquals)
DbgPrint("!=,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorLessThan)
DbgPrint("<,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorLessThanOrEquals)
DbgPrint("<=,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorGreaterThan)
DbgPrint(">,");
else if (Op->Data.BinaryOperator == RpnBinaryOperatorGreaterThanOrEquals)
DbgPrint(">=,");
else
DbgPrint("UNKNOWN OP,");
break;
case RpnOpRegister:
DbgPrint("%s,", RegisterToTrapFrame[Op->Data.Register].Name);
break;
case RpnOpDereference:
DbgPrint("[%s],",
(Op->Data.DerefMemorySize == 1) ? ("byte") :
((Op->Data.DerefMemorySize == 2) ? ("word") :
((Op->Data.DerefMemorySize == 4) ? ("dword") : ("qword"))));
break;
default:
DbgPrint("\nUnsupported Type: %d\n", Op->Type);
ul = Stack->Sp;
break;
}
}
DbgPrint("\n");
}
/*!\brief Clears the given RPN stack.
*
* \param Stack Pointer to a RPN_STACK structure.
*/
static VOID
RpnpClearStack(
OUT PRPN_STACK Stack)
{
ASSERT(Stack);
Stack->Sp = 0;
}
/*!\brief Pushes an RPN_OP onto the stack.
*
* \param Stack Pointer to a RPN_STACK structure.
* \param Op RPN_OP to be copied onto the stack.
*/
static BOOLEAN
RpnpPushStack(
IN OUT PRPN_STACK Stack,
IN PRPN_OP Op)
{
ASSERT(Stack);
ASSERT(Op);
if (Stack->Sp >= Stack->Size)
return FALSE;
memcpy(Stack->Ops + Stack->Sp, Op, sizeof (RPN_OP));
Stack->Sp++;
return TRUE;
}
/*!\brief Pops the top op from the stack.
*
* \param Stack Pointer to a RPN_STACK structure.
* \param Op Pointer to an RPN_OP to store the popped op into (can be NULL).
*
* \retval TRUE Success.
* \retval FALSE Failure (stack empty)
*/
static BOOLEAN
RpnpPopStack(
IN OUT PRPN_STACK Stack,
OUT PRPN_OP Op OPTIONAL)
{
ASSERT(Stack);
if (Stack->Sp == 0)
return FALSE;
Stack->Sp--;
if (Op)
memcpy(Op, Stack->Ops + Stack->Sp, sizeof (RPN_OP));
return TRUE;
}
/*!\brief Gets the top op from the stack (not popping it)
*
* \param Stack Pointer to a RPN_STACK structure.
* \param Op Pointer to an RPN_OP to copy the top op into.
*
* \retval TRUE Success.
* \retval FALSE Failure (stack empty)
*/
static BOOLEAN
RpnpTopStack(
IN PRPN_STACK Stack,
OUT PRPN_OP Op)
{
ASSERT(Stack);
ASSERT(Op);
if (Stack->Sp == 0)
return FALSE;
memcpy(Op, Stack->Ops + Stack->Sp - 1, sizeof (RPN_OP));
return TRUE;
}
/*!\brief Parses an expression.
*
* This functions parses the given expression until the end of string or a closing
* brace is found. As the function parses the string it pushes RPN_OPs onto the
* stack.
*
* Examples: 1+2*3 ; eax+10 ; (eax+16) * (ebx+4) ; dword[eax]
*
* \param Stack Pointer to a RPN_STACK structure.
* \param Expression String to parse.
* \param CharacterOffset Character offset of the subexpression from the beginning of the expression.
* \param End On success End is set to the character at which parsing stopped.
* \param ErrOffset On failure this is set to the character offset at which the error occoured.
* \param ErrMsg On failure a message describing the problem is copied into this buffer (128 bytes)
*
* \retval TRUE Success.
* \retval FALSE Failure.
*/
static BOOLEAN
RpnpParseExpression(
IN PRPN_STACK Stack,
IN PCHAR Expression,
OUT PCHAR *End OPTIONAL,
IN ULONG CharacterOffset,
OUT PLONG ErrOffset OPTIONAL,
OUT PCHAR ErrMsg OPTIONAL)
{
PCHAR p = Expression;
PCHAR pend;
PCHAR Operator = NULL;
LONG OperatorOffset = -1;
RPN_OP RpnOp;
RPN_OP PoppedOperator;
BOOLEAN HavePoppedOperator = FALSE;
RPN_OP ComparativeOp;
BOOLEAN ComparativeOpFilled = FALSE;
BOOLEAN IsComparativeOp;
INT i, i2;
ULONG ul;
UCHAR MemorySize;
CHAR Buffer[16];
BOOLEAN First;
ASSERT(Stack);
ASSERT(Expression);
First = TRUE;
for (;;)
{
/* Skip whitespace */
while (isspace(*p))
{
p++;
CharacterOffset++;
}
/* Check for end of expression */
if (p[0] == '\0' || p[0] == ')' || p[0] == ']')
break;
if (!First)
{
/* Remember operator */
Operator = p++;
OperatorOffset = CharacterOffset++;
/* Pop operator (to get the right operator precedence) */
HavePoppedOperator = FALSE;
if (*Operator == '*' || *Operator == '/' || *Operator == '%')
{
if (RpnpTopStack(Stack, &PoppedOperator) &&
PoppedOperator.Type == RpnOpBinaryOperator &&
(PoppedOperator.Data.BinaryOperator == RpnBinaryOperatorAdd ||
PoppedOperator.Data.BinaryOperator == RpnBinaryOperatorSub))
{
RpnpPopStack(Stack, NULL);
HavePoppedOperator = TRUE;
}
else if (PoppedOperator.Type == RpnOpNop)
{
RpnpPopStack(Stack, NULL);
/* Discard the NOP - it was only pushed to indicate there was a
* closing brace, so the previous operator shouldn't be popped.
*/
}
}
else if ((Operator[0] == '=' && Operator[1] == '=') ||
(Operator[0] == '!' && Operator[1] == '=') ||
Operator[0] == '<' || Operator[0] == '>')
{
if (Operator[0] == '=' || Operator[0] == '!' ||
(Operator[0] == '<' && Operator[1] == '=') ||
(Operator[0] == '>' && Operator[1] == '='))
{
p++;
CharacterOffset++;
}
#if 0
/* Parse rest of expression */
if (!RpnpParseExpression(Stack, p + 1, &pend, CharacterOffset + 1,
ErrOffset, ErrMsg))
{
return FALSE;
}
else if (pend == p + 1)
{
CONST_STRCPY(ErrMsg, "Expression expected");
if (ErrOffset)
*ErrOffset = CharacterOffset + 1;
return FALSE;
}
goto end_of_expression; /* return */
#endif
}
else if (Operator[0] != '+' && Operator[0] != '-')
{
CONST_STRCPY(ErrMsg, "Operator expected");
if (ErrOffset)
*ErrOffset = OperatorOffset;
return FALSE;
}
/* Skip whitespace */
while (isspace(*p))
{
p++;
CharacterOffset++;
}
}
/* Get operand */
MemorySize = sizeof(ULONG_PTR); /* default to pointer size */
get_operand:
i = strcspn(p, "+-*/%()[]<>!=");
if (i > 0)
{
i2 = i;
/* Copy register name/memory size */
while (isspace(p[--i2]));
i2 = min(i2 + 1, (INT)sizeof (Buffer) - 1);
strncpy(Buffer, p, i2);
Buffer[i2] = '\0';
/* Memory size prefix */
if (p[i] == '[')
{
if (stricmp(Buffer, "byte") == 0)
MemorySize = 1;
else if (stricmp(Buffer, "word") == 0)
MemorySize = 2;
else if (stricmp(Buffer, "dword") == 0)
MemorySize = 4;
else if (stricmp(Buffer, "qword") == 0)
MemorySize = 8;
else
{
CONST_STRCPY(ErrMsg, "Invalid memory size prefix");
if (ErrOffset)
*ErrOffset = CharacterOffset;
return FALSE;
}
p += i;
CharacterOffset += i;
goto get_operand;
}
/* Try to find register */
for (i = 0; i < RegisterToTrapFrameCount; i++)
{
if (stricmp(RegisterToTrapFrame[i].Name, Buffer) == 0)
break;
}
if (i < RegisterToTrapFrameCount)
{
RpnOp.Type = RpnOpRegister;
RpnOp.CharacterOffset = CharacterOffset;
RpnOp.Data.Register = i;
i = strlen(RegisterToTrapFrame[i].Name);
CharacterOffset += i;
p += i;
}
else
{
/* Immediate value */
/* FIXME: Need string to ULONGLONG function */
ul = strtoul(p, &pend, 0);
if (p != pend)
{
RpnOp.Type = RpnOpImmediate;
RpnOp.CharacterOffset = CharacterOffset;
RpnOp.Data.Immediate = (ULONGLONG)ul;
CharacterOffset += pend - p;
p = pend;
}
else
{
CONST_STRCPY(ErrMsg, "Operand expected");
if (ErrOffset)
*ErrOffset = CharacterOffset;
return FALSE;
}
}
/* Push operand */
if (!RpnpPushStack(Stack, &RpnOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
}
else if (i == 0)
{
if (p[0] == '(' || p[0] == '[') /* subexpression */
{
if (!RpnpParseExpression(Stack, p + 1, &pend, CharacterOffset + 1,
ErrOffset, ErrMsg))
{
return FALSE;
}
else if (pend == p + 1)
{
CONST_STRCPY(ErrMsg, "Expression expected");
if (ErrOffset)
*ErrOffset = CharacterOffset + 1;
return FALSE;
}
if (p[0] == '[') /* dereference */
{
ASSERT(MemorySize == 1 || MemorySize == 2 ||
MemorySize == 4 || MemorySize == 8);
if (pend[0] != ']')
{
CONST_STRCPY(ErrMsg, "']' expected");
if (ErrOffset)
*ErrOffset = CharacterOffset + (pend - p);
return FALSE;
}
RpnOp.Type = RpnOpDereference;
RpnOp.CharacterOffset = CharacterOffset;
RpnOp.Data.DerefMemorySize = MemorySize;
if (!RpnpPushStack(Stack, &RpnOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
}
else /* p[0] == '(' */
{
if (pend[0] != ')')
{
CONST_STRCPY(ErrMsg, "')' expected");
if (ErrOffset)
*ErrOffset = CharacterOffset + (pend - p);
return FALSE;
}
}
/* Push a "nop" to prevent popping of the + operator (which would
* result in (10+10)/2 beeing evaluated as 15)
*/
RpnOp.Type = RpnOpNop;
if (!RpnpPushStack(Stack, &RpnOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
/* Skip closing brace/bracket */
pend++;
CharacterOffset += pend - p;
p = pend;
}
else if (First && p[0] == '-') /* Allow expressions like "- eax" */
{
RpnOp.Type = RpnOpImmediate;
RpnOp.CharacterOffset = CharacterOffset;
RpnOp.Data.Immediate = 0;
if (!RpnpPushStack(Stack, &RpnOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
}
else
{
CONST_STRCPY(ErrMsg, "Operand expected");
if (ErrOffset)
*ErrOffset = CharacterOffset;
return FALSE;
}
}
else
{
CONST_STRCPY(ErrMsg, "strcspn() failed");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
if (!First)
{
/* Push operator */
RpnOp.CharacterOffset = OperatorOffset;
RpnOp.Type = RpnOpBinaryOperator;
IsComparativeOp = FALSE;
switch (*Operator)
{
case '+':
RpnOp.Data.BinaryOperator = RpnBinaryOperatorAdd;
break;
case '-':
RpnOp.Data.BinaryOperator = RpnBinaryOperatorSub;
break;
case '*':
RpnOp.Data.BinaryOperator = RpnBinaryOperatorMul;
break;
case '/':
RpnOp.Data.BinaryOperator = RpnBinaryOperatorDiv;
break;
case '%':
RpnOp.Data.BinaryOperator = RpnBinaryOperatorMod;
break;
case '=':
ASSERT(Operator[1] == '=');
IsComparativeOp = TRUE;
RpnOp.Data.BinaryOperator = RpnBinaryOperatorEquals;
break;
case '!':
ASSERT(Operator[1] == '=');
IsComparativeOp = TRUE;
RpnOp.Data.BinaryOperator = RpnBinaryOperatorNotEquals;
break;
case '<':
IsComparativeOp = TRUE;
if (Operator[1] == '=')
RpnOp.Data.BinaryOperator = RpnBinaryOperatorLessThanOrEquals;
else
RpnOp.Data.BinaryOperator = RpnBinaryOperatorLessThan;
break;
case '>':
IsComparativeOp = TRUE;
if (Operator[1] == '=')
RpnOp.Data.BinaryOperator = RpnBinaryOperatorGreaterThanOrEquals;
else
RpnOp.Data.BinaryOperator = RpnBinaryOperatorGreaterThan;
break;
default:
ASSERT(0);
break;
}
if (IsComparativeOp)
{
if (ComparativeOpFilled && !RpnpPushStack(Stack, &ComparativeOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
memcpy(&ComparativeOp, &RpnOp, sizeof(RPN_OP));
ComparativeOpFilled = TRUE;
}
else if (!RpnpPushStack(Stack, &RpnOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
/* Push popped operator */
if (HavePoppedOperator)
{
if (!RpnpPushStack(Stack, &PoppedOperator))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
}
}
First = FALSE;
}
//end_of_expression:
if (ComparativeOpFilled && !RpnpPushStack(Stack, &ComparativeOp))
{
CONST_STRCPY(ErrMsg, "RPN op stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
/* Skip whitespace */
while (isspace(*p))
{
p++;
CharacterOffset++;
}
if (End)
*End = p;
return TRUE;
}
/*!\brief Evaluates the RPN op stack and returns the result.
*
* \param Stack Pointer to a RPN_STACK structure.
* \param TrapFrame Register values.
* \param Result Pointer to an ULONG to store the result into.
* \param ErrOffset On failure this is set to the character offset at which the error occoured.
* \param ErrMsg Buffer which receives an error message on failure (128 bytes)
*
* \retval TRUE Success.
* \retval FALSE Failure.
*/
static BOOLEAN
RpnpEvaluateStack(
IN PRPN_STACK Stack,
IN PKDB_KTRAP_FRAME TrapFrame,
OUT PULONGLONG Result,
OUT PLONG ErrOffset OPTIONAL,
OUT PCHAR ErrMsg OPTIONAL)
{
ULONGLONG ValueStack[RPN_VALUE_STACK_SIZE];
ULONG ValueStackPointer = 0;
ULONG index;
ULONGLONG ull;
ULONG ul;
USHORT us;
UCHAR uc;
PVOID p;
BOOLEAN Ok;
#ifdef DEBUG_RPN
ULONG ValueStackPointerMax = 0;
#endif
ASSERT(Stack);
ASSERT(TrapFrame);
ASSERT(Result);
for (index = 0; index < Stack->Sp; index++)
{
PRPN_OP Op = Stack->Ops + index;
#ifdef DEBUG_RPN
ValueStackPointerMax = max(ValueStackPointerMax, ValueStackPointer);
#endif
switch (Op->Type)
{
case RpnOpNop:
/* No operation */
break;
case RpnOpImmediate:
if (ValueStackPointer == RPN_VALUE_STACK_SIZE)
{
CONST_STRCPY(ErrMsg, "Value stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
ValueStack[ValueStackPointer++] = Op->Data.Immediate;
break;
case RpnOpRegister:
if (ValueStackPointer == RPN_VALUE_STACK_SIZE)
{
CONST_STRCPY(ErrMsg, "Value stack overflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
ul = Op->Data.Register;
p = (PVOID)((ULONG_PTR)TrapFrame + RegisterToTrapFrame[ul].Offset);
switch (RegisterToTrapFrame[ul].Size)
{
case 1: ull = (ULONGLONG)(*(PUCHAR)p); break;
case 2: ull = (ULONGLONG)(*(PUSHORT)p); break;
case 4: ull = (ULONGLONG)(*(PULONG)p); break;
case 8: ull = (ULONGLONG)(*(PULONGLONG)p); break;
default: ASSERT(0); return FALSE; break;
}
ValueStack[ValueStackPointer++] = ull;
break;
case RpnOpDereference:
if (ValueStackPointer < 1)
{
CONST_STRCPY(ErrMsg, "Value stack underflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
/* FIXME: Print a warning when address is out of range */
p = (PVOID)(ULONG_PTR)ValueStack[ValueStackPointer - 1];
Ok = FALSE;
switch (Op->Data.DerefMemorySize)
{
case 1:
if (NT_SUCCESS(KdbpSafeReadMemory(&uc, p, sizeof (uc))))
{
Ok = TRUE;
ull = (ULONGLONG)uc;
}
break;
case 2:
if (NT_SUCCESS(KdbpSafeReadMemory(&us, p, sizeof (us))))
{
Ok = TRUE;
ull = (ULONGLONG)us;
}
break;
case 4:
if (NT_SUCCESS(KdbpSafeReadMemory(&ul, p, sizeof (ul))))
{
Ok = TRUE;
ull = (ULONGLONG)ul;
}
break;
case 8:
if (NT_SUCCESS(KdbpSafeReadMemory(&ull, p, sizeof (ull))))
{
Ok = TRUE;
}
break;
default:
ASSERT(0);
return FALSE;
break;
}
if (!Ok)
{
_snprintf(ErrMsg, 128, "Couldn't access memory at 0x%lx", (ULONG)p);
if (ErrOffset)
*ErrOffset = Op->CharacterOffset;
return FALSE;
}
ValueStack[ValueStackPointer - 1] = ull;
break;
case RpnOpBinaryOperator:
if (ValueStackPointer < 2)
{
CONST_STRCPY(ErrMsg, "Value stack underflow");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
ValueStackPointer--;
ull = ValueStack[ValueStackPointer];
if (ull == 0 && Op->Data.BinaryOperator == RpnBinaryOperatorDiv)
{
CONST_STRCPY(ErrMsg, "Division by zero");
if (ErrOffset)
*ErrOffset = Op->CharacterOffset;
return FALSE;
}
ull = Op->Data.BinaryOperator(ValueStack[ValueStackPointer - 1], ull);
ValueStack[ValueStackPointer - 1] = ull;
break;
default:
ASSERT(0);
return FALSE;
}
}
#ifdef DEBUG_RPN
DPRINT1("Max value stack pointer: %d\n", ValueStackPointerMax);
#endif
if (ValueStackPointer != 1)
{
CONST_STRCPY(ErrMsg, "Stack not empty after evaluation");
if (ErrOffset)
*ErrOffset = -1;
return FALSE;
}
*Result = ValueStack[0];
return TRUE;
}
/*!\brief Evaluates the given expression
*
* \param Expression Expression to evaluate.
* \param TrapFrame Register values.
* \param Result Variable which receives the result on success.
* \param ErrOffset Variable which receives character offset on parse error (-1 on other errors)
* \param ErrMsg Buffer which receives an error message on failure (128 bytes)
*
* \retval TRUE Success.
* \retval FALSE Failure.
*/
BOOLEAN
KdbpRpnEvaluateExpression(
IN PCHAR Expression,
IN PKDB_KTRAP_FRAME TrapFrame,
OUT PULONGLONG Result,
OUT PLONG ErrOffset OPTIONAL,
OUT PCHAR ErrMsg OPTIONAL)
{
PRPN_STACK Stack = (PRPN_STACK)&RpnStack;
ASSERT(Expression);
ASSERT(TrapFrame);
ASSERT(Result);
/* Clear the stack and parse the expression */
RpnpClearStack(Stack);
if (!RpnpParseExpression(Stack, Expression, NULL, 0, ErrOffset, ErrMsg))
return FALSE;
#ifdef DEBUG_RPN
RpnpDumpStack(Stack);
#endif
/* Evaluate the stack */
if (!RpnpEvaluateStack(Stack, TrapFrame, Result, ErrOffset, ErrMsg))
return FALSE;
return TRUE;
}
/*!\brief Parses the given expression and returns a "handle" to it.
*
* \param Expression Expression to evaluate.
* \param ErrOffset Variable which receives character offset on parse error (-1 on other errors)
* \param ErrMsg Buffer which receives an error message on failure (128 bytes)
*
* \returns "Handle" for the expression, NULL on failure.
*
* \sa KdbpRpnEvaluateExpression
*/
PVOID
KdbpRpnParseExpression(
IN PCHAR Expression,
OUT PLONG ErrOffset OPTIONAL,
OUT PCHAR ErrMsg OPTIONAL)
{
LONG Size;
PRPN_STACK Stack = (PRPN_STACK)&RpnStack;
PRPN_STACK NewStack;
ASSERT(Expression);
/* Clear the stack and parse the expression */
RpnpClearStack(Stack);
if (!RpnpParseExpression(Stack, Expression, NULL, 0, ErrOffset, ErrMsg))
return FALSE;
#ifdef DEBUG_RPN
RpnpDumpStack(Stack);
#endif
/* Duplicate the stack and return a pointer/handle to it */
ASSERT(Stack->Sp >= 1);
Size = sizeof (RPN_STACK) + (RTL_FIELD_SIZE(RPN_STACK, Ops[0]) * (Stack->Sp - 1));
NewStack = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_KDBG);
if (!NewStack)
{
CONST_STRCPY(ErrMsg, "Out of memory");
if (ErrOffset)
*ErrOffset = -1;
return NULL;
}
memcpy(NewStack, Stack, Size);
NewStack->Size = NewStack->Sp;
return NewStack;
}
/*!\brief Evaluates the given expression and returns the result.
*
* \param Expression Expression "handle" returned by KdbpRpnParseExpression.
* \param TrapFrame Register values.
* \param Result Variable which receives the result on success.
* \param ErrOffset Variable which receives character offset on parse error (-1 on other errors)
* \param ErrMsg Buffer which receives an error message on failure (128 bytes)
*
* \returns "Handle" for the expression, NULL on failure.
*
* \sa KdbpRpnParseExpression
*/
BOOLEAN
KdbpRpnEvaluateParsedExpression(
IN PVOID Expression,
IN PKDB_KTRAP_FRAME TrapFrame,
OUT PULONGLONG Result,
OUT PLONG ErrOffset OPTIONAL,
OUT PCHAR ErrMsg OPTIONAL)
{
PRPN_STACK Stack = (PRPN_STACK)Expression;
ASSERT(Expression);
ASSERT(TrapFrame);
ASSERT(Result);
/* Evaluate the stack */
return RpnpEvaluateStack(Stack, TrapFrame, Result, ErrOffset, ErrMsg);
}