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