[cil-strip] Upgrade to latest Mono.Cecil API
[mono.git] / mcs / tools / cil-strip / Mono.Cecil.Binary / ImageReader.cs
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:
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
29 namespace Mono.Cecil.Binary {
30
31         using System;
32         using System.IO;
33         using System.Text;
34
35         using Mono.Cecil.Metadata;
36
37         sealed class ImageReader : BaseImageVisitor {
38
39                 MetadataReader m_mdReader;
40                 BinaryReader m_binaryReader;
41                 Image m_image;
42
43                 public MetadataReader MetadataReader {
44                         get { return m_mdReader; }
45                 }
46
47                 public Image Image {
48                         get { return m_image; }
49                 }
50
51                 ImageReader (Image img, BinaryReader reader)
52                 {
53                         m_image = img;
54                         m_binaryReader = reader;
55                 }
56
57                 static ImageReader Read (Image img, Stream stream)
58                 {
59                         ImageReader reader = new ImageReader (img, new BinaryReader (stream));
60                         img.Accept (reader);
61                         return reader;
62                 }
63
64                 public static ImageReader Read (string file)
65                 {
66                         if (file == null)
67                                 throw new ArgumentNullException ("file");
68
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
76
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
89                         }
90                 }
91
92                 public static ImageReader Read (byte [] image)
93                 {
94                         if (image == null)
95                                 throw new ArgumentNullException ("image");
96
97                         if (image.Length == 0)
98                                 throw new ArgumentException ("Empty image array");
99
100                         return Read (new Image (), new MemoryStream (image));
101                 }
102
103                 public static ImageReader Read (Stream stream)
104                 {
105                         if (stream == null)
106                                 throw new ArgumentNullException ("stream");
107
108                         if (!stream.CanRead)
109                                 throw new ArgumentException ("Can not read from stream");
110
111                         return Read (new Image (), stream);
112                 }
113
114                 public BinaryReader GetReader ()
115                 {
116                         return m_binaryReader;
117                 }
118
119                 public override void VisitImage (Image img)
120                 {
121                         m_mdReader = new MetadataReader (this);
122                 }
123
124                 void SetPositionToAddress (RVA address)
125                 {
126                         m_binaryReader.BaseStream.Position = m_image.ResolveVirtualAddress (address);
127                 }
128
129                 public override void VisitDOSHeader (DOSHeader header)
130                 {
131                         header.Start = m_binaryReader.ReadBytes (60);
132                         header.Lfanew = m_binaryReader.ReadUInt32 ();
133                         header.End = m_binaryReader.ReadBytes (64);
134
135                         m_binaryReader.BaseStream.Position = header.Lfanew;
136
137                         if (m_binaryReader.ReadUInt16 () != 0x4550 ||
138                                 m_binaryReader.ReadUInt16 () != 0)
139
140                                 throw new ImageFormatException ("Invalid PE File Signature");
141                 }
142
143                 public override void VisitPEFileHeader (PEFileHeader header)
144                 {
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 ();
152                 }
153
154                 ulong ReadIntOrLong ()
155                 {
156                         return m_image.PEOptionalHeader.StandardFields.IsPE64 ?
157                                 m_binaryReader.ReadUInt64 () :
158                                 m_binaryReader.ReadUInt32 ();
159                 }
160
161                 RVA ReadRVA ()
162                 {
163                         return m_binaryReader.ReadUInt32 ();
164                 }
165
166                 DataDirectory ReadDataDirectory ()
167                 {
168                         return new DataDirectory (ReadRVA (), m_binaryReader.ReadUInt32 ());
169                 }
170
171                 public override void VisitNTSpecificFieldsHeader (PEOptionalHeader.NTSpecificFieldsHeader header)
172                 {
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 ();
194                 }
195
196                 public override void VisitStandardFieldsHeader (PEOptionalHeader.StandardFieldsHeader header)
197                 {
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 ();
208                 }
209
210                 public override void VisitDataDirectoriesHeader (PEOptionalHeader.DataDirectoriesHeader header)
211                 {
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 ();
228
229                         if (header.CLIHeader != DataDirectory.Zero)
230                                 m_image.CLIHeader = new CLIHeader ();
231                         if (header.ExportTable != DataDirectory.Zero)
232                                 m_image.ExportTable = new ExportTable ();
233                 }
234
235                 public override void VisitSectionCollection (SectionCollection coll)
236                 {
237                         for (int i = 0; i < m_image.PEFileHeader.NumberOfSections; i++)
238                                 coll.Add (new Section ());
239                 }
240
241                 public override void VisitSection (Section sect)
242                 {
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;
250                                 }
251                                 buffer [read++] = cur;
252                         }
253                         sect.Name = read == 0 ? string.Empty : new string (buffer, 0, read);
254                         if (sect.Name == Section.Text)
255                                 m_image.TextSection = sect;
256
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 ();
266
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;
271                 }
272
273                 public override void VisitImportAddressTable (ImportAddressTable iat)
274                 {
275                         if (m_image.PEOptionalHeader.DataDirectories.IAT.VirtualAddress == RVA.Zero)
276                                 return;
277
278                         SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.IAT.VirtualAddress);
279
280                         iat.HintNameTableRVA = ReadRVA ();
281                 }
282
283                 public override void VisitCLIHeader (CLIHeader header)
284                 {
285                         if (m_image.PEOptionalHeader.DataDirectories.Debug != DataDirectory.Zero) {
286                                 m_image.DebugHeader = new DebugHeader ();
287                                 VisitDebugHeader (m_image.DebugHeader);
288                         }
289
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 ();
303
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];
309
310                         SetPositionToAddress (m_image.CLIHeader.Metadata.VirtualAddress);
311                         m_image.MetadataRoot.Accept (m_mdReader);
312                 }
313
314                 public override void VisitDebugHeader (DebugHeader header)
315                 {
316                         if (m_image.PEOptionalHeader.DataDirectories.Debug == DataDirectory.Zero)
317                                 return;
318
319                         long pos = m_binaryReader.BaseStream.Position;
320
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 ();
330
331                         m_binaryReader.BaseStream.Position = header.PointerToRawData;
332
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 ();
337
338                         m_binaryReader.BaseStream.Position = pos;
339                 }
340
341                 string ReadZeroTerminatedString ()
342                 {
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);
349                         }
350                         return sb.ToString ();
351                 }
352
353                 public override void VisitImportTable (ImportTable it)
354                 {
355                         if (m_image.PEOptionalHeader.DataDirectories.ImportTable.VirtualAddress == RVA.Zero)
356                                 return;
357
358                         SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.ImportTable.VirtualAddress);
359
360                         it.ImportLookupTable = ReadRVA ();
361                         it.DateTimeStamp = m_binaryReader.ReadUInt32 ();
362                         it.ForwardChain = m_binaryReader.ReadUInt32 ();
363                         it.Name = ReadRVA ();
364                         it.ImportAddressTable = ReadRVA ();
365                 }
366
367                 public override void VisitImportLookupTable (ImportLookupTable ilt)
368                 {
369                         if (m_image.ImportTable.ImportLookupTable == RVA.Zero)
370                                 return;
371
372                         SetPositionToAddress (m_image.ImportTable.ImportLookupTable);
373
374                         ilt.HintNameRVA = ReadRVA ();
375                 }
376
377                 public override void VisitHintNameTable (HintNameTable hnt)
378                 {
379                         if (m_image.ImportAddressTable.HintNameTableRVA == RVA.Zero)
380                                 return;
381
382                         if ((m_image.ImportAddressTable.HintNameTableRVA & 0x80000000) != 0)
383                                 return;
384
385                         SetPositionToAddress (m_image.ImportAddressTable.HintNameTableRVA);
386
387                         hnt.Hint = m_binaryReader.ReadUInt16 ();
388
389                         byte [] bytes = m_binaryReader.ReadBytes (11);
390                         hnt.RuntimeMain = Encoding.ASCII.GetString (bytes, 0, bytes.Length);
391
392                         SetPositionToAddress (m_image.ImportTable.Name);
393
394                         bytes = m_binaryReader.ReadBytes (11);
395                         hnt.RuntimeLibrary = Encoding.ASCII.GetString (bytes, 0, bytes.Length);
396
397                         SetPositionToAddress (m_image.PEOptionalHeader.StandardFields.EntryPointRVA);
398                         hnt.EntryPoint = m_binaryReader.ReadUInt16 ();
399                         hnt.RVA = ReadRVA ();
400                 }
401
402                 public override void VisitExportTable (ExportTable et)
403                 {
404                         SetPositionToAddress (m_image.PEOptionalHeader.DataDirectories.ExportTable.VirtualAddress);
405
406                         et.Characteristics = m_binaryReader.ReadUInt32 ();
407                         et.TimeDateStamp = m_binaryReader.ReadUInt32 ();
408                         et.MajorVersion = m_binaryReader.ReadUInt16 ();
409                         et.MinorVersion = m_binaryReader.ReadUInt16 ();
410
411                         //et.Name =
412                         m_binaryReader.ReadUInt32 ();
413
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 ();
420
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];
425
426                         for (int i = 0; i < et.NumberOfFunctions; i++) {
427                                 if (et.AddressesOfFunctions [i] == 0)
428                                         continue;
429
430                                 et.Names [i] = ReadFunctionName (et, i);
431                         }
432                 }
433
434                 string ReadFunctionName (ExportTable et, int index)
435                 {
436                         for (int i = 0; i < et.NumberOfNames; i++) {
437                                 if (et.NameOrdinals [i] != index)
438                                         continue;
439
440                                 SetPositionToAddress (et.AddressesOfNames [i]);
441                                 return ReadZeroTerminatedString ();
442                         }
443
444                         return string.Empty;
445                 }
446
447                 ushort [] ReadArrayOfUInt16 (RVA position, uint length)
448                 {
449                         if (position == RVA.Zero)
450                                 return new ushort [0];
451
452                         SetPositionToAddress (position);
453                         ushort [] array = new ushort [length];
454                         for (int i = 0; i < length; i++)
455                                 array [i] = m_binaryReader.ReadUInt16 ();
456
457                         return array;
458                 }
459
460                 RVA [] ReadArrayOfRVA (RVA position, uint length)
461                 {
462                         if (position == RVA.Zero)
463                                 return new RVA [0];
464
465                         SetPositionToAddress (position);
466                         RVA [] addresses = new RVA [length];
467                         for (int i = 0; i < length; i++)
468                                 addresses [i] = m_binaryReader.ReadUInt32 ();
469
470                         return addresses;
471                 }
472
473                 public override void TerminateImage(Image img)
474                 {
475                         m_binaryReader.Close ();
476
477                         try {
478                                 ResourceReader resReader = new ResourceReader (img);
479                                 img.ResourceDirectoryRoot = resReader.Read ();
480                         } catch {
481                                 img.ResourceDirectoryRoot = null;
482                         }
483                 }
484         }
485 }