9baeca5eef165b41064c568a480298359665799e
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil.PE / ImageReader.cs
1 //
2 // ImageReader.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
29 using System;
30 using System.IO;
31
32 using Mono.Cecil.Metadata;
33
34 using RVA = System.UInt32;
35
36 namespace Mono.Cecil.PE {
37
38         sealed class ImageReader : BinaryStreamReader {
39
40                 readonly Image image;
41
42                 DataDirectory cli;
43                 DataDirectory metadata;
44
45                 public ImageReader (Stream stream)
46                         : base (stream)
47                 {
48                         image = new Image ();
49
50                         image.FileName = stream.GetFullyQualifiedName ();
51                 }
52
53                 void MoveTo (DataDirectory directory)
54                 {
55                         BaseStream.Position = image.ResolveVirtualAddress (directory.VirtualAddress);
56                 }
57
58                 void MoveTo (uint position)
59                 {
60                         BaseStream.Position = position;
61                 }
62
63                 void ReadImage ()
64                 {
65                         if (BaseStream.Length < 128)
66                                 throw new BadImageFormatException ();
67
68                         // - DOSHeader
69
70                         // PE                                   2
71                         // Start                                58
72                         // Lfanew                               4
73                         // End                                  64
74
75                         if (ReadUInt16 () != 0x5a4d)
76                                 throw new BadImageFormatException ();
77
78                         Advance (58);
79
80                         MoveTo (ReadUInt32 ());
81
82                         if (ReadUInt32 () != 0x00004550)
83                                 throw new BadImageFormatException ();
84
85                         // - PEFileHeader
86
87                         // Machine                              2
88                         image.Architecture = ReadArchitecture ();
89
90                         // NumberOfSections             2
91                         ushort sections = ReadUInt16 ();
92
93                         // TimeDateStamp                4
94                         // PointerToSymbolTable 4
95                         // NumberOfSymbols              4
96                         // OptionalHeaderSize   2
97                         Advance (14);
98
99                         // Characteristics              2
100                         ushort characteristics = ReadUInt16 ();
101
102                         ushort subsystem;
103                         ReadOptionalHeaders (out subsystem);
104                         ReadSections (sections);
105                         ReadCLIHeader ();
106                         ReadMetadata ();
107
108                         image.Kind = GetModuleKind (characteristics, subsystem);
109                 }
110
111                 TargetArchitecture ReadArchitecture ()
112                 {
113                         var machine = ReadUInt16 ();
114                         switch (machine) {
115                         case 0x014c:
116                                 return TargetArchitecture.I386;
117                         case 0x8664:
118                                 return TargetArchitecture.AMD64;
119                         case 0x0200:
120                                 return TargetArchitecture.IA64;
121                         case 0x01c4:
122                                 return TargetArchitecture.ARMv7;
123                         }
124
125                         throw new NotSupportedException ();
126                 }
127
128                 static ModuleKind GetModuleKind (ushort characteristics, ushort subsystem)
129                 {
130                         if ((characteristics & 0x2000) != 0) // ImageCharacteristics.Dll
131                                 return ModuleKind.Dll;
132
133                         if (subsystem == 0x2 || subsystem == 0x9) // SubSystem.WindowsGui || SubSystem.WindowsCeGui
134                                 return ModuleKind.Windows;
135
136                         return ModuleKind.Console;
137                 }
138
139                 void ReadOptionalHeaders (out ushort subsystem)
140                 {
141                         // - PEOptionalHeader
142                         //   - StandardFieldsHeader
143
144                         // Magic                                2
145                         bool pe64 = ReadUInt16 () == 0x20b;
146
147                         //                                              pe32 || pe64
148
149                         // LMajor                               1
150                         // LMinor                               1
151                         // CodeSize                             4
152                         // InitializedDataSize  4
153                         // UninitializedDataSize4
154                         // EntryPointRVA                4
155                         // BaseOfCode                   4
156                         // BaseOfData                   4 || 0
157
158                         //   - NTSpecificFieldsHeader
159
160                         // ImageBase                    4 || 8
161                         // SectionAlignment             4
162                         // FileAlignement               4
163                         // OSMajor                              2
164                         // OSMinor                              2
165                         // UserMajor                    2
166                         // UserMinor                    2
167                         // SubSysMajor                  2
168                         // SubSysMinor                  2
169                         // Reserved                             4
170                         // ImageSize                    4
171                         // HeaderSize                   4
172                         // FileChecksum                 4
173                         Advance (66);
174
175                         // SubSystem                    2
176                         subsystem = ReadUInt16 ();
177
178                         // DLLFlags                             2
179                         // StackReserveSize             4 || 8
180                         // StackCommitSize              4 || 8
181                         // HeapReserveSize              4 || 8
182                         // HeapCommitSize               4 || 8
183                         // LoaderFlags                  4
184                         // NumberOfDataDir              4
185
186                         //   - DataDirectoriesHeader
187
188                         // ExportTable                  8
189                         // ImportTable                  8
190                         // ResourceTable                8
191                         // ExceptionTable               8
192                         // CertificateTable             8
193                         // BaseRelocationTable  8
194
195                         Advance (pe64 ? 90 : 74);
196
197                         // Debug                                8
198                         image.Debug = ReadDataDirectory ();
199
200                         // Copyright                    8
201                         // GlobalPtr                    8
202                         // TLSTable                             8
203                         // LoadConfigTable              8
204                         // BoundImport                  8
205                         // IAT                                  8
206                         // DelayImportDescriptor8
207                         Advance (56);
208
209                         // CLIHeader                    8
210                         cli = ReadDataDirectory ();
211
212                         if (cli.IsZero)
213                                 throw new BadImageFormatException ();
214
215                         // Reserved                             8
216                         Advance (8);
217                 }
218
219                 string ReadAlignedString (int length)
220                 {
221                         int read = 0;
222                         var buffer = new char [length];
223                         while (read < length) {
224                                 var current = ReadByte ();
225                                 if (current == 0)
226                                         break;
227
228                                 buffer [read++] = (char) current;
229                         }
230
231                         Advance (-1 + ((read + 4) & ~3) - read);
232
233                         return new string (buffer, 0, read);
234                 }
235
236                 string ReadZeroTerminatedString (int length)
237                 {
238                         int read = 0;
239                         var buffer = new char [length];
240                         var bytes = ReadBytes (length);
241                         while (read < length) {
242                                 var current = bytes [read];
243                                 if (current == 0)
244                                         break;
245
246                                 buffer [read++] = (char) current;
247                         }
248
249                         return new string (buffer, 0, read);
250                 }
251
252                 void ReadSections (ushort count)
253                 {
254                         var sections = new Section [count];
255
256                         for (int i = 0; i < count; i++) {
257                                 var section = new Section ();
258
259                                 // Name
260                                 section.Name = ReadZeroTerminatedString (8);
261
262                                 // VirtualSize          4
263                                 Advance (4);
264
265                                 // VirtualAddress       4
266                                 section.VirtualAddress = ReadUInt32 ();
267                                 // SizeOfRawData        4
268                                 section.SizeOfRawData = ReadUInt32 ();
269                                 // PointerToRawData     4
270                                 section.PointerToRawData = ReadUInt32 ();
271
272                                 // PointerToRelocations         4
273                                 // PointerToLineNumbers         4
274                                 // NumberOfRelocations          2
275                                 // NumberOfLineNumbers          2
276                                 // Characteristics                      4
277                                 Advance (16);
278
279                                 sections [i] = section;
280
281                                 ReadSectionData (section);
282                         }
283
284                         image.Sections = sections;
285                 }
286
287                 void ReadSectionData (Section section)
288                 {
289                         var position = BaseStream.Position;
290
291                         MoveTo (section.PointerToRawData);
292
293                         var length = (int) section.SizeOfRawData;
294                         var data = new byte [length];
295                         int offset = 0, read;
296
297                         while ((read = Read (data, offset, length - offset)) > 0)
298                                 offset += read;
299
300                         section.Data = data;
301
302                         BaseStream.Position = position;
303                 }
304
305                 void ReadCLIHeader ()
306                 {
307                         MoveTo (cli);
308
309                         // - CLIHeader
310
311                         // Cb                                           4
312                         // MajorRuntimeVersion          2
313                         // MinorRuntimeVersion          2
314                         Advance (8);
315
316                         // Metadata                                     8
317                         metadata = ReadDataDirectory ();
318                         // Flags                                        4
319                         image.Attributes = (ModuleAttributes) ReadUInt32 ();
320                         // EntryPointToken                      4
321                         image.EntryPointToken = ReadUInt32 ();
322                         // Resources                            8
323                         image.Resources = ReadDataDirectory ();
324                         // StrongNameSignature          8
325                         // CodeManagerTable                     8
326                         // VTableFixups                         8
327                         // ExportAddressTableJumps      8
328                         // ManagedNativeHeader          8
329                 }
330
331                 void ReadMetadata ()
332                 {
333                         MoveTo (metadata);
334
335                         if (ReadUInt32 () != 0x424a5342)
336                                 throw new BadImageFormatException ();
337
338                         // MajorVersion                 2
339                         // MinorVersion                 2
340                         // Reserved                             4
341                         Advance (8);
342
343                         var version = ReadZeroTerminatedString (ReadInt32 ());
344                         image.Runtime = version.ParseRuntime ();
345
346                         // Flags                2
347                         Advance (2);
348
349                         var streams = ReadUInt16 ();
350
351                         var section = image.GetSectionAtVirtualAddress (metadata.VirtualAddress);
352                         if (section == null)
353                                 throw new BadImageFormatException ();
354
355                         image.MetadataSection = section;
356
357                         for (int i = 0; i < streams; i++)
358                                 ReadMetadataStream (section);
359
360                         if (image.TableHeap != null)
361                                 ReadTableHeap ();
362                 }
363
364                 void ReadMetadataStream (Section section)
365                 {
366                         // Offset               4
367                         uint start = metadata.VirtualAddress - section.VirtualAddress + ReadUInt32 (); // relative to the section start
368
369                         // Size                 4
370                         uint size = ReadUInt32 ();
371
372                         var name = ReadAlignedString (16);
373                         switch (name) {
374                         case "#~":
375                         case "#-":
376                                 image.TableHeap = new TableHeap (section, start, size);
377                                 break;
378                         case "#Strings":
379                                 image.StringHeap = new StringHeap (section, start, size);
380                                 break;
381                         case "#Blob":
382                                 image.BlobHeap = new BlobHeap (section, start, size);
383                                 break;
384                         case "#GUID":
385                                 image.GuidHeap = new GuidHeap (section, start, size);
386                                 break;
387                         case "#US":
388                                 image.UserStringHeap = new UserStringHeap (section, start, size);
389                                 break;
390                         }
391                 }
392
393                 void ReadTableHeap ()
394                 {
395                         var heap = image.TableHeap;
396
397                         uint start = heap.Section.PointerToRawData;
398
399                         MoveTo (heap.Offset + start);
400
401                         // Reserved                     4
402                         // MajorVersion         1
403                         // MinorVersion         1
404                         Advance (6);
405
406                         // HeapSizes            1
407                         var sizes = ReadByte ();
408
409                         // Reserved2            1
410                         Advance (1);
411
412                         // Valid                        8
413                         heap.Valid = ReadInt64 ();
414
415                         // Sorted                       8
416                         heap.Sorted = ReadInt64 ();
417
418                         for (int i = 0; i < TableHeap.TableCount; i++) {
419                                 if (!heap.HasTable ((Table) i))
420                                         continue;
421
422                                 heap.Tables [i].Length = ReadUInt32 ();
423                         }
424
425                         SetIndexSize (image.StringHeap, sizes, 0x1);
426                         SetIndexSize (image.GuidHeap, sizes, 0x2);
427                         SetIndexSize (image.BlobHeap, sizes, 0x4);
428
429                         ComputeTableInformations ();
430                 }
431
432                 static void SetIndexSize (Heap heap, uint sizes, byte flag)
433                 {
434                         if (heap == null)
435                                 return;
436
437                         heap.IndexSize = (sizes & flag) > 0 ? 4 : 2;
438                 }
439
440                 int GetTableIndexSize (Table table)
441                 {
442                         return image.GetTableIndexSize (table);
443                 }
444
445                 int GetCodedIndexSize (CodedIndex index)
446                 {
447                         return image.GetCodedIndexSize (index);
448                 }
449
450                 void ComputeTableInformations ()
451                 {
452                         uint offset = (uint) BaseStream.Position - image.MetadataSection.PointerToRawData; // header
453
454                         int stridx_size = image.StringHeap.IndexSize;
455                         int blobidx_size = image.BlobHeap != null ? image.BlobHeap.IndexSize : 2;
456
457                         var heap = image.TableHeap;
458                         var tables = heap.Tables;
459
460                         for (int i = 0; i < TableHeap.TableCount; i++) {
461                                 var table = (Table) i;
462                                 if (!heap.HasTable (table))
463                                         continue;
464
465                                 int size;
466                                 switch (table) {
467                                 case Table.Module:
468                                         size = 2        // Generation
469                                                 + stridx_size   // Name
470                                                 + (image.GuidHeap.IndexSize * 3);       // Mvid, EncId, EncBaseId
471                                         break;
472                                 case Table.TypeRef:
473                                         size = GetCodedIndexSize (CodedIndex.ResolutionScope)   // ResolutionScope
474                                                 + (stridx_size * 2);    // Name, Namespace
475                                         break;
476                                 case Table.TypeDef:
477                                         size = 4        // Flags
478                                                 + (stridx_size * 2)     // Name, Namespace
479                                                 + GetCodedIndexSize (CodedIndex.TypeDefOrRef)   // BaseType
480                                                 + GetTableIndexSize (Table.Field)       // FieldList
481                                                 + GetTableIndexSize (Table.Method);     // MethodList
482                                         break;
483                                 case Table.FieldPtr:
484                                         size = GetTableIndexSize (Table.Field); // Field
485                                         break;
486                                 case Table.Field:
487                                         size = 2        // Flags
488                                                 + stridx_size   // Name
489                                                 + blobidx_size; // Signature
490                                         break;
491                                 case Table.MethodPtr:
492                                         size = GetTableIndexSize (Table.Method);        // Method
493                                         break;
494                                 case Table.Method:
495                                         size = 8        // Rva 4, ImplFlags 2, Flags 2
496                                                 + stridx_size   // Name
497                                                 + blobidx_size  // Signature
498                                                 + GetTableIndexSize (Table.Param); // ParamList
499                                         break;
500                                 case Table.ParamPtr:
501                                         size = GetTableIndexSize (Table.Param); // Param
502                                         break;
503                                 case Table.Param:
504                                         size = 4        // Flags 2, Sequence 2
505                                                 + stridx_size;  // Name
506                                         break;
507                                 case Table.InterfaceImpl:
508                                         size = GetTableIndexSize (Table.TypeDef)        // Class
509                                                 + GetCodedIndexSize (CodedIndex.TypeDefOrRef);  // Interface
510                                         break;
511                                 case Table.MemberRef:
512                                         size = GetCodedIndexSize (CodedIndex.MemberRefParent)   // Class
513                                                 + stridx_size   // Name
514                                                 + blobidx_size; // Signature
515                                         break;
516                                 case Table.Constant:
517                                         size = 2        // Type
518                                                 + GetCodedIndexSize (CodedIndex.HasConstant)    // Parent
519                                                 + blobidx_size; // Value
520                                         break;
521                                 case Table.CustomAttribute:
522                                         size = GetCodedIndexSize (CodedIndex.HasCustomAttribute)        // Parent
523                                                 + GetCodedIndexSize (CodedIndex.CustomAttributeType)    // Type
524                                                 + blobidx_size; // Value
525                                         break;
526                                 case Table.FieldMarshal:
527                                         size = GetCodedIndexSize (CodedIndex.HasFieldMarshal)   // Parent
528                                                 + blobidx_size; // NativeType
529                                         break;
530                                 case Table.DeclSecurity:
531                                         size = 2        // Action
532                                                 + GetCodedIndexSize (CodedIndex.HasDeclSecurity)        // Parent
533                                                 + blobidx_size; // PermissionSet
534                                         break;
535                                 case Table.ClassLayout:
536                                         size = 6        // PackingSize 2, ClassSize 4
537                                                 + GetTableIndexSize (Table.TypeDef);    // Parent
538                                         break;
539                                 case Table.FieldLayout:
540                                         size = 4        // Offset
541                                                 + GetTableIndexSize (Table.Field);      // Field
542                                         break;
543                                 case Table.StandAloneSig:
544                                         size = blobidx_size;    // Signature
545                                         break;
546                                 case Table.EventMap:
547                                         size = GetTableIndexSize (Table.TypeDef)        // Parent
548                                                 + GetTableIndexSize (Table.Event);      // EventList
549                                         break;
550                                 case Table.EventPtr:
551                                         size = GetTableIndexSize (Table.Event); // Event
552                                         break;
553                                 case Table.Event:
554                                         size = 2        // Flags
555                                                 + stridx_size // Name
556                                                 + GetCodedIndexSize (CodedIndex.TypeDefOrRef);  // EventType
557                                         break;
558                                 case Table.PropertyMap:
559                                         size = GetTableIndexSize (Table.TypeDef)        // Parent
560                                                 + GetTableIndexSize (Table.Property);   // PropertyList
561                                         break;
562                                 case Table.PropertyPtr:
563                                         size = GetTableIndexSize (Table.Property);      // Property
564                                         break;
565                                 case Table.Property:
566                                         size = 2        // Flags
567                                                 + stridx_size   // Name
568                                                 + blobidx_size; // Type
569                                         break;
570                                 case Table.MethodSemantics:
571                                         size = 2        // Semantics
572                                                 + GetTableIndexSize (Table.Method)      // Method
573                                                 + GetCodedIndexSize (CodedIndex.HasSemantics);  // Association
574                                         break;
575                                 case Table.MethodImpl:
576                                         size = GetTableIndexSize (Table.TypeDef)        // Class
577                                                 + GetCodedIndexSize (CodedIndex.MethodDefOrRef) // MethodBody
578                                                 + GetCodedIndexSize (CodedIndex.MethodDefOrRef);        // MethodDeclaration
579                                         break;
580                                 case Table.ModuleRef:
581                                         size = stridx_size;     // Name
582                                         break;
583                                 case Table.TypeSpec:
584                                         size = blobidx_size;    // Signature
585                                         break;
586                                 case Table.ImplMap:
587                                         size = 2        // MappingFlags
588                                                 + GetCodedIndexSize (CodedIndex.MemberForwarded)        // MemberForwarded
589                                                 + stridx_size   // ImportName
590                                                 + GetTableIndexSize (Table.ModuleRef);  // ImportScope
591                                         break;
592                                 case Table.FieldRVA:
593                                         size = 4        // RVA
594                                                 + GetTableIndexSize (Table.Field);      // Field
595                                         break;
596                                 case Table.EncLog:
597                                 case Table.EncMap:
598                                         size = 4;
599                                         break;
600                                 case Table.Assembly:
601                                         size = 16 // HashAlgId 4, Version 4 * 2, Flags 4
602                                                 + blobidx_size  // PublicKey
603                                                 + (stridx_size * 2);    // Name, Culture
604                                         break;
605                                 case Table.AssemblyProcessor:
606                                         size = 4;       // Processor
607                                         break;
608                                 case Table.AssemblyOS:
609                                         size = 12;      // Platform 4, Version 2 * 4
610                                         break;
611                                 case Table.AssemblyRef:
612                                         size = 12       // Version 2 * 4 + Flags 4
613                                                 + (blobidx_size * 2)    // PublicKeyOrToken, HashValue
614                                                 + (stridx_size * 2);    // Name, Culture
615                                         break;
616                                 case Table.AssemblyRefProcessor:
617                                         size = 4        // Processor
618                                                 + GetTableIndexSize (Table.AssemblyRef);        // AssemblyRef
619                                         break;
620                                 case Table.AssemblyRefOS:
621                                         size = 12       // Platform 4, Version 2 * 4
622                                                 + GetTableIndexSize (Table.AssemblyRef);        // AssemblyRef
623                                         break;
624                                 case Table.File:
625                                         size = 4        // Flags
626                                                 + stridx_size   // Name
627                                                 + blobidx_size; // HashValue
628                                         break;
629                                 case Table.ExportedType:
630                                         size = 8        // Flags 4, TypeDefId 4
631                                                 + (stridx_size * 2)     // Name, Namespace
632                                                 + GetCodedIndexSize (CodedIndex.Implementation);        // Implementation
633                                         break;
634                                 case Table.ManifestResource:
635                                         size = 8        // Offset, Flags
636                                                 + stridx_size   // Name
637                                                 + GetCodedIndexSize (CodedIndex.Implementation);        // Implementation
638                                         break;
639                                 case Table.NestedClass:
640                                         size = GetTableIndexSize (Table.TypeDef)        // NestedClass
641                                                 + GetTableIndexSize (Table.TypeDef);    // EnclosingClass
642                                         break;
643                                 case Table.GenericParam:
644                                         size = 4        // Number, Flags
645                                                 + GetCodedIndexSize (CodedIndex.TypeOrMethodDef)        // Owner
646                                                 + stridx_size;  // Name
647                                         break;
648                                 case Table.MethodSpec:
649                                         size = GetCodedIndexSize (CodedIndex.MethodDefOrRef)    // Method
650                                                 + blobidx_size; // Instantiation
651                                         break;
652                                 case Table.GenericParamConstraint:
653                                         size = GetTableIndexSize (Table.GenericParam)   // Owner
654                                                 + GetCodedIndexSize (CodedIndex.TypeDefOrRef);  // Constraint
655                                         break;
656                                 default:
657                                         throw new NotSupportedException ();
658                                 }
659
660                                 tables [i].RowSize = (uint) size;
661                                 tables [i].Offset = offset;
662
663                                 offset += (uint) size * tables [i].Length;
664                         }
665                 }
666
667                 public static Image ReadImageFrom (Stream stream)
668                 {
669                         try {
670                                 var reader = new ImageReader (stream);
671                                 reader.ReadImage ();
672                                 return reader.image;
673                         } catch (EndOfStreamException e) {
674                                 throw new BadImageFormatException (stream.GetFullyQualifiedName (), e);
675                         }
676                 }
677         }
678 }