2011-09-27 07:48:47 +08:00
|
|
|
|
/*
|
2012-01-10 06:02:47 +08:00
|
|
|
|
Copyright (C) 2011-2012 de4dot@gmail.com
|
2011-09-27 07:48:47 +08:00
|
|
|
|
|
|
|
|
|
This file is part of de4dot.
|
|
|
|
|
|
|
|
|
|
de4dot 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 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
de4dot 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 de4dot. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Reflection.Emit;
|
2012-11-01 05:44:54 +08:00
|
|
|
|
using dot10.DotNet.Emit;
|
|
|
|
|
using dot10.DotNet;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
using de4dot.blocks;
|
|
|
|
|
|
2012-11-01 05:44:54 +08:00
|
|
|
|
using OpCode = dot10.DotNet.Emit.OpCode;
|
|
|
|
|
using OpCodes = dot10.DotNet.Emit.OpCodes;
|
|
|
|
|
using OperandType = dot10.DotNet.Emit.OperandType;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
using ROpCode = System.Reflection.Emit.OpCode;
|
|
|
|
|
using ROpCodes = System.Reflection.Emit.OpCodes;
|
|
|
|
|
|
|
|
|
|
namespace AssemblyData.methodsrewriter {
|
|
|
|
|
class CodeGenerator {
|
2012-11-22 16:14:51 +08:00
|
|
|
|
static Dictionary<OpCode, ROpCode> dot10ToReflection = new Dictionary<OpCode, ROpCode>();
|
2011-09-27 07:48:47 +08:00
|
|
|
|
static CodeGenerator() {
|
|
|
|
|
var refDict = new Dictionary<short, ROpCode>(0x100);
|
|
|
|
|
foreach (var f in typeof(ROpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) {
|
|
|
|
|
if (f.FieldType != typeof(ROpCode))
|
|
|
|
|
continue;
|
|
|
|
|
var ropcode = (ROpCode)f.GetValue(null);
|
|
|
|
|
refDict[ropcode.Value] = ropcode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var f in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) {
|
|
|
|
|
if (f.FieldType != typeof(OpCode))
|
|
|
|
|
continue;
|
|
|
|
|
var opcode = (OpCode)f.GetValue(null);
|
|
|
|
|
ROpCode ropcode;
|
|
|
|
|
if (!refDict.TryGetValue(opcode.Value, out ropcode))
|
|
|
|
|
continue;
|
2012-11-22 16:14:51 +08:00
|
|
|
|
dot10ToReflection[opcode] = ropcode;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMethodsRewriter methodsRewriter;
|
2011-09-28 04:06:43 +08:00
|
|
|
|
string methodName;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
IList<Instruction> allInstructions;
|
|
|
|
|
IList<ExceptionHandler> allExceptionHandlers;
|
|
|
|
|
ILGenerator ilg;
|
|
|
|
|
Type methodReturnType;
|
|
|
|
|
Type[] methodParameters;
|
|
|
|
|
Type delegateType;
|
|
|
|
|
MMethod methodInfo;
|
|
|
|
|
LocalBuilder tempObjLocal;
|
|
|
|
|
LocalBuilder tempObjArrayLocal;
|
|
|
|
|
int thisArgIndex;
|
|
|
|
|
List<LocalBuilder> locals;
|
|
|
|
|
List<Label> labels;
|
|
|
|
|
Dictionary<Instruction, int> instrToIndex;
|
2011-09-28 04:06:43 +08:00
|
|
|
|
Stack<ExceptionHandler> exceptionHandlersStack;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
|
|
|
|
|
public Type DelegateType {
|
|
|
|
|
get { return delegateType; }
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-28 04:06:43 +08:00
|
|
|
|
public CodeGenerator(IMethodsRewriter methodsRewriter, string methodName) {
|
2011-09-27 07:48:47 +08:00
|
|
|
|
this.methodsRewriter = methodsRewriter;
|
2011-09-28 04:06:43 +08:00
|
|
|
|
this.methodName = methodName;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setMethodInfo(MMethod methodInfo) {
|
|
|
|
|
this.methodInfo = methodInfo;
|
|
|
|
|
methodReturnType = ResolverUtils.getReturnType(methodInfo.methodBase);
|
|
|
|
|
methodParameters = getMethodParameterTypes(methodInfo.methodBase);
|
|
|
|
|
delegateType = Utils.getDelegateType(methodReturnType, methodParameters);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Delegate generate(IList<Instruction> allInstructions, IList<ExceptionHandler> allExceptionHandlers) {
|
|
|
|
|
this.allInstructions = allInstructions;
|
|
|
|
|
this.allExceptionHandlers = allExceptionHandlers;
|
|
|
|
|
|
2011-09-28 04:06:43 +08:00
|
|
|
|
var dm = new DynamicMethod(methodName, methodReturnType, methodParameters, methodInfo.methodBase.Module, true);
|
2011-09-27 07:48:47 +08:00
|
|
|
|
var lastInstr = allInstructions[allInstructions.Count - 1];
|
2012-11-01 05:44:54 +08:00
|
|
|
|
ilg = dm.GetILGenerator((int)lastInstr.Offset + lastInstr.GetSize());
|
2011-09-27 07:48:47 +08:00
|
|
|
|
|
|
|
|
|
initInstrToIndex();
|
|
|
|
|
initLocals();
|
|
|
|
|
initLabels();
|
|
|
|
|
|
2011-09-28 04:06:43 +08:00
|
|
|
|
exceptionHandlersStack = new Stack<ExceptionHandler>();
|
2011-09-27 07:48:47 +08:00
|
|
|
|
for (int i = 0; i < allInstructions.Count; i++) {
|
2011-09-28 04:06:43 +08:00
|
|
|
|
updateExceptionHandlers(i);
|
2011-09-27 07:48:47 +08:00
|
|
|
|
var instr = allInstructions[i];
|
|
|
|
|
ilg.MarkLabel(labels[i]);
|
|
|
|
|
if (instr.Operand is Operand)
|
|
|
|
|
writeSpecialInstr(instr, (Operand)instr.Operand);
|
|
|
|
|
else
|
|
|
|
|
writeInstr(instr);
|
|
|
|
|
}
|
2011-09-28 04:06:43 +08:00
|
|
|
|
updateExceptionHandlers(-1);
|
2011-09-27 07:48:47 +08:00
|
|
|
|
|
|
|
|
|
return dm.CreateDelegate(delegateType);
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-28 04:06:43 +08:00
|
|
|
|
Instruction getExceptionInstruction(int instructionIndex) {
|
|
|
|
|
return instructionIndex < 0 ? null : allInstructions[instructionIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateExceptionHandlers(int instructionIndex) {
|
|
|
|
|
var instr = getExceptionInstruction(instructionIndex);
|
|
|
|
|
updateExceptionHandlers(instr);
|
|
|
|
|
if (addTryStart(instr))
|
|
|
|
|
updateExceptionHandlers(instr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateExceptionHandlers(Instruction instr) {
|
|
|
|
|
while (exceptionHandlersStack.Count > 0) {
|
|
|
|
|
var ex = exceptionHandlersStack.Peek();
|
|
|
|
|
if (ex.TryEnd == instr) {
|
|
|
|
|
}
|
|
|
|
|
if (ex.FilterStart == instr) {
|
|
|
|
|
}
|
|
|
|
|
if (ex.HandlerStart == instr) {
|
2012-11-02 14:36:02 +08:00
|
|
|
|
if (ex.HandlerType == ExceptionHandlerType.Finally)
|
2011-09-28 04:06:43 +08:00
|
|
|
|
ilg.BeginFinallyBlock();
|
|
|
|
|
else
|
|
|
|
|
ilg.BeginCatchBlock(Resolver.getRtType(ex.CatchType));
|
|
|
|
|
}
|
|
|
|
|
if (ex.HandlerEnd == instr) {
|
|
|
|
|
exceptionHandlersStack.Pop();
|
|
|
|
|
if (exceptionHandlersStack.Count == 0 || !isSameTryBlock(ex, exceptionHandlersStack.Peek()))
|
|
|
|
|
ilg.EndExceptionBlock();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool addTryStart(Instruction instr) {
|
|
|
|
|
var list = new List<ExceptionHandler>();
|
|
|
|
|
foreach (var ex in allExceptionHandlers) {
|
|
|
|
|
if (ex.TryStart == instr)
|
|
|
|
|
list.Add(ex);
|
|
|
|
|
}
|
|
|
|
|
list.Reverse();
|
|
|
|
|
|
|
|
|
|
foreach (var ex in list) {
|
|
|
|
|
if (exceptionHandlersStack.Count == 0 || !isSameTryBlock(ex, exceptionHandlersStack.Peek()))
|
|
|
|
|
ilg.BeginExceptionBlock();
|
|
|
|
|
exceptionHandlersStack.Push(ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return list.Count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isSameTryBlock(ExceptionHandler ex1, ExceptionHandler ex2) {
|
|
|
|
|
return ex1.TryStart == ex2.TryStart && ex1.TryEnd == ex2.TryEnd;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-27 07:48:47 +08:00
|
|
|
|
void initInstrToIndex() {
|
|
|
|
|
instrToIndex = new Dictionary<Instruction, int>(allInstructions.Count);
|
|
|
|
|
for (int i = 0; i < allInstructions.Count; i++)
|
|
|
|
|
instrToIndex[allInstructions[i]] = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void initLocals() {
|
|
|
|
|
locals = new List<LocalBuilder>();
|
2012-11-20 00:58:34 +08:00
|
|
|
|
foreach (var local in methodInfo.methodDef.Body.Variables)
|
2012-11-09 10:45:48 +08:00
|
|
|
|
locals.Add(ilg.DeclareLocal(Resolver.getRtType(local.Type), local.Type.RemoveModifiers().IsPinned));
|
2011-09-27 07:48:47 +08:00
|
|
|
|
tempObjLocal = ilg.DeclareLocal(typeof(object));
|
|
|
|
|
tempObjArrayLocal = ilg.DeclareLocal(typeof(object[]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void initLabels() {
|
|
|
|
|
labels = new List<Label>(allInstructions.Count);
|
|
|
|
|
for (int i = 0; i < allInstructions.Count; i++)
|
|
|
|
|
labels.Add(ilg.DefineLabel());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Type[] getMethodParameterTypes(MethodBase method) {
|
|
|
|
|
var list = new List<Type>();
|
|
|
|
|
if (ResolverUtils.hasThis(method))
|
|
|
|
|
list.Add(method.DeclaringType);
|
|
|
|
|
|
|
|
|
|
foreach (var param in method.GetParameters())
|
|
|
|
|
list.Add(param.ParameterType);
|
|
|
|
|
|
|
|
|
|
thisArgIndex = list.Count;
|
|
|
|
|
list.Add(methodsRewriter.GetType());
|
|
|
|
|
|
|
|
|
|
return list.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void writeSpecialInstr(Instruction instr, Operand operand) {
|
|
|
|
|
BindingFlags flags;
|
|
|
|
|
switch (operand.type) {
|
|
|
|
|
case Operand.Type.ThisArg:
|
|
|
|
|
ilg.Emit(convertOpCode(instr.OpCode), (short)thisArgIndex);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Operand.Type.TempObj:
|
|
|
|
|
ilg.Emit(convertOpCode(instr.OpCode), tempObjLocal);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Operand.Type.TempObjArray:
|
|
|
|
|
ilg.Emit(convertOpCode(instr.OpCode), tempObjArrayLocal);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Operand.Type.OurMethod:
|
|
|
|
|
flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
|
|
|
|
|
var methodName = (string)operand.data;
|
|
|
|
|
var ourMethod = methodsRewriter.GetType().GetMethod(methodName, flags);
|
|
|
|
|
if (ourMethod == null)
|
|
|
|
|
throw new ApplicationException(string.Format("Could not find method {0}", methodName));
|
|
|
|
|
ilg.Emit(convertOpCode(instr.OpCode), ourMethod);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Operand.Type.NewMethod:
|
|
|
|
|
var methodBase = (MethodBase)operand.data;
|
|
|
|
|
var delegateType = methodsRewriter.getDelegateType(methodBase);
|
|
|
|
|
flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
|
|
|
|
|
var invokeMethod = delegateType.GetMethod("Invoke", flags);
|
|
|
|
|
ilg.Emit(convertOpCode(instr.OpCode), invokeMethod);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Operand.Type.ReflectionType:
|
|
|
|
|
ilg.Emit(convertOpCode(instr.OpCode), (Type)operand.data);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new ApplicationException(string.Format("Unknown operand type: {0}", operand.type));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label getLabel(Instruction target) {
|
|
|
|
|
return labels[instrToIndex[target]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label[] getLabels(Instruction[] targets) {
|
|
|
|
|
var labels = new Label[targets.Length];
|
|
|
|
|
for (int i = 0; i < labels.Length; i++)
|
|
|
|
|
labels[i] = getLabel(targets[i]);
|
|
|
|
|
return labels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void writeInstr(Instruction instr) {
|
|
|
|
|
var opcode = convertOpCode(instr.OpCode);
|
|
|
|
|
switch (instr.OpCode.OperandType) {
|
|
|
|
|
case OperandType.InlineNone:
|
|
|
|
|
ilg.Emit(opcode);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineBrTarget:
|
|
|
|
|
case OperandType.ShortInlineBrTarget:
|
|
|
|
|
ilg.Emit(opcode, getLabel((Instruction)instr.Operand));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineSwitch:
|
|
|
|
|
ilg.Emit(opcode, getLabels((Instruction[])instr.Operand));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.ShortInlineI:
|
|
|
|
|
if (instr.OpCode.Code == Code.Ldc_I4_S)
|
|
|
|
|
ilg.Emit(opcode, (sbyte)instr.Operand);
|
|
|
|
|
else
|
|
|
|
|
ilg.Emit(opcode, (byte)instr.Operand);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineI:
|
|
|
|
|
ilg.Emit(opcode, (int)instr.Operand);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineI8:
|
|
|
|
|
ilg.Emit(opcode, (long)instr.Operand);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineR:
|
|
|
|
|
ilg.Emit(opcode, (double)instr.Operand);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.ShortInlineR:
|
|
|
|
|
ilg.Emit(opcode, (float)instr.Operand);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineString:
|
|
|
|
|
ilg.Emit(opcode, (string)instr.Operand);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineTok:
|
|
|
|
|
case OperandType.InlineType:
|
|
|
|
|
case OperandType.InlineMethod:
|
|
|
|
|
case OperandType.InlineField:
|
2012-11-01 05:44:54 +08:00
|
|
|
|
var obj = Resolver.getRtObject((ITokenOperand)instr.Operand);
|
2011-09-27 07:48:47 +08:00
|
|
|
|
if (obj is ConstructorInfo)
|
|
|
|
|
ilg.Emit(opcode, (ConstructorInfo)obj);
|
|
|
|
|
else if (obj is MethodInfo)
|
|
|
|
|
ilg.Emit(opcode, (MethodInfo)obj);
|
|
|
|
|
else if (obj is FieldInfo)
|
|
|
|
|
ilg.Emit(opcode, (FieldInfo)obj);
|
|
|
|
|
else if (obj is Type)
|
|
|
|
|
ilg.Emit(opcode, (Type)obj);
|
|
|
|
|
else
|
2011-10-03 15:36:46 +08:00
|
|
|
|
throw new ApplicationException(string.Format("Unknown type: {0}", (obj == null ? obj : obj.GetType())));
|
2011-09-27 07:48:47 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineVar:
|
2012-11-02 14:36:02 +08:00
|
|
|
|
ilg.Emit(opcode, checked((short)((IVariable)instr.Operand).Index));
|
2011-09-27 07:48:47 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.ShortInlineVar:
|
2012-11-02 14:36:02 +08:00
|
|
|
|
ilg.Emit(opcode, checked((byte)((IVariable)instr.Operand).Index));
|
2011-09-27 07:48:47 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OperandType.InlineSig: //TODO:
|
|
|
|
|
default:
|
|
|
|
|
throw new ApplicationException(string.Format("Unknown OperandType {0}", instr.OpCode.OperandType));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ROpCode convertOpCode(OpCode opcode) {
|
2011-09-29 16:55:28 +08:00
|
|
|
|
ROpCode ropcode;
|
2012-11-22 16:14:51 +08:00
|
|
|
|
if (dot10ToReflection.TryGetValue(opcode, out ropcode))
|
2011-09-29 16:55:28 +08:00
|
|
|
|
return ropcode;
|
|
|
|
|
return ROpCodes.Nop;
|
2011-09-27 07:48:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|