PageRenderTime 55ms CodeModel.GetById 13ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/Mono.Cecil.PE/ImageWriter.cs

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