CodeReader.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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. using System;
  29. using Mono.Cecil.PE;
  30. using Mono.Collections.Generic;
  31. using RVA = System.UInt32;
  32. namespace Mono.Cecil.Cil
  33. {
  34. sealed class CodeReader : ByteBuffer
  35. {
  36. readonly internal MetadataReader reader;
  37. int start;
  38. Section code_section;
  39. MethodDefinition method;
  40. MethodBody body;
  41. int Offset
  42. {
  43. get { return base.position - start; }
  44. }
  45. public CodeReader(Section section, MetadataReader reader)
  46. : base(section.Data)
  47. {
  48. this.code_section = section;
  49. this.reader = reader;
  50. }
  51. public MethodBody ReadMethodBody(MethodDefinition method)
  52. {
  53. this.method = method;
  54. this.body = new MethodBody(method);
  55. reader.context = method;
  56. ReadMethodBody();
  57. return this.body;
  58. }
  59. public void MoveTo(int rva)
  60. {
  61. if (!IsInSection(rva))
  62. {
  63. code_section = reader.image.GetSectionAtVirtualAddress((uint)rva);
  64. Reset(code_section.Data);
  65. }
  66. base.position = rva - (int)code_section.VirtualAddress;
  67. }
  68. bool IsInSection(int rva)
  69. {
  70. return code_section.VirtualAddress <= rva && rva < code_section.VirtualAddress + code_section.SizeOfRawData;
  71. }
  72. void ReadMethodBody()
  73. {
  74. MoveTo(method.RVA);
  75. var flags = ReadByte();
  76. switch (flags & 0x3)
  77. {
  78. case 0x2: // tiny
  79. body.code_size = flags >> 2;
  80. body.MaxStackSize = 8;
  81. ReadCode();
  82. break;
  83. case 0x3: // fat
  84. base.position--;
  85. ReadFatMethod();
  86. break;
  87. default:
  88. throw new InvalidOperationException();
  89. }
  90. var symbol_reader = reader.module.symbol_reader;
  91. if (symbol_reader != null)
  92. {
  93. var instructions = body.Instructions;
  94. symbol_reader.Read(body, offset => GetInstruction(instructions, offset));
  95. }
  96. }
  97. void ReadFatMethod()
  98. {
  99. var flags = ReadUInt16();
  100. body.max_stack_size = ReadUInt16();
  101. body.code_size = (int)ReadUInt32();
  102. body.local_var_token = new MetadataToken(ReadUInt32());
  103. body.init_locals = (flags & 0x10) != 0;
  104. if (body.local_var_token.RID != 0)
  105. body.variables = ReadVariables(body.local_var_token);
  106. ReadCode();
  107. if ((flags & 0x8) != 0)
  108. ReadSection();
  109. }
  110. public VariableDefinitionCollection ReadVariables(MetadataToken local_var_token)
  111. {
  112. var position = reader.position;
  113. var variables = reader.ReadVariables(local_var_token);
  114. reader.position = position;
  115. return variables;
  116. }
  117. void ReadCode()
  118. {
  119. start = position;
  120. var code_size = body.code_size;
  121. if (code_size < 0 || buffer.Length <= (uint)(code_size + position))
  122. code_size = 0;
  123. var end = start + code_size;
  124. var instructions = body.instructions = new InstructionCollection((code_size + 1) / 2);
  125. while (position < end)
  126. {
  127. var offset = base.position - start;
  128. var opcode = ReadOpCode();
  129. var current = new Instruction(offset, opcode);
  130. if (opcode.OperandType != OperandType.InlineNone)
  131. current.operand = ReadOperand(current);
  132. instructions.Add(current);
  133. }
  134. ResolveBranches(instructions);
  135. }
  136. OpCode ReadOpCode()
  137. {
  138. var il_opcode = ReadByte();
  139. return il_opcode != 0xfe
  140. ? OpCodes.OneByteOpCode[il_opcode]
  141. : OpCodes.TwoBytesOpCode[ReadByte()];
  142. }
  143. object ReadOperand(Instruction instruction)
  144. {
  145. switch (instruction.opcode.OperandType)
  146. {
  147. case OperandType.InlineSwitch:
  148. var length = ReadInt32();
  149. var base_offset = Offset + (4 * length);
  150. var branches = new int[length];
  151. for (int i = 0; i < length; i++)
  152. branches[i] = base_offset + ReadInt32();
  153. return branches;
  154. case OperandType.ShortInlineBrTarget:
  155. return ReadSByte() + Offset;
  156. case OperandType.InlineBrTarget:
  157. return ReadInt32() + Offset;
  158. case OperandType.ShortInlineI:
  159. if (instruction.opcode == OpCodes.Ldc_I4_S)
  160. return ReadSByte();
  161. return ReadByte();
  162. case OperandType.InlineI:
  163. return ReadInt32();
  164. case OperandType.ShortInlineR:
  165. return ReadSingle();
  166. case OperandType.InlineR:
  167. return ReadDouble();
  168. case OperandType.InlineI8:
  169. return ReadInt64();
  170. case OperandType.ShortInlineVar:
  171. return GetVariable(ReadByte());
  172. case OperandType.InlineVar:
  173. return GetVariable(ReadUInt16());
  174. case OperandType.ShortInlineArg:
  175. return GetParameter(ReadByte());
  176. case OperandType.InlineArg:
  177. return GetParameter(ReadUInt16());
  178. case OperandType.InlineSig:
  179. return GetCallSite(ReadToken());
  180. case OperandType.InlineString:
  181. return GetString(ReadToken());
  182. case OperandType.InlineTok:
  183. case OperandType.InlineType:
  184. case OperandType.InlineMethod:
  185. case OperandType.InlineField:
  186. return reader.LookupToken(ReadToken());
  187. default:
  188. throw new NotSupportedException();
  189. }
  190. }
  191. public string GetString(MetadataToken token)
  192. {
  193. return reader.image.UserStringHeap.Read(token.RID);
  194. }
  195. public ParameterDefinition GetParameter(int index)
  196. {
  197. return Mixin.GetParameter(body, index);
  198. }
  199. public VariableDefinition GetVariable(int index)
  200. {
  201. return Mixin.GetVariable(body, index);
  202. }
  203. public CallSite GetCallSite(MetadataToken token)
  204. {
  205. return reader.ReadCallSite(token);
  206. }
  207. void ResolveBranches(Collection<Instruction> instructions)
  208. {
  209. var items = instructions.items;
  210. var size = instructions.size;
  211. for (int i = 0; i < size; i++)
  212. {
  213. var instruction = items[i];
  214. switch (instruction.opcode.OperandType)
  215. {
  216. case OperandType.ShortInlineBrTarget:
  217. case OperandType.InlineBrTarget:
  218. instruction.operand = GetInstruction((int)instruction.operand);
  219. break;
  220. case OperandType.InlineSwitch:
  221. var offsets = (int[])instruction.operand;
  222. var branches = new Instruction[offsets.Length];
  223. for (int j = 0; j < offsets.Length; j++)
  224. branches[j] = GetInstruction(offsets[j]);
  225. instruction.operand = branches;
  226. break;
  227. }
  228. }
  229. }
  230. Instruction GetInstruction(int offset)
  231. {
  232. return GetInstruction(body.Instructions, offset);
  233. }
  234. static Instruction GetInstruction(Collection<Instruction> instructions, int offset)
  235. {
  236. var size = instructions.size;
  237. var items = instructions.items;
  238. if (offset < 0 || offset > items[size - 1].offset)
  239. return null;
  240. int min = 0;
  241. int max = size - 1;
  242. while (min <= max)
  243. {
  244. int mid = min + ((max - min) / 2);
  245. var instruction = items[mid];
  246. var instruction_offset = instruction.offset;
  247. if (offset == instruction_offset)
  248. return instruction;
  249. if (offset < instruction_offset)
  250. max = mid - 1;
  251. else
  252. min = mid + 1;
  253. }
  254. return null;
  255. }
  256. void ReadSection()
  257. {
  258. Align(4);
  259. const byte fat_format = 0x40;
  260. const byte more_sects = 0x80;
  261. var flags = ReadByte();
  262. if ((flags & fat_format) == 0)
  263. ReadSmallSection();
  264. else
  265. ReadFatSection();
  266. if ((flags & more_sects) != 0)
  267. ReadSection();
  268. }
  269. void ReadSmallSection()
  270. {
  271. var count = ReadByte() / 12;
  272. Advance(2);
  273. ReadExceptionHandlers(
  274. count,
  275. () => (int)ReadUInt16(),
  276. () => (int)ReadByte());
  277. }
  278. void ReadFatSection()
  279. {
  280. position--;
  281. var count = (ReadInt32() >> 8) / 24;
  282. ReadExceptionHandlers(
  283. count,
  284. ReadInt32,
  285. ReadInt32);
  286. }
  287. // inline ?
  288. void ReadExceptionHandlers(int count, Func<int> read_entry, Func<int> read_length)
  289. {
  290. for (int i = 0; i < count; i++)
  291. {
  292. var handler = new ExceptionHandler(
  293. (ExceptionHandlerType)(read_entry() & 0x7));
  294. handler.TryStart = GetInstruction(read_entry());
  295. handler.TryEnd = GetInstruction(handler.TryStart.Offset + read_length());
  296. handler.HandlerStart = GetInstruction(read_entry());
  297. handler.HandlerEnd = GetInstruction(handler.HandlerStart.Offset + read_length());
  298. ReadExceptionHandlerSpecific(handler);
  299. this.body.ExceptionHandlers.Add(handler);
  300. }
  301. }
  302. void ReadExceptionHandlerSpecific(ExceptionHandler handler)
  303. {
  304. switch (handler.HandlerType)
  305. {
  306. case ExceptionHandlerType.Catch:
  307. handler.CatchType = (TypeReference)reader.LookupToken(ReadToken());
  308. break;
  309. case ExceptionHandlerType.Filter:
  310. handler.FilterStart = GetInstruction(ReadInt32());
  311. break;
  312. default:
  313. Advance(4);
  314. break;
  315. }
  316. }
  317. void Align(int align)
  318. {
  319. align--;
  320. Advance(((position + align) & ~align) - position);
  321. }
  322. public MetadataToken ReadToken()
  323. {
  324. return new MetadataToken(ReadUInt32());
  325. }
  326. }
  327. }