blob: 9705d6a9b30fc2a81b50f2ead2ccfc6c2ea06911 [file] [log] [blame] [raw]
//
// CodeDisassembler.cs
//
// Author:
// Jb Evain (jbevain@gmail.com)
//
// (C) 2005 Jb Evain
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Text;
using System.Text.RegularExpressions;
namespace Mono.Disassembler {
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
class CodeDisassembler : BaseCodeVisitor {
ReflectionDisassembler m_reflectDis;
CilWriter m_writer;
static readonly int [] m_methodVisibilityVals = new int [] {
(int) MethodAttributes.Private, (int) MethodAttributes.FamANDAssem, (int) MethodAttributes.Assem,
(int) MethodAttributes.Family, (int) MethodAttributes.FamORAssem, (int) MethodAttributes.Public
};
static readonly string [] m_methodVisibilityMap = new string [] {
"private", "famandassem", "assembly", "family", "famorassem", "public"
};
void WriteMethodVisibility (MethodAttributes attributes)
{
m_writer.WriteFlags ((int) attributes, (int) (ushort) MethodAttributes.MemberAccessMask,
m_methodVisibilityVals, m_methodVisibilityMap);
}
static readonly int [] m_methodFlagsVals = new int [] {
(int) MethodAttributes.Static, (int) MethodAttributes.Final, (int) MethodAttributes.Virtual, (int) MethodAttributes.HideBySig,
(int) MethodAttributes.Abstract, (int) MethodAttributes.SpecialName, (int) MethodAttributes.PInvokeImpl,
(int) MethodAttributes.UnmanagedExport, (int) MethodAttributes.RTSpecialName, (int) MethodAttributes.RequireSecObject,
(int) MethodAttributes.NewSlot
};
static readonly string [] m_methodFlagsMap = new string [] {
"static", "final", "virtual", "hidebysig", "abstract",
"specialname", "pinvokeimpl", "export", "rtspecialname",
"requiresecobj", "newslot"
};
void WriteMethodAttributes (MethodAttributes attributes)
{
m_writer.WriteFlags ((int) attributes, m_methodFlagsVals, m_methodFlagsMap);
}
static readonly int [] m_methodCallConvVals = new int [] {
(int) MethodCallingConvention.Default, (int) MethodCallingConvention.C, (int) MethodCallingConvention.StdCall,
(int) MethodCallingConvention.ThisCall, (int) MethodCallingConvention.FastCall, (int) MethodCallingConvention.VarArg,
/* ma.Generic */
};
static readonly string [] m_methodCallConvMap = new string [] {
"default", "unmanaged cdecl", "unmanaged stdcall",
"unmanaged thiscall", "unmanaged fastcall", "vararg"
};
void WriteMethodCallingConvention (MethodCallingConvention cc)
{
m_writer.WriteFlags ((int) cc, m_methodCallConvVals, m_methodCallConvMap);
}
static readonly int [] m_methodImplVals = new int [] {
(int) MethodImplAttributes.IL, (int) MethodImplAttributes.Native, (int) MethodImplAttributes.OPTIL,
(int) MethodImplAttributes.Runtime
};
static readonly string [] m_methodImplMap = new string [] {
"cil", "native", "optil", "runtime"
};
void WriteMethodCodeType (MethodImplAttributes attributes)
{
m_writer.WriteFlags ((int) attributes, (int) MethodImplAttributes.CodeTypeMask, m_methodImplVals, m_methodImplMap);
}
static readonly int [] m_methodManagedTypeVals = new int [] {
(int) MethodImplAttributes.Managed, (int) MethodImplAttributes.Unmanaged
};
static readonly string [] m_methodManagedTypeMap = new string [] {
"managed", "unmanaged"
};
void WriteMethodManagedType (MethodImplAttributes attributes)
{
m_writer.WriteFlags ((int) attributes, (int) MethodImplAttributes.ManagedMask, m_methodManagedTypeVals, m_methodManagedTypeMap);
}
public CodeDisassembler (ReflectionDisassembler dis)
{
m_reflectDis = dis;
}
public CilWriter Writer {
get { return m_writer; }
set { m_writer = value; }
}
public bool NoAlias {
get { return m_reflectDis.NoAlias; }
set { m_reflectDis.NoAlias = value ; }
}
//FIXME: Move to ReflectionDisassembler ?
public void DisassembleMethod (MethodDefinition method, CilWriter writer)
{
m_writer = writer;
// .method public hidebysig specialname
// instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed
//
// write method header
m_writer.Write (".method ");
//emit flags
WriteMethodVisibility (method.Attributes);
WriteMethodAttributes (method.Attributes);
if(method.IsPInvokeImpl) {
//Console.Error.WriteLine(method.PInvokeInfo);
String module = method.PInvokeInfo.Module.Name;
String symbol = method.PInvokeInfo.EntryPoint;
m_writer.BaseWriter.Write("(");
m_writer.BaseWriter.Write(String.Format("\"{0}\" as \"{1}\"", module, symbol));
m_writer.BaseWriter.Write(")");
}
m_writer.WriteLine ();
m_writer.Indent ();
if (method.HasThis)
m_writer.Write ("instance ");
//call convention
WriteMethodCallingConvention (method.CallingConvention);
//return type
//method.ReturnType.ReturnType.Accept (m_reflectDis);
//FIXME: another visitor for printing names (refs to methoddef/typedef/typeref etc
m_writer.Write (Formater.Signature (method.ReturnType.ReturnType, false, !NoAlias));
m_writer.Write (Formater.Escape(method.Name));
//( params )
m_writer.BaseWriter.Write (" (");
method.Parameters.Accept (m_reflectDis);
m_writer.BaseWriter.Write (") ");
//cil managed
WriteMethodCodeType (method.ImplAttributes);
WriteMethodManagedType (method.ImplAttributes);
m_writer.Unindent ();
m_writer.OpenBlock ();
m_reflectDis.VisitCustomAttributeCollection (method.CustomAttributes);
m_reflectDis.VisitSecurityDeclarationCollection (method.SecurityDeclarations);
if (method.HasBody)
VisitMethodBody (method.Body);
m_writer.CloseBlock ();
}
public override void VisitMethodBody (MethodBody body)
{
m_writer.WriteLine ("// Method begins at RVA 0x{0}",
body.Method.RVA.Value.ToString ("x4"));
m_writer.WriteLine ("// Code size {0} (0x{0:x})", body.CodeSize);
m_writer.WriteLine (".maxstack {0}", body.MaxStack);
if (body.Method.DeclaringType.Module.Assembly.EntryPoint != null &&
body.Method.DeclaringType.Module.Assembly.EntryPoint.ToString () == body.Method.ToString ()) {
m_writer.WriteLine (".entrypoint");
}
VisitVariableDefinitionCollection (body.Variables);
VisitInstructionCollection (body.Instructions);
}
public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
{
if (variables.Count == 0)
return;
//FIXME: check for 'init' from method header
m_writer.WriteLine (".locals init (");
m_writer.Indent ();
for (int i = 0; i < variables.Count; i++) {
VisitVariableDefinition (variables [i]);
m_writer.WriteLine (i < variables.Count - 1 ? "," : String.Empty);
}
m_writer.Unindent ();
m_writer.WriteLine (")");
}
public override void VisitVariableDefinition (VariableDefinition var)
{
//m_writer.Write (Formater.Signature (var.VariableType));
m_writer.Write (Formater.Signature (var.VariableType, false, !NoAlias));
m_writer.Write (Formater.Escape (var.Name));
}
string Label (Instruction instr)
{
return string.Concat ("IL_", instr.Offset.ToString ("x4"));
}
void CheckExceptionHandlers (Instruction instr)
{
}
//FIXME: Where should this be? move to ReflectionDisassembler ?
public void VisitMemberReference (MemberReference member)
{
if (member == null)
return;
MethodReference method = member as MethodReference;
if (method != null) {
StringBuilder sb = new StringBuilder (" ");
if (method.HasThis)
sb.Append ("instance ");
if (method.ReturnType.ReturnType == null)
Console.WriteLine ("method = {0}, rt = {1}", method.Name, method.ReturnType);
sb.Append (Formater.Signature (method.ReturnType.ReturnType, false, !NoAlias));
sb.Append (" ");
//sb.Append (Formater.Signature (method.DeclaringType));
sb.Append(Formater.Signature(method.DeclaringType, false, !NoAlias));
sb.Append ("::");
sb.Append (Formater.Escape (method.Name));
sb.Append ("(");
for (int i = 0; i < method.Parameters.Count; i++) {
ParameterDefinition param = method.Parameters[i];
TypeReference param_type = param.ParameterType;
//Console.WriteLine("debug: param.Name: \"{0}\", param.IsLcid: \"{1}\", param.Attributes = {2}",
// param.Name, param.IsLcid, param.Attributes);
//Console.WriteLine("param_type is {0}, param_type.Module == null ? {1}, param_type.IsValueType: {2}, param_type.IsNested: {3}, param_type.HasCustomAttributes: {4}, param_type.FullName: \"{5}\"",
// param_type.GetType(), param_type.Module == null, param_type.IsValueType, param_type.IsNested, param_type.HasCustomAttributes, param_type.FullName);
sb.Append (Formater.Signature (param_type, param_type.Module == null, !NoAlias));
if (i < method.Parameters.Count - 1)
sb.Append (", ");
}
sb.Append (")");
m_writer.Write (sb.ToString ());
return;
}
FieldReference field = member as FieldReference;
if (field != null) {
StringBuilder sb = new StringBuilder (" ");
sb.Append (Formater.Signature (field.FieldType, false, !NoAlias));
sb.Append (" ");
sb.Append (Formater.Signature (field.DeclaringType, true));
sb.Append ("::");
sb.Append (Formater.Escape (field.Name));
m_writer.Write (sb.ToString ());
return;
}
}
public override void VisitInstructionCollection (InstructionCollection instructions)
{
foreach (Instruction instr in instructions) {
CheckExceptionHandlers (instr);
m_writer.Write ("{0}: {1}",
Label (instr), instr.OpCode.Name);
switch (instr.OpCode.OperandType) {
case OperandType.InlineNone :
break;
case OperandType.InlineSwitch :
m_writer.WriteLine ("(");
m_writer.Indent ();
Instruction [] targets = (Instruction []) instr.Operand;
for (int i = 0; i < targets.Length; i++) {
m_writer.Write (Label (targets [i]));
m_writer.WriteLine (i < targets.Length - 1 ? "," : string.Empty);
}
m_writer.Unindent ();
m_writer.Write (")");
break;
case OperandType.ShortInlineBrTarget :
case OperandType.InlineBrTarget :
m_writer.WriteLine (Label ((Instruction) instr.Operand));
break;
case OperandType.ShortInlineVar :
case OperandType.InlineVar :
VariableDefinition var = (VariableDefinition) instr.Operand;
if (var.Name != null && var.Name.Length > 0)
m_writer.Write (var.Name);
else
m_writer.Write (instructions.Container.Variables.IndexOf (var));
break;
case OperandType.ShortInlineParam :
case OperandType.InlineParam :
ParameterDefinition param = (ParameterDefinition) instr.Operand;
if (param.Name != null && param.Name.Length > 0)
m_writer.Write (Formater.Escape (param.Name));
else
m_writer.Write (instructions.Container.Method.Parameters.IndexOf (param));
break;
case OperandType.InlineI :
case OperandType.InlineI8 :
case OperandType.InlineR :
case OperandType.ShortInlineI :
case OperandType.ShortInlineR :
m_writer.Write (instr.Operand.ToString ());
break;
case OperandType.InlineString :
// FIXME: Handle unicode strings with non zero high byte
//StringBuilder sb = new StringBuilder (instr.Operand.ToString ());
// FIXME: extract to a method
//sb.Replace ("\"", "\\\"");
//sb.Replace ("\t", "\\t");
//sb.Replace ("\r", "\\r");
//sb.Replace ("\n", "\\n");
String s = Formater.LiteralReplace(instr.Operand.ToString());
m_writer.Write(String.Concat("\"", s, "\""));
break;
case OperandType.InlineType :
m_writer.Write (Formater.Signature ((TypeReference) instr.Operand));
break;
case OperandType.InlineMethod :
case OperandType.InlineField :
VisitMemberReference (instr.Operand as MemberReference);
break;
case OperandType.InlineTok :
if (instr.Operand is TypeReference) {
m_writer.Write (Formater.Signature ((TypeReference) instr.Operand, true));
} else if (instr.Operand is MemberReference) {
if (instr.Operand is FieldReference)
m_writer.Write ("field");
//FIXME: method
VisitMemberReference ((MemberReference) instr.Operand);
}
break;
}
m_writer.WriteLine ();
}
}
}
}