internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ResourceSection resources, int entryPointToken, Stream stream) { if (stream == null) { string fileName = moduleBuilder.FullyQualifiedName; bool mono = System.Type.GetType("Mono.Runtime") != null; if (mono) { try { // Mono mmaps the file, so unlink the previous version since it may be in use File.Delete(fileName); } catch { } } using (FileStream fs = new FileStream(fileName, FileMode.Create)) { WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, fs); } // if we're running on Mono, mark the module as executable by using a Mono private API extension if (mono) { File.SetAttributes(fileName, (FileAttributes)(unchecked ((int)0x80000000))); } } else { WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, stream); } }
private static void WriteModuleImpl(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ResourceSection resources, int entryPointToken, Stream stream) { moduleBuilder.ApplyUnmanagedExports(imageFileMachine); moduleBuilder.FixupMethodBodyTokens(); int moduleVersionIdIndex = moduleBuilder.Guids.Add(moduleBuilder.GetModuleVersionIdOrEmpty()); moduleBuilder.ModuleTable.Add(0, moduleBuilder.Strings.Add(moduleBuilder.moduleName), moduleVersionIdIndex, 0, 0); if (moduleBuilder.UserStrings.IsEmpty) { // for compat with Ref.Emit, if there aren't any user strings, we add one moduleBuilder.UserStrings.Add(" "); } if (resources != null) { resources.Finish(); } PEWriter writer = new PEWriter(stream); writer.Headers.OptionalHeader.FileAlignment = (uint)moduleBuilder.__FileAlignment; switch (imageFileMachine) { case ImageFileMachine.I386: writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386; writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE; writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000); break; case ImageFileMachine.ARM: writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM; writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000); writer.Headers.OptionalHeader.SectionAlignment = 0x1000; break; case ImageFileMachine.AMD64: writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64; writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000); writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000; writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; break; case ImageFileMachine.IA64: writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64; writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000); writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000; writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; break; default: throw new ArgumentOutOfRangeException("imageFileMachine"); } if (fileKind == PEFileKinds.Dll) { writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_DLL; } switch (fileKind) { case PEFileKinds.WindowApplication: writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_GUI; break; default: writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_CUI; break; } writer.Headers.OptionalHeader.DllCharacteristics = (ushort)moduleBuilder.__DllCharacteristics; CliHeader cliHeader = new CliHeader(); cliHeader.Cb = 0x48; cliHeader.MajorRuntimeVersion = 2; cliHeader.MinorRuntimeVersion = moduleBuilder.MDStreamVersion < 0x20000 ? (ushort)0 : (ushort)5; if ((portableExecutableKind & PortableExecutableKinds.ILOnly) != 0) { cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_ILONLY; } if ((portableExecutableKind & PortableExecutableKinds.Required32Bit) != 0) { cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED; } if ((portableExecutableKind & PortableExecutableKinds.Preferred32Bit) != 0) { cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED; } if (keyPair != null) { cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_STRONGNAMESIGNED; } if (ModuleBuilder.IsPseudoToken(entryPointToken)) { entryPointToken = moduleBuilder.ResolvePseudoToken(entryPointToken); } cliHeader.EntryPointToken = (uint)entryPointToken; moduleBuilder.Strings.Freeze(); moduleBuilder.UserStrings.Freeze(); moduleBuilder.Guids.Freeze(); moduleBuilder.Blobs.Freeze(); var mw = new ModuleBuilderMetadataWriter(moduleBuilder, stream); moduleBuilder.Tables.Freeze(mw); TextSection code = new TextSection(writer, cliHeader, moduleBuilder, ComputeStrongNameSignatureLength(publicKey)); // Export Directory if (code.ExportDirectoryLength != 0) { writer.Headers.OptionalHeader.DataDirectory[0].VirtualAddress = code.ExportDirectoryRVA; writer.Headers.OptionalHeader.DataDirectory[0].Size = code.ExportDirectoryLength; } // Import Directory if (code.ImportDirectoryLength != 0) { writer.Headers.OptionalHeader.DataDirectory[1].VirtualAddress = code.ImportDirectoryRVA; writer.Headers.OptionalHeader.DataDirectory[1].Size = code.ImportDirectoryLength; } // Import Address Table Directory if (code.ImportAddressTableLength != 0) { writer.Headers.OptionalHeader.DataDirectory[12].VirtualAddress = code.ImportAddressTableRVA; writer.Headers.OptionalHeader.DataDirectory[12].Size = code.ImportAddressTableLength; } // COM Descriptor Directory writer.Headers.OptionalHeader.DataDirectory[14].VirtualAddress = code.ComDescriptorRVA; writer.Headers.OptionalHeader.DataDirectory[14].Size = code.ComDescriptorLength; // Debug Directory if (code.DebugDirectoryLength != 0) { writer.Headers.OptionalHeader.DataDirectory[6].VirtualAddress = code.DebugDirectoryRVA; writer.Headers.OptionalHeader.DataDirectory[6].Size = code.DebugDirectoryLength; } // Set the PE File timestamp writer.Headers.FileHeader.TimeDateStamp = moduleBuilder.GetTimeDateStamp(); // we need to start by computing the number of sections, because code.PointerToRawData depends on that writer.Headers.FileHeader.NumberOfSections = 2; if (moduleBuilder.initializedData.Length != 0) { // .sdata writer.Headers.FileHeader.NumberOfSections++; } if (resources != null) { // .rsrc writer.Headers.FileHeader.NumberOfSections++; } SectionHeader text = new SectionHeader(); text.Name = ".text"; text.VirtualAddress = code.BaseRVA; text.VirtualSize = (uint)code.Length; text.PointerToRawData = code.PointerToRawData; text.SizeOfRawData = writer.ToFileAlignment((uint)code.Length); text.Characteristics = SectionHeader.IMAGE_SCN_CNT_CODE | SectionHeader.IMAGE_SCN_MEM_EXECUTE | SectionHeader.IMAGE_SCN_MEM_READ; SectionHeader sdata = new SectionHeader(); sdata.Name = ".sdata"; sdata.VirtualAddress = text.VirtualAddress + writer.ToSectionAlignment(text.VirtualSize); sdata.VirtualSize = (uint)moduleBuilder.initializedData.Length; sdata.PointerToRawData = text.PointerToRawData + text.SizeOfRawData; sdata.SizeOfRawData = writer.ToFileAlignment((uint)moduleBuilder.initializedData.Length); sdata.Characteristics = SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_MEM_WRITE; SectionHeader rsrc = new SectionHeader(); rsrc.Name = ".rsrc"; rsrc.VirtualAddress = sdata.VirtualAddress + writer.ToSectionAlignment(sdata.VirtualSize); rsrc.PointerToRawData = sdata.PointerToRawData + sdata.SizeOfRawData; rsrc.VirtualSize = resources == null ? 0 : (uint)resources.Length; rsrc.SizeOfRawData = writer.ToFileAlignment(rsrc.VirtualSize); rsrc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA; if (rsrc.SizeOfRawData != 0) { // Resource Directory writer.Headers.OptionalHeader.DataDirectory[2].VirtualAddress = rsrc.VirtualAddress; writer.Headers.OptionalHeader.DataDirectory[2].Size = rsrc.VirtualSize; } SectionHeader reloc = new SectionHeader(); reloc.Name = ".reloc"; reloc.VirtualAddress = rsrc.VirtualAddress + writer.ToSectionAlignment(rsrc.VirtualSize); reloc.VirtualSize = code.PackRelocations(); reloc.PointerToRawData = rsrc.PointerToRawData + rsrc.SizeOfRawData; reloc.SizeOfRawData = writer.ToFileAlignment(reloc.VirtualSize); reloc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_DISCARDABLE; if (reloc.SizeOfRawData != 0) { // Base Relocation Directory writer.Headers.OptionalHeader.DataDirectory[5].VirtualAddress = reloc.VirtualAddress; writer.Headers.OptionalHeader.DataDirectory[5].Size = reloc.VirtualSize; } writer.Headers.OptionalHeader.SizeOfCode = text.SizeOfRawData; writer.Headers.OptionalHeader.SizeOfInitializedData = sdata.SizeOfRawData + rsrc.SizeOfRawData + reloc.SizeOfRawData; writer.Headers.OptionalHeader.SizeOfUninitializedData = 0; writer.Headers.OptionalHeader.SizeOfImage = reloc.VirtualAddress + writer.ToSectionAlignment(reloc.VirtualSize); writer.Headers.OptionalHeader.SizeOfHeaders = text.PointerToRawData; writer.Headers.OptionalHeader.BaseOfCode = code.BaseRVA; writer.Headers.OptionalHeader.BaseOfData = sdata.VirtualAddress; writer.Headers.OptionalHeader.ImageBase = (ulong)moduleBuilder.__ImageBase; if (imageFileMachine == ImageFileMachine.IA64) { // apparently for IA64 AddressOfEntryPoint points to the address of the entry point // (i.e. there is an additional layer of indirection), so we add the offset to the pointer writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + 0x20; } else { writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + writer.Thumb; } var timeDateStampPosition = writer.WritePEHeaders(); writer.WriteSectionHeader(text); if (sdata.SizeOfRawData != 0) { writer.WriteSectionHeader(sdata); } if (rsrc.SizeOfRawData != 0) { writer.WriteSectionHeader(rsrc); } if (reloc.SizeOfRawData != 0) { writer.WriteSectionHeader(reloc); } stream.Seek(text.PointerToRawData, SeekOrigin.Begin); uint guidHeapOffset; code.Write(mw, sdata.VirtualAddress, out guidHeapOffset); if (sdata.SizeOfRawData != 0) { stream.Seek(sdata.PointerToRawData, SeekOrigin.Begin); mw.Write(moduleBuilder.initializedData); } if (rsrc.SizeOfRawData != 0) { stream.Seek(rsrc.PointerToRawData, SeekOrigin.Begin); resources.Write(mw, rsrc.VirtualAddress); } if (reloc.SizeOfRawData != 0) { stream.Seek(reloc.PointerToRawData, SeekOrigin.Begin); code.WriteRelocations(mw); } // file alignment stream.SetLength(reloc.PointerToRawData + reloc.SizeOfRawData); if (moduleBuilder.symbolWriter != null) { moduleBuilder.WriteSymbolTokenMap(); moduleBuilder.symbolWriter.Close(); } // if we don't have a guid or timestamp, generate one based on the contents of the assembly if (moduleBuilder.universe.Deterministic && (moduleBuilder.GetModuleVersionIdOrEmpty() == Guid.Empty || moduleBuilder.GetTimeDateStamp() == 0 || moduleBuilder.symbolWriter != null)) { uint timestamp; Guid guid = GenerateModuleVersionIdAndPseudoTimestamp(stream, out timestamp); if (moduleBuilder.GetModuleVersionIdOrEmpty() == Guid.Empty) { // patch the MVID in the GUID stream stream.Position = guidHeapOffset + (moduleVersionIdIndex - 1) * 16; stream.Write(guid.ToByteArray(), 0, 16); moduleBuilder.__SetModuleVersionId(guid); } if (moduleBuilder.GetTimeDateStamp() == 0) { // patch the TimeDateStamp in IMAGE_FILE_HEADER stream.Position = timeDateStampPosition; stream.Write(BitConverter.GetBytes(timestamp), 0, 4); moduleBuilder.SetTimeDateStamp(timestamp); } if (moduleBuilder.symbolWriter != null) { stream.Position = code.DebugDirectoryRVA - code.BaseRVA + code.PointerToRawData; code.PatchDebugDirectory(mw); } } // do the strong naming if (keyPair != null) { StrongName(stream, keyPair, writer.HeaderSize, text.PointerToRawData, code.StrongNameSignatureRVA - text.VirtualAddress + text.PointerToRawData, code.StrongNameSignatureLength); } }
internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ResourceSection resources, int entryPointToken) { WriteModule(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, null); }