修复EvalStackCalculator计算input eval stack data类型时没有对多个inbound basic block的input计算共享类型的bug
parent
5557b27724
commit
b0699ecf5c
|
@ -2,6 +2,7 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Assertions;
|
||||
|
@ -62,6 +63,7 @@ namespace Obfuz.Emit
|
|||
class EvalStackCalculator
|
||||
{
|
||||
private readonly MethodDef _method;
|
||||
private readonly ICorLibTypes _corLibTypes;
|
||||
private readonly BasicBlockCollection _basicBlocks;
|
||||
private readonly Dictionary<Instruction, InstructionParameterInfo> _instructionParameterInfos = new Dictionary<Instruction, InstructionParameterInfo>();
|
||||
private readonly Dictionary<Instruction, EvalDataType> _evalStackTopDataTypeAfterInstructions = new Dictionary<Instruction, EvalDataType>();
|
||||
|
@ -70,6 +72,7 @@ namespace Obfuz.Emit
|
|||
public EvalStackCalculator(MethodDef method)
|
||||
{
|
||||
_method = method;
|
||||
_corLibTypes = method.Module.CorLibTypes;
|
||||
_basicBlocks = new BasicBlockCollection(method, false);
|
||||
_blockEvalStackStates = _basicBlocks.Blocks.ToDictionary(b => b, b => new EvalStackState());
|
||||
|
||||
|
@ -956,27 +959,401 @@ namespace Obfuz.Emit
|
|||
_evalStackTopDataTypeAfterInstructions[inst] = stackDatas.Last().type;
|
||||
}
|
||||
}
|
||||
|
||||
ReduceOutputArgsToBaseType(block, stackDatas);
|
||||
|
||||
foreach (BasicBlock outBb in block.outBlocks)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,6 +118,34 @@ namespace Obfuz.Utils
|
|||
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)
|
||||
{
|
||||
TypeDef cur = typeDef;
|
||||
|
|
Loading…
Reference in New Issue