2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Mono.Cecil / Mono.Cecil.Binary / ImageReader.cs
blobe1b2bf1c506c96953230c7be070b653ea9dc2ccc
1 //
2 // ImageReader.cs
3 //
4 // Author:
5 // Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2005 - 2007 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:
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.
29 namespace Mono.Cecil.Binary {
31 using System;
32 using System.IO;
33 using System.Text;
35 using Mono.Cecil.Metadata;
37 sealed class ImageReader : BaseImageVisitor {
39 MetadataReader m_mdReader;
40 BinaryReader m_binaryReader;
41 Image m_image;
43 public MetadataReader MetadataReader {
44 get { return m_mdReader; }
47 public Image Image {
48 get { return m_image; }
51 ImageReader (Image img, BinaryReader reader)
53 m_image = img;
54 m_binaryReader = reader;
57 static ImageReader Read (Image img, Stream stream)
59 ImageReader reader = new ImageReader (img, new BinaryReader (stream));
60 img.Accept (reader);
61 return reader;
64 public static ImageReader Read (string file)
66 if (file == null)
67 throw new ArgumentNullException ("file");
69 FileInfo fi = new FileInfo (file);
70 if (!File.Exists (fi.FullName))
71 #if CF_1_0 || CF_2_0
72 throw new FileNotFoundException (fi.FullName);
73 #else
74 throw new FileNotFoundException (string.Format ("File '{0}' not found.", fi.FullName), fi.FullName);
75 #endif
77 FileStream stream = null;
78 try {
79 stream = new FileStream (fi.FullName, FileMode.Open, FileAccess.Read, FileShare.Read);
80 return Read (new Image (fi), stream);
81 } catch (Exception e) {
82 if (stream != null)
83 stream.Close ();
84 #if CF_1_0 || CF_2_0
85 throw new BadImageFormatException ("Invalid PE file: " + file, e);
86 #else
87 throw new BadImageFormatException ("Invalid PE file", file, e);
88 #endif
92 public static ImageReader Read (byte [] image)
94 if (image == null)
95 throw new ArgumentNullException ("image");
97 if (image.Length == 0)
98 throw new ArgumentException ("Empty image array");
100 return Read (new Image (), new MemoryStream (image));
103 public static ImageReader Read (Stream stream)
105 if (stream == null)
106 throw new ArgumentNullException ("stream");
108 if (!stream.CanRead)
109 throw new ArgumentException ("Can not read from stream");
111 return Read (new Image (), stream);
114 public BinaryReader GetReader ()
116 return m_binaryReader;
119 public override void VisitImage (Image img)
121 m_mdReader = new MetadataReader (this);
124 void SetPositionToAddress (RVA address)
126 m_binaryReader.BaseStream.Position = m_image.ResolveVirtualAddress (address);
129 public override void VisitDOSHeader (DOSHeader header)
131 header.Start = m_binaryReader.ReadBytes (60);
132 header.Lfanew = m_binaryReader.ReadUInt32 ();
133 header.End = m_binaryReader.ReadBytes (64);
135 m_binaryReader.BaseStream.Position = header.Lfanew;
137 if (m_binaryReader.ReadUInt16 () != 0x4550 ||
138 m_binaryReader.ReadUInt16 () != 0)
140 throw new ImageFormatException ("Invalid PE File Signature");
143 public override void VisitPEFileHeader (PEFileHeader header)
145 header.Machine = m_binaryReader.ReadUInt16 ();
146 header.NumberOfSections = m_binaryReader.ReadUInt16 ();
147 header.TimeDateStamp = m_binaryReader.ReadUInt32 ();
148 header.PointerToSymbolTable = m_binaryReader.ReadUInt32 ();
149 header.NumberOfSymbols = m_binaryReader.ReadUInt32 ();
150 header.OptionalHeaderSize = m_binaryReader.ReadUInt16 ();
151 header.Characteristics = (ImageCharacteristics) m_binaryReader.ReadUInt16 ();
154 ulong ReadIntOrLong ()
156 return m_image.PEOptionalHeader.StandardFields.IsPE64 ?
157 m_binaryReader.ReadUInt64 () :
158 m_binaryReader.ReadUInt32 ();
161 RVA ReadRVA ()
163 return m_binaryReader.ReadUInt32 ();
166 DataDirectory ReadDataDirectory ()
168 return new DataDirectory (ReadRVA (), m_binaryReader.ReadUInt32 ());
171 public override void VisitNTSpecificFieldsHeader (PEOptionalHeader.NTSpecificFieldsHeader header)
173 header.ImageBase = ReadIntOrLong ();
174 header.SectionAlignment = m_binaryReader.ReadUInt32 ();
175 header.FileAlignment = m_binaryReader.ReadUInt32 ();
176 header.OSMajor = m_binaryReader.ReadUInt16 ();
177 header.OSMinor = m_binaryReader.ReadUInt16 ();
178 header.UserMajor = m_binaryReader.ReadUInt16 ();
179 header.UserMinor = m_binaryReader.ReadUInt16 ();
180 header.SubSysMajor = m_binaryReader.ReadUInt16 ();
181 header.SubSysMinor = m_binaryReader.ReadUInt16 ();
182 header.Reserved = m_binaryReader.ReadUInt32 ();
183 header.ImageSize = m_binaryReader.ReadUInt32 ();
184 header.HeaderSize = m_binaryReader.ReadUInt32 ();
185 header.FileChecksum = m_binaryReader.ReadUInt32 ();
186 header.SubSystem = (SubSystem) m_binaryReader.ReadUInt16 ();
187 header.DLLFlags = m_binaryReader.ReadUInt16 ();
188 header.StackReserveSize = ReadIntOrLong ();
189 header.StackCommitSize = ReadIntOrLong ();
190 header.HeapReserveSize = ReadIntOrLong ();
191 header.HeapCommitSize = ReadIntOrLong ();
192 header.LoaderFlags = m_binaryReader.ReadUInt32 ();
193 header.NumberOfDataDir = m_binaryReader.ReadUInt32 ();
196 public override void VisitStandardFieldsHeader (PEOptionalHeader.StandardFieldsHeader header)
198 header.Magic = m_binaryReader.ReadUInt16 ();
199 header.LMajor = m_binaryReader.ReadByte ();
200 header.LMinor = m_binaryReader.ReadByte ();
201 header.CodeSize = m_binaryReader.ReadUInt32 ();
202 header.InitializedDataSize = m_binaryReader.ReadUInt32 ();
203 header.UninitializedDataSize = m_binaryReader.ReadUInt32 ();
204 header.EntryPointRVA = ReadRVA ();
205 header.BaseOfCode = ReadRVA ();
206 if (!header.IsPE64)
207 header.BaseOfData = ReadRVA ();
210 public override void VisitDataDirectoriesHeader (PEOptionalHeader.DataDirectoriesHeader header)
212 header.ExportTable = ReadDataDirectory ();
213 header.ImportTable = ReadDataDirectory ();
214 header.ResourceTable = ReadDataDirectory ();
215 header.ExceptionTable = ReadDataDirectory ();
216 header.CertificateTable = ReadDataDirectory ();
217 header.BaseRelocationTable = ReadDataDirectory ();
218 header.Debug = ReadDataDirectory ();
219 header.Copyright = ReadDataDirectory ();
220 header.GlobalPtr = ReadDataDirectory ();
221 header.TLSTable = ReadDataDirectory ();
222 header.LoadConfigTable = ReadDataDirectory ();
223 header.BoundImport = ReadDataDirectory ();
224 header.IAT = ReadDataDirectory ();
225 header.DelayImportDescriptor = ReadDataDirectory ();
226 header.CLIHeader = ReadDataDirectory ();
227 header.Reserved = ReadDataDirectory ();
229 if (header.CLIHeader != DataDirectory.Zero)
230 m_image.CLIHeader = new CLIHeader ();
231 if (header.ExportTable != DataDirectory.Zero)
232 m_image.ExportTable = new ExportTable ();
235 public override void VisitSectionCollection (SectionCollection coll)
237 for (int i = 0; i < m_image.PEFileHeader.NumberOfSections; i++)
238 coll.Add (new Section ());
241 public override void VisitSection (Section sect)
243 char [] buffer = new char [8];
244 int read = 0;
245 while (read < 8) {
246 char cur = (char) m_binaryReader.ReadSByte ();
247 if (cur == '\0') {
248 m_binaryReader.BaseStream.Position += 8 - read - 1;
249 break;
251 buffer [read++] = cur;
253 sect.Name = read == 0 ? string.Empty : new string (buffer, 0, read);
254 if (sect.Name == Section.Text)
255 m_image.TextSection = sect;
257 sect.VirtualSize = m_binaryReader.ReadUInt32 ();
258 sect.VirtualAddress = ReadRVA ();
259 sect.SizeOfRawData = m_binaryReader.ReadUInt32 ();
260 sect.PointerToRawData = ReadRVA ();
261 sect.PointerToRelocations = ReadRVA ();
262 sect.PointerToLineNumbers = ReadRVA ();
263 sect.NumberOfRelocations = m_binaryReader.ReadUInt16 ();
264 sect.NumberOfLineNumbers = m_binaryReader.ReadUInt16 ();
265 sect.Characteristics = (SectionCharacteristics) m_binaryReader.ReadUInt32 ();
267 long pos = m_binaryReader.BaseStream.Position;
268 m_binaryReader.BaseStream.Position = sect.PointerToRawData;
269 sect.Data = m_binaryReader.ReadBytes ((int) sect.SizeOfRawData);
270 m_binaryReader.BaseStream.Position = pos;
273 public override void VisitImportAddressTable (ImportAddressTable iat)
275 if (m_image.PEOptionalHeader.DataDirectories.IAT.VirtualAddress == RVA.Zero)
276 return;
278 SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.IAT.VirtualAddress);
280 iat.HintNameTableRVA = ReadRVA ();
283 public override void VisitCLIHeader (CLIHeader header)
285 if (m_image.PEOptionalHeader.DataDirectories.Debug != DataDirectory.Zero) {
286 m_image.DebugHeader = new DebugHeader ();
287 VisitDebugHeader (m_image.DebugHeader);
290 SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.CLIHeader.VirtualAddress);
291 header.Cb = m_binaryReader.ReadUInt32 ();
292 header.MajorRuntimeVersion = m_binaryReader.ReadUInt16 ();
293 header.MinorRuntimeVersion = m_binaryReader.ReadUInt16 ();
294 header.Metadata = ReadDataDirectory ();
295 header.Flags = (RuntimeImage) m_binaryReader.ReadUInt32 ();
296 header.EntryPointToken = m_binaryReader.ReadUInt32 ();
297 header.Resources = ReadDataDirectory ();
298 header.StrongNameSignature = ReadDataDirectory ();
299 header.CodeManagerTable = ReadDataDirectory ();
300 header.VTableFixups = ReadDataDirectory ();
301 header.ExportAddressTableJumps = ReadDataDirectory ();
302 header.ManagedNativeHeader = ReadDataDirectory ();
304 if (header.StrongNameSignature != DataDirectory.Zero) {
305 SetPositionToAddress (header.StrongNameSignature.VirtualAddress);
306 header.ImageHash = m_binaryReader.ReadBytes ((int) header.StrongNameSignature.Size);
307 } else
308 header.ImageHash = new byte [0];
310 SetPositionToAddress (m_image.CLIHeader.Metadata.VirtualAddress);
311 m_image.MetadataRoot.Accept (m_mdReader);
314 public override void VisitDebugHeader (DebugHeader header)
316 if (m_image.PEOptionalHeader.DataDirectories.Debug == DataDirectory.Zero)
317 return;
319 long pos = m_binaryReader.BaseStream.Position;
321 SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.Debug.VirtualAddress);
322 header.Characteristics = m_binaryReader.ReadUInt32 ();
323 header.TimeDateStamp = m_binaryReader.ReadUInt32 ();
324 header.MajorVersion = m_binaryReader.ReadUInt16 ();
325 header.MinorVersion = m_binaryReader.ReadUInt16 ();
326 header.Type = (DebugStoreType) m_binaryReader.ReadUInt32 ();
327 header.SizeOfData = m_binaryReader.ReadUInt32 ();
328 header.AddressOfRawData = ReadRVA ();
329 header.PointerToRawData = m_binaryReader.ReadUInt32 ();
331 m_binaryReader.BaseStream.Position = header.PointerToRawData;
333 header.Magic = m_binaryReader.ReadUInt32 ();
334 header.Signature = new Guid (m_binaryReader.ReadBytes (16));
335 header.Age = m_binaryReader.ReadUInt32 ();
336 header.FileName = ReadZeroTerminatedString ();
338 m_binaryReader.BaseStream.Position = pos;
341 string ReadZeroTerminatedString ()
343 StringBuilder sb = new StringBuilder ();
344 while (true) {
345 byte chr = m_binaryReader.ReadByte ();
346 if (chr == 0)
347 break;
348 sb.Append ((char) chr);
350 return sb.ToString ();
353 public override void VisitImportTable (ImportTable it)
355 if (m_image.PEOptionalHeader.DataDirectories.ImportTable.VirtualAddress == RVA.Zero)
356 return;
358 SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.ImportTable.VirtualAddress);
360 it.ImportLookupTable = ReadRVA ();
361 it.DateTimeStamp = m_binaryReader.ReadUInt32 ();
362 it.ForwardChain = m_binaryReader.ReadUInt32 ();
363 it.Name = ReadRVA ();
364 it.ImportAddressTable = ReadRVA ();
367 public override void VisitImportLookupTable (ImportLookupTable ilt)
369 if (m_image.ImportTable.ImportLookupTable == RVA.Zero)
370 return;
372 SetPositionToAddress (m_image.ImportTable.ImportLookupTable);
374 ilt.HintNameRVA = ReadRVA ();
377 public override void VisitHintNameTable (HintNameTable hnt)
379 if (m_image.ImportAddressTable.HintNameTableRVA == RVA.Zero)
380 return;
382 if ((m_image.ImportAddressTable.HintNameTableRVA & 0x80000000) != 0)
383 return;
385 SetPositionToAddress (m_image.ImportAddressTable.HintNameTableRVA);
387 hnt.Hint = m_binaryReader.ReadUInt16 ();
389 byte [] bytes = m_binaryReader.ReadBytes (11);
390 hnt.RuntimeMain = Encoding.ASCII.GetString (bytes, 0, bytes.Length);
392 SetPositionToAddress (m_image.ImportTable.Name);
394 bytes = m_binaryReader.ReadBytes (11);
395 hnt.RuntimeLibrary = Encoding.ASCII.GetString (bytes, 0, bytes.Length);
397 SetPositionToAddress (m_image.PEOptionalHeader.StandardFields.EntryPointRVA);
398 hnt.EntryPoint = m_binaryReader.ReadUInt16 ();
399 hnt.RVA = ReadRVA ();
402 public override void VisitExportTable (ExportTable et)
404 SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.ExportTable.VirtualAddress);
406 et.Characteristics = m_binaryReader.ReadUInt32 ();
407 et.TimeDateStamp = m_binaryReader.ReadUInt32 ();
408 et.MajorVersion = m_binaryReader.ReadUInt16 ();
409 et.MinorVersion = m_binaryReader.ReadUInt16 ();
411 //et.Name =
412 m_binaryReader.ReadUInt32 ();
414 et.Base = m_binaryReader.ReadUInt32 ();
415 et.NumberOfFunctions = m_binaryReader.ReadUInt32 ();
416 et.NumberOfNames = m_binaryReader.ReadUInt32 ();
417 et.AddressOfFunctions = m_binaryReader.ReadUInt32 ();
418 et.AddressOfNames = m_binaryReader.ReadUInt32 ();
419 et.AddressOfNameOrdinals = m_binaryReader.ReadUInt32 ();
421 et.AddressesOfFunctions = ReadArrayOfRVA (et.AddressOfFunctions, et.NumberOfFunctions);
422 et.AddressesOfNames = ReadArrayOfRVA (et.AddressOfNames, et.NumberOfNames);
423 et.NameOrdinals = ReadArrayOfUInt16 (et.AddressOfNameOrdinals, et.NumberOfNames);
424 et.Names = new string [et.NumberOfFunctions];
426 for (int i = 0; i < et.NumberOfFunctions; i++) {
427 if (et.AddressesOfFunctions [i] == 0)
428 continue;
430 et.Names [i] = ReadFunctionName (et, i);
434 string ReadFunctionName (ExportTable et, int index)
436 for (int i = 0; i < et.NumberOfNames; i++) {
437 if (et.NameOrdinals [i] != index)
438 continue;
440 SetPositionToAddress (et.AddressesOfNames [i]);
441 return ReadZeroTerminatedString ();
444 return string.Empty;
447 ushort [] ReadArrayOfUInt16 (RVA position, uint length)
449 if (position == RVA.Zero)
450 return new ushort [0];
452 SetPositionToAddress (position);
453 ushort [] array = new ushort [length];
454 for (int i = 0; i < length; i++)
455 array [i] = m_binaryReader.ReadUInt16 ();
457 return array;
460 RVA [] ReadArrayOfRVA (RVA position, uint length)
462 if (position == RVA.Zero)
463 return new RVA [0];
465 SetPositionToAddress (position);
466 RVA [] addresses = new RVA [length];
467 for (int i = 0; i < length; i++)
468 addresses [i] = m_binaryReader.ReadUInt32 ();
470 return addresses;
473 public override void TerminateImage(Image img)
475 m_binaryReader.Close ();
477 try {
478 ResourceReader resReader = new ResourceReader (img);
479 img.ResourceDirectoryRoot = resReader.Read ();
480 } catch {
481 img.ResourceDirectoryRoot = null;