修复EvalStackCalculator计算input eval stack data类型时没有对多个inbound basic block的input计算共享类型的bug

main
walon 2025-09-06 12:26:02 +08:00
parent 5557b27724
commit b0699ecf5c
2 changed files with 1134 additions and 729 deletions

View File

@ -2,6 +2,7 @@
using dnlib.DotNet.Emit; using dnlib.DotNet.Emit;
using Obfuz.Utils; using Obfuz.Utils;
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine.Assertions; using UnityEngine.Assertions;
@ -62,6 +63,7 @@ namespace Obfuz.Emit
class EvalStackCalculator class EvalStackCalculator
{ {
private readonly MethodDef _method; private readonly MethodDef _method;
private readonly ICorLibTypes _corLibTypes;
private readonly BasicBlockCollection _basicBlocks; private readonly BasicBlockCollection _basicBlocks;
private readonly Dictionary<Instruction, InstructionParameterInfo> _instructionParameterInfos = new Dictionary<Instruction, InstructionParameterInfo>(); private readonly Dictionary<Instruction, InstructionParameterInfo> _instructionParameterInfos = new Dictionary<Instruction, InstructionParameterInfo>();
private readonly Dictionary<Instruction, EvalDataType> _evalStackTopDataTypeAfterInstructions = new Dictionary<Instruction, EvalDataType>(); private readonly Dictionary<Instruction, EvalDataType> _evalStackTopDataTypeAfterInstructions = new Dictionary<Instruction, EvalDataType>();
@ -70,6 +72,7 @@ namespace Obfuz.Emit
public EvalStackCalculator(MethodDef method) public EvalStackCalculator(MethodDef method)
{ {
_method = method; _method = method;
_corLibTypes = method.Module.CorLibTypes;
_basicBlocks = new BasicBlockCollection(method, false); _basicBlocks = new BasicBlockCollection(method, false);
_blockEvalStackStates = _basicBlocks.Blocks.ToDictionary(b => b, b => new EvalStackState()); _blockEvalStackStates = _basicBlocks.Blocks.ToDictionary(b => b, b => new EvalStackState());
@ -956,27 +959,401 @@ namespace Obfuz.Emit
_evalStackTopDataTypeAfterInstructions[inst] = stackDatas.Last().type; _evalStackTopDataTypeAfterInstructions[inst] = stackDatas.Last().type;
} }
} }
ReduceOutputArgsToBaseType(block, stackDatas);
foreach (BasicBlock outBb in block.outBlocks) foreach (BasicBlock outBb in block.outBlocks)
{ {
EvalStackState outState = _blockEvalStackStates[outBb]; EvalStackState outState = _blockEvalStackStates[outBb];
if (outState.visited) if (!outState.visited)
{ {
if (stackDatas.Count != outState.inputStackDatas.Count)
{
throw new Exception($"Block {block} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
}
}
else if (outState.inputStackDatas.Count != stackDatas.Count)
{
if (outState.inputStackDatas.Count > 0)
{
throw new Exception($"Block {outBb} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
}
outState.inputStackDatas.AddRange(stackDatas);
blockWalkStack.Push(outBb); blockWalkStack.Push(outBb);
} }
} }
} }
} }
private void ReduceOutputArgsToBaseType(BasicBlock block, List<EvalDataTypeWithSig> stackDatas)
{
foreach (BasicBlock outBb in block.outBlocks)
{
if (outBb == block)
{
continue;
}
EvalStackState outState = _blockEvalStackStates[outBb];
if (outState.inputStackDatas.Count == stackDatas.Count)
{
ReduceOutputArgsToBaseType(stackDatas, outState.inputStackDatas);
}
else
{
if (outState.inputStackDatas.Count > 0 || outState.visited)
{
throw new Exception($"Block {outBb} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
}
}
}
foreach (BasicBlock outBb in block.outBlocks)
{
if (outBb == block)
{
continue;
}
EvalStackState outState = _blockEvalStackStates[outBb];
outState.inputStackDatas.Clear();
outState.inputStackDatas.AddRange(stackDatas);
}
}
private TypeSig ReduceBaseType(TypeSig type1, TypeSig type2)
{
if (TypeEqualityComparer.Instance.Equals(type1, type2))
{
return type1;
}
type1 = type1.RemovePinnedAndModifiers();
type2 = type2.RemovePinnedAndModifiers();
ElementType t1 = type1.ElementType;
ElementType t2 = type2.ElementType;
if (t1 > t2)
{
(t1, t2) = (t2, t1);
(type1, type2) = (type2, type1);
}
switch (t1)
{
case ElementType.Void:
case ElementType.R4:
case ElementType.R8:
case ElementType.R:
case ElementType.TypedByRef:
case ElementType.ValueArray:
{
if (t1 != t2)
{
break;
}
return type1;
}
case ElementType.Boolean:
case ElementType.Char:
case ElementType.I1:
case ElementType.U1:
case ElementType.I2:
case ElementType.U2:
case ElementType.I4:
case ElementType.U4:
{
switch (t2)
{
case ElementType.Boolean:
case ElementType.Char:
case ElementType.I1:
case ElementType.U1:
case ElementType.I2:
case ElementType.U2:
case ElementType.I4:
case ElementType.U4:
{
return _corLibTypes.Int32;
}
case ElementType.I:
case ElementType.U:
case ElementType.I8:
case ElementType.U8:
{
return type2;
}
}
break;
}
case ElementType.I8:
case ElementType.U8:
{
switch (t2)
{
case ElementType.I:
case ElementType.U:
case ElementType.I8:
case ElementType.U8:
return type1;
}
break;
}
case ElementType.I:
case ElementType.U:
{
switch (t2)
{
case ElementType.I:
case ElementType.U:
{
return type1;
}
case ElementType.FnPtr:
{
return type2;
}
}
break;
}
case ElementType.String:
{
if (t1 == t2)
{
return type1;
}
return _corLibTypes.Object;
}
case ElementType.Ptr:
{
switch (t2)
{
case ElementType.Ptr: return new PtrSig(ReduceBaseType(type1.Next, type2.Next));
case ElementType.ByRef: return new ByRefSig(ReduceBaseType(type1.Next, type2.Next));
case ElementType.I:
case ElementType.U: return type1;
}
break;
}
case ElementType.ByRef:
{
switch (t2)
{
case ElementType.ByRef: return new ByRefSig(ReduceBaseType(type1.Next, type2.Next));
case ElementType.I:
case ElementType.U: return type1;
}
break;
}
case ElementType.ValueType:
{
break;
}
case ElementType.Class:
{
switch (t2)
{
case ElementType.Class:
{
TypeDef typeDef1 = type1.ToTypeDefOrRef().ResolveTypeDefThrow();
TypeDef typeDef2 = type2.ToTypeDefOrRef().ResolveTypeDefThrow();
while (typeDef2 != null)
{
if (MetaUtil.IsAssignableFrom(typeDef1, typeDef2))
{
return typeDef2.ToTypeSig();
}
typeDef2 = typeDef2.BaseType?.ResolveTypeDefThrow();
}
return _corLibTypes.Object;
}
case ElementType.Array:
case ElementType.SZArray:
{
TypeDef typeDef1 = type1.ToTypeDefOrRef().ResolveTypeDefThrow();
if (typeDef1.Namespace.StartsWith("System"))
{
switch (typeDef1.Name.ToString())
{
case "Array":
case "ICloneable":
case "IList":
case "IList`1":
case "ICollection":
case "ICollection`1":
case "IEnumerable":
case "IEnumerable`1":
case "IStructuralComparable":
case "IStructuralEquatable":
{
return type1;
}
}
}
return _corLibTypes.Object;
}
case ElementType.GenericInst:
{
return type2;
}
case ElementType.Var:
case ElementType.Object:
case ElementType.MVar:
{
return _corLibTypes.Object;
}
}
break;
}
case ElementType.Array:
{
switch (t2)
{
case ElementType.Array:
return new ArraySig(ReduceBaseType(type1.Next, type2.Next));
case ElementType.SZArray:
return _method.Module.ImportAsTypeSig(typeof(Array));
case ElementType.GenericInst:
{
var gis = (GenericInstSig)type2;
switch (gis.GenericType.GetName())
{
case "IList`1":
case "ICollection`1":
return type2;
}
return _corLibTypes.Object;
}
case ElementType.Object:
{
return _corLibTypes.Object;
}
}
break;
}
case ElementType.GenericInst:
{
var gis = (GenericInstSig)type1;
switch (t2)
{
case ElementType.Object:
{
return _corLibTypes.Object;
}
case ElementType.MVar:
{
return type2;
}
case ElementType.SZArray:
{
switch (gis.GenericType.GetName())
{
case "IList`1":
case "ICollection`1":
{
return type1;
}
// FIXME
default: return _corLibTypes.Object;
}
}
case ElementType.GenericInst:
{
// FIXME
return type1;
}
}
break;
}
case ElementType.FnPtr:
{
switch (t2)
{
case ElementType.FnPtr:
{
// FIXME
return type1;
}
}
break;
}
case ElementType.Object:
return type1;
case ElementType.SZArray:
{
switch (t2)
{
case ElementType.SZArray:
return new SZArraySig(ReduceBaseType(type1.Next, type2.Next));
case ElementType.MVar:
return _corLibTypes.Object;
}
break;
}
case ElementType.Var:
case ElementType.MVar:
{
if (t1 == t2)
{
// FIXME!!!
return type1;
}
if (t2 == ElementType.Class || t2 == ElementType.GenericInst)
{
return type2;
}
return _corLibTypes.Object;
}
}
UnityEngine.Debug.LogError($"reduce base type of {type1} and {type2} is unsupported");
return type1;
}
private EvalDataTypeWithSig ReduceBaseType(EvalDataTypeWithSig type1, EvalDataTypeWithSig type2)
{
if (type1.type != type2.type)
{
throw new Exception($"Method {_method.FullName} has inconsistent stack data. Expected {type1.type}, but got {type2.type}.");
}
switch (type1.type)
{
case EvalDataType.Int32:
case EvalDataType.Int64:
case EvalDataType.Float:
case EvalDataType.Double:
case EvalDataType.Token:
case EvalDataType.Unknown:
{
return type1;
}
case EvalDataType.I:
case EvalDataType.Ref:
{
if (type1.typeSig == null)
{
return type2;
}
if (type2.typeSig == null)
{
return type1;
}
if (TypeEqualityComparer.Instance.Equals(type1.typeSig, type2.typeSig))
{
return type1;
}
return new EvalDataTypeWithSig(type1.type, ReduceBaseType(type1.typeSig, type2.typeSig));
}
case EvalDataType.ValueType:
{
if (!TypeEqualityComparer.Instance.Equals(type1.typeSig, type2.typeSig))
{
throw new Exception($"Method {_method.FullName} has inconsistent stack data. Expected {type1.typeSig}, but got {type2.typeSig}.");
}
return type1;
}
default: throw new Exception($"not supported EvalDataType: {type1.type} in method: {_method.FullName}.");
}
}
private void ReduceOutputArgsToBaseType(List<EvalDataTypeWithSig> stackDatasRet, List<EvalDataTypeWithSig> stackDatasNew)
{
if (stackDatasNew.Count == 0)
{
return;
}
if (stackDatasRet.Count != stackDatasNew.Count)
{
throw new Exception($"Method {_method.FullName} has inconsistent stack data. Expected {stackDatasRet.Count}, but got {stackDatasNew.Count}.");
}
for (int i = 0; i < stackDatasRet.Count; i++)
{
EvalDataTypeWithSig oldType = stackDatasRet[i];
EvalDataTypeWithSig newType = stackDatasNew[i];
stackDatasRet[i] = ReduceBaseType(oldType, newType);
}
}
} }
} }

View File

@ -118,6 +118,34 @@ namespace Obfuz.Utils
return null; return null;
} }
public static bool IsAssignableFrom(TypeDef fromType, TypeDef toType)
{
TypeDef cur = fromType;
while (true)
{
if (cur == toType)
{
return true;
}
if (toType.IsInterface)
{
foreach (var interfaceType in cur.Interfaces)
{
TypeDef interfaceTypeDef = interfaceType.Interface.ResolveTypeDef();
if (interfaceTypeDef != null && interfaceTypeDef == toType)
{
return true;
}
}
}
cur = GetBaseTypeDef(cur);
if (cur == null)
{
return false;
}
}
}
public static bool IsInheritFromDOTSTypes(TypeDef typeDef) public static bool IsInheritFromDOTSTypes(TypeDef typeDef)
{ {
TypeDef cur = typeDef; TypeDef cur = typeDef;