Fix null sessions in HttpContextWrapper.Session
[mono.git] / mcs / class / IKVM.Reflection / Writer / ModuleWriter.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.Collections.Generic;
26 using System.Diagnostics;
27 using System.IO;
28 using System.Security.Cryptography;
29 using IKVM.Reflection.Emit;
30 using IKVM.Reflection.Impl;
31 using IKVM.Reflection.Metadata;
32
33 namespace IKVM.Reflection.Writer
34 {
35         static class ModuleWriter
36         {
37                 internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder,
38                         PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine,
39                         ResourceSection resources, int entryPointToken)
40                 {
41                         WriteModule(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, null);
42                 }
43
44                 internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder,
45                         PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine,
46                         ResourceSection resources, int entryPointToken, Stream stream)
47                 {
48                         if (stream == null)
49                         {
50                                 string fileName = moduleBuilder.FullyQualifiedName;
51                                 bool mono = System.Type.GetType("Mono.Runtime") != null;
52                                 if (mono)
53                                 {
54                                         try
55                                         {
56                                                 // Mono mmaps the file, so unlink the previous version since it may be in use
57                                                 File.Delete(fileName);
58                                         }
59                                         catch { }
60                                 }
61                                 using (FileStream fs = new FileStream(fileName, FileMode.Create))
62                                 {
63                                         WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, fs);
64                                 }
65                                 // if we're running on Mono, mark the module as executable by using a Mono private API extension
66                                 if (mono)
67                                 {
68                                         File.SetAttributes(fileName, (FileAttributes)(unchecked((int)0x80000000)));
69                                 }
70                         }
71                         else
72                         {
73                                 WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, stream);
74                         }
75                 }
76
77                 private static void WriteModuleImpl(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder,
78                         PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine,
79                         ResourceSection resources, int entryPointToken, Stream stream)
80                 {
81                         moduleBuilder.ApplyUnmanagedExports(imageFileMachine);
82                         moduleBuilder.FixupMethodBodyTokens();
83
84                         moduleBuilder.ModuleTable.Add(0, moduleBuilder.Strings.Add(moduleBuilder.moduleName), moduleBuilder.Guids.Add(moduleBuilder.ModuleVersionId), 0, 0);
85
86                         if (moduleBuilder.UserStrings.IsEmpty)
87                         {
88                                 // for compat with Ref.Emit, if there aren't any user strings, we add one
89                                 moduleBuilder.UserStrings.Add(" ");
90                         }
91
92                         if (resources != null)
93                         {
94                                 resources.Finish();
95                         }
96
97                         PEWriter writer = new PEWriter(stream);
98                         writer.Headers.OptionalHeader.FileAlignment = (uint)moduleBuilder.__FileAlignment;
99                         switch (imageFileMachine)
100                         {
101                                 case ImageFileMachine.I386:
102                                         writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386;
103                                         writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE;
104                                         writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000);
105                                         break;
106                                 case ImageFileMachine.ARM:
107                                         writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM;
108                                         writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE;
109                                         writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000);
110                                         break;
111                                 case ImageFileMachine.AMD64:
112                                         writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64;
113                                         writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE;
114                                         writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0;
115                                         writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC;
116                                         writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000);
117                                         writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000;
118                                         writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000;
119                                         break;
120                                 case ImageFileMachine.IA64:
121                                         writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64;
122                                         writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE;
123                                         writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0;
124                                         writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC;
125                                         writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000);
126                                         writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000;
127                                         writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000;
128                                         break;
129                                 default:
130                                         throw new ArgumentOutOfRangeException("imageFileMachine");
131                         }
132                         if (fileKind == PEFileKinds.Dll)
133                         {
134                                 writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_DLL;
135                         }
136
137                         switch (fileKind)
138                         {
139                                 case PEFileKinds.WindowApplication:
140                                         writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_GUI;
141                                         break;
142                                 default:
143                                         writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_CUI;
144                                         break;
145                         }
146                         writer.Headers.OptionalHeader.DllCharacteristics = (ushort)moduleBuilder.__DllCharacteristics;
147
148                         CliHeader cliHeader = new CliHeader();
149                         cliHeader.Cb = 0x48;
150                         cliHeader.MajorRuntimeVersion = 2;
151                         cliHeader.MinorRuntimeVersion = moduleBuilder.MDStreamVersion < 0x20000 ? (ushort)0 : (ushort)5;
152                         if ((portableExecutableKind & PortableExecutableKinds.ILOnly) != 0)
153                         {
154                                 cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_ILONLY;
155                         }
156                         if ((portableExecutableKind & PortableExecutableKinds.Required32Bit) != 0)
157                         {
158                                 cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED;
159                         }
160                         if ((portableExecutableKind & PortableExecutableKinds.Preferred32Bit) != 0)
161                         {
162                                 cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED;
163                         }
164                         if (keyPair != null)
165                         {
166                                 cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_STRONGNAMESIGNED;
167                         }
168                         if (moduleBuilder.IsPseudoToken(entryPointToken))
169                         {
170                                 entryPointToken = moduleBuilder.ResolvePseudoToken(entryPointToken);
171                         }
172                         cliHeader.EntryPointToken = (uint)entryPointToken;
173
174                         moduleBuilder.Strings.Freeze();
175                         moduleBuilder.UserStrings.Freeze();
176                         moduleBuilder.Guids.Freeze();
177                         moduleBuilder.Blobs.Freeze();
178                         MetadataWriter mw = new MetadataWriter(moduleBuilder, stream);
179                         moduleBuilder.Tables.Freeze(mw);
180                         TextSection code = new TextSection(writer, cliHeader, moduleBuilder, ComputeStrongNameSignatureLength(publicKey));
181
182                         // Export Directory
183                         if (code.ExportDirectoryLength != 0)
184                         {
185                                 writer.Headers.OptionalHeader.DataDirectory[0].VirtualAddress = code.ExportDirectoryRVA;
186                                 writer.Headers.OptionalHeader.DataDirectory[0].Size = code.ExportDirectoryLength;
187                         }
188
189                         // Import Directory
190                         if (code.ImportDirectoryLength != 0)
191                         {
192                                 writer.Headers.OptionalHeader.DataDirectory[1].VirtualAddress = code.ImportDirectoryRVA;
193                                 writer.Headers.OptionalHeader.DataDirectory[1].Size = code.ImportDirectoryLength;
194                         }
195
196                         // Import Address Table Directory
197                         if (code.ImportAddressTableLength != 0)
198                         {
199                                 writer.Headers.OptionalHeader.DataDirectory[12].VirtualAddress = code.ImportAddressTableRVA;
200                                 writer.Headers.OptionalHeader.DataDirectory[12].Size = code.ImportAddressTableLength;
201                         }
202
203                         // COM Descriptor Directory
204                         writer.Headers.OptionalHeader.DataDirectory[14].VirtualAddress = code.ComDescriptorRVA;
205                         writer.Headers.OptionalHeader.DataDirectory[14].Size = code.ComDescriptorLength;
206
207                         // Debug Directory
208                         if (code.DebugDirectoryLength != 0)
209                         {
210                                 writer.Headers.OptionalHeader.DataDirectory[6].VirtualAddress = code.DebugDirectoryRVA;
211                                 writer.Headers.OptionalHeader.DataDirectory[6].Size = code.DebugDirectoryLength;
212                         }
213
214                         // we need to start by computing the number of sections, because code.PointerToRawData depends on that
215                         writer.Headers.FileHeader.NumberOfSections = 1;
216
217                         if (moduleBuilder.initializedData.Length != 0)
218                         {
219                                 // .sdata
220                                 writer.Headers.FileHeader.NumberOfSections++;
221                         }
222
223                         if (resources != null)
224                         {
225                                 // .rsrc
226                                 writer.Headers.FileHeader.NumberOfSections++;
227                         }
228
229                         if (imageFileMachine != ImageFileMachine.ARM)
230                         {
231                                 // .reloc
232                                 writer.Headers.FileHeader.NumberOfSections++;
233                         }
234
235                         SectionHeader text = new SectionHeader();
236                         text.Name = ".text";
237                         text.VirtualAddress = code.BaseRVA;
238                         text.VirtualSize = (uint)code.Length;
239                         text.PointerToRawData = code.PointerToRawData;
240                         text.SizeOfRawData = writer.ToFileAlignment((uint)code.Length);
241                         text.Characteristics = SectionHeader.IMAGE_SCN_CNT_CODE | SectionHeader.IMAGE_SCN_MEM_EXECUTE | SectionHeader.IMAGE_SCN_MEM_READ;
242
243                         SectionHeader sdata = new SectionHeader();
244                         sdata.Name = ".sdata";
245                         sdata.VirtualAddress = text.VirtualAddress + writer.ToSectionAlignment(text.VirtualSize);
246                         sdata.VirtualSize = (uint)moduleBuilder.initializedData.Length;
247                         sdata.PointerToRawData = text.PointerToRawData + text.SizeOfRawData;
248                         sdata.SizeOfRawData = writer.ToFileAlignment((uint)moduleBuilder.initializedData.Length);
249                         sdata.Characteristics = SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_MEM_WRITE;
250
251                         SectionHeader rsrc = new SectionHeader();
252                         rsrc.Name = ".rsrc";
253                         rsrc.VirtualAddress = sdata.VirtualAddress + writer.ToSectionAlignment(sdata.VirtualSize);
254                         rsrc.PointerToRawData = sdata.PointerToRawData + sdata.SizeOfRawData;
255                         rsrc.VirtualSize = resources == null ? 0 : (uint)resources.Length;
256                         rsrc.SizeOfRawData = writer.ToFileAlignment(rsrc.VirtualSize);
257                         rsrc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA;
258
259                         if (rsrc.SizeOfRawData != 0)
260                         {
261                                 // Resource Directory
262                                 writer.Headers.OptionalHeader.DataDirectory[2].VirtualAddress = rsrc.VirtualAddress;
263                                 writer.Headers.OptionalHeader.DataDirectory[2].Size = rsrc.VirtualSize;
264                         }
265
266                         SectionHeader reloc = new SectionHeader();
267                         reloc.Name = ".reloc";
268                         reloc.VirtualAddress = rsrc.VirtualAddress + writer.ToSectionAlignment(rsrc.VirtualSize);
269                         if (imageFileMachine != ImageFileMachine.ARM)
270                         {
271                                 reloc.VirtualSize = ((uint)moduleBuilder.unmanagedExports.Count + 1) * 12;
272                         }
273                         reloc.PointerToRawData = rsrc.PointerToRawData + rsrc.SizeOfRawData;
274                         reloc.SizeOfRawData = writer.ToFileAlignment(reloc.VirtualSize);
275                         reloc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_DISCARDABLE;
276
277                         if (reloc.SizeOfRawData != 0)
278                         {
279                                 // Base Relocation Directory
280                                 writer.Headers.OptionalHeader.DataDirectory[5].VirtualAddress = reloc.VirtualAddress;
281                                 writer.Headers.OptionalHeader.DataDirectory[5].Size = reloc.VirtualSize;
282                         }
283
284                         writer.Headers.OptionalHeader.SizeOfCode = text.SizeOfRawData;
285                         writer.Headers.OptionalHeader.SizeOfInitializedData = sdata.SizeOfRawData + rsrc.SizeOfRawData + reloc.SizeOfRawData;
286                         writer.Headers.OptionalHeader.SizeOfUninitializedData = 0;
287                         writer.Headers.OptionalHeader.SizeOfImage = reloc.VirtualAddress + writer.ToSectionAlignment(reloc.VirtualSize);
288                         writer.Headers.OptionalHeader.SizeOfHeaders = text.PointerToRawData;
289                         writer.Headers.OptionalHeader.BaseOfCode = code.BaseRVA;
290                         writer.Headers.OptionalHeader.BaseOfData = sdata.VirtualAddress;
291                         writer.Headers.OptionalHeader.ImageBase = (ulong)moduleBuilder.__ImageBase;
292
293                         if (imageFileMachine == ImageFileMachine.IA64)
294                         {
295                                 // apparently for IA64 AddressOfEntryPoint points to the address of the entry point
296                                 // (i.e. there is an additional layer of indirection), so we add the offset to the pointer
297                                 writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + 0x20;
298                         }
299                         else if (imageFileMachine != ImageFileMachine.ARM)
300                         {
301                                 writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA;
302                         }
303
304                         writer.WritePEHeaders();
305                         writer.WriteSectionHeader(text);
306                         if (sdata.SizeOfRawData != 0)
307                         {
308                                 writer.WriteSectionHeader(sdata);
309                         }
310                         if (rsrc.SizeOfRawData != 0)
311                         {
312                                 writer.WriteSectionHeader(rsrc);
313                         }
314                         if (reloc.SizeOfRawData != 0)
315                         {
316                                 writer.WriteSectionHeader(reloc);
317                         }
318
319                         stream.Seek(text.PointerToRawData, SeekOrigin.Begin);
320                         code.Write(mw, sdata.VirtualAddress);
321
322                         if (sdata.SizeOfRawData != 0)
323                         {
324                                 stream.Seek(sdata.PointerToRawData, SeekOrigin.Begin);
325                                 mw.Write(moduleBuilder.initializedData);
326                         }
327
328                         if (rsrc.SizeOfRawData != 0)
329                         {
330                                 stream.Seek(rsrc.PointerToRawData, SeekOrigin.Begin);
331                                 resources.Write(mw, rsrc.VirtualAddress);
332                         }
333
334                         if (reloc.SizeOfRawData != 0)
335                         {
336                                 stream.Seek(reloc.PointerToRawData, SeekOrigin.Begin);
337                                 code.WriteRelocations(mw);
338                         }
339
340                         // file alignment
341                         stream.SetLength(reloc.PointerToRawData + reloc.SizeOfRawData);
342
343                         // do the strong naming
344                         if (keyPair != null)
345                         {
346                                 StrongName(stream, keyPair, writer.HeaderSize, text.PointerToRawData, code.StrongNameSignatureRVA - text.VirtualAddress + text.PointerToRawData, code.StrongNameSignatureLength);
347                         }
348
349                         if (moduleBuilder.symbolWriter != null)
350                         {
351                                 moduleBuilder.WriteSymbolTokenMap();
352                                 moduleBuilder.symbolWriter.Close();
353                         }
354                 }
355
356                 private static int ComputeStrongNameSignatureLength(byte[] publicKey)
357                 {
358                         if (publicKey == null)
359                         {
360                                 return 0;
361                         }
362                         else if (publicKey.Length == 16)
363                         {
364                                 // it must be the ECMA pseudo public key, we don't know the key size of the real key, but currently both Mono and Microsoft use a 1024 bit key size
365                                 return 128;
366                         }
367                         else
368                         {
369                                 // for the supported strong naming algorithms, the signature size is the same as the key size
370                                 // (we have to subtract 32 for the header)
371                                 return publicKey.Length - 32;
372                         }
373                 }
374
375                 private static void StrongName(Stream stream, StrongNameKeyPair keyPair, uint headerLength, uint textSectionFileOffset, uint strongNameSignatureFileOffset, uint strongNameSignatureLength)
376                 {
377                         SHA1Managed hash = new SHA1Managed();
378                         using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write))
379                         {
380                                 stream.Seek(0, SeekOrigin.Begin);
381                                 byte[] buf = new byte[8192];
382                                 HashChunk(stream, cs, buf, (int)headerLength);
383                                 stream.Seek(textSectionFileOffset, SeekOrigin.Begin);
384                                 HashChunk(stream, cs, buf, (int)(strongNameSignatureFileOffset - textSectionFileOffset));
385                                 stream.Seek(strongNameSignatureLength, SeekOrigin.Current);
386                                 HashChunk(stream, cs, buf, (int)(stream.Length - (strongNameSignatureFileOffset + strongNameSignatureLength)));
387                         }
388                         using (RSA rsa = keyPair.CreateRSA())
389                         {
390                                 RSAPKCS1SignatureFormatter sign = new RSAPKCS1SignatureFormatter(rsa);
391                                 byte[] signature = sign.CreateSignature(hash);
392                                 Array.Reverse(signature);
393                                 if (signature.Length != strongNameSignatureLength)
394                                 {
395                                         throw new InvalidOperationException("Signature length mismatch");
396                                 }
397                                 stream.Seek(strongNameSignatureFileOffset, SeekOrigin.Begin);
398                                 stream.Write(signature, 0, signature.Length);
399                         }
400
401                         // compute the PE checksum
402                         stream.Seek(0, SeekOrigin.Begin);
403                         int count = (int)stream.Length / 4;
404                         BinaryReader br = new BinaryReader(stream);
405                         long sum = 0;
406                         for (int i = 0; i < count; i++)
407                         {
408                                 sum += br.ReadUInt32();
409                                 int carry = (int)(sum >> 32);
410                                 sum &= 0xFFFFFFFFU;
411                                 sum += carry;
412                         }
413                         while ((sum >> 16) != 0)
414                         {
415                                 sum = (sum & 0xFFFF) + (sum >> 16);
416                         }
417                         sum += stream.Length;
418
419                         // write the PE checksum, note that it is always at offset 0xD8 in the file
420                         ByteBuffer bb = new ByteBuffer(4);
421                         bb.Write((int)sum);
422                         stream.Seek(0xD8, SeekOrigin.Begin);
423                         bb.WriteTo(stream);
424                 }
425
426                 internal static void HashChunk(Stream stream, CryptoStream cs, byte[] buf, int length)
427                 {
428                         while (length > 0)
429                         {
430                                 int read = stream.Read(buf, 0, Math.Min(buf.Length, length));
431                                 cs.Write(buf, 0, read);
432                                 length -= read;
433                         }
434                 }
435         }
436 }