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