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 |
---|