do not check order sequence if option /order was not used
[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                                 idd.AddressOfRawData = DebugDirectoryRVA + DebugDirectoryLength;
483                                 mw.Write(idd.Characteristics);
484                                 mw.Write(idd.TimeDateStamp);
485                                 mw.Write(idd.MajorVersion);
486                                 mw.Write(idd.MinorVersion);
487                                 mw.Write(idd.Type);
488                                 mw.Write(idd.SizeOfData);
489                                 mw.Write(idd.AddressOfRawData);
490                                 mw.Write(idd.PointerToRawData);
491                                 mw.Write(buf);
492                         }
493                 }
494
495                 private sealed class ExportTables
496                 {
497                         private readonly TextSection text;
498                         internal readonly uint entries;
499                         internal readonly uint ordinalBase;
500                         internal readonly uint nameCount;
501                         internal readonly uint namesLength;
502                         internal readonly uint exportAddressTableRVA;
503                         internal readonly uint exportNamePointerTableRVA;
504                         internal readonly uint exportOrdinalTableRVA;
505                         internal readonly uint namesRVA;
506                         internal readonly uint stubsRVA;
507                         private readonly uint stubLength;
508
509                         internal ExportTables(TextSection text)
510                         {
511                                 this.text = text;
512                                 ordinalBase = GetOrdinalBase(out entries);
513                                 namesLength = GetExportNamesLength(out nameCount);
514                                 exportAddressTableRVA = text.ExportTablesRVA;
515                                 exportNamePointerTableRVA = exportAddressTableRVA + 4 * entries;
516                                 exportOrdinalTableRVA = exportNamePointerTableRVA + 4 * nameCount;
517                                 namesRVA = exportOrdinalTableRVA + 2 * nameCount;
518                                 stubsRVA = (namesRVA + namesLength + 15) & ~15U;
519                                 // note that we align the stubs to avoid having to deal with the relocation crossing a page boundary
520                                 switch (text.peWriter.Headers.FileHeader.Machine)
521                                 {
522                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
523                                                 stubLength = 8;
524                                                 break;
525                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
526                                                 stubLength = 16;
527                                                 break;
528                                         default:
529                                                 throw new NotImplementedException();
530                                 }
531                         }
532
533                         internal uint Length
534                         {
535                                 get { return (stubsRVA + stubLength * (uint)text.moduleBuilder.unmanagedExports.Count) - text.ExportTablesRVA; }
536                         }
537
538                         private uint GetOrdinalBase(out uint entries)
539                         {
540                                 uint min = uint.MaxValue;
541                                 uint max = uint.MinValue;
542                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
543                                 {
544                                         uint ordinal = (uint)exp.ordinal;
545                                         min = Math.Min(min, ordinal);
546                                         max = Math.Max(max, ordinal);
547                                 }
548                                 entries = 1 + (max - min);
549                                 return min;
550                         }
551
552                         private uint GetExportNamesLength(out uint nameCount)
553                         {
554                                 nameCount = 0;
555                                 // the first name in the names list is the module name (the Export Directory contains a name of the current module)
556                                 uint length = (uint)text.moduleBuilder.fileName.Length + 1;
557                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
558                                 {
559                                         if (exp.name != null)
560                                         {
561                                                 nameCount++;
562                                                 length += (uint)exp.name.Length + 1;
563                                         }
564                                 }
565                                 return length;
566                         }
567
568                         internal void Write(MetadataWriter mw, uint sdataRVA)
569                         {
570                                 // sort the exports by ordinal
571                                 text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportOrdinals);
572
573                                 // Now write the Export Address Table
574                                 text.AssertRVA(mw, exportAddressTableRVA);
575                                 for (int i = 0, pos = 0; i < entries; i++)
576                                 {
577                                         if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
578                                         {
579                                                 mw.Write(stubsRVA + (uint)pos * stubLength);
580                                                 pos++;
581                                         }
582                                         else
583                                         {
584                                                 mw.Write(0);
585                                         }
586                                 }
587
588                                 // sort the exports by name
589                                 text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportNames);
590
591                                 // Now write the Export Name Pointer Table
592                                 text.AssertRVA(mw, exportNamePointerTableRVA);
593                                 uint nameOffset = (uint)text.moduleBuilder.fileName.Length + 1;
594                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
595                                 {
596                                         if (exp.name != null)
597                                         {
598                                                 mw.Write(namesRVA + nameOffset);
599                                                 nameOffset += (uint)exp.name.Length + 1;
600                                         }
601                                 }
602
603                                 // Now write the Export Ordinal Table
604                                 text.AssertRVA(mw, exportOrdinalTableRVA);
605                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
606                                 {
607                                         if (exp.name != null)
608                                         {
609                                                 mw.Write((ushort)(exp.ordinal - ordinalBase));
610                                         }
611                                 }
612
613                                 // Now write the actual names
614                                 text.AssertRVA(mw, namesRVA);
615                                 mw.Write(Encoding.ASCII.GetBytes(text.moduleBuilder.fileName));
616                                 mw.Write((byte)0);
617                                 foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
618                                 {
619                                         if (exp.name != null)
620                                         {
621                                                 mw.Write(Encoding.ASCII.GetBytes(exp.name));
622                                                 mw.Write((byte)0);
623                                         }
624                                 }
625                                 text.AssertRVA(mw, namesRVA + namesLength);
626
627                                 // alignment padding
628                                 for (int i = (int)(stubsRVA - (namesRVA + namesLength)); i > 0; i--)
629                                 {
630                                         mw.Write((byte)0);
631                                 }
632
633                                 // sort the exports by ordinal
634                                 text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportOrdinals);
635
636                                 // Now write the stubs
637                                 text.AssertRVA(mw, stubsRVA);
638
639                                 for (int i = 0, pos = 0; i < entries; i++)
640                                 {
641                                         if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
642                                         {
643                                                 switch (text.peWriter.Headers.FileHeader.Machine)
644                                                 {
645                                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
646                                                                 mw.Write((byte)0xFF);
647                                                                 mw.Write((byte)0x25);
648                                                                 mw.Write((uint)text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
649                                                                 mw.Write((short)0);     // alignment
650                                                                 break;
651                                                         case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
652                                                                 mw.Write((byte)0x48);
653                                                                 mw.Write((byte)0xA1);
654                                                                 mw.Write(text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
655                                                                 mw.Write((byte)0xFF);
656                                                                 mw.Write((byte)0xE0);
657                                                                 mw.Write(0); // alignment
658                                                                 break;
659                                                         default:
660                                                                 throw new NotImplementedException();
661                                                 }
662                                                 pos++;
663                                         }
664                                 }
665                         }
666
667                         private static int CompareUnmanagedExportNames(UnmanagedExport x, UnmanagedExport y)
668                         {
669                                 if (x.name == null)
670                                 {
671                                         return y.name == null ? 0 : 1;
672                                 }
673                                 if (y.name == null)
674                                 {
675                                         return -1;
676                                 }
677                                 return x.name.CompareTo(y.name);
678                         }
679
680                         private static int CompareUnmanagedExportOrdinals(UnmanagedExport x, UnmanagedExport y)
681                         {
682                                 return x.ordinal.CompareTo(y.ordinal);
683                         }
684
685                         internal void WriteRelocations(MetadataWriter mw)
686                         {
687                                 // we assume that unmanagedExports is still sorted by ordinal
688                                 for (int i = 0, pos = 0; i < entries; i++)
689                                 {
690                                         if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
691                                         {
692                                                 // both I386 and AMD64 have the address at offset 2
693                                                 text.WriteRelocationBlock(mw, stubsRVA + 2 + (uint)pos * stubLength);
694                                                 pos++;
695                                         }
696                                 }
697                         }
698                 }
699
700                 private uint GetOrdinalBase(out uint entries)
701                 {
702                         uint min = uint.MaxValue;
703                         uint max = uint.MinValue;
704                         foreach (UnmanagedExport exp in moduleBuilder.unmanagedExports)
705                         {
706                                 uint ordinal = (uint)exp.ordinal;
707                                 min = Math.Min(min, ordinal);
708                                 max = Math.Max(max, ordinal);
709                         }
710                         entries = 1 + (max - min);
711                         return min;
712                 }
713
714                 private uint GetExportNamesLength(out uint nameCount)
715                 {
716                         nameCount = 0;
717                         uint length = 0;
718                         foreach (UnmanagedExport exp in moduleBuilder.unmanagedExports)
719                         {
720                                 if (exp.name != null)
721                                 {
722                                         nameCount++;
723                                         length += (uint)exp.name.Length + 1;
724                                 }
725                         }
726                         return length;
727                 }
728
729                 private void WriteExportDirectory(MetadataWriter mw)
730                 {
731                         if (ExportDirectoryLength != 0)
732                         {
733                                 // Flags
734                                 mw.Write(0);
735                                 // Date/Time Stamp
736                                 mw.Write(peWriter.Headers.FileHeader.TimeDateStamp);
737                                 // Major Version
738                                 mw.Write((short)0);
739                                 // Minor Version
740                                 mw.Write((short)0);
741                                 // Name RVA
742                                 mw.Write(exportTables.namesRVA);
743                                 // Ordinal Base
744                                 mw.Write(exportTables.ordinalBase);
745                                 // Address Table Entries
746                                 mw.Write(exportTables.entries);
747                                 // Number of Name Pointers
748                                 mw.Write(exportTables.nameCount);
749                                 // Export Address Table RVA
750                                 mw.Write(exportTables.exportAddressTableRVA);
751                                 // Name Pointer RVA
752                                 mw.Write(exportTables.exportNamePointerTableRVA);
753                                 // Ordinal Table RVA
754                                 mw.Write(exportTables.exportOrdinalTableRVA);
755                         }
756                 }
757
758                 private void WriteExportTables(MetadataWriter mw, uint sdataRVA)
759                 {
760                         if (exportTables != null)
761                         {
762                                 exportTables.Write(mw, sdataRVA);
763                         }
764                 }
765
766                 private void WriteImportDirectory(MetadataWriter mw)
767                 {
768                         mw.Write(ImportDirectoryRVA + 40);              // ImportLookupTable
769                         mw.Write(0);                                                    // DateTimeStamp
770                         mw.Write(0);                                                    // ForwarderChain
771                         mw.Write(ImportHintNameTableRVA + 14);  // Name
772                         mw.Write(ImportAddressTableRVA);
773                         mw.Write(new byte[20]);
774                         // Import Lookup Table
775                         mw.Write(ImportHintNameTableRVA);               // Hint/Name Table RVA
776                         int size = 48;
777                         if (peWriter.Headers.FileHeader.Machine != IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386)
778                         {
779                                 size += 4;
780                                 mw.Write(0);
781                         }
782                         mw.Write(0);
783
784                         // alignment padding
785                         for (int i = (int)(ImportHintNameTableRVA - (ImportDirectoryRVA + size)); i > 0; i--)
786                         {
787                                 mw.Write((byte)0);
788                         }
789
790                         // Hint/Name Table
791                         AssertRVA(mw, ImportHintNameTableRVA);
792                         mw.Write((ushort)0);            // Hint
793                         if ((peWriter.Headers.FileHeader.Characteristics & IMAGE_FILE_HEADER.IMAGE_FILE_DLL) != 0)
794                         {
795                                 mw.Write(System.Text.Encoding.ASCII.GetBytes("_CorDllMain"));
796                         }
797                         else
798                         {
799                                 mw.Write(System.Text.Encoding.ASCII.GetBytes("_CorExeMain"));
800                         }
801                         mw.Write((byte)0);
802                         // Name
803                         mw.Write(System.Text.Encoding.ASCII.GetBytes("mscoree.dll"));
804                         mw.Write((ushort)0);
805                 }
806
807                 internal int Length
808                 {
809                         get { return (int)(StartupStubRVA - BaseRVA + StartupStubLength); }
810                 }
811
812                 internal void WriteRelocations(MetadataWriter mw)
813                 {
814                         uint relocAddress = this.StartupStubRVA;
815                         switch (peWriter.Headers.FileHeader.Machine)
816                         {
817                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
818                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
819                                         relocAddress += 2;
820                                         break;
821                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
822                                         relocAddress += 0x20;
823                                         break;
824                         }
825                         WriteRelocationBlock(mw, relocAddress);
826                         if (exportTables != null)
827                         {
828                                 exportTables.WriteRelocations(mw);
829                         }
830                 }
831
832                 // note that we're lazy and write a new relocation block for every relocation
833                 // even if they are in the same page (since there is typically only one anyway)
834                 private void WriteRelocationBlock(MetadataWriter mw, uint relocAddress)
835                 {
836                         uint pageRVA = relocAddress & ~0xFFFU;
837                         mw.Write(pageRVA);      // PageRVA
838                         mw.Write(0x000C);       // Block Size
839                         switch (peWriter.Headers.FileHeader.Machine)
840                         {
841                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
842                                         mw.Write(0x3000 + relocAddress - pageRVA);                              // Type / Offset
843                                         break;
844                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
845                                         mw.Write(0xA000 + relocAddress - pageRVA);                              // Type / Offset
846                                         break;
847                                 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
848                                         // on IA64 the StartupStubRVA is 16 byte aligned, so these two addresses won't cross a page boundary
849                                         mw.Write((short)(0xA000 + relocAddress - pageRVA));             // Type / Offset
850                                         mw.Write((short)(0xA000 + relocAddress - pageRVA + 8)); // Type / Offset
851                                         break;
852                         }
853                 }
854         }
855 }