5 // Jb Evain (jbevain@gmail.com)
7 // Copyright (c) 2008 - 2010 Jb Evain
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using Mono.Collections.Generic;
34 using RVA = System.UInt32;
36 namespace Mono.Cecil.Cil {
38 sealed class CodeReader : ByteBuffer {
40 readonly internal MetadataReader reader;
45 MethodDefinition method;
49 get { return base.position - start; }
52 CodeReader (Section section, MetadataReader reader)
55 this.code_section = section;
59 public static CodeReader CreateCodeReader (MetadataReader metadata)
61 return new CodeReader (metadata.image.MetadataSection, metadata);
64 public MethodBody ReadMethodBody (MethodDefinition method)
67 this.body = new MethodBody (method);
69 reader.context = method;
76 public void MoveTo (int rva)
78 if (!IsInSection (rva)) {
79 code_section = reader.image.GetSectionAtVirtualAddress ((uint) rva);
80 Reset (code_section.Data);
83 base.position = rva - (int) code_section.VirtualAddress;
86 bool IsInSection (int rva)
88 return code_section.VirtualAddress <= rva && rva < code_section.VirtualAddress + code_section.SizeOfRawData;
91 void ReadMethodBody ()
95 var flags = ReadByte ();
96 switch (flags & 0x3) {
98 body.code_size = flags >> 2;
99 body.MaxStackSize = 8;
107 throw new InvalidOperationException ();
110 var symbol_reader = reader.module.SymbolReader;
112 if (symbol_reader != null) {
113 var instructions = body.Instructions;
114 symbol_reader.Read (body, offset => GetInstruction (instructions, offset));
118 void ReadFatMethod ()
120 var flags = ReadUInt16 ();
121 body.max_stack_size = ReadUInt16 ();
122 body.code_size = (int) ReadUInt32 ();
123 body.local_var_token = new MetadataToken (ReadUInt32 ());
124 body.init_locals = (flags & 0x10) != 0;
126 if (body.local_var_token.RID != 0)
127 body.variables = ReadVariables (body.local_var_token);
131 if ((flags & 0x8) != 0)
135 public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token)
137 var position = reader.position;
138 var variables = reader.ReadVariables (local_var_token);
139 reader.position = position;
147 var code_size = body.code_size;
149 if (code_size < 0 || buffer.Length <= (uint) (code_size + position))
152 var end = start + code_size;
153 var instructions = body.instructions = new InstructionCollection (code_size / 3);
155 while (position < end) {
156 var offset = base.position - start;
157 var opcode = ReadOpCode ();
158 var current = new Instruction (offset, opcode);
160 if (opcode.OperandType != OperandType.InlineNone)
161 current.operand = ReadOperand (current);
163 instructions.Add (current);
166 ResolveBranches (instructions);
171 var il_opcode = ReadByte ();
172 return il_opcode != 0xfe
173 ? OpCodes.OneByteOpCode [il_opcode]
174 : OpCodes.TwoBytesOpCode [ReadByte ()];
177 object ReadOperand (Instruction instruction)
179 switch (instruction.opcode.OperandType) {
180 case OperandType.InlineSwitch:
181 var length = ReadInt32 ();
182 var base_offset = Offset + (4 * length);
183 var branches = new int [length];
184 for (int i = 0; i < length; i++)
185 branches [i] = base_offset + ReadInt32 ();
187 case OperandType.ShortInlineBrTarget:
188 return ReadSByte () + Offset;
189 case OperandType.InlineBrTarget:
190 return ReadInt32 () + Offset;
191 case OperandType.ShortInlineI:
192 if (instruction.opcode == OpCodes.Ldc_I4_S)
196 case OperandType.InlineI:
198 case OperandType.ShortInlineR:
199 return ReadSingle ();
200 case OperandType.InlineR:
201 return ReadDouble ();
202 case OperandType.InlineI8:
204 case OperandType.ShortInlineVar:
205 return GetVariable (ReadByte ());
206 case OperandType.InlineVar:
207 return GetVariable (ReadUInt16 ());
208 case OperandType.ShortInlineArg:
209 return GetParameter (ReadByte ());
210 case OperandType.InlineArg:
211 return GetParameter (ReadUInt16 ());
212 case OperandType.InlineSig:
213 return GetCallSite (ReadToken ());
214 case OperandType.InlineString:
215 return GetString (ReadToken ());
216 case OperandType.InlineTok:
217 case OperandType.InlineType:
218 case OperandType.InlineMethod:
219 case OperandType.InlineField:
220 return reader.LookupToken (ReadToken ());
222 throw new NotSupportedException ();
226 public string GetString (MetadataToken token)
228 return reader.image.UserStringHeap.Read (token.RID);
231 public ParameterDefinition GetParameter (int index)
233 return body.GetParameter (index);
236 public VariableDefinition GetVariable (int index)
238 return body.GetVariable (index);
241 public CallSite GetCallSite (MetadataToken token)
243 return reader.ReadCallSite (token);
246 void ResolveBranches (Collection<Instruction> instructions)
248 var items = instructions.items;
249 var size = instructions.size;
251 for (int i = 0; i < size; i++) {
252 var instruction = items [i];
253 switch (instruction.opcode.OperandType) {
254 case OperandType.ShortInlineBrTarget:
255 case OperandType.InlineBrTarget:
256 instruction.operand = GetInstruction ((int) instruction.operand);
258 case OperandType.InlineSwitch:
259 var offsets = (int []) instruction.operand;
260 var branches = new Instruction [offsets.Length];
261 for (int j = 0; j < offsets.Length; j++)
262 branches [j] = GetInstruction (offsets [j]);
264 instruction.operand = branches;
270 Instruction GetInstruction (int offset)
272 return GetInstruction (body.Instructions, offset);
275 static Instruction GetInstruction (Collection<Instruction> instructions, int offset)
277 var size = instructions.size;
278 var items = instructions.items;
279 if (offset < 0 || offset > items [size - 1].offset)
285 int mid = min + ((max - min) / 2);
286 var instruction = items [mid];
287 var instruction_offset = instruction.offset;
289 if (offset == instruction_offset)
292 if (offset < instruction_offset)
305 const byte fat_format = 0x40;
306 const byte more_sects = 0x80;
308 var flags = ReadByte ();
309 if ((flags & fat_format) == 0)
314 if ((flags & more_sects) != 0)
318 void ReadSmallSection ()
320 var count = ReadByte () / 12;
323 ReadExceptionHandlers (
325 () => (int) ReadUInt16 (),
326 () => (int) ReadByte ());
329 void ReadFatSection ()
332 var count = (ReadInt32 () >> 8) / 24;
334 ReadExceptionHandlers (
341 void ReadExceptionHandlers (int count, Func<int> read_entry, Func<int> read_length)
343 for (int i = 0; i < count; i++) {
344 var handler = new ExceptionHandler (
345 (ExceptionHandlerType) (read_entry () & 0x7));
347 handler.TryStart = GetInstruction (read_entry ());
348 handler.TryEnd = GetInstruction (handler.TryStart.Offset + read_length ());
350 handler.HandlerStart = GetInstruction (read_entry ());
351 handler.HandlerEnd = GetInstruction (handler.HandlerStart.Offset + read_length ());
353 ReadExceptionHandlerSpecific (handler);
355 this.body.ExceptionHandlers.Add (handler);
359 void ReadExceptionHandlerSpecific (ExceptionHandler handler)
361 switch (handler.HandlerType) {
362 case ExceptionHandlerType.Catch:
363 handler.CatchType = (TypeReference) reader.LookupToken (ReadToken ());
365 case ExceptionHandlerType.Filter:
366 handler.FilterStart = GetInstruction (ReadInt32 ());
367 handler.FilterEnd = handler.HandlerStart.Previous;
375 void Align (int align)
378 Advance (((position + align) & ~align) - position);
381 public MetadataToken ReadToken ()
383 return new MetadataToken (ReadUInt32 ());
388 public ByteBuffer PatchRawMethodBody (MethodDefinition method, CodeWriter writer, out MethodSymbols symbols)
390 var buffer = new ByteBuffer ();
391 symbols = new MethodSymbols (method.Name);
393 this.method = method;
394 reader.context = method;
398 var flags = ReadByte ();
400 MetadataToken local_var_token;
402 switch (flags & 0x3) {
404 buffer.WriteByte (flags);
405 local_var_token = MetadataToken.Zero;
406 symbols.code_size = flags >> 2;
407 PatchRawCode (buffer, symbols.code_size, writer);
412 PatchRawFatMethod (buffer, symbols, writer, out local_var_token);
415 throw new NotSupportedException ();
418 var symbol_reader = reader.module.SymbolReader;
419 if (symbol_reader != null && writer.metadata.write_symbols) {
420 symbols.method_token = GetOriginalToken (writer.metadata, method);
421 symbols.local_var_token = local_var_token;
422 symbol_reader.Read (symbols);
428 void PatchRawFatMethod (ByteBuffer buffer, MethodSymbols symbols, CodeWriter writer, out MetadataToken local_var_token)
430 var flags = ReadUInt16 ();
431 buffer.WriteUInt16 (flags);
432 buffer.WriteUInt16 (ReadUInt16 ());
433 symbols.code_size = ReadInt32 ();
434 buffer.WriteInt32 (symbols.code_size);
435 local_var_token = ReadToken ();
437 if (local_var_token.RID > 0) {
438 var variables = symbols.variables = ReadVariables (local_var_token);
439 buffer.WriteUInt32 (variables != null
440 ? writer.GetStandAloneSignature (symbols.variables).ToUInt32 ()
443 buffer.WriteUInt32 (0);
445 PatchRawCode (buffer, symbols.code_size, writer);
447 if ((flags & 0x8) != 0)
448 PatchRawSection (buffer, writer.metadata);
451 static MetadataToken GetOriginalToken (MetadataBuilder metadata, MethodDefinition method)
453 MetadataToken original;
454 if (metadata.TryGetOriginalMethodToken (method.token, out original))
457 return MetadataToken.Zero;
460 void PatchRawCode (ByteBuffer buffer, int code_size, CodeWriter writer)
462 var metadata = writer.metadata;
463 buffer.WriteBytes (ReadBytes (code_size));
464 var end = buffer.position;
465 buffer.position -= code_size;
467 while (buffer.position < end) {
469 var il_opcode = buffer.ReadByte ();
470 if (il_opcode != 0xfe) {
471 opcode = OpCodes.OneByteOpCode [il_opcode];
473 var il_opcode2 = buffer.ReadByte ();
474 opcode = OpCodes.TwoBytesOpCode [il_opcode2];
477 switch (opcode.OperandType) {
478 case OperandType.ShortInlineI:
479 case OperandType.ShortInlineBrTarget:
480 case OperandType.ShortInlineVar:
481 case OperandType.ShortInlineArg:
482 buffer.position += 1;
484 case OperandType.InlineVar:
485 case OperandType.InlineArg:
486 buffer.position += 2;
488 case OperandType.InlineBrTarget:
489 case OperandType.ShortInlineR:
490 case OperandType.InlineI:
491 buffer.position += 4;
493 case OperandType.InlineI8:
494 case OperandType.InlineR:
495 buffer.position += 8;
497 case OperandType.InlineSwitch:
498 var length = buffer.ReadInt32 ();
499 buffer.position += length * 4;
501 case OperandType.InlineString:
502 var @string = GetString (new MetadataToken (buffer.ReadUInt32 ()));
503 buffer.position -= 4;
507 metadata.user_string_heap.GetStringIndex (@string)).ToUInt32 ());
509 case OperandType.InlineSig:
510 var call_site = GetCallSite (new MetadataToken (buffer.ReadUInt32 ()));
511 buffer.position -= 4;
512 buffer.WriteUInt32 (writer.GetStandAloneSignature (call_site).ToUInt32 ());
514 case OperandType.InlineTok:
515 case OperandType.InlineType:
516 case OperandType.InlineMethod:
517 case OperandType.InlineField:
518 var provider = reader.LookupToken (new MetadataToken (buffer.ReadUInt32 ()));
519 buffer.position -= 4;
520 buffer.WriteUInt32 (metadata.LookupToken (provider).ToUInt32 ());
526 void PatchRawSection (ByteBuffer buffer, MetadataBuilder metadata)
528 var position = base.position;
530 buffer.WriteBytes (base.position - position);
532 const byte fat_format = 0x40;
533 const byte more_sects = 0x80;
535 var flags = ReadByte ();
536 if ((flags & fat_format) == 0) {
537 buffer.WriteByte (flags);
538 PatchRawSmallSection (buffer, metadata);
540 PatchRawFatSection (buffer, metadata);
542 if ((flags & more_sects) != 0)
543 PatchRawSection (buffer, metadata);
546 void PatchRawSmallSection (ByteBuffer buffer, MetadataBuilder metadata)
548 var length = ReadByte ();
549 buffer.WriteByte (length);
552 buffer.WriteUInt16 (0);
554 var count = length / 12;
556 PatchRawExceptionHandlers (buffer, metadata, count, false);
559 void PatchRawFatSection (ByteBuffer buffer, MetadataBuilder metadata)
562 var length = ReadInt32 ();
563 buffer.WriteInt32 (length);
565 var count = (length >> 8) / 24;
567 PatchRawExceptionHandlers (buffer, metadata, count, true);
570 void PatchRawExceptionHandlers (ByteBuffer buffer, MetadataBuilder metadata, int count, bool fat_entry)
572 const int fat_entry_size = 16;
573 const int small_entry_size = 6;
575 for (int i = 0; i < count; i++) {
576 ExceptionHandlerType handler_type;
578 var type = ReadUInt32 ();
579 handler_type = (ExceptionHandlerType) (type & 0x7);
580 buffer.WriteUInt32 (type);
582 var type = ReadUInt16 ();
583 handler_type = (ExceptionHandlerType) (type & 0x7);
584 buffer.WriteUInt16 (type);
587 buffer.WriteBytes (ReadBytes (fat_entry ? fat_entry_size : small_entry_size));
589 switch (handler_type) {
590 case ExceptionHandlerType.Catch:
591 var exception = reader.LookupToken (ReadToken ());
592 buffer.WriteUInt32 (metadata.LookupToken (exception).ToUInt32 ());
595 buffer.WriteUInt32 (ReadUInt32 ());