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