/Mono.Cecil.Cil/CodeReader.cs
http://github.com/jbevain/cecil · C# · 666 lines · 542 code · 114 blank · 10 comment · 86 complexity · d56b062e59bc0c825ae6aae86603c05c MD5 · raw file
- //
- // Author:
- // Jb Evain (jbevain@gmail.com)
- //
- // Copyright (c) 2008 - 2015 Jb Evain
- // Copyright (c) 2008 - 2011 Novell, Inc.
- //
- // Licensed under the MIT/X11 license.
- //
- using System;
- using Mono.Cecil.PE;
- using Mono.Collections.Generic;
- using RVA = System.UInt32;
- namespace Mono.Cecil.Cil {
- sealed class CodeReader : BinaryStreamReader {
- readonly internal MetadataReader reader;
- int start;
- MethodDefinition method;
- MethodBody body;
- int Offset {
- get { return Position - start; }
- }
- public CodeReader (MetadataReader reader)
- : base (reader.image.Stream.value)
- {
- this.reader = reader;
- }
- public int MoveTo (MethodDefinition method)
- {
- this.method = method;
- this.reader.context = method;
- var position = this.Position;
- this.Position = (int) reader.image.ResolveVirtualAddress ((uint) method.RVA);
- return position;
- }
- public void MoveBackTo (int position)
- {
- this.reader.context = null;
- this.Position = position;
- }
- public MethodBody ReadMethodBody (MethodDefinition method)
- {
- var position = MoveTo (method);
- this.body = new MethodBody (method);
- ReadMethodBody ();
- MoveBackTo (position);
- return this.body;
- }
- public int ReadCodeSize (MethodDefinition method)
- {
- var position = MoveTo (method);
- var code_size = ReadCodeSize ();
- MoveBackTo (position);
- return code_size;
- }
- int ReadCodeSize ()
- {
- var flags = ReadByte ();
- switch (flags & 0x3) {
- case 0x2: // tiny
- return flags >> 2;
- case 0x3: // fat
- Advance (-1 + 2 + 2); // go back, 2 bytes flags, 2 bytes stack size
- return (int) ReadUInt32 ();
- default:
- throw new InvalidOperationException ();
- }
- }
- void ReadMethodBody ()
- {
- var flags = ReadByte ();
- switch (flags & 0x3) {
- case 0x2: // tiny
- body.code_size = flags >> 2;
- body.MaxStackSize = 8;
- ReadCode ();
- break;
- case 0x3: // fat
- Advance (-1);
- ReadFatMethod ();
- break;
- default:
- throw new InvalidOperationException ();
- }
- var symbol_reader = reader.module.symbol_reader;
- if (symbol_reader != null && method.debug_info == null)
- method.debug_info = symbol_reader.Read (method);
- if (method.debug_info != null)
- ReadDebugInfo ();
- }
- void ReadFatMethod ()
- {
- var flags = ReadUInt16 ();
- body.max_stack_size = ReadUInt16 ();
- body.code_size = (int) ReadUInt32 ();
- body.local_var_token = new MetadataToken (ReadUInt32 ());
- body.init_locals = (flags & 0x10) != 0;
- if (body.local_var_token.RID != 0)
- body.variables = ReadVariables (body.local_var_token);
- ReadCode ();
- if ((flags & 0x8) != 0)
- ReadSection ();
- }
- public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token)
- {
- var position = reader.position;
- var variables = reader.ReadVariables (local_var_token);
- reader.position = position;
- return variables;
- }
- void ReadCode ()
- {
- start = Position;
- var code_size = body.code_size;
- if (code_size < 0 || Length <= (uint) (code_size + Position))
- code_size = 0;
- var end = start + code_size;
- var instructions = body.instructions = new InstructionCollection (method, (code_size + 1) / 2);
- while (Position < end) {
- var offset = Position - start;
- var opcode = ReadOpCode ();
- var current = new Instruction (offset, opcode);
- if (opcode.OperandType != OperandType.InlineNone)
- current.operand = ReadOperand (current);
- instructions.Add (current);
- }
- ResolveBranches (instructions);
- }
- OpCode ReadOpCode ()
- {
- var il_opcode = ReadByte ();
- return il_opcode != 0xfe
- ? OpCodes.OneByteOpCode [il_opcode]
- : OpCodes.TwoBytesOpCode [ReadByte ()];
- }
- object ReadOperand (Instruction instruction)
- {
- switch (instruction.opcode.OperandType) {
- case OperandType.InlineSwitch:
- var length = ReadInt32 ();
- var base_offset = Offset + (4 * length);
- var branches = new int [length];
- for (int i = 0; i < length; i++)
- branches [i] = base_offset + ReadInt32 ();
- return branches;
- case OperandType.ShortInlineBrTarget:
- return ReadSByte () + Offset;
- case OperandType.InlineBrTarget:
- return ReadInt32 () + Offset;
- case OperandType.ShortInlineI:
- if (instruction.opcode == OpCodes.Ldc_I4_S)
- return ReadSByte ();
- return ReadByte ();
- case OperandType.InlineI:
- return ReadInt32 ();
- case OperandType.ShortInlineR:
- return ReadSingle ();
- case OperandType.InlineR:
- return ReadDouble ();
- case OperandType.InlineI8:
- return ReadInt64 ();
- case OperandType.ShortInlineVar:
- return GetVariable (ReadByte ());
- case OperandType.InlineVar:
- return GetVariable (ReadUInt16 ());
- case OperandType.ShortInlineArg:
- return GetParameter (ReadByte ());
- case OperandType.InlineArg:
- return GetParameter (ReadUInt16 ());
- case OperandType.InlineSig:
- return GetCallSite (ReadToken ());
- case OperandType.InlineString:
- return GetString (ReadToken ());
- case OperandType.InlineTok:
- case OperandType.InlineType:
- case OperandType.InlineMethod:
- case OperandType.InlineField:
- return reader.LookupToken (ReadToken ());
- default:
- throw new NotSupportedException ();
- }
- }
- public string GetString (MetadataToken token)
- {
- return reader.image.UserStringHeap.Read (token.RID);
- }
- public ParameterDefinition GetParameter (int index)
- {
- return body.GetParameter (index);
- }
- public VariableDefinition GetVariable (int index)
- {
- return body.GetVariable (index);
- }
- public CallSite GetCallSite (MetadataToken token)
- {
- return reader.ReadCallSite (token);
- }
- void ResolveBranches (Collection<Instruction> instructions)
- {
- var items = instructions.items;
- var size = instructions.size;
- for (int i = 0; i < size; i++) {
- var instruction = items [i];
- switch (instruction.opcode.OperandType) {
- case OperandType.ShortInlineBrTarget:
- case OperandType.InlineBrTarget:
- instruction.operand = GetInstruction ((int) instruction.operand);
- break;
- case OperandType.InlineSwitch:
- var offsets = (int []) instruction.operand;
- var branches = new Instruction [offsets.Length];
- for (int j = 0; j < offsets.Length; j++)
- branches [j] = GetInstruction (offsets [j]);
- instruction.operand = branches;
- break;
- }
- }
- }
- Instruction GetInstruction (int offset)
- {
- return GetInstruction (body.Instructions, offset);
- }
- static Instruction GetInstruction (Collection<Instruction> instructions, int offset)
- {
- var size = instructions.size;
- var items = instructions.items;
- if (offset < 0 || offset > items [size - 1].offset)
- return null;
- int min = 0;
- int max = size - 1;
- while (min <= max) {
- int mid = min + ((max - min) / 2);
- var instruction = items [mid];
- var instruction_offset = instruction.offset;
- if (offset == instruction_offset)
- return instruction;
- if (offset < instruction_offset)
- max = mid - 1;
- else
- min = mid + 1;
- }
- return null;
- }
- void ReadSection ()
- {
- Align (4);
- const byte fat_format = 0x40;
- const byte more_sects = 0x80;
- var flags = ReadByte ();
- if ((flags & fat_format) == 0)
- ReadSmallSection ();
- else
- ReadFatSection ();
- if ((flags & more_sects) != 0)
- ReadSection ();
- }
- void ReadSmallSection ()
- {
- var count = ReadByte () / 12;
- Advance (2);
- ReadExceptionHandlers (
- count,
- () => (int) ReadUInt16 (),
- () => (int) ReadByte ());
- }
- void ReadFatSection ()
- {
- Advance (-1);
- var count = (ReadInt32 () >> 8) / 24;
- ReadExceptionHandlers (
- count,
- ReadInt32,
- ReadInt32);
- }
- // inline ?
- void ReadExceptionHandlers (int count, Func<int> read_entry, Func<int> read_length)
- {
- for (int i = 0; i < count; i++) {
- var handler = new ExceptionHandler (
- (ExceptionHandlerType) (read_entry () & 0x7));
- handler.TryStart = GetInstruction (read_entry ());
- handler.TryEnd = GetInstruction (handler.TryStart.Offset + read_length ());
- handler.HandlerStart = GetInstruction (read_entry ());
- handler.HandlerEnd = GetInstruction (handler.HandlerStart.Offset + read_length ());
- ReadExceptionHandlerSpecific (handler);
- this.body.ExceptionHandlers.Add (handler);
- }
- }
- void ReadExceptionHandlerSpecific (ExceptionHandler handler)
- {
- switch (handler.HandlerType) {
- case ExceptionHandlerType.Catch:
- handler.CatchType = (TypeReference) reader.LookupToken (ReadToken ());
- break;
- case ExceptionHandlerType.Filter:
- handler.FilterStart = GetInstruction (ReadInt32 ());
- break;
- default:
- Advance (4);
- break;
- }
- }
- public MetadataToken ReadToken ()
- {
- return new MetadataToken (ReadUInt32 ());
- }
- void ReadDebugInfo ()
- {
- if (method.debug_info.sequence_points != null)
- ReadSequencePoints ();
- if (method.debug_info.scope != null)
- ReadScope (method.debug_info.scope);
- if (method.custom_infos != null)
- ReadCustomDebugInformations (method);
- }
- void ReadCustomDebugInformations (MethodDefinition method)
- {
- var custom_infos = method.custom_infos;
- for (int i = 0; i < custom_infos.Count; i++) {
- var state_machine_scope = custom_infos [i] as StateMachineScopeDebugInformation;
- if (state_machine_scope != null)
- ReadStateMachineScope (state_machine_scope);
- var async_method = custom_infos [i] as AsyncMethodBodyDebugInformation;
- if (async_method != null)
- ReadAsyncMethodBody (async_method);
- }
- }
- void ReadAsyncMethodBody (AsyncMethodBodyDebugInformation async_method)
- {
- if (async_method.catch_handler.Offset > -1)
- async_method.catch_handler = new InstructionOffset (GetInstruction (async_method.catch_handler.Offset));
- if (!async_method.yields.IsNullOrEmpty ())
- for (int i = 0; i < async_method.yields.Count; i++)
- async_method.yields [i] = new InstructionOffset (GetInstruction (async_method.yields [i].Offset));
- if (!async_method.resumes.IsNullOrEmpty ())
- for (int i = 0; i < async_method.resumes.Count; i++)
- async_method.resumes [i] = new InstructionOffset (GetInstruction (async_method.resumes [i].Offset));
- }
- void ReadStateMachineScope (StateMachineScopeDebugInformation state_machine_scope)
- {
- if (state_machine_scope.scopes.IsNullOrEmpty ())
- return;
- foreach (var scope in state_machine_scope.scopes) {
- scope.start = new InstructionOffset (GetInstruction (scope.start.Offset));
- var end_instruction = GetInstruction (scope.end.Offset);
- scope.end = end_instruction == null
- ? new InstructionOffset ()
- : new InstructionOffset (end_instruction);
- }
- }
- void ReadSequencePoints ()
- {
- var symbol = method.debug_info;
- for (int i = 0; i < symbol.sequence_points.Count; i++) {
- var sequence_point = symbol.sequence_points [i];
- var instruction = GetInstruction (sequence_point.Offset);
- if (instruction != null)
- sequence_point.offset = new InstructionOffset (instruction);
- }
- }
- void ReadScopes (Collection<ScopeDebugInformation> scopes)
- {
- for (int i = 0; i < scopes.Count; i++)
- ReadScope (scopes [i]);
- }
- void ReadScope (ScopeDebugInformation scope)
- {
- var start_instruction = GetInstruction (scope.Start.Offset);
- if (start_instruction != null)
- scope.Start = new InstructionOffset (start_instruction);
- var end_instruction = GetInstruction (scope.End.Offset);
- scope.End = end_instruction != null
- ? new InstructionOffset (end_instruction)
- : new InstructionOffset ();
- if (!scope.variables.IsNullOrEmpty ()) {
- for (int i = 0; i < scope.variables.Count; i++) {
- var variable_info = scope.variables [i];
- var variable = GetVariable (variable_info.Index);
- if (variable != null)
- variable_info.index = new VariableIndex (variable);
- }
- }
- if (!scope.scopes.IsNullOrEmpty ())
- ReadScopes (scope.scopes);
- }
- public ByteBuffer PatchRawMethodBody (MethodDefinition method, CodeWriter writer, out int code_size, out MetadataToken local_var_token)
- {
- var position = MoveTo (method);
- var buffer = new ByteBuffer ();
- var flags = ReadByte ();
- switch (flags & 0x3) {
- case 0x2: // tiny
- buffer.WriteByte (flags);
- local_var_token = MetadataToken.Zero;
- code_size = flags >> 2;
- PatchRawCode (buffer, code_size, writer);
- break;
- case 0x3: // fat
- Advance (-1);
- PatchRawFatMethod (buffer, writer, out code_size, out local_var_token);
- break;
- default:
- throw new NotSupportedException ();
- }
- MoveBackTo (position);
- return buffer;
- }
- void PatchRawFatMethod (ByteBuffer buffer, CodeWriter writer, out int code_size, out MetadataToken local_var_token)
- {
- var flags = ReadUInt16 ();
- buffer.WriteUInt16 (flags);
- buffer.WriteUInt16 (ReadUInt16 ());
- code_size = ReadInt32 ();
- buffer.WriteInt32 (code_size);
- local_var_token = ReadToken ();
- if (local_var_token.RID > 0) {
- var variables = ReadVariables (local_var_token);
- buffer.WriteUInt32 (variables != null
- ? writer.GetStandAloneSignature (variables).ToUInt32 ()
- : 0);
- } else
- buffer.WriteUInt32 (0);
- PatchRawCode (buffer, code_size, writer);
- if ((flags & 0x8) != 0)
- PatchRawSection (buffer, writer.metadata);
- }
- void PatchRawCode (ByteBuffer buffer, int code_size, CodeWriter writer)
- {
- var metadata = writer.metadata;
- buffer.WriteBytes (ReadBytes (code_size));
- var end = buffer.position;
- buffer.position -= code_size;
- while (buffer.position < end) {
- OpCode opcode;
- var il_opcode = buffer.ReadByte ();
- if (il_opcode != 0xfe) {
- opcode = OpCodes.OneByteOpCode [il_opcode];
- } else {
- var il_opcode2 = buffer.ReadByte ();
- opcode = OpCodes.TwoBytesOpCode [il_opcode2];
- }
- switch (opcode.OperandType) {
- case OperandType.ShortInlineI:
- case OperandType.ShortInlineBrTarget:
- case OperandType.ShortInlineVar:
- case OperandType.ShortInlineArg:
- buffer.position += 1;
- break;
- case OperandType.InlineVar:
- case OperandType.InlineArg:
- buffer.position += 2;
- break;
- case OperandType.InlineBrTarget:
- case OperandType.ShortInlineR:
- case OperandType.InlineI:
- buffer.position += 4;
- break;
- case OperandType.InlineI8:
- case OperandType.InlineR:
- buffer.position += 8;
- break;
- case OperandType.InlineSwitch:
- var length = buffer.ReadInt32 ();
- buffer.position += length * 4;
- break;
- case OperandType.InlineString:
- var @string = GetString (new MetadataToken (buffer.ReadUInt32 ()));
- buffer.position -= 4;
- buffer.WriteUInt32 (
- new MetadataToken (
- TokenType.String,
- metadata.user_string_heap.GetStringIndex (@string)).ToUInt32 ());
- break;
- case OperandType.InlineSig:
- var call_site = GetCallSite (new MetadataToken (buffer.ReadUInt32 ()));
- buffer.position -= 4;
- buffer.WriteUInt32 (writer.GetStandAloneSignature (call_site).ToUInt32 ());
- break;
- case OperandType.InlineTok:
- case OperandType.InlineType:
- case OperandType.InlineMethod:
- case OperandType.InlineField:
- var provider = reader.LookupToken (new MetadataToken (buffer.ReadUInt32 ()));
- buffer.position -= 4;
- buffer.WriteUInt32 (metadata.LookupToken (provider).ToUInt32 ());
- break;
- }
- }
- }
- void PatchRawSection (ByteBuffer buffer, MetadataBuilder metadata)
- {
- var position = Position;
- Align (4);
- buffer.WriteBytes (Position - position);
- const byte fat_format = 0x40;
- const byte more_sects = 0x80;
- var flags = ReadByte ();
- if ((flags & fat_format) == 0) {
- buffer.WriteByte (flags);
- PatchRawSmallSection (buffer, metadata);
- } else
- PatchRawFatSection (buffer, metadata);
- if ((flags & more_sects) != 0)
- PatchRawSection (buffer, metadata);
- }
- void PatchRawSmallSection (ByteBuffer buffer, MetadataBuilder metadata)
- {
- var length = ReadByte ();
- buffer.WriteByte (length);
- Advance (2);
- buffer.WriteUInt16 (0);
- var count = length / 12;
- PatchRawExceptionHandlers (buffer, metadata, count, false);
- }
- void PatchRawFatSection (ByteBuffer buffer, MetadataBuilder metadata)
- {
- Advance (-1);
- var length = ReadInt32 ();
- buffer.WriteInt32 (length);
- var count = (length >> 8) / 24;
- PatchRawExceptionHandlers (buffer, metadata, count, true);
- }
- void PatchRawExceptionHandlers (ByteBuffer buffer, MetadataBuilder metadata, int count, bool fat_entry)
- {
- const int fat_entry_size = 16;
- const int small_entry_size = 6;
- for (int i = 0; i < count; i++) {
- ExceptionHandlerType handler_type;
- if (fat_entry) {
- var type = ReadUInt32 ();
- handler_type = (ExceptionHandlerType) (type & 0x7);
- buffer.WriteUInt32 (type);
- } else {
- var type = ReadUInt16 ();
- handler_type = (ExceptionHandlerType) (type & 0x7);
- buffer.WriteUInt16 (type);
- }
- buffer.WriteBytes (ReadBytes (fat_entry ? fat_entry_size : small_entry_size));
- switch (handler_type) {
- case ExceptionHandlerType.Catch:
- var exception = reader.LookupToken (ReadToken ());
- buffer.WriteUInt32 (metadata.LookupToken (exception).ToUInt32 ());
- break;
- default:
- buffer.WriteUInt32 (ReadUInt32 ());
- break;
- }
- }
- }
- }
- }