EvalStackCalculator计算EvalStack变量时如果为ValueType则包含类型信息

dev
walon 2025-06-22 11:45:23 +08:00
parent 832a955167
commit 73915db7ca
1 changed files with 75 additions and 34 deletions

View File

@ -17,9 +17,26 @@ namespace Obfuz.Emit
Double, Double,
I, I,
Ref, Ref,
ValueType,
Token,
Unknown, Unknown,
} }
struct EvalDataTypeWithSig
{
public readonly EvalDataType type;
public readonly TypeSig typeSig;
public EvalDataTypeWithSig(EvalDataType type, TypeSig typeSig)
{
this.type = type;
this.typeSig = typeSig;
}
public override string ToString()
{
return $"{type} ({typeSig})";
}
}
class InstructionParameterInfo class InstructionParameterInfo
{ {
public readonly EvalDataType op1; public readonly EvalDataType op1;
@ -62,11 +79,11 @@ namespace Obfuz.Emit
{ {
public bool visited; public bool visited;
public readonly List<EvalDataType> inputStackDatas = new List<EvalDataType>(); public readonly List<EvalDataTypeWithSig> inputStackDatas = new List<EvalDataTypeWithSig>();
public readonly List<EvalDataType> runStackDatas = new List<EvalDataType>(); public readonly List<EvalDataTypeWithSig> runStackDatas = new List<EvalDataTypeWithSig>();
} }
private void PushStack(List<EvalDataType> datas, TypeSig type) private void PushStack(List<EvalDataTypeWithSig> datas, TypeSig type)
{ {
type = type.RemovePinnedAndModifiers(); type = type.RemovePinnedAndModifiers();
switch (type.ElementType) switch (type.ElementType)
@ -80,31 +97,31 @@ namespace Obfuz.Emit
case ElementType.U2: case ElementType.U2:
case ElementType.I4: case ElementType.I4:
case ElementType.U4: case ElementType.U4:
datas.Add(EvalDataType.Int32); datas.Add(new EvalDataTypeWithSig(EvalDataType.Int32, null));
break; break;
case ElementType.I8: case ElementType.I8:
case ElementType.U8: case ElementType.U8:
datas.Add(EvalDataType.Int64); datas.Add(new EvalDataTypeWithSig(EvalDataType.Int64, null));
break; break;
case ElementType.R4: case ElementType.R4:
datas.Add(EvalDataType.Float); datas.Add(new EvalDataTypeWithSig(EvalDataType.Float, null));
break; break;
case ElementType.R8: case ElementType.R8:
datas.Add(EvalDataType.Double); datas.Add(new EvalDataTypeWithSig(EvalDataType.Double, null));
break; break;
case ElementType.I: case ElementType.I:
case ElementType.U: case ElementType.U:
case ElementType.Ptr: case ElementType.Ptr:
case ElementType.FnPtr: case ElementType.FnPtr:
case ElementType.ByRef: case ElementType.ByRef:
datas.Add(EvalDataType.I); datas.Add(new EvalDataTypeWithSig(EvalDataType.I, null));
break; break;
case ElementType.String: case ElementType.String:
case ElementType.Class: case ElementType.Class:
case ElementType.Array: case ElementType.Array:
case ElementType.SZArray: case ElementType.SZArray:
case ElementType.Object: case ElementType.Object:
datas.Add(EvalDataType.Ref); datas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, type));
break; break;
case ElementType.ValueType: case ElementType.ValueType:
{ {
@ -115,20 +132,38 @@ namespace Obfuz.Emit
} }
else else
{ {
PushStack(datas, EvalDataType.Unknown); PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
} }
break; break;
} }
case ElementType.GenericInst: case ElementType.GenericInst:
{ {
GenericInstSig genericInstSig = (GenericInstSig)type; GenericInstSig genericInstSig = (GenericInstSig)type;
PushStack(datas, genericInstSig.GenericType); TypeDef typeDef = genericInstSig.GenericType.ToTypeDefOrRef().ResolveTypeDefThrow();
if (!typeDef.IsValueType)
{
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.Ref, type));
}
else if (typeDef.IsEnum)
{
PushStack(datas, typeDef.GetEnumUnderlyingType());
}
else
{
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
}
break;
}
case ElementType.TypedByRef:
{
// TypedByRef is a special type used in dynamic method invocation and reflection.
// It is treated as a reference type in the evaluation stack.
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
break; break;
} }
case ElementType.Var: case ElementType.Var:
case ElementType.MVar: case ElementType.MVar:
case ElementType.ValueArray: case ElementType.ValueArray:
case ElementType.TypedByRef:
case ElementType.R: case ElementType.R:
case ElementType.CModOpt: case ElementType.CModOpt:
case ElementType.CModReqd: case ElementType.CModReqd:
@ -142,17 +177,23 @@ namespace Obfuz.Emit
} }
} }
private void PushStack(List<EvalDataType> datas, ITypeDefOrRef type) private void PushStack(List<EvalDataTypeWithSig> datas, ITypeDefOrRef type)
{ {
PushStack(datas, type.ToTypeSig()); PushStack(datas, type.ToTypeSig());
} }
private void PushStack(List<EvalDataType> datas, EvalDataType type) private void PushStack(List<EvalDataTypeWithSig> datas, EvalDataType type)
{
Assert.IsTrue(type != EvalDataType.ValueType, "Cannot push EvalDataType.Value without type sig onto the stack.");
datas.Add(new EvalDataTypeWithSig(type, null));
}
private void PushStack(List<EvalDataTypeWithSig> datas, EvalDataTypeWithSig type)
{ {
datas.Add(type); datas.Add(type);
} }
private EvalDataType CalcBasicBinOpRetType(List<EvalDataType> datas, EvalDataType op1, EvalDataType op2) private EvalDataType CalcBasicBinOpRetType(EvalDataType op1, EvalDataType op2)
{ {
switch (op1) switch (op1)
{ {
@ -220,17 +261,17 @@ namespace Obfuz.Emit
if (handler.IsCatch) if (handler.IsCatch)
{ {
BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.HandlerStart); BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.HandlerStart);
blockEvalStackStates[bb].runStackDatas.Add(EvalDataType.Ref); // Exception object is pushed onto the stack. blockEvalStackStates[bb].runStackDatas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, null)); // Exception object is pushed onto the stack.
} }
else if (handler.IsFilter) else if (handler.IsFilter)
{ {
BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.FilterStart); BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.FilterStart);
blockEvalStackStates[bb].runStackDatas.Add(EvalDataType.Ref); // Exception object is pushed onto the stack. blockEvalStackStates[bb].runStackDatas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, null)); // Exception object is pushed onto the stack.
} }
} }
} }
var newPushedDatas = new List<EvalDataType>(); var newPushedDatas = new List<EvalDataTypeWithSig>();
IList<TypeSig> methodTypeGenericArgument = _method.DeclaringType.GenericParameters.Count > 0 IList<TypeSig> methodTypeGenericArgument = _method.DeclaringType.GenericParameters.Count > 0
? (IList<TypeSig>)_method.DeclaringType.GenericParameters.Select(p => (TypeSig)new GenericVar(p.Number)).ToList() ? (IList<TypeSig>)_method.DeclaringType.GenericParameters.Select(p => (TypeSig)new GenericVar(p.Number)).ToList()
: null; : null;
@ -248,7 +289,7 @@ namespace Obfuz.Emit
continue; continue;
state.visited = true; state.visited = true;
state.runStackDatas.AddRange(state.inputStackDatas); state.runStackDatas.AddRange(state.inputStackDatas);
List<EvalDataType> stackDatas = state.runStackDatas; List<EvalDataTypeWithSig> stackDatas = state.runStackDatas;
foreach (var inst in block.instructions) foreach (var inst in block.instructions)
{ {
int stackSize = stackDatas.Count; int stackSize = stackDatas.Count;
@ -344,7 +385,7 @@ namespace Obfuz.Emit
case Code.Dup: case Code.Dup:
{ {
Assert.IsTrue(stackSize > 0); Assert.IsTrue(stackSize > 0);
EvalDataType type = stackDatas[stackSize - 1]; EvalDataTypeWithSig type = stackDatas[stackSize - 1];
PushStack(newPushedDatas, type); PushStack(newPushedDatas, type);
PushStack(newPushedDatas, type); PushStack(newPushedDatas, type);
break; break;
@ -412,8 +453,8 @@ namespace Obfuz.Emit
case Code.Clt_Un: case Code.Clt_Un:
{ {
Assert.IsTrue(stackSize >= 2); Assert.IsTrue(stackSize >= 2);
EvalDataType op2 = stackDatas[stackSize - 1]; EvalDataType op2 = stackDatas[stackSize - 1].type;
EvalDataType op1 = stackDatas[stackSize - 2]; EvalDataType op1 = stackDatas[stackSize - 2].type;
EvalDataType ret = EvalDataType.Int32; EvalDataType ret = EvalDataType.Int32;
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret)); _instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
PushStack(newPushedDatas, ret); PushStack(newPushedDatas, ret);
@ -497,9 +538,9 @@ namespace Obfuz.Emit
case Code.Xor: case Code.Xor:
{ {
Assert.IsTrue(stackSize >= 2); Assert.IsTrue(stackSize >= 2);
EvalDataType op2 = stackDatas[stackSize - 1]; EvalDataType op2 = stackDatas[stackSize - 1].type;
EvalDataType op1 = stackDatas[stackSize - 2]; EvalDataType op1 = stackDatas[stackSize - 2].type;
EvalDataType ret = CalcBasicBinOpRetType(stackDatas, op1, op2); EvalDataType ret = CalcBasicBinOpRetType(op1, op2);
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret)); _instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
PushStack(newPushedDatas, ret); PushStack(newPushedDatas, ret);
break; break;
@ -509,8 +550,8 @@ namespace Obfuz.Emit
case Code.Shr_Un: case Code.Shr_Un:
{ {
Assert.IsTrue(stackSize >= 2); Assert.IsTrue(stackSize >= 2);
EvalDataType op2 = stackDatas[stackSize - 1]; EvalDataType op2 = stackDatas[stackSize - 1].type;
EvalDataType op1 = stackDatas[stackSize - 2]; EvalDataType op1 = stackDatas[stackSize - 2].type;
if (op1 != EvalDataType.Int32 && op1 != EvalDataType.Int64 && op1 != EvalDataType.I) if (op1 != EvalDataType.Int32 && op1 != EvalDataType.Int64 && op1 != EvalDataType.I)
throw new Exception($"Unsupported operand type: {op1} in shift operation."); throw new Exception($"Unsupported operand type: {op1} in shift operation.");
if (op2 != EvalDataType.Int32 && op2 != EvalDataType.Int64) if (op2 != EvalDataType.Int32 && op2 != EvalDataType.Int64)
@ -523,7 +564,7 @@ namespace Obfuz.Emit
case Code.Neg: case Code.Neg:
{ {
Assert.IsTrue(stackSize > 0); Assert.IsTrue(stackSize > 0);
EvalDataType op = stackDatas[stackSize - 1]; EvalDataType op = stackDatas[stackSize - 1].type;
EvalDataType ret = op; EvalDataType ret = op;
switch (op) switch (op)
{ {
@ -543,7 +584,7 @@ namespace Obfuz.Emit
case Code.Not: case Code.Not:
{ {
Assert.IsTrue(stackSize > 0); Assert.IsTrue(stackSize > 0);
EvalDataType op = stackDatas[stackSize - 1]; EvalDataType op = stackDatas[stackSize - 1].type;
EvalDataType ret = op; EvalDataType ret = op;
if (op != EvalDataType.Int32 && op != EvalDataType.Int64 && op != EvalDataType.I) if (op != EvalDataType.Int32 && op != EvalDataType.Int64 && op != EvalDataType.I)
throw new Exception($"Unsupported operand type: {op} in unary operation."); throw new Exception($"Unsupported operand type: {op} in unary operation.");
@ -780,23 +821,23 @@ namespace Obfuz.Emit
} }
case Code.Mkrefany: case Code.Mkrefany:
{ {
PushStack(newPushedDatas, EvalDataType.Unknown); PushStack(newPushedDatas, new EvalDataTypeWithSig(EvalDataType.ValueType, _method.Module.CorLibTypes.TypedReference));
break; break;
} }
case Code.Refanytype: case Code.Refanytype:
{ {
PushStack(newPushedDatas, EvalDataType.Unknown); PushStack(newPushedDatas, EvalDataType.Token);
break; break;
} }
case Code.Refanyval: case Code.Refanyval:
{ {
Assert.IsTrue(stackSize > 0); Assert.IsTrue(stackSize > 0);
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand); PushStack(newPushedDatas, EvalDataType.I);
break; break;
} }
case Code.Ldtoken: case Code.Ldtoken:
{ {
PushStack(newPushedDatas, EvalDataType.Unknown); PushStack(newPushedDatas, EvalDataType.Token);
break; break;
} }
case Code.Endfinally: case Code.Endfinally:
@ -867,7 +908,7 @@ namespace Obfuz.Emit
} }
if (pushed > 0 && stackDatas.Count > 0) if (pushed > 0 && stackDatas.Count > 0)
{ {
_evalStackTopDataTypeAfterInstructions[inst] = stackDatas.Last(); _evalStackTopDataTypeAfterInstructions[inst] = stackDatas.Last().type;
} }
} }
foreach (BasicBlock outBb in block.outBlocks) foreach (BasicBlock outBb in block.outBlocks)