Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.Cecil/0.9.5/Mono.Cecil-0.9.5/Mono.Cecil/Mono.Cecil.Cil/CodeReader.cs @ 15888

Last change on this file since 15888 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

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