PageRenderTime 61ms CodeModel.GetById 2ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/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
  1//
  2// Author:
  3//   Jb Evain (jbevain@gmail.com)
  4//
  5// Copyright (c) 2008 - 2015 Jb Evain
  6// Copyright (c) 2008 - 2011 Novell, Inc.
  7//
  8// Licensed under the MIT/X11 license.
  9//
 10
 11using System;
 12
 13using Mono.Cecil.PE;
 14using Mono.Collections.Generic;
 15
 16using RVA = System.UInt32;
 17
 18namespace Mono.Cecil.Cil {
 19
 20	sealed class CodeReader : BinaryStreamReader {
 21
 22		readonly internal MetadataReader reader;
 23
 24		int start;
 25
 26		MethodDefinition method;
 27		MethodBody body;
 28
 29		int Offset {
 30			get { return Position - start; }
 31		}
 32
 33		public CodeReader (MetadataReader reader)
 34			: base (reader.image.Stream.value)
 35		{
 36			this.reader = reader;
 37		}
 38
 39		public int MoveTo (MethodDefinition method)
 40		{
 41			this.method = method;
 42			this.reader.context = method;
 43			var position = this.Position;
 44			this.Position = (int) reader.image.ResolveVirtualAddress ((uint) method.RVA);
 45			return position;
 46		}
 47
 48		public void MoveBackTo (int position)
 49		{
 50			this.reader.context = null;
 51			this.Position = position;
 52		}
 53
 54		public MethodBody ReadMethodBody (MethodDefinition method)
 55		{
 56			var position = MoveTo (method);
 57			this.body = new MethodBody (method);
 58
 59			ReadMethodBody ();
 60
 61			MoveBackTo (position);
 62			return this.body;
 63		}
 64
 65		public int ReadCodeSize (MethodDefinition method)
 66		{
 67			var position = MoveTo (method);
 68
 69			var code_size = ReadCodeSize ();
 70
 71			MoveBackTo (position);
 72			return code_size;
 73		}
 74
 75		int ReadCodeSize ()
 76		{
 77			var flags = ReadByte ();
 78			switch (flags & 0x3) {
 79			case 0x2: // tiny
 80				return flags >> 2;
 81			case 0x3: // fat
 82				Advance (-1 + 2 + 2); // go back, 2 bytes flags, 2 bytes stack size
 83				return (int) ReadUInt32 ();
 84			default:
 85				throw new InvalidOperationException ();
 86			}
 87		}
 88
 89		void ReadMethodBody ()
 90		{
 91			var flags = ReadByte ();
 92			switch (flags & 0x3) {
 93			case 0x2: // tiny
 94				body.code_size = flags >> 2;
 95				body.MaxStackSize = 8;
 96				ReadCode ();
 97				break;
 98			case 0x3: // fat
 99				Advance (-1);
100				ReadFatMethod ();
101				break;
102			default:
103				throw new InvalidOperationException ();
104			}
105
106			var symbol_reader = reader.module.symbol_reader;
107
108			if (symbol_reader != null && method.debug_info == null)
109				method.debug_info = symbol_reader.Read (method);
110
111			if (method.debug_info != null)
112				ReadDebugInfo ();
113		}
114
115		void ReadFatMethod ()
116		{
117			var flags = ReadUInt16 ();
118			body.max_stack_size = ReadUInt16 ();
119			body.code_size = (int) ReadUInt32 ();
120			body.local_var_token = new MetadataToken (ReadUInt32 ());
121			body.init_locals = (flags & 0x10) != 0;
122
123			if (body.local_var_token.RID != 0)
124				body.variables = ReadVariables (body.local_var_token);
125
126			ReadCode ();
127
128			if ((flags & 0x8) != 0)
129				ReadSection ();
130		}
131
132		public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token)
133		{
134			var position = reader.position;
135			var variables = reader.ReadVariables (local_var_token);
136			reader.position = position;
137
138			return variables;
139		}
140
141		void ReadCode ()
142		{
143			start = Position;
144			var code_size = body.code_size;
145
146			if (code_size < 0 || Length <= (uint) (code_size + Position))
147				code_size = 0;
148
149			var end = start + code_size;
150			var instructions = body.instructions = new InstructionCollection (method, (code_size + 1) / 2);
151
152			while (Position < end) {
153				var offset = Position - start;
154				var opcode = ReadOpCode ();
155				var current = new Instruction (offset, opcode);
156
157				if (opcode.OperandType != OperandType.InlineNone)
158					current.operand = ReadOperand (current);
159
160				instructions.Add (current);
161			}
162
163			ResolveBranches (instructions);
164		}
165
166		OpCode ReadOpCode ()
167		{
168			var il_opcode = ReadByte ();
169			return il_opcode != 0xfe
170				? OpCodes.OneByteOpCode [il_opcode]
171				: OpCodes.TwoBytesOpCode [ReadByte ()];
172		}
173
174		object ReadOperand (Instruction instruction)
175		{
176			switch (instruction.opcode.OperandType) {
177			case OperandType.InlineSwitch:
178				var length = ReadInt32 ();
179				var base_offset = Offset + (4 * length);
180				var branches = new int [length];
181				for (int i = 0; i < length; i++)
182					branches [i] = base_offset + ReadInt32 ();
183				return branches;
184			case OperandType.ShortInlineBrTarget:
185				return ReadSByte () + Offset;
186			case OperandType.InlineBrTarget:
187				return ReadInt32 () + Offset;
188			case OperandType.ShortInlineI:
189				if (instruction.opcode == OpCodes.Ldc_I4_S)
190					return ReadSByte ();
191
192				return ReadByte ();
193			case OperandType.InlineI:
194				return ReadInt32 ();
195			case OperandType.ShortInlineR:
196				return ReadSingle ();
197			case OperandType.InlineR:
198				return ReadDouble ();
199			case OperandType.InlineI8:
200				return ReadInt64 ();
201			case OperandType.ShortInlineVar:
202				return GetVariable (ReadByte ());
203			case OperandType.InlineVar:
204				return GetVariable (ReadUInt16 ());
205			case OperandType.ShortInlineArg:
206				return GetParameter (ReadByte ());
207			case OperandType.InlineArg:
208				return GetParameter (ReadUInt16 ());
209			case OperandType.InlineSig:
210				return GetCallSite (ReadToken ());
211			case OperandType.InlineString:
212				return GetString (ReadToken ());
213			case OperandType.InlineTok:
214			case OperandType.InlineType:
215			case OperandType.InlineMethod:
216			case OperandType.InlineField:
217				return reader.LookupToken (ReadToken ());
218			default:
219				throw new NotSupportedException ();
220			}
221		}
222
223		public string GetString (MetadataToken token)
224		{
225			return reader.image.UserStringHeap.Read (token.RID);
226		}
227
228		public ParameterDefinition GetParameter (int index)
229		{
230			return body.GetParameter (index);
231		}
232
233		public VariableDefinition GetVariable (int index)
234		{
235			return body.GetVariable (index);
236		}
237
238		public CallSite GetCallSite (MetadataToken token)
239		{
240			return reader.ReadCallSite (token);
241		}
242
243		void ResolveBranches (Collection<Instruction> instructions)
244		{
245			var items = instructions.items;
246			var size = instructions.size;
247
248			for (int i = 0; i < size; i++) {
249				var instruction = items [i];
250				switch (instruction.opcode.OperandType) {
251				case OperandType.ShortInlineBrTarget:
252				case OperandType.InlineBrTarget:
253					instruction.operand = GetInstruction ((int) instruction.operand);
254					break;
255				case OperandType.InlineSwitch:
256					var offsets = (int []) instruction.operand;
257					var branches = new Instruction [offsets.Length];
258					for (int j = 0; j < offsets.Length; j++)
259						branches [j] = GetInstruction (offsets [j]);
260
261					instruction.operand = branches;
262					break;
263				}
264			}
265		}
266
267		Instruction GetInstruction (int offset)
268		{
269			return GetInstruction (body.Instructions, offset);
270		}
271
272		static Instruction GetInstruction (Collection<Instruction> instructions, int offset)
273		{
274			var size = instructions.size;
275			var items = instructions.items;
276			if (offset < 0 || offset > items [size - 1].offset)
277				return null;
278
279			int min = 0;
280			int max = size - 1;
281			while (min <= max) {
282				int mid = min + ((max - min) / 2);
283				var instruction = items [mid];
284				var instruction_offset = instruction.offset;
285
286				if (offset == instruction_offset)
287					return instruction;
288
289				if (offset < instruction_offset)
290					max = mid - 1;
291				else
292					min = mid + 1;
293			}
294
295			return null;
296		}
297
298		void ReadSection ()
299		{
300			Align (4);
301
302			const byte fat_format = 0x40;
303			const byte more_sects = 0x80;
304
305			var flags = ReadByte ();
306			if ((flags & fat_format) == 0)
307				ReadSmallSection ();
308			else
309				ReadFatSection ();
310
311			if ((flags & more_sects) != 0)
312				ReadSection ();
313		}
314
315		void ReadSmallSection ()
316		{
317			var count = ReadByte () / 12;
318			Advance (2);
319
320			ReadExceptionHandlers (
321				count,
322				() => (int) ReadUInt16 (),
323				() => (int) ReadByte ());
324		}
325
326		void ReadFatSection ()
327		{
328			Advance (-1);
329			var count = (ReadInt32 () >> 8) / 24;
330
331			ReadExceptionHandlers (
332				count,
333				ReadInt32,
334				ReadInt32);
335		}
336
337		// inline ?
338		void ReadExceptionHandlers (int count, Func<int> read_entry, Func<int> read_length)
339		{
340			for (int i = 0; i < count; i++) {
341				var handler = new ExceptionHandler (
342					(ExceptionHandlerType) (read_entry () & 0x7));
343
344				handler.TryStart = GetInstruction (read_entry ());
345				handler.TryEnd = GetInstruction (handler.TryStart.Offset + read_length ());
346
347				handler.HandlerStart = GetInstruction (read_entry ());
348				handler.HandlerEnd = GetInstruction (handler.HandlerStart.Offset + read_length ());
349
350				ReadExceptionHandlerSpecific (handler);
351
352				this.body.ExceptionHandlers.Add (handler);
353			}
354		}
355
356		void ReadExceptionHandlerSpecific (ExceptionHandler handler)
357		{
358			switch (handler.HandlerType) {
359			case ExceptionHandlerType.Catch:
360				handler.CatchType = (TypeReference) reader.LookupToken (ReadToken ());
361				break;
362			case ExceptionHandlerType.Filter:
363				handler.FilterStart = GetInstruction (ReadInt32 ());
364				break;
365			default:
366				Advance (4);
367				break;
368			}
369		}
370
371		public MetadataToken ReadToken ()
372		{
373			return new MetadataToken (ReadUInt32 ());
374		}
375
376		void ReadDebugInfo ()
377		{
378			if (method.debug_info.sequence_points != null)
379				ReadSequencePoints ();
380
381			if (method.debug_info.scope != null)
382				ReadScope (method.debug_info.scope);
383
384			if (method.custom_infos != null)
385				ReadCustomDebugInformations (method);
386		}
387
388		void ReadCustomDebugInformations (MethodDefinition method)
389		{
390			var custom_infos = method.custom_infos;
391
392			for (int i = 0; i < custom_infos.Count; i++) {
393				var state_machine_scope = custom_infos [i] as StateMachineScopeDebugInformation;
394				if (state_machine_scope != null)
395					ReadStateMachineScope (state_machine_scope);
396
397				var async_method = custom_infos [i] as AsyncMethodBodyDebugInformation;
398				if (async_method != null)
399					ReadAsyncMethodBody (async_method);
400			}
401		}
402
403		void ReadAsyncMethodBody (AsyncMethodBodyDebugInformation async_method)
404		{
405			if (async_method.catch_handler.Offset > -1)
406				async_method.catch_handler = new InstructionOffset (GetInstruction (async_method.catch_handler.Offset));
407
408			if (!async_method.yields.IsNullOrEmpty ())
409				for (int i = 0; i < async_method.yields.Count; i++)
410					async_method.yields [i] = new InstructionOffset (GetInstruction (async_method.yields [i].Offset));
411
412			if (!async_method.resumes.IsNullOrEmpty ())
413				for (int i = 0; i < async_method.resumes.Count; i++)
414					async_method.resumes [i] = new InstructionOffset (GetInstruction (async_method.resumes [i].Offset));
415		}
416
417		void ReadStateMachineScope (StateMachineScopeDebugInformation state_machine_scope)
418		{
419			if (state_machine_scope.scopes.IsNullOrEmpty ())
420				return;
421
422			foreach (var scope in state_machine_scope.scopes) {
423				scope.start = new InstructionOffset (GetInstruction (scope.start.Offset));
424
425				var end_instruction = GetInstruction (scope.end.Offset);
426				scope.end = end_instruction == null
427					? new InstructionOffset ()
428					: new InstructionOffset (end_instruction);
429			}
430		}
431
432		void ReadSequencePoints ()
433		{
434			var symbol = method.debug_info;
435
436			for (int i = 0; i < symbol.sequence_points.Count; i++) {
437				var sequence_point = symbol.sequence_points [i];
438				var instruction = GetInstruction (sequence_point.Offset);
439				if (instruction != null)
440					sequence_point.offset = new InstructionOffset (instruction);
441			}
442		}
443
444		void ReadScopes (Collection<ScopeDebugInformation> scopes)
445		{
446			for (int i = 0; i < scopes.Count; i++)
447				ReadScope (scopes [i]);
448		}
449
450		void ReadScope (ScopeDebugInformation scope)
451		{
452			var start_instruction = GetInstruction (scope.Start.Offset);
453			if (start_instruction != null)
454				scope.Start = new InstructionOffset (start_instruction);
455
456			var end_instruction = GetInstruction (scope.End.Offset);
457			scope.End = end_instruction != null
458				? new InstructionOffset (end_instruction)
459				: new InstructionOffset ();
460
461			if (!scope.variables.IsNullOrEmpty ()) {
462				for (int i = 0; i < scope.variables.Count; i++) {
463					var variable_info = scope.variables [i];
464					var variable = GetVariable (variable_info.Index);
465					if (variable != null)
466						variable_info.index = new VariableIndex (variable);
467				}
468			}
469
470			if (!scope.scopes.IsNullOrEmpty ())
471				ReadScopes (scope.scopes);
472		}
473
474		public ByteBuffer PatchRawMethodBody (MethodDefinition method, CodeWriter writer, out int code_size, out MetadataToken local_var_token)
475		{
476			var position = MoveTo (method);
477
478			var buffer = new ByteBuffer ();
479
480			var flags = ReadByte ();
481
482			switch (flags & 0x3) {
483			case 0x2: // tiny
484				buffer.WriteByte (flags);
485				local_var_token = MetadataToken.Zero;
486				code_size = flags >> 2;
487				PatchRawCode (buffer, code_size, writer);
488				break;
489			case 0x3: // fat
490				Advance (-1);
491				PatchRawFatMethod (buffer, writer, out code_size, out local_var_token);
492				break;
493			default:
494				throw new NotSupportedException ();
495			}
496
497			MoveBackTo (position);
498
499			return buffer;
500		}
501
502		void PatchRawFatMethod (ByteBuffer buffer, CodeWriter writer, out int code_size, out MetadataToken local_var_token)
503		{
504			var flags = ReadUInt16 ();
505			buffer.WriteUInt16 (flags);
506			buffer.WriteUInt16 (ReadUInt16 ());
507			code_size = ReadInt32 ();
508			buffer.WriteInt32 (code_size);
509			local_var_token = ReadToken ();
510
511			if (local_var_token.RID > 0) {
512				var variables = ReadVariables (local_var_token);
513				buffer.WriteUInt32 (variables != null
514					? writer.GetStandAloneSignature (variables).ToUInt32 ()
515					: 0);
516			} else
517				buffer.WriteUInt32 (0);
518
519			PatchRawCode (buffer, code_size, writer);
520
521			if ((flags & 0x8) != 0)
522				PatchRawSection (buffer, writer.metadata);
523		}
524
525		void PatchRawCode (ByteBuffer buffer, int code_size, CodeWriter writer)
526		{
527			var metadata = writer.metadata;
528			buffer.WriteBytes (ReadBytes (code_size));
529			var end = buffer.position;
530			buffer.position -= code_size;
531
532			while (buffer.position < end) {
533				OpCode opcode;
534				var il_opcode = buffer.ReadByte ();
535				if (il_opcode != 0xfe) {
536					opcode = OpCodes.OneByteOpCode [il_opcode];
537				} else {
538					var il_opcode2 = buffer.ReadByte ();
539					opcode = OpCodes.TwoBytesOpCode [il_opcode2];
540				}
541
542				switch (opcode.OperandType) {
543				case OperandType.ShortInlineI:
544				case OperandType.ShortInlineBrTarget:
545				case OperandType.ShortInlineVar:
546				case OperandType.ShortInlineArg:
547					buffer.position += 1;
548					break;
549				case OperandType.InlineVar:
550				case OperandType.InlineArg:
551					buffer.position += 2;
552					break;
553				case OperandType.InlineBrTarget:
554				case OperandType.ShortInlineR:
555				case OperandType.InlineI:
556					buffer.position += 4;
557					break;
558				case OperandType.InlineI8:
559				case OperandType.InlineR:
560					buffer.position += 8;
561					break;
562				case OperandType.InlineSwitch:
563					var length = buffer.ReadInt32 ();
564					buffer.position += length * 4;
565					break;
566				case OperandType.InlineString:
567					var @string = GetString (new MetadataToken (buffer.ReadUInt32 ()));
568					buffer.position -= 4;
569					buffer.WriteUInt32 (
570						new MetadataToken (
571							TokenType.String,
572							metadata.user_string_heap.GetStringIndex (@string)).ToUInt32 ());
573					break;
574				case OperandType.InlineSig:
575					var call_site = GetCallSite (new MetadataToken (buffer.ReadUInt32 ()));
576					buffer.position -= 4;
577					buffer.WriteUInt32 (writer.GetStandAloneSignature (call_site).ToUInt32 ());
578					break;
579				case OperandType.InlineTok:
580				case OperandType.InlineType:
581				case OperandType.InlineMethod:
582				case OperandType.InlineField:
583					var provider = reader.LookupToken (new MetadataToken (buffer.ReadUInt32 ()));
584					buffer.position -= 4;
585					buffer.WriteUInt32 (metadata.LookupToken (provider).ToUInt32 ());
586					break;
587				}
588			}
589		}
590
591		void PatchRawSection (ByteBuffer buffer, MetadataBuilder metadata)
592		{
593			var position = Position;
594			Align (4);
595			buffer.WriteBytes (Position - position);
596
597			const byte fat_format = 0x40;
598			const byte more_sects = 0x80;
599
600			var flags = ReadByte ();
601			if ((flags & fat_format) == 0) {
602				buffer.WriteByte (flags);
603				PatchRawSmallSection (buffer, metadata);
604			} else
605				PatchRawFatSection (buffer, metadata);
606
607			if ((flags & more_sects) != 0)
608				PatchRawSection (buffer, metadata);
609		}
610
611		void PatchRawSmallSection (ByteBuffer buffer, MetadataBuilder metadata)
612		{
613			var length = ReadByte ();
614			buffer.WriteByte (length);
615			Advance (2);
616
617			buffer.WriteUInt16 (0);
618
619			var count = length / 12;
620
621			PatchRawExceptionHandlers (buffer, metadata, count, false);
622		}
623
624		void PatchRawFatSection (ByteBuffer buffer, MetadataBuilder metadata)
625		{
626			Advance (-1);
627			var length = ReadInt32 ();
628			buffer.WriteInt32 (length);
629
630			var count = (length >> 8) / 24;
631
632			PatchRawExceptionHandlers (buffer, metadata, count, true);
633		}
634
635		void PatchRawExceptionHandlers (ByteBuffer buffer, MetadataBuilder metadata, int count, bool fat_entry)
636		{
637			const int fat_entry_size = 16;
638			const int small_entry_size = 6;
639
640			for (int i = 0; i < count; i++) {
641				ExceptionHandlerType handler_type;
642				if (fat_entry) {
643					var type = ReadUInt32 ();
644					handler_type = (ExceptionHandlerType) (type & 0x7);
645					buffer.WriteUInt32 (type);
646				} else {
647					var type = ReadUInt16 ();
648					handler_type = (ExceptionHandlerType) (type & 0x7);
649					buffer.WriteUInt16 (type);
650				}
651
652				buffer.WriteBytes (ReadBytes (fat_entry ? fat_entry_size : small_entry_size));
653
654				switch (handler_type) {
655				case ExceptionHandlerType.Catch:
656					var exception = reader.LookupToken (ReadToken ());
657					buffer.WriteUInt32 (metadata.LookupToken (exception).ToUInt32 ());
658					break;
659				default:
660					buffer.WriteUInt32 (ReadUInt32 ());
661					break;
662				}
663			}
664		}
665	}
666}