Don't create temporary module builder for dynamic context, compiler no longer relies...
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil.PE / ImageWriter.cs
1 //
2 // ImageWriter.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 #if !READ_ONLY
33
34 using Mono.Cecil.Cil;
35 using Mono.Cecil.Metadata;
36
37 using RVA = System.UInt32;
38
39 namespace Mono.Cecil.PE {
40
41         sealed class ImageWriter : BinaryStreamWriter {
42
43                 readonly ModuleDefinition module;
44                 readonly MetadataBuilder metadata;
45                 readonly TextMap text_map;
46
47                 ImageDebugDirectory debug_directory;
48                 byte [] debug_data;
49
50                 ByteBuffer win32_resources;
51
52                 const uint pe_header_size = 0x178u;
53                 const uint section_header_size = 0x28u;
54                 const uint file_alignment = 0x200;
55                 const uint section_alignment = 0x2000;
56                 const ulong image_base = 0x00400000;
57
58                 internal const RVA text_rva = 0x2000;
59
60                 readonly bool pe64;
61                 readonly uint time_stamp;
62
63                 internal Section text;
64                 internal Section rsrc;
65                 internal Section reloc;
66
67                 ushort sections;
68
69                 ImageWriter (ModuleDefinition module, MetadataBuilder metadata, Stream stream)
70                         : base (stream)
71                 {
72                         this.module = module;
73                         this.metadata = metadata;
74                         this.pe64 = module.Architecture != TargetArchitecture.I386;
75                         this.GetDebugHeader ();
76                         this.GetWin32Resources ();
77                         this.text_map = BuildTextMap ();
78                         this.sections = (ushort) (pe64 ? 1 : 2); // text + reloc
79                         this.time_stamp = (uint) DateTime.UtcNow.Subtract (new DateTime (1970, 1, 1)).TotalSeconds;
80                 }
81
82                 void GetDebugHeader ()
83                 {
84                         var symbol_writer = metadata.symbol_writer;
85                         if (symbol_writer == null)
86                                 return;
87
88                         if (!symbol_writer.GetDebugHeader (out debug_directory, out debug_data))
89                                 debug_data = Empty<byte>.Array;
90                 }
91
92                 void GetWin32Resources ()
93                 {
94                         var rsrc = GetImageResourceSection ();
95                         if (rsrc == null)
96                                 return;
97
98                         var raw_resources = new byte [rsrc.Data.Length];
99                         Buffer.BlockCopy (rsrc.Data, 0, raw_resources, 0, rsrc.Data.Length);
100                         win32_resources = new ByteBuffer (raw_resources);
101                 }
102
103                 Section GetImageResourceSection ()
104                 {
105                         if (!module.HasImage)
106                                 return null;
107
108                         const string rsrc_section = ".rsrc";
109
110                         return module.Image.GetSection (rsrc_section);
111                 }
112
113                 public static ImageWriter CreateWriter (ModuleDefinition module, MetadataBuilder metadata, Stream stream)
114                 {
115                         var writer = new ImageWriter (module, metadata, stream);
116                         writer.BuildSections ();
117                         return writer;
118                 }
119
120                 void BuildSections ()
121                 {
122                         var has_win32_resources = win32_resources != null;
123                         if (has_win32_resources)
124                                 sections++;
125
126                         text = CreateSection (".text", text_map.GetLength (), null);
127                         var previous = text;
128
129                         if (has_win32_resources) {
130                                 rsrc = CreateSection (".rsrc", (uint) win32_resources.length, previous);
131
132                                 PatchWin32Resources (win32_resources);
133                                 previous = rsrc;
134                         }
135
136                         if (!pe64)
137                                 reloc = CreateSection (".reloc", 12u, previous);
138                 }
139
140                 Section CreateSection (string name, uint size, Section previous)
141                 {
142                         return new Section {
143                                 Name = name,
144                                 VirtualAddress = previous != null
145                                         ? previous.VirtualAddress + Align (previous.VirtualSize, section_alignment)
146                                         : text_rva,
147                                 VirtualSize = size,
148                                 PointerToRawData = previous != null
149                                         ? previous.PointerToRawData + previous.SizeOfRawData
150                                         : Align (GetHeaderSize (), file_alignment),
151                                 SizeOfRawData = Align (size, file_alignment)
152                         };
153                 }
154
155                 static uint Align (uint value, uint align)
156                 {
157                         align--;
158                         return (value + align) & ~align;
159                 }
160
161                 void WriteDOSHeader ()
162                 {
163                         Write (new byte [] {
164                                 // dos header start
165                                 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00,
166                                 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff,
167                                 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00,
168                                 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
169                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173                                 0x00, 0x00, 0x00, 0x00,
174                                 // lfanew
175                                 0x80, 0x00, 0x00, 0x00,
176                                 // dos header end
177                                 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09,
178                                 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21,
179                                 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72,
180                                 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63,
181                                 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62,
182                                 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69,
183                                 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d,
184                                 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a,
185                                 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
186                                 0x00
187                         });
188                 }
189
190                 void WritePEFileHeader ()
191                 {
192                         WriteUInt32 (0x00004550);               // Magic
193                         WriteUInt16 (GetMachine ());    // Machine
194                         WriteUInt16 (sections);                 // NumberOfSections
195                         WriteUInt32 (time_stamp);
196                         WriteUInt32 (0);        // PointerToSymbolTable
197                         WriteUInt32 (0);        // NumberOfSymbols
198                         WriteUInt16 ((ushort) (!pe64 ? 0xe0 : 0xf0));   // SizeOfOptionalHeader
199
200                         // ExecutableImage | (pe64 ? 32BitsMachine : LargeAddressAware)
201                         var characteristics = (ushort) (0x0002 | (!pe64 ? 0x0100 : 0x0020));
202                         if (module.Kind == ModuleKind.Dll || module.Kind == ModuleKind.NetModule)
203                                 characteristics |= 0x2000;
204                         WriteUInt16 (characteristics);  // Characteristics
205                 }
206
207                 ushort GetMachine ()
208                 {
209                         switch (module.Architecture) {
210                         case TargetArchitecture.I386:
211                                 return 0x014c;
212                         case TargetArchitecture.AMD64:
213                                 return 0x8664;
214                         case TargetArchitecture.IA64:
215                                 return 0x0200;
216                         }
217
218                         throw new NotSupportedException ();
219                 }
220
221                 Section LastSection ()
222                 {
223                         if (reloc != null)
224                                 return reloc;
225
226                         if (rsrc != null)
227                                 return rsrc;
228
229                         return text;
230                 }
231
232                 void WriteOptionalHeaders ()
233                 {
234                         WriteUInt16 ((ushort) (!pe64 ? 0x10b : 0x20b)); // Magic
235                         WriteByte (8);  // LMajor
236                         WriteByte (0);  // LMinor
237                         WriteUInt32 (text.SizeOfRawData);       // CodeSize
238                         WriteUInt32 (reloc != null ? reloc.SizeOfRawData : 0
239                                 + (rsrc != null ? rsrc.SizeOfRawData : 0));     // InitializedDataSize
240                         WriteUInt32 (0);        // UninitializedDataSize
241
242                         var startub_stub = text_map.GetRange (TextSegment.StartupStub);
243                         WriteUInt32 (startub_stub.Length > 0 ? startub_stub.Start : 0);  // EntryPointRVA
244                         WriteUInt32 (text_rva); // BaseOfCode
245
246                         if (!pe64) {
247                                 WriteUInt32 (0);        // BaseOfData
248                                 WriteUInt32 ((uint) image_base);        // ImageBase
249                         } else {
250                                 WriteUInt64 (image_base);       // ImageBase
251                         }
252
253                         WriteUInt32 (section_alignment);        // SectionAlignment
254                         WriteUInt32 (file_alignment);           // FileAlignment
255
256                         WriteUInt16 (4);        // OSMajor
257                         WriteUInt16 (0);        // OSMinor
258                         WriteUInt16 (0);        // UserMajor
259                         WriteUInt16 (0);        // UserMinor
260                         WriteUInt16 (4);        // SubSysMajor
261                         WriteUInt16 (0);        // SubSysMinor
262                         WriteUInt32 (0);        // Reserved
263
264                         var last_section = LastSection();
265                         WriteUInt32 (last_section.VirtualAddress + Align (last_section.VirtualSize, section_alignment));        // ImageSize
266                         WriteUInt32 (text.PointerToRawData);    // HeaderSize
267
268                         WriteUInt32 (0);        // Checksum
269                         WriteUInt16 (GetSubSystem ());  // SubSystem
270                         WriteUInt16 (0x8540);   // DLLFlags
271
272                         const ulong stack_reserve = 0x100000;
273                         const ulong stack_commit = 0x1000;
274                         const ulong heap_reserve = 0x100000;
275                         const ulong heap_commit = 0x1000;
276
277                         if (!pe64) {
278                                 WriteUInt32 ((uint) stack_reserve);
279                                 WriteUInt32 ((uint) stack_commit);
280                                 WriteUInt32 ((uint) heap_reserve);
281                                 WriteUInt32 ((uint) heap_commit);
282                         } else {
283                                 WriteUInt64 (stack_reserve);
284                                 WriteUInt64 (stack_commit);
285                                 WriteUInt64 (heap_reserve);
286                                 WriteUInt64 (heap_commit);
287                         }
288
289                         WriteUInt32 (0);        // LoaderFlags
290                         WriteUInt32 (16);       // NumberOfDataDir
291
292                         WriteZeroDataDirectory ();      // ExportTable
293                         WriteDataDirectory (text_map.GetDataDirectory (TextSegment.ImportDirectory));   // ImportTable
294                         if (rsrc != null) {                                                     // ResourceTable
295                                 WriteUInt32 (rsrc.VirtualAddress);
296                                 WriteUInt32 (rsrc.VirtualSize);
297                         } else
298                                 WriteZeroDataDirectory ();
299
300                         WriteZeroDataDirectory ();      // ExceptionTable
301                         WriteZeroDataDirectory ();      // CertificateTable
302                         WriteUInt32 (reloc != null ? reloc.VirtualAddress : 0);                 // BaseRelocationTable
303                         WriteUInt32 (reloc != null ? reloc.VirtualSize : 0);
304
305                         if (text_map.GetLength (TextSegment.DebugDirectory) > 0) {
306                                 WriteUInt32 (text_map.GetRVA (TextSegment.DebugDirectory));
307                                 WriteUInt32 (28u);
308                         } else
309                                 WriteZeroDataDirectory ();
310
311                         WriteZeroDataDirectory ();      // Copyright
312                         WriteZeroDataDirectory ();      // GlobalPtr
313                         WriteZeroDataDirectory ();      // TLSTable
314                         WriteZeroDataDirectory ();      // LoadConfigTable
315                         WriteZeroDataDirectory ();      // BoundImport
316                         WriteDataDirectory (text_map.GetDataDirectory (TextSegment.ImportAddressTable));        // IAT
317                         WriteZeroDataDirectory ();      // DelayImportDesc
318                         WriteDataDirectory (text_map.GetDataDirectory (TextSegment.CLIHeader)); // CLIHeader
319                         WriteZeroDataDirectory ();      // Reserved
320                 }
321
322                 void WriteZeroDataDirectory ()
323                 {
324                         WriteUInt32 (0);
325                         WriteUInt32 (0);
326                 }
327
328                 ushort GetSubSystem ()
329                 {
330                         switch (module.Kind) {
331                         case ModuleKind.Console:
332                         case ModuleKind.Dll:
333                         case ModuleKind.NetModule:
334                                 return 0x3;
335                         case ModuleKind.Windows:
336                                 return 0x2;
337                         default:
338                                 throw new ArgumentOutOfRangeException ();
339                         }
340                 }
341
342                 void WriteSectionHeaders ()
343                 {
344                         WriteSection (text, 0x60000020);
345
346                         if (rsrc != null)
347                                 WriteSection (rsrc, 0x40000040);
348
349                         if (reloc != null)
350                                 WriteSection (reloc, 0x42000040);
351                 }
352
353                 void WriteSection (Section section, uint characteristics)
354                 {
355                         var name = new byte [8];
356                         var sect_name = section.Name;
357                         for (int i = 0; i < sect_name.Length; i++)
358                                 name [i] = (byte) sect_name [i];
359
360                         WriteBytes (name);
361                         WriteUInt32 (section.VirtualSize);
362                         WriteUInt32 (section.VirtualAddress);
363                         WriteUInt32 (section.SizeOfRawData);
364                         WriteUInt32 (section.PointerToRawData);
365                         WriteUInt32 (0);        // PointerToRelocations
366                         WriteUInt32 (0);        // PointerToLineNumbers
367                         WriteUInt16 (0);        // NumberOfRelocations
368                         WriteUInt16 (0);        // NumberOfLineNumbers
369                         WriteUInt32 (characteristics);
370                 }
371
372                 void MoveTo (uint pointer)
373                 {
374                         BaseStream.Seek (pointer, SeekOrigin.Begin);
375                 }
376
377                 void MoveToRVA (Section section, RVA rva)
378                 {
379                         BaseStream.Seek (section.PointerToRawData + rva - section.VirtualAddress, SeekOrigin.Begin);
380                 }
381
382                 void MoveToRVA (TextSegment segment)
383                 {
384                         MoveToRVA (text, text_map.GetRVA (segment));
385                 }
386
387                 void WriteRVA (RVA rva)
388                 {
389                         if (!pe64)
390                                 WriteUInt32 (rva);
391                         else
392                                 WriteUInt64 (rva);
393                 }
394
395                 void WriteText ()
396                 {
397                         MoveTo (text.PointerToRawData);
398
399                         // ImportAddressTable
400
401                         if (!pe64) {
402                                 WriteRVA (text_map.GetRVA (TextSegment.ImportHintNameTable));
403                                 WriteRVA (0);
404                         }
405
406                         // CLIHeader
407
408                         WriteUInt32 (0x48);
409                         WriteUInt16 (2);
410                         WriteUInt16 ((ushort) ((module.Runtime <= TargetRuntime.Net_1_1) ? 0 : 5));
411
412                         WriteUInt32 (text_map.GetRVA (TextSegment.MetadataHeader));
413                         WriteUInt32 (GetMetadataLength ());
414                         WriteUInt32 ((uint) module.Attributes);
415                         WriteUInt32 (metadata.entry_point.ToUInt32 ());
416                         WriteDataDirectory (text_map.GetDataDirectory (TextSegment.Resources));
417                         WriteDataDirectory (text_map.GetDataDirectory (TextSegment.StrongNameSignature));
418                         WriteZeroDataDirectory ();      // CodeManagerTable
419                         WriteZeroDataDirectory ();      // VTableFixups
420                         WriteZeroDataDirectory ();      // ExportAddressTableJumps
421                         WriteZeroDataDirectory ();      // ManagedNativeHeader
422
423                         // Code
424
425                         MoveToRVA (TextSegment.Code);
426                         WriteBuffer (metadata.code);
427
428                         // Resources
429
430                         MoveToRVA (TextSegment.Resources);
431                         WriteBuffer (metadata.resources);
432
433                         // Data
434
435                         if (metadata.data.length > 0) {
436                                 MoveToRVA (TextSegment.Data);
437                                 WriteBuffer (metadata.data);
438                         }
439
440                         // StrongNameSignature
441                         // stays blank
442
443                         // MetadataHeader
444
445                         MoveToRVA (TextSegment.MetadataHeader);
446                         WriteMetadataHeader ();
447
448                         WriteMetadata ();
449
450                         // DebugDirectory
451                         if (text_map.GetLength (TextSegment.DebugDirectory) > 0) {
452                                 MoveToRVA (TextSegment.DebugDirectory);
453                                 WriteDebugDirectory ();
454                         }
455
456                         if (pe64)
457                                 return;
458
459                         // ImportDirectory
460                         MoveToRVA (TextSegment.ImportDirectory);
461                         WriteImportDirectory ();
462
463                         // StartupStub
464                         MoveToRVA (TextSegment.StartupStub);
465                         WriteStartupStub ();
466                 }
467
468                 uint GetMetadataLength ()
469                 {
470                         return text_map.GetRVA (TextSegment.DebugDirectory) - text_map.GetRVA (TextSegment.MetadataHeader);
471                 }
472
473                 void WriteMetadataHeader ()
474                 {
475                         WriteUInt32 (0x424a5342);       // Signature
476                         WriteUInt16 (1);        // MajorVersion
477                         WriteUInt16 (1);        // MinorVersion
478                         WriteUInt32 (0);        // Reserved
479
480                         var version = GetZeroTerminatedString (GetVersion ());
481                         WriteUInt32 ((uint) version.Length);
482                         WriteBytes (version);
483                         WriteUInt16 (0);        // Flags
484                         WriteUInt16 (GetStreamCount ());
485
486                         uint offset = text_map.GetRVA (TextSegment.TableHeap) - text_map.GetRVA (TextSegment.MetadataHeader);
487
488                         WriteStreamHeader (ref offset, TextSegment.TableHeap, "#~");
489                         WriteStreamHeader (ref offset, TextSegment.StringHeap, "#Strings");
490                         WriteStreamHeader (ref offset, TextSegment.UserStringHeap, "#US");
491                         WriteStreamHeader (ref offset, TextSegment.GuidHeap, "#GUID");
492                         WriteStreamHeader (ref offset, TextSegment.BlobHeap, "#Blob");
493                 }
494
495                 string GetVersion ()
496                 {
497                         switch (module.Runtime) {
498                         case TargetRuntime.Net_1_0:
499                                 return "v1.0.3705";
500                         case TargetRuntime.Net_1_1:
501                                 return "v1.1.4322";
502                         case TargetRuntime.Net_2_0:
503                                 return "v2.0.50727";
504                         case TargetRuntime.Net_4_0:
505                         default:
506                                 return "v4.0.30319";
507                         }
508                 }
509
510                 ushort GetStreamCount ()
511                 {
512                         return (ushort) (
513                                 1       // #~
514                                 + 1     // #Strings
515                                 + (metadata.user_string_heap.IsEmpty ? 0 : 1)   // #US
516                                 + 1     // GUID
517                                 + (metadata.blob_heap.IsEmpty ? 0 : 1));        // #Blob
518                 }
519
520                 void WriteStreamHeader (ref uint offset, TextSegment heap, string name)
521                 {
522                         var length = (uint) text_map.GetLength (heap);
523                         if (length == 0)
524                                 return;
525
526                         WriteUInt32 (offset);
527                         WriteUInt32 (length);
528                         WriteBytes (GetZeroTerminatedString (name));
529                         offset += length;
530                 }
531
532                 static byte [] GetZeroTerminatedString (string @string)
533                 {
534                         return GetString (@string, (@string.Length + 1 + 3) & ~3);
535                 }
536
537                 static byte [] GetSimpleString (string @string)
538                 {
539                         return GetString (@string, @string.Length);
540                 }
541
542                 static byte [] GetString (string @string, int length)
543                 {
544                         var bytes = new byte [length];
545                         for (int i = 0; i < @string.Length; i++)
546                                 bytes [i] = (byte) @string [i];
547
548                         return bytes;
549                 }
550
551                 void WriteMetadata ()
552                 {
553                         WriteHeap (TextSegment.TableHeap, metadata.table_heap);
554                         WriteHeap (TextSegment.StringHeap, metadata.string_heap);
555                         WriteHeap (TextSegment.UserStringHeap, metadata.user_string_heap);
556                         WriteGuidHeap ();
557                         WriteHeap (TextSegment.BlobHeap, metadata.blob_heap);
558                 }
559
560                 void WriteHeap (TextSegment heap, HeapBuffer buffer)
561                 {
562                         if (buffer.IsEmpty)
563                                 return;
564
565                         MoveToRVA (heap);
566                         WriteBuffer (buffer);
567                 }
568
569                 void WriteGuidHeap ()
570                 {
571                         MoveToRVA (TextSegment.GuidHeap);
572                         WriteBytes (module.Mvid.ToByteArray ());
573                 }
574
575                 void WriteDebugDirectory ()
576                 {
577                         WriteInt32 (debug_directory.Characteristics);
578                         WriteUInt32 (time_stamp);
579                         WriteInt16 (debug_directory.MajorVersion);
580                         WriteInt16 (debug_directory.MinorVersion);
581                         WriteInt32 (debug_directory.Type);
582                         WriteInt32 (debug_directory.SizeOfData);
583                         WriteInt32 (debug_directory.AddressOfRawData);
584                         WriteInt32 ((int) BaseStream.Position + 4);
585
586                         WriteBytes (debug_data);
587                 }
588
589                 void WriteImportDirectory ()
590                 {
591                         WriteUInt32 (text_map.GetRVA (TextSegment.ImportDirectory) + 40);       // ImportLookupTable
592                         WriteUInt32 (0);        // DateTimeStamp
593                         WriteUInt32 (0);        // ForwarderChain
594                         WriteUInt32 (text_map.GetRVA (TextSegment.ImportHintNameTable) + 14);
595                         WriteUInt32 (text_map.GetRVA (TextSegment.ImportAddressTable));
596                         Advance (20);
597
598                         // ImportLookupTable
599                         WriteUInt32 (text_map.GetRVA (TextSegment.ImportHintNameTable));
600
601                         // ImportHintNameTable
602                         MoveToRVA (TextSegment.ImportHintNameTable);
603
604                         WriteUInt16 (0);        // Hint
605                         WriteBytes (GetRuntimeMain ());
606                         WriteByte (0);
607                         WriteBytes (GetSimpleString ("mscoree.dll"));
608                         WriteUInt16 (0);
609                 }
610
611                 byte [] GetRuntimeMain ()
612                 {
613                         return module.Kind == ModuleKind.Dll || module.Kind == ModuleKind.NetModule
614                                 ? GetSimpleString ("_CorDllMain")
615                                 : GetSimpleString ("_CorExeMain");
616                 }
617
618                 void WriteStartupStub ()
619                 {
620                         switch (module.Architecture) {
621                         case TargetArchitecture.I386:
622                                 WriteUInt16 (0x25ff);
623                                 WriteUInt32 ((uint) image_base + text_map.GetRVA (TextSegment.ImportAddressTable));
624                                 return;
625                         default:
626                                 throw new NotSupportedException ();
627                         }
628                 }
629
630                 void WriteRsrc ()
631                 {
632                         MoveTo (rsrc.PointerToRawData);
633                         WriteBuffer (win32_resources);
634                 }
635
636                 void WriteReloc ()
637                 {
638                         MoveTo (reloc.PointerToRawData);
639
640                         var reloc_rva = text_map.GetRVA (TextSegment.StartupStub);
641                         reloc_rva += module.Architecture == TargetArchitecture.IA64 ? 0x20u : 2;
642                         var page_rva = reloc_rva & ~0xfffu;
643
644                         WriteUInt32 (page_rva); // PageRVA
645                         WriteUInt32 (0x000c);   // Block Size
646
647                         switch (module.Architecture) {
648                         case TargetArchitecture.I386:
649                                 WriteUInt32 (0x3000 + reloc_rva - page_rva);
650                                 break;
651                         default:
652                                 throw new NotSupportedException();
653                         }
654
655                         WriteBytes (new byte [file_alignment - reloc.VirtualSize]);
656                 }
657
658                 public void WriteImage ()
659                 {
660                         WriteDOSHeader ();
661                         WritePEFileHeader ();
662                         WriteOptionalHeaders ();
663                         WriteSectionHeaders ();
664                         WriteText ();
665                         if (rsrc != null)
666                                 WriteRsrc ();
667                         if (reloc != null)
668                                 WriteReloc ();
669                 }
670
671                 TextMap BuildTextMap ()
672                 {
673                         var map = metadata.text_map;
674
675                         map.AddMap (TextSegment.Code, metadata.code.length, !pe64 ? 4 : 16);
676                         map.AddMap (TextSegment.Resources, metadata.resources.length, 8);
677                         map.AddMap (TextSegment.Data, metadata.data.length, 4);
678                         if (metadata.data.length > 0)
679                                 metadata.table_heap.FixupData (map.GetRVA (TextSegment.Data));
680                         map.AddMap (TextSegment.StrongNameSignature, GetStrongNameLength (), 4);
681
682                         map.AddMap (TextSegment.MetadataHeader, GetMetadataHeaderLength ());
683                         map.AddMap (TextSegment.TableHeap, metadata.table_heap.length, 4);
684                         map.AddMap (TextSegment.StringHeap, metadata.string_heap.length, 4);
685                         map.AddMap (TextSegment.UserStringHeap, metadata.user_string_heap.IsEmpty ? 0 : metadata.user_string_heap.length, 4);
686                         map.AddMap (TextSegment.GuidHeap, 16);
687                         map.AddMap (TextSegment.BlobHeap, metadata.blob_heap.IsEmpty ? 0 : metadata.blob_heap.length, 4);
688
689                         int debug_dir_len = 0;
690                         if (!debug_data.IsNullOrEmpty ()) {
691                                 const int debug_dir_header_len = 28;
692
693                                 debug_directory.AddressOfRawData = (int) map.GetNextRVA (TextSegment.BlobHeap) + debug_dir_header_len;
694                                 debug_dir_len = debug_data.Length + debug_dir_header_len;
695                         }
696
697                         map.AddMap (TextSegment.DebugDirectory, debug_dir_len, 4);
698
699                         if (pe64) {
700                                 var start = map.GetNextRVA (TextSegment.DebugDirectory);
701                                 map.AddMap (TextSegment.ImportDirectory, new Range (start, 0));
702                                 map.AddMap (TextSegment.ImportHintNameTable, new Range (start, 0));
703                                 map.AddMap (TextSegment.StartupStub, new Range (start, 0));
704                                 return map;
705                         }
706
707                         RVA import_dir_rva = map.GetNextRVA (TextSegment.DebugDirectory);
708                         RVA import_hnt_rva = import_dir_rva + 48u;
709                         import_hnt_rva = (import_hnt_rva + 15u) & ~15u;
710                         uint import_dir_len = (import_hnt_rva - import_dir_rva) + 27u;
711
712                         RVA startup_stub_rva = import_dir_rva + import_dir_len;
713                         startup_stub_rva = module.Architecture == TargetArchitecture.IA64
714                                 ? (startup_stub_rva + 15u) & ~15u
715                                 : 2 + ((startup_stub_rva + 3u) & ~3u);
716
717                         map.AddMap (TextSegment.ImportDirectory, new Range (import_dir_rva, import_dir_len));
718                         map.AddMap (TextSegment.ImportHintNameTable, new Range (import_hnt_rva, 0));
719                         map.AddMap (TextSegment.StartupStub, new Range (startup_stub_rva, GetStartupStubLength ()));
720
721                         return map;
722                 }
723
724                 uint GetStartupStubLength ()
725                 {
726                         switch (module.Architecture) {
727                         case TargetArchitecture.I386:
728                                 return 6;
729                         default:
730                                 throw new NotSupportedException ();
731                         }
732                 }
733
734                 int GetMetadataHeaderLength ()
735                 {
736                         return
737                                 // MetadataHeader
738                                 40
739                                 // #~ header
740                                 + 12
741                                 // #Strings header
742                                 + 20
743                                 // #US header
744                                 + (metadata.user_string_heap.IsEmpty ? 0 : 12)
745                                 // #GUID header
746                                 + 16
747                                 // #Blob header
748                                 + (metadata.blob_heap.IsEmpty ? 0 : 16);
749                 }
750
751                 int GetStrongNameLength ()
752                 {
753                         if (module.Assembly == null)
754                                 return 0;
755
756                         var public_key = module.Assembly.Name.PublicKey;
757
758                         if (public_key != null) {
759                                 // in fx 2.0 the key may be from 384 to 16384 bits
760                                 // so we must calculate the signature size based on
761                                 // the size of the public key (minus the 32 byte header)
762                                 int size = public_key.Length;
763                                 if (size > 32)
764                                         return size - 32;
765                                 // note: size == 16 for the ECMA "key" which is replaced
766                                 // by the runtime with a 1024 bits key (128 bytes)
767                         }
768
769                         return 128; // default strongname signature size
770                 }
771
772                 public DataDirectory GetStrongNameSignatureDirectory ()
773                 {
774                         return text_map.GetDataDirectory (TextSegment.StrongNameSignature);
775                 }
776
777                 public uint GetHeaderSize ()
778                 {
779                         return pe_header_size + (sections * section_header_size);
780                 }
781
782                 void PatchWin32Resources (ByteBuffer resources)
783                 {
784                         PatchResourceDirectoryTable (resources);
785                 }
786
787                 void PatchResourceDirectoryTable (ByteBuffer resources)
788                 {
789                         resources.Advance (12);
790
791                         var entries = resources.ReadUInt16 () + resources.ReadUInt16 ();
792
793                         for (int i = 0; i < entries; i++)
794                                 PatchResourceDirectoryEntry (resources);
795                 }
796
797                 void PatchResourceDirectoryEntry (ByteBuffer resources)
798                 {
799                         resources.Advance (4);
800                         var child = resources.ReadUInt32 ();
801
802                         var position = resources.position;
803                         resources.position = (int) child & 0x7fffffff;
804
805                         if ((child & 0x80000000) != 0)
806                                 PatchResourceDirectoryTable (resources);
807                         else
808                                 PatchResourceDataEntry (resources);
809
810                         resources.position = position;
811                 }
812
813                 void PatchResourceDataEntry (ByteBuffer resources)
814                 {
815                         var old_rsrc = GetImageResourceSection ();
816                         var rva = resources.ReadUInt32 ();
817                         resources.position -= 4;
818                         resources.WriteUInt32 (rva - old_rsrc.VirtualAddress + rsrc.VirtualAddress);
819                 }
820         }
821 }
822
823 #endif