PageRenderTime 58ms CodeModel.GetById 15ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 1ms

/Mono.Cecil/Mono.Cecil.PE/ImageWriter.cs

http://github.com/icsharpcode/ILSpy
C# | 824 lines | 624 code | 145 blank | 55 comment | 71 complexity | e70d2b7260d08936e17502d92a4c24e2 MD5 | raw file
  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
 29using System;
 30using System.IO;
 31
 32#if !READ_ONLY
 33
 34using Mono.Cecil.Cil;
 35using Mono.Cecil.Metadata;
 36
 37using RVA = System.UInt32;
 38
 39namespace 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 ((ushort) module.Characteristics);	// 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			if (public_key.IsNullOrEmpty ())
758				return 0;
759
760			// in fx 2.0 the key may be from 384 to 16384 bits
761			// so we must calculate the signature size based on
762			// the size of the public key (minus the 32 byte header)
763			int size = public_key.Length;
764			if (size > 32)
765				return size - 32;
766
767			// note: size == 16 for the ECMA "key" which is replaced
768			// by the runtime with a 1024 bits key (128 bytes)
769
770			return 128; // default strongname signature size
771		}
772
773		public DataDirectory GetStrongNameSignatureDirectory ()
774		{
775			return text_map.GetDataDirectory (TextSegment.StrongNameSignature);
776		}
777
778		public uint GetHeaderSize ()
779		{
780			return pe_header_size + (sections * section_header_size);
781		}
782
783		void PatchWin32Resources (ByteBuffer resources)
784		{
785			PatchResourceDirectoryTable (resources);
786		}
787
788		void PatchResourceDirectoryTable (ByteBuffer resources)
789		{
790			resources.Advance (12);
791
792			var entries = resources.ReadUInt16 () + resources.ReadUInt16 ();
793
794			for (int i = 0; i < entries; i++)
795				PatchResourceDirectoryEntry (resources);
796		}
797
798		void PatchResourceDirectoryEntry (ByteBuffer resources)
799		{
800			resources.Advance (4);
801			var child = resources.ReadUInt32 ();
802
803			var position = resources.position;
804			resources.position = (int) child & 0x7fffffff;
805
806			if ((child & 0x80000000) != 0)
807				PatchResourceDirectoryTable (resources);
808			else
809				PatchResourceDataEntry (resources);
810
811			resources.position = position;
812		}
813
814		void PatchResourceDataEntry (ByteBuffer resources)
815		{
816			var old_rsrc = GetImageResourceSection ();
817			var rva = resources.ReadUInt32 ();
818			resources.position -= 4;
819			resources.WriteUInt32 (rva - old_rsrc.VirtualAddress + rsrc.VirtualAddress);
820		}
821	}
822}
823
824#endif