示例#1
0
        public void PerformEmitting(
            Stream sink,
            EmittingArguments eArgs
            )
        {
            ArgumentValidator.ValidateNotNull("Stream", sink);
            ArgumentValidator.ValidateNotNull("Emitting arguments", eArgs);

            Boolean isPE64, hasRelocations;
            UInt16  peOptionalHeaderSize;
            UInt32  numSections, iatSize;

            CheckEmittingArgs(eArgs, out isPE64, out hasRelocations, out numSections, out peOptionalHeaderSize, out iatSize);

            var fAlign         = eArgs.FileAlignment;
            var sAlign         = eArgs.SectionAlignment;
            var importHintName = eArgs.ImportHintName;
            var imageBase      = eArgs.ImageBase;
            var moduleKind     = eArgs.ModuleKind;
            var strongName     = eArgs.StrongName;

            IList <CILMethodBase> allMethodDefs;
            CILAssemblyName       an;

            using (var md = new MetaDataWriter(eArgs, this._context, this._module, eArgs.AssemblyMapper, out allMethodDefs, out an))
            {
                var clrEntryPointToken = 0;
                if (eArgs.CLREntryPoint != null)
                {
                    var listIdx = allMethodDefs.IndexOf(eArgs.CLREntryPoint);
                    if (listIdx < 0)
                    {
                        throw new ArgumentException("Entry point method " + eArgs.CLREntryPoint + " is not from this module (" + this._module.Name + ").");
                    }
                    clrEntryPointToken = TokenUtils.EncodeToken(Tables.MethodDef, listIdx + 1);
                }

                // Start emitting headers
                // MS-DOS header
                var currentArray = new Byte[HeaderFieldOffsetsAndLengths.DOS_HEADER_AND_PE_SIG.Length];
                Array.Copy(HeaderFieldOffsetsAndLengths.DOS_HEADER_AND_PE_SIG, currentArray, HeaderFieldOffsetsAndLengths.DOS_HEADER_AND_PE_SIG.Length);
                sink.Write(currentArray);

                // PE file header
                currentArray = new Byte[HeaderFieldOffsetsAndLengths.PE_FILE_HEADER_SIZE];
                var characteristics = HeaderFieldPossibleValues.IMAGE_FILE_EXECUTABLE_IMAGE | (isPE64 ? HeaderFieldPossibleValues.IMAGE_FILE_LARGE_ADDRESS_AWARE : HeaderFieldPossibleValues.IMAGE_FILE_32BIT_MACHINE);
                if (moduleKind.IsDLL())
                {
                    characteristics |= HeaderFieldPossibleValues.IMAGE_FILE_DLL;
                }
                var idx = 0;
                currentArray
                .WriteUInt16LEToBytes(ref idx, (UInt16)eArgs.Machine)
                .WriteUInt16LEToBytes(ref idx, (UInt16)numSections)
                .WriteInt32LEToBytes(ref idx, Convert.ToInt32(DateTime.Now.Subtract(PE_HEADER_START_TIME).TotalSeconds))
                .Skip(ref idx, 8)
                .WriteUInt16LEToBytes(ref idx, peOptionalHeaderSize)
                .WriteInt16LEToBytes(ref idx, (Int16)characteristics);
                sink.Write(currentArray);

                // PE optional header + section headers + padding + IAT + CLI header + Strong signature
                var codeSectionVirtualOffset = sAlign;
                // Strong name signature

                var           useStrongName = strongName != null;
                var           snSize        = 0u;
                var           snRVA         = 0u;
                var           snPadding     = 0u;
                var           delaySign     = eArgs.DelaySign || (!useStrongName && !an.PublicKey.IsNullOrEmpty());
                RSAParameters rParams;
                var           signingAlgorithm = AssemblyHashAlgorithm.SHA1;
                if (useStrongName || delaySign)
                {
                    // Set appropriate module flags
                    eArgs.ModuleFlags |= ModuleFlags.StrongNameSigned;

                    // Check algorithm override
                    var algoOverride           = eArgs.SigningAlgorithm;
                    var algoOverrideWasInvalid = algoOverride.HasValue && (algoOverride.Value == AssemblyHashAlgorithm.MD5 || algoOverride.Value == AssemblyHashAlgorithm.None);
                    if (algoOverrideWasInvalid)
                    {
                        algoOverride = AssemblyHashAlgorithm.SHA1;
                    }

                    Byte[] pkToProcess;
                    if ((useStrongName && strongName.ContainerName != null) || (!useStrongName && delaySign))
                    {
                        if (an.PublicKey.IsNullOrEmpty())
                        {
                            an.PublicKey = this._context.ExtractPublicKeyFromCSP(strongName.ContainerName);
                        }
                        pkToProcess = an.PublicKey;
                    }
                    else
                    {
                        // Get public key from BLOB
                        pkToProcess = strongName.KeyPair.ToArray();
                    }

                    // Create RSA parameters and process public key so that it will have proper, full format.
                    Byte[] pk;
                    rParams      = CryptoUtils.CreateSigningInformationFromKeyBLOB(pkToProcess, algoOverride, out pk, out signingAlgorithm);
                    an.PublicKey = pk;
                    snSize       = (UInt32)rParams.Modulus.Length;
                }
                else
                {
                    rParams = default(RSAParameters);
                }

                if (useStrongName || delaySign)
                {
                    snRVA = codeSectionVirtualOffset + iatSize + HeaderFieldOffsetsAndLengths.CLI_HEADER_SIZE;
                    if (snSize <= 32)
                    {
                        // The "Standard Public Key", ECMA-335 p. 116
                        // It is replaced by the runtime with 128 bytes key
                        snSize = 128;
                    }
                    snPadding = BitUtils.MultipleOf4(snSize) - snSize;
                }

                var revisitableOffset    = HeaderFieldOffsetsAndLengths.DOS_HEADER_AND_PE_SIG.Length + currentArray.Length;
                var revisitableArraySize = fAlign + iatSize + HeaderFieldOffsetsAndLengths.CLI_HEADER_SIZE - revisitableOffset;
                // Cheat a bit - skip now, and re-visit it after all other emitting is done
                sink.Seek(revisitableArraySize + snSize + snPadding, SeekOrigin.Current);

                // First section
                // Start with method ILs
                // Current offset within section
                var currentOffset = iatSize + snSize + snPadding + HeaderFieldOffsetsAndLengths.CLI_HEADER_SIZE;
                var methodRVAs    = new Dictionary <CILMethodBase, UInt32>(allMethodDefs.Count);


                foreach (var method in allMethodDefs)
                {
                    if (method.HasILMethodBody())
                    {
                        Boolean isTiny;
                        var     array = new MethodILWriter(this._context, md, method, eArgs.AssemblyMapper)
                                        .PerformEmitting(currentOffset, out isTiny);
                        if (!isTiny)
                        {
                            sink.SkipToNextAlignment(ref currentOffset, 4);
                        }
                        methodRVAs.Add(method, codeSectionVirtualOffset + currentOffset);
                        sink.Write(array);
                        currentOffset += (UInt32)array.Length;
                    }
                }

                // Write padding
                sink.SkipToNextAlignment(ref currentOffset, isPE64 ? 0x10u : 0x04u);

                // Write manifest resources here
                var mRes     = this._module.ManifestResources;
                var mResInfo = new Dictionary <String, UInt32>();
                var mResRVA  = mRes.Values.Any(mr => mr is EmbeddedManifestResource) ?
                               codeSectionVirtualOffset + currentOffset :
                               0u;
                var mResSize = 0u;
                if (mResRVA > 0u)
                {
                    var tmpArray = new Byte[4];
                    foreach (var kvp in mRes)
                    {
                        if (kvp.Value is EmbeddedManifestResource)
                        {
                            var data = ((EmbeddedManifestResource)kvp.Value).Data;
                            if (data != null && data.Length > 0)
                            {
                                mResInfo.Add(kvp.Key, mResSize);
                                tmpArray.WriteInt32LEToBytesNoRef(0, data.Length);
                                sink.Write(tmpArray);
                                sink.Write(data);
                                mResSize += 4 + (UInt32)data.Length;
                            }
                        }
                    }

                    // Write padding
                    currentOffset += mResSize;
                    sink.SkipToNextAlignment(ref currentOffset, isPE64 ? 0x10u : 0x04u);
                }
                // Finalize & write metadata
                var    mdRVA = codeSectionVirtualOffset + currentOffset;
                UInt32 addedToOffsetBeforeMD;
                var    mdSize = md.WriteMetaData(sink, mdRVA, eArgs, methodRVAs, mResInfo, out addedToOffsetBeforeMD);
                mdRVA         += addedToOffsetBeforeMD;
                currentOffset += mdSize + addedToOffsetBeforeMD;

                // Pad
                sink.SkipToNextAlignment(ref currentOffset, 0x4);

                // Write debug header if present
                var dbgInfo = eArgs.DebugInformation;
                var dbgRVA  = 0u;
                if (dbgInfo != null)
                {
                    dbgRVA = codeSectionVirtualOffset + currentOffset;
                    var dbgData = dbgInfo.DebugData;
                    currentArray = new Byte[MetaDataConstants.DEBUG_DD_SIZE + dbgData.Length];
                    idx          = 0;
                    currentArray
                    .WriteInt32LEToBytes(ref idx, dbgInfo.Characteristics)
                    .WriteInt32LEToBytes(ref idx, dbgInfo.Timestamp)
                    .WriteInt16LEToBytes(ref idx, dbgInfo.VersionMajor)
                    .WriteInt16LEToBytes(ref idx, dbgInfo.VersionMinor)
                    .WriteInt32LEToBytes(ref idx, MetaDataConstants.CODE_VIEW_DEBUG_TYPE)
                    .WriteInt32LEToBytes(ref idx, dbgData.Length)
                    .WriteUInt32LEToBytes(ref idx, dbgRVA + MetaDataConstants.DEBUG_DD_SIZE)
                    .WriteUInt32LEToBytes(ref idx, fAlign + currentOffset + (UInt32)idx + 4)  // Pointer to data, end Debug Data Directory
                    .BlockCopyFrom(ref idx, dbgData);
                    sink.Write(currentArray);
                    currentOffset += (UInt32)currentArray.Length;
                    sink.SkipToNextAlignment(ref currentOffset, 0x4);
                }


                var entryPointCodeRVA   = 0u;
                var importDirectoryRVA  = 0u;
                var importDirectorySize = 0u;
                var hnRVA = 0u;
                if (hasRelocations)
                {
                    // TODO write all of these in a single array
                    // Import Directory
                    // First, the table
                    importDirectoryRVA  = codeSectionVirtualOffset + currentOffset;
                    importDirectorySize = HeaderFieldOffsetsAndLengths.IMPORT_DIRECTORY_SIZE;
                    currentArray        = new Byte[importDirectorySize];
                    idx = 0;
                    currentArray
                    .WriteUInt32LEToBytes(ref idx, codeSectionVirtualOffset + currentOffset + (UInt32)currentArray.Length)                                                                                                                               // RVA of the ILT
                    .WriteInt32LEToBytes(ref idx, 0)                                                                                                                                                                                                     // DateTimeStamp
                    .WriteInt32LEToBytes(ref idx, 0)                                                                                                                                                                                                     // ForwarderChain
                    .WriteUInt32LEToBytes(ref idx, codeSectionVirtualOffset + currentOffset + (UInt32)currentArray.Length + HeaderFieldOffsetsAndLengths.ILT_SIZE + HeaderFieldOffsetsAndLengths.HINT_NAME_MIN_SIZE + (UInt32)importHintName.Length + 1) // RVA of Import Directory name (mscoree.dll)
                    .WriteUInt32LEToBytes(ref idx, codeSectionVirtualOffset);                                                                                                                                                                            // RVA of Import Address Table
                    // The rest are zeroes
                    sink.Write(currentArray);
                    currentOffset += (UInt32)currentArray.Length;

                    // ILT
                    currentArray = new Byte[HeaderFieldOffsetsAndLengths.ILT_SIZE];
                    idx          = 0;
                    currentArray
                    .WriteUInt32LEToBytes(ref idx, codeSectionVirtualOffset + currentOffset + (UInt32)currentArray.Length);  // RVA of the hint/name table
                    // The rest are zeroes
                    sink.Write(currentArray);
                    currentOffset += (UInt32)currentArray.Length;

                    // Hint/Name table
                    currentArray = new Byte[HeaderFieldOffsetsAndLengths.HINT_NAME_MIN_SIZE + importHintName.Length + 1];
                    hnRVA        = currentOffset + codeSectionVirtualOffset;
                    // Skip first two bytes
                    idx = HeaderFieldOffsetsAndLengths.HINT_NAME_MIN_SIZE;
                    currentArray.WriteASCIIString(ref idx, importHintName, true);
                    sink.Write(currentArray);
                    currentOffset += (UInt32)currentArray.Length;

                    // Import DirectoryName
                    foreach (var chr in eArgs.ImportDirectoryName)
                    {
                        sink.WriteByte((Byte)chr); // TODO properly ASCII-encoded string
                        ++currentOffset;
                    }
                    sink.WriteByte(0); // String-terminating null
                    ++currentOffset;

                    // Then, a zero int
                    // TODO investigate if this is really needed...
                    sink.SeekFromCurrent(sizeof(Int32));
                    currentOffset += sizeof(Int32);

                    // Then, a PE entrypoint
                    entryPointCodeRVA = currentOffset + codeSectionVirtualOffset;
                    currentArray      = new Byte[sizeof(Int16) + sizeof(Int32)];
                    idx = 0;
                    currentArray
                    .WriteInt16LEToBytes(ref idx, eArgs.EntryPointInstruction)
                    .WriteUInt32LEToBytes(ref idx, (UInt32)imageBase + codeSectionVirtualOffset);
                    sink.Write(currentArray);
                    currentOffset += (UInt32)currentArray.Length;
                }

                // TODO Win32 resources section
                var hasResourceSection = false;

                var textSectionInfo = new SectionInfo(sink, null, currentOffset, sAlign, fAlign, !hasRelocations && !hasResourceSection);
                var prevSectionInfo = textSectionInfo;

                // TODO Win32 resources section
                var rsrcSectionInfo = new SectionInfo();

                // Final section - relocation section
                var relocSectionInfo = new SectionInfo();
                if (hasRelocations)
                {
                    // Need to build relocation fixup for the argument of the entry point
                    currentOffset = 0;
                    var relocRVA = entryPointCodeRVA + 2;
                    var pageRVA  = relocRVA & ~(RELOCATION_PAGE_SIZE - 1);

                    currentArray = new Byte[HeaderFieldOffsetsAndLengths.RELOC_ARRAY_BASE_SIZE];
                    idx          = 0;
                    currentArray
                    .WriteUInt32LEToBytes(ref idx, pageRVA)
                    .WriteUInt32LEToBytes(ref idx, HeaderFieldOffsetsAndLengths.RELOC_ARRAY_BASE_SIZE)  // Block size
                    .WriteUInt32LEToBytes(ref idx, (RELOCATION_FIXUP_TYPE << 12) + relocRVA - pageRVA); // Type (high 4 bits) + Offset (lower 12 bits) + dummy entry (16 bits)
                    sink.Write(currentArray);
                    currentOffset += (UInt32)currentArray.Length;

                    relocSectionInfo = new SectionInfo(sink, prevSectionInfo, currentOffset, sAlign, fAlign, true);
                    prevSectionInfo  = relocSectionInfo;
                }

                // Revisit PE optional header + section headers + padding + IAT + CLI header
                currentArray = new Byte[revisitableArraySize];
                idx          = 0;
                // PE optional header, ECMA-335 pp. 279-281
                // Standard fields
                currentArray
                .WriteInt16LEToBytes(ref idx, isPE64 ? HeaderFieldPossibleValues.PE64 : HeaderFieldPossibleValues.PE32) // Magic
                .WriteByteToBytes(ref idx, eArgs.LinkerMajor)                                                           // Linker major version
                .WriteByteToBytes(ref idx, eArgs.LinkerMinor)                                                           // Linker minor version
                .WriteUInt32LEToBytes(ref idx, textSectionInfo.rawSize)                                                 // Code size
                .WriteUInt32LEToBytes(ref idx, relocSectionInfo.rawSize + rsrcSectionInfo.rawSize)                      // Initialized data size
                .WriteUInt32LEToBytes(ref idx, 0)                                                                       // Unitialized data size
                .WriteUInt32LEToBytes(ref idx, entryPointCodeRVA)                                                       // Entry point RVA
                .WriteUInt32LEToBytes(ref idx, textSectionInfo.virtualAddress);                                         // Base of code
                if (!isPE64)
                {
                    currentArray.WriteUInt32LEToBytes(ref idx, hasResourceSection ? rsrcSectionInfo.virtualAddress : relocSectionInfo.virtualAddress); // Base of data
                }
                // WinNT-specific fields
                var dllFlags = DLLFlags.TerminalServerAware | DLLFlags.NXCompatible | DLLFlags.NoSEH | DLLFlags.DynamicBase;
                if (eArgs.HighEntropyVA)
                {
                    dllFlags |= DLLFlags.HighEntropyVA;
                }
                (isPE64 ? currentArray.WriteUInt64LEToBytes(ref idx, imageBase) : currentArray.WriteUInt32LEToBytes(ref idx, (UInt32)imageBase))
                .WriteUInt32LEToBytes(ref idx, sAlign)                                                                                    // Section alignment
                .WriteUInt32LEToBytes(ref idx, fAlign)                                                                                    // File alignment
                .WriteUInt16LEToBytes(ref idx, eArgs.OSMajor)                                                                             // OS Major
                .WriteUInt16LEToBytes(ref idx, eArgs.OSMinor)                                                                             // OS Minor
                .WriteUInt16LEToBytes(ref idx, eArgs.UserMajor)                                                                           // User Major
                .WriteUInt16LEToBytes(ref idx, eArgs.UserMinor)                                                                           // User Minor
                .WriteUInt16LEToBytes(ref idx, eArgs.SubSysMajor)                                                                         // SubSys Major
                .WriteUInt16LEToBytes(ref idx, eArgs.SubSysMinor)                                                                         // SubSys Minor
                .WriteUInt32LEToBytes(ref idx, 0)                                                                                         // Reserved
                .WriteUInt32LEToBytes(ref idx, prevSectionInfo.virtualAddress + BitUtils.MultipleOf(sAlign, prevSectionInfo.virtualSize)) // Image Size
                .WriteUInt32LEToBytes(ref idx, textSectionInfo.rawPointer)                                                                // Header Size
                .WriteUInt32LEToBytes(ref idx, 0)                                                                                         // File Checksum
                .WriteUInt16LEToBytes(ref idx, GetSubSystem(moduleKind))                                                                  // SubSystem
                .WriteUInt16LEToBytes(ref idx, (UInt16)dllFlags);                                                                         // DLL Characteristics
                if (isPE64)
                {
                    currentArray
                    .WriteUInt64LEToBytes(ref idx, eArgs.StackReserve) // Stack Reserve Size
                    .WriteUInt64LEToBytes(ref idx, eArgs.StackCommit)  // Stack Commit Size
                    .WriteUInt64LEToBytes(ref idx, eArgs.HeapReserve)  // Heap Reserve Size
                    .WriteUInt64LEToBytes(ref idx, eArgs.HeapCommit);  // Heap Commit Size
                }
                else
                {
                    currentArray
                    .WriteUInt32LEToBytes(ref idx, (UInt32)eArgs.StackReserve) // Stack Reserve Size
                    .WriteUInt32LEToBytes(ref idx, (UInt32)eArgs.StackCommit)  // Stack Commit Size
                    .WriteUInt32LEToBytes(ref idx, (UInt32)eArgs.HeapReserve)  // Heap Reserve Size
                    .WriteUInt32LEToBytes(ref idx, (UInt32)eArgs.HeapCommit);  // Heap Commit Size
                }
                currentArray
                .WriteUInt32LEToBytes(ref idx, 0)  // Loader Flags
                .WriteUInt32LEToBytes(ref idx, HeaderFieldOffsetsAndLengths.NUMBER_OF_DATA_DIRS)
                // Data Directories
                .WriteZeroDataDirectory(ref idx)                                                                               // Export Table
                .WriteDataDirectory(ref idx, importDirectoryRVA, importDirectorySize)                                          // Import Table
                .WriteDataDirectory(ref idx, rsrcSectionInfo.virtualAddress, rsrcSectionInfo.virtualSize)                      // Resource Table
                .WriteZeroDataDirectory(ref idx)                                                                               // Exception Table
                .WriteZeroDataDirectory(ref idx)                                                                               // Certificate Table
                .WriteDataDirectory(ref idx, relocSectionInfo.virtualAddress, relocSectionInfo.virtualSize)                    // BaseRelocationTable
                .WriteDataDirectory(ref idx, dbgRVA > 0u ? dbgRVA : 0u, dbgRVA > 0u ? MetaDataConstants.DEBUG_DD_SIZE : 0u)    // Debug Table
                .WriteZeroDataDirectory(ref idx)                                                                               // Copyright Table
                .WriteZeroDataDirectory(ref idx)                                                                               // Global Ptr
                .WriteZeroDataDirectory(ref idx)                                                                               // TLS Table
                .WriteZeroDataDirectory(ref idx)                                                                               // Load Config Table
                .WriteZeroDataDirectory(ref idx)                                                                               // Bound Import
                .WriteDataDirectory(ref idx, iatSize == 0 ? 0 : codeSectionVirtualOffset, iatSize == 0 ? 0 : iatSize)          // IAT
                .WriteZeroDataDirectory(ref idx)                                                                               // Delay Import Descriptor
                .WriteDataDirectory(ref idx, codeSectionVirtualOffset + iatSize, HeaderFieldOffsetsAndLengths.CLI_HEADER_SIZE) // CLI Header
                .WriteZeroDataDirectory(ref idx)                                                                               // Reserved
                // Section headers
                .WriteSectionInfo(ref idx, textSectionInfo, CODE_SECTION_NAME, HeaderFieldPossibleValues.MEM_READ | HeaderFieldPossibleValues.MEM_EXECUTE | HeaderFieldPossibleValues.CONTAINS_CODE)
                .WriteSectionInfo(ref idx, rsrcSectionInfo, RESOURCE_SECTION_NAME, HeaderFieldPossibleValues.MEM_READ | HeaderFieldPossibleValues.CONTAINS_INITIALIZED_DATA)
                .WriteSectionInfo(ref idx, relocSectionInfo, RELOCATION_SECTION_NAME, HeaderFieldPossibleValues.MEM_READ | HeaderFieldPossibleValues.MEM_DISCARDABLE | HeaderFieldPossibleValues.CONTAINS_INITIALIZED_DATA);
                var headersSize = (UInt32)(revisitableOffset + idx);

                // Skip to beginning of .text section
                currentArray.Skip(ref idx, (Int32)(fAlign - (UInt32)revisitableOffset - idx));

                // Write IAT if needed
                if (hasRelocations)
                {
                    currentArray
                    .WriteUInt32LEToBytes(ref idx, hnRVA)
                    .WriteUInt32LEToBytes(ref idx, 0);
                }
                // CLI Header, ECMA-335, p. 283
                // At the moment, the 32BitRequired flag must be specified as well, if 32BitPreferred flag is specified.
                // This is for backwards compatibility.
                // Actually, since CorFlags lets specify Preferred32Bit separately, allow this to do too.
                var moduleFlags = eArgs.ModuleFlags;
                //if ( moduleFlags.HasFlag( ModuleFlags.Preferred32Bit ) )
                //{
                //   moduleFlags |= ModuleFlags.Required32Bit;
                //}
                currentArray
                .WriteUInt32LEToBytes(ref idx, HeaderFieldOffsetsAndLengths.CLI_HEADER_SIZE) // Cb
                .WriteUInt16LEToBytes(ref idx, eArgs.CLIMajor)                               // MajorRuntimeVersion
                .WriteUInt16LEToBytes(ref idx, eArgs.CLIMinor)                               // MinorRuntimeVersion
                .WriteDataDirectory(ref idx, mdRVA, mdSize)                                  // MetaData
                .WriteInt32LEToBytes(ref idx, (Int32)moduleFlags)                            // Flags
                .WriteInt32LEToBytes(ref idx, clrEntryPointToken)                            // EntryPointToken
                .WriteDataDirectory(ref idx, mResRVA, mResSize);                             // Resources
                var snDataDirOffset = revisitableOffset + idx;
                currentArray
                .WriteDataDirectory(ref idx, snRVA, snSize) // StrongNameSignature
                .WriteZeroDataDirectory(ref idx)            // CodeManagerTable
                .WriteZeroDataDirectory(ref idx)            // VTableFixups
                .WriteZeroDataDirectory(ref idx)            // ExportAddressTableJumps
                .WriteZeroDataDirectory(ref idx);           // ManagedNativeHeader
#if DEBUG
                if (idx != currentArray.Length)
                {
                    throw new Exception("Something went wrong when emitting file headers. Emitted " + idx + " bytes, but was supposed to emit " + currentArray.Length + " bytes.");
                }
#endif
                sink.Seek(revisitableOffset, SeekOrigin.Begin);
                sink.Write(currentArray);

                if (useStrongName || delaySign)
                {
                    if (!delaySign)
                    {
                        // Try create RSA first
                        var rsaArgs = strongName.ContainerName == null ? new RSACreationEventArgs(rParams) : new RSACreationEventArgs(strongName.ContainerName);
                        this._context.LaunchRSACreationEvent(rsaArgs);
                        using (var rsa = rsaArgs.RSA)
                        {
                            var           buffer = new Byte[MetaDataConstants.STREAM_COPY_BUFFER_SIZE];
                            Func <Stream> hashStream; Func <Byte[]> hashGetter; IDisposable transform;
                            this._context.LaunchHashStreamEvent(signingAlgorithm, out hashStream, out hashGetter, out transform);

                            RSASignatureCreationEventArgs sigArgs;
                            using (var tf = transform)
                            {
                                using (var cryptoStream = hashStream())
                                {
                                    // Calculate hash of required parts of file (ECMA-335, p.117)
                                    sink.Seek(0, SeekOrigin.Begin);
                                    sink.CopyStreamPart(cryptoStream, buffer, headersSize);

                                    sink.Seek(fAlign, SeekOrigin.Begin);
                                    sink.CopyStreamPart(cryptoStream, buffer, snRVA - codeSectionVirtualOffset);

                                    sink.Seek(snSize + snPadding, SeekOrigin.Current);
                                    sink.CopyStream(cryptoStream, buffer);
                                }
                                sigArgs = new RSASignatureCreationEventArgs(rsa, signingAlgorithm, hashGetter());
                            }


                            this._context.LaunchRSASignatureCreationEvent(sigArgs);
                            var strongNameArray = sigArgs.Signature;
                            if (snSize != strongNameArray.Length)
                            {
                                throw new CryptographicException("Calculated and actual strong name size differ (calculated: " + snSize + ", actual: " + strongNameArray.Length + ").");
                            }
                            Array.Reverse(strongNameArray);

                            // Write strong name
                            sink.Seek(snRVA - codeSectionVirtualOffset + fAlign, SeekOrigin.Begin);
                            sink.Write(strongNameArray);
                        }
                    }

                    currentArray = new Byte[8];
                    idx          = 0;
                    currentArray.WriteDataDirectory(ref idx, snRVA, snSize);
                    sink.Seek(snDataDirOffset, SeekOrigin.Begin);
                    sink.Write(currentArray);
                }
            }
        }
示例#2
0
        internal Byte[] PerformEmitting(UInt32 currentOffset, out Boolean isTiny)
        {
            if (this._methodIL._labelOffsets.Any(offset => offset == MethodILImpl.NO_OFFSET))
            {
                throw new InvalidOperationException("Not all labels have been marked.");
            }
            if (this._methodIL._currentExceptionBlocks.Any())
            {
                throw new InvalidOperationException("Not all exception blocks have been completed.");
            }

            // Remember that inner exception blocks must precede outer ones
            var allExceptionBlocksCorrectlyOrdered = this._methodIL._allExceptionBlocks.ToArray();

            Array.Sort(
                allExceptionBlocksCorrectlyOrdered,
                (item1, item2) =>
            {
                // Return -1 if item1 is inner block of item2, 0 if they are same, 1 if item1 is not inner block of item2
                return(Object.ReferenceEquals(item1, item2) ? 0 :
                       (item1._tryOffset >= item2._handlerOffset + item2._handlerLength || (item1._tryOffset <= item2._tryOffset && item1._handlerOffset + item1._handlerLength > item2._handlerOffset + item2._handlerLength) ? 1 : -1));
            });

            // Setup stack sizes based on exception blocks
            foreach (var block in allExceptionBlocksCorrectlyOrdered)
            {
                switch (block._blockType)
                {
                case ExceptionBlockType.Exception:
                    this._stackSizes[block._handlerOffset] = 1;
                    break;

                case ExceptionBlockType.Filter:
                    this._stackSizes[block._handlerOffset] = 1;
                    this._stackSizes[block._filterOffset]  = 1;
                    break;
                }
            }

            // Emit opcodes and arguments
            foreach (var info in this._methodIL._opCodes)
            {
                info.EmitOpCode(this);
                ++this._methodILOffset;
            }

            // Mark label targets
            for (var i = 0; i < this._labelInfoIndex; ++i)
            {
                var thisOffset       = this._labelInfos[i].byteOffset;
                var startCountOffset = this._labelInfos[i].startCountOffset;
                var amountToJump     = this._opCodeInfoOffsets[this._methodIL._labelOffsets[this._labelInfos[i].labelIdx]] - (thisOffset + startCountOffset);
                if (startCountOffset == 1)
                {
                    if (amountToJump >= SByte.MinValue && amountToJump <= SByte.MaxValue)
                    {
                        this._ilCode.WriteSByteToBytes(ref thisOffset, amountToJump);
                    }
                    else
                    {
                        throw new InvalidOperationException("Tried to use one-byte branch instruction for offset of amount " + amountToJump);
                    }
                }
                else
                {
                    this._ilCode.WriteInt32LEToBytes(ref thisOffset, amountToJump);
                }
            }

            // Create exception blocks with byte offsets
            byte[][]  exceptionBlocks  = new byte[allExceptionBlocksCorrectlyOrdered.Length][];
            Boolean[] exceptionFormats = new Boolean[exceptionBlocks.Length];

            // TODO PEVerify doesn't like mixed small and fat blocks at all (however, at least Cecil understands that kind of situation)
            // TODO Apparently, PEVerify doesn't like multiple small blocks either (Cecil still loads code fine)
            // Also, because of exception block ordering, it is easier to do this way.
            var allAreSmall = allExceptionBlocksCorrectlyOrdered.Length <= MAX_SMALL_EXC_HANDLERS_IN_ONE_SECTION &&
                              allExceptionBlocksCorrectlyOrdered.All(excBlock =>
            {
                var tryOffset     = this._opCodeInfoOffsets[excBlock._tryOffset];
                var tryLength     = this._opCodeInfoOffsets[excBlock._tryOffset + excBlock._tryLength] - tryOffset;
                var handlerOffset = this._opCodeInfoOffsets[excBlock._handlerOffset];
                var handlerLength = this._opCodeInfoOffsets[excBlock._handlerOffset + excBlock._handlerLength] - handlerOffset;
                return(tryLength <= Byte.MaxValue && handlerLength <= Byte.MaxValue && tryOffset <= UInt16.MaxValue && handlerOffset <= UInt16.MaxValue);
            });

            for (var i = 0; i < exceptionBlocks.Length; ++i)
            {
                // ECMA-335, pp. 286-287
                var    block = allExceptionBlocksCorrectlyOrdered[i];
                Int32  idx   = 0;
                Byte[] array;
                var    tryOffset      = this._opCodeInfoOffsets[block._tryOffset];
                var    tryLength      = this._opCodeInfoOffsets[block._tryOffset + block._tryLength] - tryOffset;
                var    handlerOffset  = this._opCodeInfoOffsets[block._handlerOffset];
                var    handlerLength  = this._opCodeInfoOffsets[block._handlerOffset + block._handlerLength] - handlerOffset;
                var    useSmallFormat = allAreSmall &&
                                        tryLength <= Byte.MaxValue && handlerLength <= Byte.MaxValue && tryOffset <= UInt16.MaxValue && handlerOffset <= UInt16.MaxValue;
                exceptionFormats[i] = useSmallFormat;
                if (useSmallFormat)
                {
                    array = new Byte[12];
                    array.WriteInt16LEToBytes(ref idx, (Int16)block._blockType)
                    .WriteUInt16LEToBytes(ref idx, (UInt16)tryOffset)
                    .WriteByteToBytes(ref idx, (Byte)tryLength)
                    .WriteUInt16LEToBytes(ref idx, (UInt16)handlerOffset)
                    .WriteByteToBytes(ref idx, (Byte)handlerLength);
                }
                else
                {
                    array = new Byte[24];
                    array.WriteInt32LEToBytes(ref idx, (Int32)block._blockType)
                    .WriteInt32LEToBytes(ref idx, tryOffset)
                    .WriteInt32LEToBytes(ref idx, tryLength)
                    .WriteInt32LEToBytes(ref idx, handlerOffset)
                    .WriteInt32LEToBytes(ref idx, handlerLength);
                }

                if (ExceptionBlockType.Exception == block._blockType)
                {
                    array.WriteInt32LEToBytes(ref idx, this._metaData.GetTokenFor(this._assemblyMapper == null ? block._exceptionType : this._assemblyMapper.MapTypeBase(block._exceptionType), false));
                }
                else if (ExceptionBlockType.Filter == block._blockType)
                {
                    array.WriteInt32LEToBytes(ref idx, block._filterOffset);
                }
                exceptionBlocks[i] = array;
            }

            // Write method header, extra data sections, and IL
            Byte[] result;
            isTiny = this._ilCodeCount < 64 &&
                     exceptionBlocks.Length == 0 &&
                     this._maxStack <= 8 &&
                     this._methodIL._locals.Count == 0;
            var resultIndex               = 0;
            var hasAnyExc                 = false;
            var hasSmallExc               = false;
            var hasLargExc                = false;
            var smallExcCount             = 0;
            var largeExcCount             = 0;
            var amountToNext4ByteBoundary = 0;

            if (isTiny)
            {
                // Can use tiny header
                result = new Byte[this._ilCodeCount + 1];
                result[resultIndex++] = (Byte)((Int32)MethodHeaderFlags.TinyFormat | (this._ilCodeCount << 2));
            }
            else
            {
                // Use fat header
                hasAnyExc     = exceptionBlocks.Length > 0;
                hasSmallExc   = hasAnyExc && exceptionFormats.Any(excFormat => excFormat);
                hasLargExc    = hasAnyExc && exceptionFormats.Any(excFormat => !excFormat);
                smallExcCount = hasSmallExc ? exceptionFormats.Count(excFormat => excFormat) : 0;
                largeExcCount = hasLargExc ? exceptionFormats.Count(excFormat => !excFormat) : 0;
                var offsetAfterIL = (Int32)(BitUtils.MultipleOf4(currentOffset) + 12 + (UInt32)this._ilCodeCount);
                amountToNext4ByteBoundary = BitUtils.MultipleOf4(offsetAfterIL) - offsetAfterIL;

                result = new Byte[12
                                  + this._ilCodeCount +
                                  (hasAnyExc ? amountToNext4ByteBoundary : 0) +
                                  (hasSmallExc ? METHOD_DATA_SECTION_SIZE : 0) +
                                  (hasLargExc ? METHOD_DATA_SECTION_SIZE : 0) +
                                  smallExcCount * 12 +
                                  (smallExcCount / MAX_SMALL_EXC_HANDLERS_IN_ONE_SECTION) * METHOD_DATA_SECTION_SIZE + // (Amount of extra section headers ) * section size
                                  largeExcCount * 24
                         ];
                var flags = MethodHeaderFlags.FatFormat;
                if (hasAnyExc)
                {
                    flags |= MethodHeaderFlags.MoreSections;
                }
                if (this._methodIL.InitLocals)
                {
                    flags |= MethodHeaderFlags.InitLocals;
                }

                result.WriteInt16LEToBytes(ref resultIndex, (Int16)(((Int32)flags) | (3 << 12)))
                .WriteInt16LEToBytes(ref resultIndex, (Int16)this._maxStack)
                .WriteInt32LEToBytes(ref resultIndex, this._ilCodeCount)
                .WriteInt32LEToBytes(ref resultIndex, this._metaData.GetSignatureTokenFor(this._method, this._methodIL._locals.ToArray()));
            }

            Array.Copy(this._ilCode, 0, result, resultIndex, this._ilCodeCount);
            resultIndex += this._ilCodeCount;

            if (hasAnyExc)
            {
                var processedIndices = new HashSet <Int32>();
                resultIndex += amountToNext4ByteBoundary;
                var flags = MethodDataFlags.ExceptionHandling;
                // First, write fat sections
                if (hasLargExc)
                {
                    // TODO like with small sections, what if too many exception clauses to be fit into DataSize?
                    flags |= MethodDataFlags.FatFormat;
                    if (hasSmallExc)
                    {
                        flags |= MethodDataFlags.MoreSections;
                    }
                    result.WriteByteToBytes(ref resultIndex, (Byte)flags)
                    .WriteInt32LEToBytes(ref resultIndex, largeExcCount * 24 + METHOD_DATA_SECTION_SIZE);
                    --resultIndex;
                    for (var i = 0; i < exceptionBlocks.Length; ++i)
                    {
                        if (!exceptionFormats[i] && processedIndices.Add(i))
                        {
                            var length = exceptionBlocks[i].Length;
                            Array.Copy(exceptionBlocks[i], 0, result, resultIndex, length);
                            resultIndex += length;
                        }
                    }
                }
                // Then, write small sections
                // If exception counts * 12 + 4 are > Byte.MaxValue, have to write several sections
                // (Max 20 handlers per section)
                flags = MethodDataFlags.ExceptionHandling;
                if (hasSmallExc)
                {
                    var curSmallIdx = 0;
                    while (smallExcCount > 0)
                    {
                        var amountToBeWritten = Math.Min(smallExcCount, MAX_SMALL_EXC_HANDLERS_IN_ONE_SECTION);
                        if (amountToBeWritten < smallExcCount)
                        {
                            flags |= MethodDataFlags.MoreSections;
                        }
                        else
                        {
                            flags = flags & ~(MethodDataFlags.MoreSections);
                        }

                        result.WriteByteToBytes(ref resultIndex, (Byte)flags)
                        .WriteByteToBytes(ref resultIndex, (Byte)(amountToBeWritten * 12 + METHOD_DATA_SECTION_SIZE))
                        .WriteInt16LEToBytes(ref resultIndex, 0);
                        var amountActuallyWritten = 0;
                        while (curSmallIdx < exceptionBlocks.Length && amountActuallyWritten < amountToBeWritten)
                        {
                            if (exceptionFormats[curSmallIdx])
                            {
                                var length = exceptionBlocks[curSmallIdx].Length;
                                Array.Copy(exceptionBlocks[curSmallIdx], 0, result, resultIndex, length);
                                resultIndex += length;
                                ++amountActuallyWritten;
                            }
                            ++curSmallIdx;
                        }
                        smallExcCount -= amountToBeWritten;
                    }
                }
            }
#if DEBUG
            if (resultIndex != result.Length)
            {
                throw new Exception("Something went wrong when emitting method headers and body. Emitted " + resultIndex + " bytes, but was supposed to emit " + result.Length + " bytes.");
            }
#endif
            return(result);
        }