Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / IKVM.Reflection / Writer / TextSection.cs
1 /*
2   Copyright (C) 2008-2011 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Diagnostics;
26 using System.IO;
27 using System.Collections.Generic;
28 using System.Text;
29 using IKVM.Reflection.Emit;
30 using IKVM.Reflection.Impl;
31 using IKVM.Reflection.Metadata;
32
33 namespace IKVM.Reflection.Writer
34 {
35         sealed class TextSection
36         {
37                 private readonly PEWriter peWriter;
38                 private readonly CliHeader cliHeader;
39                 private readonly ModuleBuilder moduleBuilder;
40                 private readonly uint strongNameSignatureLength;
41                 private readonly ExportTables exportTables;
42
43                 internal TextSection(PEWriter peWriter, CliHeader cliHeader, ModuleBuilder moduleBuilder, int strongNameSignatureLength)
44                 {
45                         this.peWriter = peWriter;
46                         this.cliHeader = cliHeader;
47                         this.moduleBuilder = moduleBuilder;
48                         this.strongNameSignatureLength = (uint)strongNameSignatureLength;
49                         if (moduleBuilder.unmanagedExports.Count != 0)
50                         {
51                                 this.exportTables = new ExportTables(this);
52                         }
53                 }
54
55                 internal uint PointerToRawData
56                 {
57                         get { return peWriter.ToFileAlignment(peWriter.HeaderSize); }
58                 }
59
60                 internal uint BaseRVA
61                 {
62                         get { return 0x2000; }
63                 }
64
65                 internal uint ImportAddressTableRVA
66                 {
67                         get { return BaseRVA; }
68                 }
69
70                 internal uint ImportAddressTableLength
71                 {
72                         get
73                         {
74                                 switch (peWriter.Headers.FileHeader.Machine)
75                                 {
76                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
77                                                 return 8;
78                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
79                                                 return 0;
80                                         default:
81                                                 return 16;
82                                 }
83                         }
84                 }
85
86                 internal uint ComDescriptorRVA
87                 {
88                         get { return ImportAddressTableRVA + ImportAddressTableLength; }
89                 }
90
91                 internal uint ComDescriptorLength
92                 {
93                         get { return cliHeader.Cb; }
94                 }
95
96                 internal uint MethodBodiesRVA
97                 {
98                         get { return (ComDescriptorRVA + ComDescriptorLength + 7) & ~7U; }
99                 }
100
101                 private uint MethodBodiesLength
102                 {
103                         get { return (uint)moduleBuilder.methodBodies.Length; }
104                 }
105
106                 private uint ResourcesRVA
107                 {
108                         get
109                         {
110                                 switch (peWriter.Headers.FileHeader.Machine)
111                                 {
112                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
113                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
114                                                 return (MethodBodiesRVA + MethodBodiesLength + 3) & ~3U;
115                                         default:
116                                                 return (MethodBodiesRVA + MethodBodiesLength + 15) & ~15U;
117                                 }
118                         }
119                 }
120
121                 private uint ResourcesLength
122                 {
123                         get { return (uint)moduleBuilder.manifestResources.Length; }
124                 }
125
126                 internal uint StrongNameSignatureRVA
127                 {
128                         get
129                         {
130                                 return (ResourcesRVA + ResourcesLength + 3) & ~3U;
131                         }
132                 }
133
134                 internal uint StrongNameSignatureLength
135                 {
136                         get
137                         {
138                                 return strongNameSignatureLength;
139                         }
140                 }
141
142                 private uint MetadataRVA
143                 {
144                         get
145                         {
146                                 return (StrongNameSignatureRVA + StrongNameSignatureLength + 3) & ~3U;
147                         }
148                 }
149
150                 private uint MetadataLength
151                 {
152                         get { return (uint)moduleBuilder.MetadataLength; }
153                 }
154
155                 private uint VTableFixupsRVA
156                 {
157                         get { return (MetadataRVA + MetadataLength + 7) & ~7U; }
158                 }
159
160                 private uint VTableFixupsLength
161                 {
162                         get { return (uint)moduleBuilder.vtablefixups.Count * 8; }
163                 }
164
165                 internal uint DebugDirectoryRVA
166                 {
167                         get { return VTableFixupsRVA + VTableFixupsLength; }
168                 }
169
170                 internal uint DebugDirectoryLength
171                 {
172                         get
173                         {
174                                 if (DebugDirectoryContentsLength != 0)
175                                 {
176                                         return 28;
177                                 }
178                                 return 0;
179                         }
180                 }
181
182                 private uint DebugDirectoryContentsLength
183                 {
184                         get
185                         {
186                                 if (moduleBuilder.symbolWriter != null)
187                                 {
188                                         IMAGE_DEBUG_DIRECTORY idd = new IMAGE_DEBUG_DIRECTORY();
189                                         return (uint)SymbolSupport.GetDebugInfo(moduleBuilder.symbolWriter, ref idd).Length;
190                                 }
191                                 return 0;
192                         }
193                 }
194
195                 internal uint ExportDirectoryRVA
196                 {
197                         get { return (DebugDirectoryRVA + DebugDirectoryLength + DebugDirectoryContentsLength + 15) & ~15U; }
198                 }
199
200                 internal uint ExportDirectoryLength
201                 {
202                         get { return moduleBuilder.unmanagedExports.Count == 0 ? 0U : 40U; }
203                 }
204
205                 private uint ExportTablesRVA
206                 {
207                         get { return ExportDirectoryRVA + ExportDirectoryLength; }
208                 }
209
210                 private uint ExportTablesLength
211                 {
212                         get { return exportTables == null ? 0U : exportTables.Length; }
213                 }
214
215                 internal uint ImportDirectoryRVA
216                 {
217                         // on AMD64 (and probably IA64) the import directory needs to be 16 byte aligned (on I386 4 byte alignment is sufficient)
218                         get { return (ExportTablesRVA + ExportTablesLength + 15) & ~15U; }
219                 }
220
221                 internal uint ImportDirectoryLength
222                 {
223                         get
224                         {
225                                 switch (peWriter.Headers.FileHeader.Machine)
226                                 {
227                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
228                                                 return 0;
229                                         default:
230                                                 return (ImportHintNameTableRVA - ImportDirectoryRVA) + 27;
231                                 }
232                         }
233                 }
234
235                 private uint ImportHintNameTableRVA
236                 {
237                         get
238                         {
239                                 if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386)
240                                 {
241                                         return (ImportDirectoryRVA + 48 + 15) & ~15U;
242                                 }
243                                 else
244                                 {
245                                         return (ImportDirectoryRVA + 48 + 4 + 15) & ~15U;
246                                 }
247                         }
248                 }
249
250                 internal uint StartupStubRVA
251                 {
252                         get
253                         {
254                                 if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64)
255                                 {
256                                         // note that the alignment is driven by the requirement that the two relocation fixups are in a single page
257                                         return (ImportDirectoryRVA + ImportDirectoryLength + 15U) & ~15U;
258                                 }
259                                 else
260                                 {
261                                         // the additional 2 bytes padding are to align the address in the jump (which is a relocation fixup)
262                                         return 2 + ((ImportDirectoryRVA + ImportDirectoryLength + 3U) & ~3U);
263                                 }
264                         }
265                 }
266
267                 internal uint StartupStubLength
268                 {
269                         get
270                         {
271                                 switch (peWriter.Headers.FileHeader.Machine)
272                                 {
273                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
274                                                 return 6;
275                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
276                                                 return 12;
277                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
278                                                 return 48;
279                                         default:
280                                                 return 0;
281                                 }
282                         }
283                 }
284
285                 private void WriteRVA(MetadataWriter mw, uint rva)
286                 {
287                         switch (peWriter.Headers.FileHeader.Machine)
288                         {
289                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
290                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
291                                         mw.Write(rva);
292                                         break;
293                                 default:
294                                         mw.Write((ulong)rva);
295                                         break;
296                         }
297                 }
298
299                 internal void Write(MetadataWriter mw, uint sdataRVA)
300                 {
301                         // Now that we're ready to start writing, we need to do some fix ups
302                         moduleBuilder.TypeRef.Fixup(moduleBuilder);
303                         moduleBuilder.MethodDef.Fixup(this);
304                         moduleBuilder.MethodImpl.Fixup(moduleBuilder);
305                         moduleBuilder.MethodSemantics.Fixup(moduleBuilder);
306                         moduleBuilder.InterfaceImpl.Fixup();
307                         moduleBuilder.ResolveInterfaceImplPseudoTokens();
308                         moduleBuilder.MemberRef.Fixup(moduleBuilder);
309                         moduleBuilder.Constant.Fixup(moduleBuilder);
310                         moduleBuilder.FieldMarshal.Fixup(moduleBuilder);
311                         moduleBuilder.DeclSecurity.Fixup(moduleBuilder);
312                         moduleBuilder.GenericParam.Fixup(moduleBuilder);
313                         moduleBuilder.CustomAttribute.Fixup(moduleBuilder);
314                         moduleBuilder.FieldLayout.Fixup(moduleBuilder);
315                         moduleBuilder.FieldRVA.Fixup(moduleBuilder, (int)sdataRVA, (int)this.MethodBodiesRVA);
316                         moduleBuilder.ImplMap.Fixup(moduleBuilder);
317                         moduleBuilder.ExportedType.Fixup(moduleBuilder);
318                         moduleBuilder.ManifestResource.Fixup(moduleBuilder);
319                         moduleBuilder.MethodSpec.Fixup(moduleBuilder);
320                         moduleBuilder.GenericParamConstraint.Fixup(moduleBuilder);
321
322                         // Import Address Table
323                         AssertRVA(mw, ImportAddressTableRVA);
324                         if (ImportAddressTableLength != 0)
325                         {
326                                 WriteRVA(mw, ImportHintNameTableRVA);
327                                 WriteRVA(mw, 0);
328                         }
329
330                         // CLI Header
331                         AssertRVA(mw, ComDescriptorRVA);
332                         cliHeader.MetaData.VirtualAddress = MetadataRVA;
333                         cliHeader.MetaData.Size = MetadataLength;
334                         if (ResourcesLength != 0)
335                         {
336                                 cliHeader.Resources.VirtualAddress = ResourcesRVA;
337                                 cliHeader.Resources.Size = ResourcesLength;
338                         }
339                         if (StrongNameSignatureLength != 0)
340                         {
341                                 cliHeader.StrongNameSignature.VirtualAddress = StrongNameSignatureRVA;
342                                 cliHeader.StrongNameSignature.Size = StrongNameSignatureLength;
343                         }
344                         if (VTableFixupsLength != 0)
345                         {
346                                 cliHeader.VTableFixups.VirtualAddress = VTableFixupsRVA;
347                                 cliHeader.VTableFixups.Size = VTableFixupsLength;
348                         }
349                         cliHeader.Write(mw);
350
351                         // alignment padding
352                         for (int i = (int)(MethodBodiesRVA - (ComDescriptorRVA + ComDescriptorLength)); i > 0; i--)
353                         {
354                                 mw.Write((byte)0);
355                         }
356
357                         // Method Bodies
358                         mw.Write(moduleBuilder.methodBodies);
359
360                         // alignment padding
361                         for (int i = (int)(ResourcesRVA - (MethodBodiesRVA + MethodBodiesLength)); i > 0; i--)
362                         {
363                                 mw.Write((byte)0);
364                         }
365
366                         // Resources
367                         mw.Write(moduleBuilder.manifestResources);
368
369                         // The strong name signature live here (if it exists), but it will written later
370                         // and the following alignment padding will take care of reserving the space.
371
372                         // alignment padding
373                         for (int i = (int)(MetadataRVA - (ResourcesRVA + ResourcesLength)); i > 0; i--)
374                         {
375                                 mw.Write((byte)0);
376                         }
377
378                         // Metadata
379                         AssertRVA(mw, MetadataRVA);
380                         moduleBuilder.WriteMetadata(mw);
381
382                         // alignment padding
383                         for (int i = (int)(VTableFixupsRVA - (MetadataRVA + MetadataLength)); i > 0; i--)
384                         {
385                                 mw.Write((byte)0);
386                         }
387
388                         // VTableFixups
389                         AssertRVA(mw, VTableFixupsRVA);
390                         WriteVTableFixups(mw, sdataRVA);
391
392                         // Debug Directory
393                         AssertRVA(mw, DebugDirectoryRVA);
394                         WriteDebugDirectory(mw);
395
396                         // alignment padding
397                         for (int i = (int)(ExportDirectoryRVA - (DebugDirectoryRVA + DebugDirectoryLength + DebugDirectoryContentsLength)); i > 0; i--)
398                         {
399                                 mw.Write((byte)0);
400                         }
401
402                         // Export Directory
403                         AssertRVA(mw, ExportDirectoryRVA);
404                         WriteExportDirectory(mw);
405
406                         // Export Tables
407                         AssertRVA(mw, ExportTablesRVA);
408                         WriteExportTables(mw, sdataRVA);
409         
410                         // alignment padding
411                         for (int i = (int)(ImportDirectoryRVA - (ExportTablesRVA + ExportTablesLength)); i > 0; i--)
412                         {
413                                 mw.Write((byte)0);
414                         }
415
416                         // Import Directory
417                         AssertRVA(mw, ImportDirectoryRVA);
418                         if (ImportDirectoryLength != 0)
419                         {
420                                 WriteImportDirectory(mw);
421                         }
422
423                         // alignment padding
424                         for (int i = (int)(StartupStubRVA - (ImportDirectoryRVA + ImportDirectoryLength)); i > 0; i--)
425                         {
426                                 mw.Write((byte)0);
427                         }
428
429                         // Startup Stub
430                         AssertRVA(mw, StartupStubRVA);
431                         if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64)
432                         {
433                                 /*
434                                  *   48 A1 00 20 40 00 00 00 00 00        mov         rax,qword ptr [0000000000402000h]
435                                  *   FF E0                                jmp         rax
436                                  */
437                                 mw.Write((ushort)0xA148);
438                                 mw.Write(peWriter.Headers.OptionalHeader.ImageBase + ImportAddressTableRVA);
439                                 mw.Write((ushort)0xE0FF);
440                         }
441                         else if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64)
442                         {
443                                 mw.Write(new byte[] {
444                                                 0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40, 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00,
445                                                 0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50, 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00
446                                         });
447                                 mw.Write(peWriter.Headers.OptionalHeader.ImageBase + StartupStubRVA);
448                                 mw.Write(peWriter.Headers.OptionalHeader.ImageBase + BaseRVA);
449                         }
450                         else if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386)
451                         {
452                                 mw.Write((ushort)0x25FF);
453                                 mw.Write((uint)peWriter.Headers.OptionalHeader.ImageBase + ImportAddressTableRVA);
454                         }
455                 }
456
457                 [Conditional("DEBUG")]
458                 private void AssertRVA(MetadataWriter mw, uint rva)
459                 {
460                         Debug.Assert(mw.Position - PointerToRawData + BaseRVA == rva);
461                 }
462
463                 private void WriteVTableFixups(MetadataWriter mw, uint sdataRVA)
464                 {
465                         foreach (ModuleBuilder.VTableFixups fixups in moduleBuilder.vtablefixups)
466                         {
467                                 mw.Write(fixups.initializedDataOffset + sdataRVA);
468                                 mw.Write(fixups.count);
469                                 mw.Write(fixups.type);
470                         }
471                 }
472
473                 private void WriteDebugDirectory(MetadataWriter mw)
474                 {
475                         if (DebugDirectoryLength != 0)
476                         {
477                                 IMAGE_DEBUG_DIRECTORY idd = new IMAGE_DEBUG_DIRECTORY();
478                                 idd.Characteristics = 0;
479                                 idd.TimeDateStamp = peWriter.Headers.FileHeader.TimeDateStamp;
480                                 byte[] buf = SymbolSupport.GetDebugInfo(moduleBuilder.symbolWriter, ref idd);
481                                 idd.PointerToRawData = (DebugDirectoryRVA - BaseRVA) + DebugDirectoryLength + PointerToRawData;
482                                 mw.Write(idd.Characteristics);
483                                 mw.Write(idd.TimeDateStamp);
484                                 mw.Write(idd.MajorVersion);
485                                 mw.Write(idd.MinorVersion);
486                                 mw.Write(idd.Type);
487                                 mw.Write(idd.SizeOfData);
488                                 mw.Write(idd.AddressOfRawData);
489                                 mw.Write(idd.PointerToRawData);
490                                 mw.Write(buf);
491                         }
492                 }
493
494                 private sealed class ExportTables
495                 {
496                         private readonly TextSection text;
497                         internal readonly uint entries;
498                         internal readonly uint ordinalBase;
499                         internal readonly uint nameCount;
500                         internal readonly uint namesLength;
501                         internal readonly uint exportAddressTableRVA;
502                         internal readonly uint exportNamePointerTableRVA;
503                         internal readonly uint exportOrdinalTableRVA;
504                         internal readonly uint namesRVA;
505                         internal readonly uint stubsRVA;
506                         private readonly uint stubLength;
507
508                         internal ExportTables(TextSection text)
509                         {
510                                 this.text = text;
511                                 ordinalBase = GetOrdinalBase(out entries);
512                                 namesLength = GetExportNamesLength(out nameCount);
513                                 exportAddressTableRVA = text.ExportTablesRVA;
514                                 exportNamePointerTableRVA = exportAddressTableRVA + 4 * entries;
515                                 exportOrdinalTableRVA = exportNamePointerTableRVA + 4 * nameCount;
516                                 namesRVA = exportOrdinalTableRVA + 2 * nameCount;
517                                 stubsRVA = (namesRVA + namesLength + 15) & ~15U;
518                                 // note that we align the stubs to avoid having to deal with the relocation crossing a page boundary
519                                 switch (text.peWriter.Headers.FileHeader.Machine)
520                                 {
521                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
522                                                 stubLength = 8;
523                                                 break;
524                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
525                                                 stubLength = 16;
526                                                 break;
527                                         default:
528                                                 throw new NotImplementedException();
529                                 }
530                         }
531
532                         internal uint Length
533                         {
534                                 get { return (stubsRVA + stubLength * (uint)text.moduleBuilder.unmanagedExports.Count) - text.ExportTablesRVA; }
535                         }
536
537                         private uint GetOrdinalBase(out uint entries)
538                         {
539                                 uint min = uint.MaxValue;
540                                 uint max = uint.MinValue;
541                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
542                                 {
543                                         uint ordinal = (uint)exp.ordinal;
544                                         min = Math.Min(min, ordinal);
545                                         max = Math.Max(max, ordinal);
546                                 }
547                                 entries = 1 + (max - min);
548                                 return min;
549                         }
550
551                         private uint GetExportNamesLength(out uint nameCount)
552                         {
553                                 nameCount = 0;
554                                 // the first name in the names list is the module name (the Export Directory contains a name of the current module)
555                                 uint length = (uint)text.moduleBuilder.fileName.Length + 1;
556                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
557                                 {
558                                         if (exp.name != null)
559                                         {
560                                                 nameCount++;
561                                                 length += (uint)exp.name.Length + 1;
562                                         }
563                                 }
564                                 return length;
565                         }
566
567                         internal void Write(MetadataWriter mw, uint sdataRVA)
568                         {
569                                 // sort the exports by ordinal
570                                 text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportOrdinals);
571
572                                 // Now write the Export Address Table
573                                 text.AssertRVA(mw, exportAddressTableRVA);
574                                 for (int i = 0, pos = 0; i < entries; i++)
575                                 {
576                                         if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
577                                         {
578                                                 mw.Write(stubsRVA + (uint)pos * stubLength);
579                                                 pos++;
580                                         }
581                                         else
582                                         {
583                                                 mw.Write(0);
584                                         }
585                                 }
586
587                                 // sort the exports by name
588                                 text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportNames);
589
590                                 // Now write the Export Name Pointer Table
591                                 text.AssertRVA(mw, exportNamePointerTableRVA);
592                                 uint nameOffset = (uint)text.moduleBuilder.fileName.Length + 1;
593                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
594                                 {
595                                         if (exp.name != null)
596                                         {
597                                                 mw.Write(namesRVA + nameOffset);
598                                                 nameOffset += (uint)exp.name.Length + 1;
599                                         }
600                                 }
601
602                                 // Now write the Export Ordinal Table
603                                 text.AssertRVA(mw, exportOrdinalTableRVA);
604                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
605                                 {
606                                         if (exp.name != null)
607                                         {
608                                                 mw.Write((ushort)(exp.ordinal - ordinalBase));
609                                         }
610                                 }
611
612                                 // Now write the actual names
613                                 text.AssertRVA(mw, namesRVA);
614                                 mw.Write(Encoding.ASCII.GetBytes(text.moduleBuilder.fileName));
615                                 mw.Write((byte)0);
616                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
617                                 {
618                                         if (exp.name != null)
619                                         {
620                                                 mw.Write(Encoding.ASCII.GetBytes(exp.name));
621                                                 mw.Write((byte)0);
622                                         }
623                                 }
624                                 text.AssertRVA(mw, namesRVA + namesLength);
625
626                                 // alignment padding
627                                 for (int i = (int)(stubsRVA - (namesRVA + namesLength)); i > 0; i--)
628                                 {
629                                         mw.Write((byte)0);
630                                 }
631
632                                 // sort the exports by ordinal
633                                 text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportOrdinals);
634
635                                 // Now write the stubs
636                                 text.AssertRVA(mw, stubsRVA);
637
638                                 for (int i = 0, pos = 0; i < entries; i++)
639                                 {
640                                         if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
641                                         {
642                                                 switch (text.peWriter.Headers.FileHeader.Machine)
643                                                 {
644                                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
645                                                                 mw.Write((byte)0xFF);
646                                                                 mw.Write((byte)0x25);
647                                                                 mw.Write((uint)text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
648                                                                 mw.Write((short)0);     // alignment
649                                                                 break;
650                                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
651                                                                 mw.Write((byte)0x48);
652                                                                 mw.Write((byte)0xA1);
653                                                                 mw.Write(text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
654                                                                 mw.Write((byte)0xFF);
655                                                                 mw.Write((byte)0xE0);
656                                                                 mw.Write(0); // alignment
657                                                                 break;
658                                                         default:
659                                                                 throw new NotImplementedException();
660                                                 }
661                                                 pos++;
662                                         }
663                                 }
664                         }
665
666                         private static int CompareUnmanagedExportNames(UnmanagedExport x, UnmanagedExport y)
667                         {
668                                 if (x.name == null)
669                                 {
670                                         return y.name == null ? 0 : 1;
671                                 }
672                                 if (y.name == null)
673                                 {
674                                         return -1;
675                                 }
676                                 return x.name.CompareTo(y.name);
677                         }
678
679                         private static int CompareUnmanagedExportOrdinals(UnmanagedExport x, UnmanagedExport y)
680                         {
681                                 return x.ordinal.CompareTo(y.ordinal);
682                         }
683
684                         internal void WriteRelocations(MetadataWriter mw)
685                         {
686                                 // we assume that unmanagedExports is still sorted by ordinal
687                                 for (int i = 0, pos = 0; i < entries; i++)
688                                 {
689                                         if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
690                                         {
691                                                 // both I386 and AMD64 have the address at offset 2
692                                                 text.WriteRelocationBlock(mw, stubsRVA + 2 + (uint)pos * stubLength);
693                                                 pos++;
694                                         }
695                                 }
696                         }
697                 }
698
699                 private uint GetOrdinalBase(out uint entries)
700                 {
701                         uint min = uint.MaxValue;
702                         uint max = uint.MinValue;
703                         foreach (UnmanagedExport exp in moduleBuilder.unmanagedExports)
704                         {
705                                 uint ordinal = (uint)exp.ordinal;
706                                 min = Math.Min(min, ordinal);
707                                 max = Math.Max(max, ordinal);
708                         }
709                         entries = 1 + (max - min);
710                         return min;
711                 }
712
713                 private uint GetExportNamesLength(out uint nameCount)
714                 {
715                         nameCount = 0;
716                         uint length = 0;
717                         foreach (UnmanagedExport exp in moduleBuilder.unmanagedExports)
718                         {
719                                 if (exp.name != null)
720                                 {
721                                         nameCount++;
722                                         length += (uint)exp.name.Length + 1;
723                                 }
724                         }
725                         return length;
726                 }
727
728                 private void WriteExportDirectory(MetadataWriter mw)
729                 {
730                         if (ExportDirectoryLength != 0)
731                         {
732                                 // Flags
733                                 mw.Write(0);
734                                 // Date/Time Stamp
735                                 mw.Write(peWriter.Headers.FileHeader.TimeDateStamp);
736                                 // Major Version
737                                 mw.Write((short)0);
738                                 // Minor Version
739                                 mw.Write((short)0);
740                                 // Name RVA
741                                 mw.Write(exportTables.namesRVA);
742                                 // Ordinal Base
743                                 mw.Write(exportTables.ordinalBase);
744                                 // Address Table Entries
745                                 mw.Write(exportTables.entries);
746                                 // Number of Name Pointers
747                                 mw.Write(exportTables.nameCount);
748                                 // Export Address Table RVA
749                                 mw.Write(exportTables.exportAddressTableRVA);
750                                 // Name Pointer RVA
751                                 mw.Write(exportTables.exportNamePointerTableRVA);
752                                 // Ordinal Table RVA
753                                 mw.Write(exportTables.exportOrdinalTableRVA);
754                         }
755                 }
756
757                 private void WriteExportTables(MetadataWriter mw, uint sdataRVA)
758                 {
759                         if (exportTables != null)
760                         {
761                                 exportTables.Write(mw, sdataRVA);
762                         }
763                 }
764
765                 private void WriteImportDirectory(MetadataWriter mw)
766                 {
767                         mw.Write(ImportDirectoryRVA + 40);              // ImportLookupTable
768                         mw.Write(0);                                                    // DateTimeStamp
769                         mw.Write(0);                                                    // ForwarderChain
770                         mw.Write(ImportHintNameTableRVA + 14);  // Name
771                         mw.Write(ImportAddressTableRVA);
772                         mw.Write(new byte[20]);
773                         // Import Lookup Table
774                         mw.Write(ImportHintNameTableRVA);               // Hint/Name Table RVA
775                         int size = 48;
776                         if (peWriter.Headers.FileHeader.Machine != IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386)
777                         {
778                                 size += 4;
779                                 mw.Write(0);
780                         }
781                         mw.Write(0);
782
783                         // alignment padding
784                         for (int i = (int)(ImportHintNameTableRVA - (ImportDirectoryRVA + size)); i > 0; i--)
785                         {
786                                 mw.Write((byte)0);
787                         }
788
789                         // Hint/Name Table
790                         AssertRVA(mw, ImportHintNameTableRVA);
791                         mw.Write((ushort)0);            // Hint
792                         if ((peWriter.Headers.FileHeader.Characteristics & IMAGE_FILE_HEADER.IMAGE_FILE_DLL) != 0)
793                         {
794                                 mw.Write(System.Text.Encoding.ASCII.GetBytes("_CorDllMain"));
795                         }
796                         else
797                         {
798                                 mw.Write(System.Text.Encoding.ASCII.GetBytes("_CorExeMain"));
799                         }
800                         mw.Write((byte)0);
801                         // Name
802                         mw.Write(System.Text.Encoding.ASCII.GetBytes("mscoree.dll"));
803                         mw.Write((ushort)0);
804                 }
805
806                 internal int Length
807                 {
808                         get { return (int)(StartupStubRVA - BaseRVA + StartupStubLength); }
809                 }
810
811                 internal void WriteRelocations(MetadataWriter mw)
812                 {
813                         uint relocAddress = this.StartupStubRVA;
814                         switch (peWriter.Headers.FileHeader.Machine)
815                         {
816                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
817                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
818                                         relocAddress += 2;
819                                         break;
820                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
821                                         relocAddress += 0x20;
822                                         break;
823                         }
824                         WriteRelocationBlock(mw, relocAddress);
825                         if (exportTables != null)
826                         {
827                                 exportTables.WriteRelocations(mw);
828                         }
829                 }
830
831                 // note that we're lazy and write a new relocation block for every relocation
832                 // even if they are in the same page (since there is typically only one anyway)
833                 private void WriteRelocationBlock(MetadataWriter mw, uint relocAddress)
834                 {
835                         uint pageRVA = relocAddress & ~0xFFFU;
836                         mw.Write(pageRVA);      // PageRVA
837                         mw.Write(0x000C);       // Block Size
838                         switch (peWriter.Headers.FileHeader.Machine)
839                         {
840                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
841                                         mw.Write(0x3000 + relocAddress - pageRVA);                              // Type / Offset
842                                         break;
843                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
844                                         mw.Write(0xA000 + relocAddress - pageRVA);                              // Type / Offset
845                                         break;
846                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
847                                         // on IA64 the StartupStubRVA is 16 byte aligned, so these two addresses won't cross a page boundary
848                                         mw.Write((short)(0xA000 + relocAddress - pageRVA));             // Type / Offset
849                                         mw.Write((short)(0xA000 + relocAddress - pageRVA + 8)); // Type / Offset
850                                         break;
851                         }
852                 }
853         }
854 }