Пример #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 MethodILImpl(CILModule module, Boolean initLocals, Byte[] il, Tuple <Boolean, CILTypeBase>[] locals, Tuple <ExceptionBlockType, Int32, Int32, Int32, Int32, CILType, Int32>[] excBlocks, Func <Int32, ILResolveKind, Object> tokenResolver)
            : this( module )
        {
            this._initLocals = initLocals;
            foreach (var lInfo in locals)
            {
                this.DeclareLocal(lInfo.Item2, lInfo.Item1);
            }
            var            ilOffset          = 0;
            var            labelsDic         = new Dictionary <Int32, ILLabel>();
            var            codeInfoILOffsets = new Dictionary <Int32, Int32>();
            Func <ILLabel> ilLabelDefiner    = () => this.DefineLabel();
            while (ilOffset < il.Length)
            {
                codeInfoILOffsets.Add(ilOffset, this._opCodes.Count);

                var curInstruction = (Int32)il[ilOffset++];
                if (curInstruction == OpCode.MAX_ONE_BYTE_INSTRUCTION)
                {
                    curInstruction = (curInstruction << 8) | (Int32)il[ilOffset++];
                }

                var     code = OpCodes.Codes[(OpCodeEncoding)curInstruction];
                Int32   int32;
                ILLabel label;
                Object  resolvedToken;
                switch (code.OperandType)
                {
                case OperandType.InlineNone:
                    this._opCodes.Add(OpCodes.CodeInfosWithNoOperand[(OpCodeEncoding)curInstruction]);
                    break;

                case OperandType.ShortInlineBrTarget:
                    int32 = ilOffset + 1 + (Int32)((SByte)il[ilOffset]);
                    ++ilOffset;
                    label = labelsDic.GetOrAdd_NotThreadSafe(int32, ilLabelDefiner);
                    this._opCodes.Add(new OpCodeInfoForFixedBranchOrLeave(code, label));  //  OpCodes.Leave_S == code ? (OpCodeInfo) new OpCodeInfoForLeave( label ) : new OpCodeInfoForBranch( code, true, label ) );
                    ++this._branchTargetsCount;
                    break;

                case OperandType.ShortInlineI:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandInt32(code, (SByte)il[ilOffset++]));
                    break;

                case OperandType.ShortInlineVar:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandUInt16(code, il[ilOffset++]));
                    break;

                case OperandType.ShortInlineR:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandSingle(code, il.ReadSingleLEFromBytes(ref ilOffset)));
                    break;

                case OperandType.InlineBrTarget:
                    int32  = il.ReadInt32LEFromBytes(ref ilOffset);
                    int32 += ilOffset;
                    label  = labelsDic.GetOrAdd_NotThreadSafe(int32, ilLabelDefiner);
                    this._opCodes.Add(new OpCodeInfoForFixedBranchOrLeave(code, label));  // OpCodes.Leave == code ? (OpCodeInfo) new OpCodeInfoForLeave( label ) : new OpCodeInfoForBranch( code, false, label ) );
                    ++this._branchTargetsCount;
                    break;

                case OperandType.InlineI:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandInt32(code, il.ReadInt32LEFromBytes(ref ilOffset)));
                    break;

                case OperandType.InlineVar:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandUInt16(code, il.ReadInt16LEFromBytes(ref ilOffset)));
                    break;

                case OperandType.InlineR:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandDouble(code, il.ReadDoubleLEFromBytes(ref ilOffset)));
                    break;

                case OperandType.InlineI8:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandInt64(code, il.ReadInt64LEFromBytes(ref ilOffset)));
                    break;

                case OperandType.InlineString:
                    this._opCodes.Add(new OpCodeInfoWithFixedSizeOperandString((String)tokenResolver(il.ReadInt32LEFromBytes(ref ilOffset), ILResolveKind.String)));
                    break;

                case OperandType.InlineField:
                    this._opCodes.Add(new OpCodeInfoWithFieldToken(code, (CILField)tokenResolver(il.ReadInt32LEFromBytes(ref ilOffset), ILResolveKind.Field)));
                    break;

                case OperandType.InlineMethod:
                    resolvedToken = tokenResolver(il.ReadInt32LEFromBytes(ref ilOffset), ILResolveKind.MethodBase);
                    // Because of base.X() calls to virtual methods, we can not use Call/Callvirt deduction here
                    this._opCodes.Add(resolvedToken is CILMethod ?
                                      (OpCodeInfo) new OpCodeInfoWithMethodToken(code, (CILMethod)resolvedToken) :
                                      new OpCodeInfoWithCtorToken(code, (CILConstructor)resolvedToken));
                    //var isMethod = resolvedToken is CILMethod;
                    //var canEmitNormalOrVirtual = isMethod && OpCodes.Callvirt.Equals( code );
                    //var isCallOrVirt = canEmitNormalOrVirtual;
                    //if ( canEmitNormalOrVirtual )
                    //{
                    //   canEmitNormalOrVirtual = this._opCodes.Count == 0;
                    //   if ( canEmitNormalOrVirtual )
                    //   {
                    //      var last = this._opCodes[this._opCodes.Count - 1];
                    //      canEmitNormalOrVirtual = !( last is OpCodeInfoWithTypeToken ) || !OpCodes.Constrained_.Equals( ( (OpCodeInfoWithTypeToken) last ).Code );
                    //   }
                    //}
                    //else if ( isMethod )
                    //{
                    //   isCallOrVirt = OpCodes.Call.Equals( code );
                    //   canEmitNormalOrVirtual = isCallOrVirt || OpCodes.Ldftn.Equals( code ) || OpCodes.Ldvirtftn.Equals( code );
                    //}

                    //this._opCodes.Add(
                    //   canEmitNormalOrVirtual ?
                    //      ( isCallOrVirt ?
                    //         OpCodeInfoForNormalOrVirtual.OpCodeInfoForCall( (CILMethod) resolvedToken ) :
                    //         OpCodeInfoForNormalOrVirtual.OpCodeInfoForLdFtn( (CILMethod) resolvedToken ) ) :
                    //      ( isMethod ?
                    //         (OpCodeInfo) new OpCodeInfoWithMethodToken( code, (CILMethod) resolvedToken ) :
                    //         new OpCodeInfoWithCtorToken( code, (CILConstructor) resolvedToken ) )
                    //         );
                    break;

                case OperandType.InlineType:
                    this._opCodes.Add(new OpCodeInfoWithTypeToken(code, (CILTypeBase)tokenResolver(il.ReadInt32LEFromBytes(ref ilOffset), ILResolveKind.Type)));
                    break;

                case OperandType.InlineTok:
                    int32 = il.ReadInt32LEFromBytes(ref ilOffset);
                    Tables refTable; Int32 dummy;
                    TokenUtils.DecodeToken(int32, out refTable, out dummy);
                    ILResolveKind resolveKind;
                    switch (refTable)
                    {
                    case Tables.TypeDef:
                    case Tables.TypeRef:
                    case Tables.TypeSpec:
                        resolveKind = ILResolveKind.Type;
                        break;

                    case Tables.Field:
                        resolveKind = ILResolveKind.Field;
                        break;

                    case Tables.MethodDef:
                    case Tables.MethodSpec:
                        resolveKind = ILResolveKind.MethodBase;
                        break;

                    case Tables.MemberRef:
                        resolveKind = ILResolveKind.MethodBaseOrField;
                        break;

                    default:
                        throw new BadImageFormatException("Unknown inline token in IL at offset " + ilOffset + " (" + new TableIndex(int32) + ")");
                    }
                    resolvedToken = tokenResolver(int32, resolveKind);
                    this._opCodes.Add(resolvedToken is CILTypeBase ?
                                      new OpCodeInfoWithTypeToken(code, (CILTypeBase)resolvedToken, (Tables.TypeDef == refTable || Tables.TypeRef == refTable)) :
                                      (resolvedToken is CILField ?
                                       new OpCodeInfoWithFieldToken(code, (CILField)resolvedToken, true) :                                                                           // TODO the last boolean parameter properly (how?)
                                       (resolvedToken is CILMethod ?
                                        (OpCodeInfo) new OpCodeInfoWithMethodToken(code, (CILMethod)resolvedToken, (Tables.MemberRef == refTable || Tables.MethodDef == refTable)) : // TODO last boolean parameter propertly (how?)
                                        new OpCodeInfoWithCtorToken(code, (CILConstructor)resolvedToken, true)                                                                       // TODO last boolean parameter propertly (how?)
                                       )
                                      )
                                      );
                    break;

                case OperandType.InlineSwitch:
                    int32 = il.ReadInt32LEFromBytes(ref ilOffset);
                    ILLabel[] labels = new ILLabel[int32];
                    for (var i = 0; i < int32; ++i)
                    {
                        var lTarget = il.ReadInt32LEFromBytes(ref ilOffset);
                        lTarget  += ilOffset + (int32 - i - 1) * 4;
                        labels[i] = labelsDic.GetOrAdd_NotThreadSafe(lTarget, () => this.DefineLabel());
                    }
                    this._opCodes.Add(new OpCodeInfoForSwitch(labels));
                    this._branchTargetsCount += int32;
                    break;

                case OperandType.InlineSig:
                    int32 = il.ReadInt32LEFromBytes(ref ilOffset);
                    var methodSig = (Tuple <CILMethodSignature, Tuple <CILCustomModifier[], CILTypeBase>[]>)tokenResolver(int32, ILResolveKind.Signature);
                    this._opCodes.Add(new OpCodeInfoWithMethodSig(methodSig.Item1, methodSig.Item2));
                    break;

                default:
                    throw new ArgumentException("Unknown operand type: " + code.OperandType + " for " + code + ".");
                }
            }

            // Then, process labels
            foreach (var kvp in labelsDic)
            {
                this.MarkLabel(kvp.Value, codeInfoILOffsets[kvp.Key]);
            }

            // And exception blocks
            foreach (var block in excBlocks)
            {
                var info = new ExceptionBlockInfo(codeInfoILOffsets[block.Item2], labelsDic.GetOrAdd_NotThreadSafe(block.Item4 + block.Item5, () =>
                {
                    var result = this.DefineLabel();
                    this.MarkLabel(result, codeInfoILOffsets[block.Item4 + block.Item5]);
                    return(result);
                }));
                info._blockType     = block.Item1;
                info._tryLength     = codeInfoILOffsets[block.Item2 + block.Item3] - info._tryOffset;
                info._handlerOffset = codeInfoILOffsets[block.Item4];
                info._handlerLength = codeInfoILOffsets[block.Item4 + block.Item5] - info._handlerOffset;
                var excType = block.Item6;
                if (excType != null)
                {
                    info._exceptionType = excType;
                }
                if (codeInfoILOffsets.ContainsKey(block.Item7))
                {
                    info._filterOffset = block.Item7;
                }
                this._allExceptionBlocks.Add(info);
            }
        }