PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/ApiChange.Api/src/Introspection/CorFlagsReader.cs

http://apichange.codeplex.com
C# | 271 lines | 152 code | 33 blank | 86 comment | 28 complexity | 5cab1dcccc74b79d4625b20717eed777 MD5 | raw file
  1. // This code is inspired from the Gallio project and modified a bit to suit our needs better.
  2. // Copyright 2005-2010 Gallio Project - http://www.gallio.org/
  3. // Portions Copyright 2000-2004 Jonathan de Halleux
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Globalization;
  19. using System.IO;
  20. using System.Reflection;
  21. using System.Text;
  22. namespace ApiChange.Api.Introspection
  23. {
  24. /// <summary>
  25. /// Provides basic information about an Assembly derived from its metadata.
  26. /// </summary>
  27. public class CorFlagsReader
  28. {
  29. private readonly ushort majorRuntimeVersion;
  30. private readonly ushort minorRuntimeVersion;
  31. private readonly CorFlags corflags;
  32. private readonly PEFormat peFormat;
  33. private CorFlagsReader(ushort majorRuntimeVersion, ushort minorRuntimeVersion, CorFlags corflags, PEFormat peFormat)
  34. {
  35. this.majorRuntimeVersion = majorRuntimeVersion;
  36. this.minorRuntimeVersion = minorRuntimeVersion;
  37. this.corflags = corflags;
  38. this.peFormat = peFormat;
  39. }
  40. /// <summary>
  41. /// Gets the major version of the CLI runtime required for the assembly.
  42. /// </summary>
  43. /// <remarks>
  44. /// <para>
  45. /// Typical runtime versions:
  46. /// <list type="bullet">
  47. /// <item>0 for unmanaged PE Files.</item>
  48. /// <item>2.0: .Net 1.0 / .Net 1.1</item>
  49. /// <item>2.5: .Net 2.0 / .Net 3.0 / .Net 3.5 / .Net 4.0</item>
  50. /// </list>
  51. /// </para>
  52. /// </remarks>
  53. public int MajorRuntimeVersion
  54. {
  55. get { return majorRuntimeVersion; }
  56. }
  57. /// <summary>
  58. /// Gets the minor version of the CLI runtime required for the assembly.
  59. /// </summary>
  60. /// <remarks>
  61. /// <para>
  62. /// Typical runtime versions:
  63. /// <list type="bullet">
  64. /// <item>0 for unmanaged PE Files.</item>
  65. /// <item>2.0: .Net 1.0 / .Net 1.1</item>
  66. /// <item>2.5: .Net 2.0 / .Net 3.0 / .Net 3.5 / .Net 4.0</item>
  67. /// </list>
  68. /// </para>
  69. /// </remarks>
  70. public int MinorRuntimeVersion
  71. {
  72. get { return minorRuntimeVersion; }
  73. }
  74. /// <summary>
  75. /// Gets the processor architecture required for the assembly or
  76. /// </summary>
  77. public ProcessorArchitecture ProcessorArchitecture
  78. {
  79. get
  80. {
  81. if (peFormat == PEFormat.PE32Plus)
  82. return ProcessorArchitecture.Amd64;
  83. if ((corflags & CorFlags.F32BitsRequired) != 0 || !IsPureIL)
  84. return ProcessorArchitecture.X86;
  85. return ProcessorArchitecture.MSIL;
  86. }
  87. }
  88. /// <summary>
  89. /// If true the PE files does not contain any unmanaged parts. Otherwise it is a managed C++ Target.
  90. /// </summary>
  91. public bool IsPureIL
  92. {
  93. get
  94. {
  95. return (corflags & CorFlags.ILOnly) == CorFlags.ILOnly;
  96. }
  97. }
  98. /// <summary>
  99. /// Returns true when the assembly is signed.
  100. /// </summary>
  101. public bool IsSigned
  102. {
  103. get
  104. {
  105. return (corflags & CorFlags.StrongNameSigned) == CorFlags.StrongNameSigned;
  106. }
  107. }
  108. /// <summary>
  109. /// Read the PE file
  110. /// </summary>
  111. /// <param name="fileName">PE file to read from</param>
  112. /// <returns>null if the PE file was not valid.
  113. /// an instance of the CorFlagsReader class containing the requested data.</returns>
  114. /// <exception cref="FileNotFoundException">When the file could not be found.</exception>
  115. public static CorFlagsReader ReadAssemblyMetadata(string fileName)
  116. {
  117. if (String.IsNullOrEmpty(fileName))
  118. {
  119. throw new ArgumentException("file name was null or empty");
  120. }
  121. using (var fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
  122. {
  123. return ReadAssemblyMetadata(fStream);
  124. }
  125. }
  126. /// <summary>
  127. /// Read the PE file
  128. /// </summary>
  129. /// <param name="stream">PE file stream to read from.</param>
  130. /// <returns>null if the PE file was not valid.
  131. /// an instance of the CorFlagsReader class containing the requested data.</returns>
  132. public static CorFlagsReader ReadAssemblyMetadata(Stream stream)
  133. {
  134. if (stream == null)
  135. {
  136. throw new ArgumentNullException("stream");
  137. }
  138. long length = stream.Length;
  139. if (length < 0x40)
  140. return null;
  141. BinaryReader reader = new BinaryReader(stream);
  142. // Read the pointer to the PE header.
  143. stream.Position = 0x3c;
  144. uint peHeaderPtr = reader.ReadUInt32();
  145. if (peHeaderPtr == 0)
  146. peHeaderPtr = 0x80;
  147. // Ensure there is at least enough room for the following structures:
  148. // 24 byte PE Signature & Header
  149. // 28 byte Standard Fields (24 bytes for PE32+)
  150. // 68 byte NT Fields (88 bytes for PE32+)
  151. // >= 128 byte Data Dictionary Table
  152. if (peHeaderPtr > length - 256)
  153. return null;
  154. // Check the PE signature. Should equal 'PE\0\0'.
  155. stream.Position = peHeaderPtr;
  156. uint peSignature = reader.ReadUInt32();
  157. if (peSignature != 0x00004550)
  158. return null;
  159. // Read PE header fields.
  160. ushort machine = reader.ReadUInt16();
  161. ushort numberOfSections = reader.ReadUInt16();
  162. uint timeStamp = reader.ReadUInt32();
  163. uint symbolTablePtr = reader.ReadUInt32();
  164. uint numberOfSymbols = reader.ReadUInt32();
  165. ushort optionalHeaderSize = reader.ReadUInt16();
  166. ushort characteristics = reader.ReadUInt16();
  167. // Read PE magic number from Standard Fields to determine format.
  168. PEFormat peFormat = (PEFormat)reader.ReadUInt16();
  169. if (peFormat != PEFormat.PE32 && peFormat != PEFormat.PE32Plus)
  170. return null;
  171. // Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
  172. // When this is non-zero then the file contains CLI data otherwise not.
  173. stream.Position = peHeaderPtr + (peFormat == PEFormat.PE32 ? 232 : 248);
  174. uint cliHeaderRva = reader.ReadUInt32();
  175. if (cliHeaderRva == 0)
  176. return new CorFlagsReader(0,0,0, peFormat);
  177. // Read section headers. Each one is 40 bytes.
  178. // 8 byte Name
  179. // 4 byte Virtual Size
  180. // 4 byte Virtual Address
  181. // 4 byte Data Size
  182. // 4 byte Data Pointer
  183. // ... total of 40 bytes
  184. uint sectionTablePtr = peHeaderPtr + 24 + optionalHeaderSize;
  185. Section[] sections = new Section[numberOfSections];
  186. for (int i = 0; i < numberOfSections; i++)
  187. {
  188. stream.Position = sectionTablePtr + i * 40 + 8;
  189. Section section = new Section();
  190. section.VirtualSize = reader.ReadUInt32();
  191. section.VirtualAddress = reader.ReadUInt32();
  192. reader.ReadUInt32();
  193. section.Pointer = reader.ReadUInt32();
  194. sections[i] = section;
  195. }
  196. // Read parts of the CLI header.
  197. uint cliHeaderPtr = ResolveRva(sections, cliHeaderRva);
  198. if (cliHeaderPtr == 0)
  199. return null;
  200. stream.Position = cliHeaderPtr + 4;
  201. ushort majorRuntimeVersion = reader.ReadUInt16();
  202. ushort minorRuntimeVersion = reader.ReadUInt16();
  203. uint metadataRva = reader.ReadUInt32();
  204. uint metadataSize = reader.ReadUInt32();
  205. CorFlags corflags = (CorFlags)reader.ReadUInt32();
  206. // Done.
  207. return new CorFlagsReader(majorRuntimeVersion, minorRuntimeVersion, corflags, peFormat);
  208. }
  209. private static uint ResolveRva(Section[] sections, uint rva)
  210. {
  211. foreach (Section section in sections)
  212. {
  213. if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
  214. return rva - section.VirtualAddress + section.Pointer;
  215. }
  216. return 0;
  217. }
  218. private enum PEFormat : ushort
  219. {
  220. PE32 = 0x10b,
  221. PE32Plus = 0x20b
  222. }
  223. [Flags]
  224. private enum CorFlags : uint
  225. {
  226. F32BitsRequired = 2,
  227. ILOnly = 1,
  228. StrongNameSigned = 8,
  229. TrackDebugData = 0x10000
  230. }
  231. private class Section
  232. {
  233. public uint VirtualAddress;
  234. public uint VirtualSize;
  235. public uint Pointer;
  236. }
  237. }
  238. }