Beispiel #1
0
        public EcmaModule GetModuleFromPath(string filePath)
        {
            // This is called once for every assembly that should be verified, so linear search is acceptable.
            foreach (KeyValuePair <EcmaModule, ModuleData> entry in _moduleData)
            {
                EcmaModule curModule = entry.Key;
                ModuleData curData   = entry.Value;
                if (curData.Path == filePath)
                {
                    return(curModule);
                }
            }

            PEReader   peReader = new PEReader(File.OpenRead(filePath));
            EcmaModule module   = EcmaModule.Create(this, peReader);

            MetadataReader metadataReader = module.MetadataReader;
            string         simpleName     = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);

            if (_modules.ContainsKey(simpleName))
            {
                throw new CommandLineException("Module with same simple name already exists " + filePath);
            }

            _modules.Add(simpleName, module);

            ModuleData moduleData = new ModuleData()
            {
                Path = filePath
            };

            _moduleData.Add(module, moduleData);

            return(module);
        }
        private EcmaModule CreateModuleForSimpleName(string simpleName)
        {
            string filePath;

            if (!InputFilePaths.TryGetValue(simpleName, out filePath))
            {
                if (!ReferenceFilePaths.TryGetValue(simpleName, out filePath))
                {
                    throw new CommandLineException("Assembly not found: " + simpleName);
                }
            }

            PEReader   peReader = new PEReader(File.OpenRead(filePath));
            EcmaModule module   = EcmaModule.Create(this, peReader);

            MetadataReader metadataReader   = module.MetadataReader;
            string         actualSimpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);

            if (!actualSimpleName.Equals(simpleName, StringComparison.OrdinalIgnoreCase))
            {
                throw new CommandLineException("Assembly name does not match filename " + filePath);
            }

            _modules.Add(simpleName, module);

            ModuleData moduleData = new ModuleData()
            {
                Path = filePath
            };

            _moduleData.Add(module, moduleData);

            return(module);
        }
Beispiel #3
0
        private EcmaModule AddModule(string filePath, string expectedSimpleName)
        {
            MemoryMappedViewAccessor mappedViewAccessor = null;
            PdbSymbolReader          pdbReader          = null;

            try
            {
                PEReader peReader = OpenPEFile(filePath, out mappedViewAccessor);
                pdbReader = OpenAssociatedSymbolFile(filePath);

                EcmaModule module = EcmaModule.Create(this, peReader, pdbReader);

                MetadataReader metadataReader = module.MetadataReader;
                string         simpleName     = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);

                if (expectedSimpleName != null && !simpleName.Equals(expectedSimpleName, StringComparison.OrdinalIgnoreCase))
                {
                    throw new FileNotFoundException("Assembly name does not match filename " + filePath);
                }

                ModuleData moduleData = new ModuleData()
                {
                    SimpleName         = simpleName,
                    FilePath           = filePath,
                    Module             = module,
                    MappedViewAccessor = mappedViewAccessor
                };

                lock (this)
                {
                    ModuleData actualModuleData = _simpleNameHashtable.AddOrGetExisting(moduleData);
                    if (actualModuleData != moduleData)
                    {
                        if (actualModuleData.FilePath != filePath)
                        {
                            throw new FileNotFoundException("Module with same simple name already exists " + filePath);
                        }
                        return(actualModuleData.Module);
                    }
                    mappedViewAccessor = null; // Ownership has been transfered
                    pdbReader          = null; // Ownership has been transferred

                    _moduleHashtable.AddOrGetExisting(moduleData);
                }

                return(module);
            }
            finally
            {
                if (mappedViewAccessor != null)
                {
                    mappedViewAccessor.Dispose();
                }
                if (pdbReader != null)
                {
                    pdbReader.Dispose();
                }
            }
        }
Beispiel #4
0
        private EcmaModule AddModule(string filePath, string expectedSimpleName, byte[] moduleDataBytes, bool useForBinding)
        {
            MemoryMappedViewAccessor mappedViewAccessor = null;
            PdbSymbolReader          pdbReader          = null;

            try
            {
                PEReader peReader = OpenPEFile(filePath, moduleDataBytes, out mappedViewAccessor);
                pdbReader = OpenAssociatedSymbolFile(filePath, peReader);

                EcmaModule module = EcmaModule.Create(this, peReader, containingAssembly: null, pdbReader);

                MetadataReader metadataReader = module.MetadataReader;
                string         simpleName     = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);

                ModuleData moduleData = new ModuleData()
                {
                    SimpleName         = simpleName,
                    FilePath           = filePath,
                    Module             = module,
                    MappedViewAccessor = mappedViewAccessor
                };

                lock (this)
                {
                    if (useForBinding)
                    {
                        ModuleData actualModuleData = _simpleNameHashtable.AddOrGetExisting(moduleData);
                        if (actualModuleData != moduleData)
                        {
                            if (actualModuleData.FilePath != filePath)
                            {
                                throw new FileNotFoundException("Module with same simple name already exists " + filePath);
                            }
                            return(actualModuleData.Module);
                        }
                    }
                    mappedViewAccessor = null; // Ownership has been transfered
                    pdbReader          = null; // Ownership has been transferred

                    _moduleHashtable.AddOrGetExisting(moduleData);
                }

                return(module);
            }
            finally
            {
                if (mappedViewAccessor != null)
                {
                    mappedViewAccessor.Dispose();
                }
                if (pdbReader != null)
                {
                    pdbReader.Dispose();
                }
            }
        }
Beispiel #5
0
        public static MibcConfig ParseMibcConfig(TypeSystemContext tsc, PEReader pEReader)
        {
            EcmaModule mibcModule    = EcmaModule.Create(tsc, pEReader, null);
            EcmaMethod mibcConfigMth = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod(nameof(MibcConfig), null);

            if (mibcConfigMth == null)
            {
                return(null);
            }

            var ilBody   = EcmaMethodIL.Create(mibcConfigMth);
            var ilReader = new ILReader(ilBody.GetILBytes());

            // Parse:
            //
            //   ldstr "key1"
            //   ldstr "value1"
            //   pop
            //   pop
            //   ldstr "key2"
            //   ldstr "value2"
            //   pop
            //   pop
            //   ...
            //   ret
            string fieldName = null;
            Dictionary <string, string> keyValue = new();

            while (ilReader.HasNext)
            {
                ILOpcode opcode = ilReader.ReadILOpcode();
                switch (opcode)
                {
                case ILOpcode.ldstr:
                    var ldStrValue = (string)ilBody.GetObject(ilReader.ReadILToken());
                    if (fieldName != null)
                    {
                        keyValue[fieldName] = ldStrValue;
                    }
                    else
                    {
                        fieldName = ldStrValue;
                    }
                    break;

                case ILOpcode.ret:
                case ILOpcode.pop:
                    fieldName = null;
                    break;

                default:
                    throw new InvalidOperationException($"Unexpected opcode: {opcode}");
                }
            }

            return(MibcConfig.FromKeyValueMap(keyValue));
        }
Beispiel #6
0
        public EcmaModule CreateModuleForSimpleName(string simpleName)
        {
            EcmaModule module = null;

            try
            {
                module = EcmaModule.Create(this, new PEReader(File.OpenRead(simpleName + ".dll")));
            }
            catch (FileNotFoundException)
            {
                // FileNotFound is treated as being unable to load the module
            }

            _modules.Add(simpleName, module);
            return(module);
        }
Beispiel #7
0
        internal EcmaModule GetModule(PEReader peReader)
        {
            if (peReader == null)
            {
                return(null);
            }

            if (_modulesCache.TryGetValue(peReader, out EcmaModule existingModule))
            {
                return(existingModule);
            }

            EcmaModule module = EcmaModule.Create(this, peReader);

            _modulesCache.Add(peReader, module);
            return(module);
        }
Beispiel #8
0
        internal EcmaModule GetModule(PEReader peReader, IAssemblyDesc containingAssembly = null)
        {
            if (peReader == null)
            {
                return(null);
            }

            if (_modulesCache.TryGetValue(peReader, out EcmaModule existingModule))
            {
                if (containingAssembly != null && existingModule.Assembly != containingAssembly)
                {
                    throw new VerifierException($"Containing assembly for module '{existingModule}' must be '{containingAssembly}'");
                }
                return(existingModule);
            }

            EcmaModule module = EcmaModule.Create(this, peReader, containingAssembly);

            _modulesCache.Add(peReader, module);
            return(module);
        }
Beispiel #9
0
        static IEnumerable <MIbcData> ReadMIbcData(TraceTypeSystemContext tsc, FileInfo outputFileName, byte[] moduleBytes)
        {
            var peReader = new System.Reflection.PortableExecutable.PEReader(System.Collections.Immutable.ImmutableArray.Create <byte>(moduleBytes));
            var module   = EcmaModule.Create(tsc, peReader, null, null, new CustomCanonResolver(tsc));

            var          loadedMethod = (EcmaMethod)module.GetGlobalModuleType().GetMethod("AssemblyDictionary", null);
            EcmaMethodIL ilBody       = EcmaMethodIL.Create(loadedMethod);

            byte[] ilBytes       = ilBody.GetILBytes();
            int    currentOffset = 0;

            while (currentOffset < ilBytes.Length)
            {
                ILOpcode opcode = (ILOpcode)ilBytes[currentOffset];
                if (opcode == ILOpcode.prefix1)
                {
                    opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1];
                }
                switch (opcode)
                {
                case ILOpcode.ldtoken:
                    UInt32 token = (UInt32)(ilBytes[currentOffset + 1] + (ilBytes[currentOffset + 2] << 8) + (ilBytes[currentOffset + 3] << 16) + (ilBytes[currentOffset + 4] << 24));
                    foreach (var data in ReadMIbcGroup(tsc, (EcmaMethod)ilBody.GetObject((int)token)))
                    {
                        yield return(data);
                    }
                    break;

                case ILOpcode.pop:
                    break;
                }

                // This isn't correct if there is a switch opcode, but since we won't do that, its ok
                currentOffset += opcode.GetSize();
            }
            GC.KeepAlive(peReader);
        }
        public EcmaModule GetModuleFromPath(string filePath)
        {
            PEReader   peReader = new PEReader(File.OpenRead(filePath));
            EcmaModule module   = EcmaModule.Create(this, peReader);

            MetadataReader metadataReader = module.MetadataReader;
            string         simpleName     = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);

            if (_modules.ContainsKey(simpleName))
            {
                throw new CommandLineException("Module with same simple name already exists " + filePath);
            }

            _modules.Add(simpleName, module);

            ModuleData moduleData = new ModuleData()
            {
                Path = filePath
            };

            _moduleData.Add(module, moduleData);

            return(module);
        }
        /// <summary>
        /// Parse an MIBC file for the methods that are interesting.
        /// The version bubble must be specified and will describe the restrict the set of methods parsed to those relevant to the compilation
        /// The onlyDefinedInAssembly parameter is used to restrict the set of types parsed to include only those which are defined in a specific module. Specify null to allow definitions from all modules.
        /// This limited parsing is not necessarily an exact set of prevention, so detailed algorithms that work at the individual method level are still necessary, but this allows avoiding excessive parsing.
        ///
        /// The format of the Mibc file is that of a .NET dll, with a global method named "AssemblyDictionary". Inside of that file are a series of references that are broken up by which assemblies define the individual methods.
        /// These references are encoded as IL code that represents the details.
        /// The format of these IL instruction is as follows.
        ///
        /// ldstr mibcGroupName
        /// ldtoken mibcGroupMethod
        /// pop
        /// {Repeat the above pattern N times, once per Mibc group}
        ///
        /// See comment above ReadMIbcGroup for details of the group format
        ///
        /// The mibcGroupName is in the following format "Assembly_{definingAssemblyName};{OtherAssemblyName};{OtherAssemblyName};...; (OtherAssemblyName is ; delimited)
        ///
        /// </summary>
        /// <returns></returns>
        public static ProfileData ParseMIbcFile(TypeSystemContext tsc, PEReader peReader, HashSet <string> assemblyNamesInVersionBubble, string onlyDefinedInAssembly)
        {
            var mibcModule = EcmaModule.Create(tsc, peReader, null, null, new CustomCanonResolver(tsc));

            var assemblyDictionary = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod("AssemblyDictionary", null);
            IEnumerable <MethodProfileData> loadedMethodProfileData = Enumerable.Empty <MethodProfileData>();

            EcmaMethodIL ilBody = EcmaMethodIL.Create(assemblyDictionary);

            byte[] ilBytes       = ilBody.GetILBytes();
            int    currentOffset = 0;

            string mibcGroupName = "";

            while (currentOffset < ilBytes.Length)
            {
                ILOpcode opcode = (ILOpcode)ilBytes[currentOffset];
                if (opcode == ILOpcode.prefix1)
                {
                    opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1];
                }
                switch (opcode)
                {
                case ILOpcode.ldstr:
                    Debug.Assert(mibcGroupName == "");
                    if (mibcGroupName == "")
                    {
                        UInt32 userStringToken = BinaryPrimitives.ReadUInt32LittleEndian(ilBytes.AsSpan(currentOffset + 1));
                        mibcGroupName = (string)ilBody.GetObject((int)userStringToken);
                    }
                    break;

                case ILOpcode.ldtoken:
                    if (String.IsNullOrEmpty(mibcGroupName))
                    {
                        break;
                    }

                    string[] assembliesByName = mibcGroupName.Split(';');

                    bool hasMatchingDefinition = (onlyDefinedInAssembly == null) || assembliesByName[0].Equals(onlyDefinedInAssembly);

                    if (!hasMatchingDefinition)
                    {
                        break;
                    }

                    if (assemblyNamesInVersionBubble != null)
                    {
                        bool areAllEntriesInVersionBubble = true;
                        foreach (string s in assembliesByName)
                        {
                            if (string.IsNullOrEmpty(s))
                            {
                                continue;
                            }

                            if (!assemblyNamesInVersionBubble.Contains(s))
                            {
                                areAllEntriesInVersionBubble = false;
                                break;
                            }
                        }

                        if (!areAllEntriesInVersionBubble)
                        {
                            break;
                        }
                    }

                    uint token = BinaryPrimitives.ReadUInt32LittleEndian(ilBytes.AsSpan(currentOffset + 1));
                    loadedMethodProfileData = loadedMethodProfileData.Concat(ReadMIbcGroup(tsc, (EcmaMethod)ilBody.GetObject((int)token)));
                    break;

                case ILOpcode.pop:
                    mibcGroupName = "";
                    break;
                }

                // This isn't correct if there is a switch opcode, but since we won't do that, its ok
                currentOffset += opcode.GetSize();
            }

            return(new IBCProfileData(false, loadedMethodProfileData));
        }
        /// <summary>
        /// Parse an MIBC file for the methods that are interesting.
        /// The version bubble must be specified and will describe the restrict the set of methods parsed to those relevant to the compilation
        /// The onlyDefinedInAssembly parameter is used to restrict the set of types parsed to include only those which are defined in a specific module. Specify null to allow definitions from all modules.
        /// This limited parsing is not necessarily an exact set of prevention, so detailed algorithms that work at the individual method level are still necessary, but this allows avoiding excessive parsing.
        ///
        /// The format of the Mibc file is that of a .NET dll, with a global method named "AssemblyDictionary". Inside of that file are a series of references that are broken up by which assemblies define the individual methods.
        /// These references are encoded as IL code that represents the details.
        /// The format of these IL instruction is as follows.
        ///
        /// ldstr mibcGroupName
        /// ldtoken mibcGroupMethod
        /// pop
        /// {Repeat the above pattern N times, once per Mibc group}
        ///
        /// See comment above ReadMIbcGroup for details of the group format
        ///
        /// The mibcGroupName is in the following format "Assembly_{definingAssemblyName};{OtherAssemblyName};{OtherAssemblyName};...; (OtherAssemblyName is ; delimited)
        ///
        /// </summary>
        /// <returns></returns>
        public static ProfileData ParseMIbcFile(CompilerTypeSystemContext tsc, string filename, HashSet <string> assemblyNamesInVersionBubble, string onlyDefinedInAssembly)
        {
            byte[] peData;

            using (var zipFile = ZipFile.OpenRead(filename))
            {
                var mibcDataEntry = zipFile.GetEntry(Path.GetFileName(filename) + ".dll");
                using (var mibcDataStream = mibcDataEntry.Open())
                {
                    peData = new byte[mibcDataEntry.Length];
                    using (BinaryReader br = new BinaryReader(mibcDataStream))
                    {
                        peData = br.ReadBytes(checked ((int)mibcDataEntry.Length));
                    }
                }
            }

            using (var peReader = new System.Reflection.PortableExecutable.PEReader(System.Collections.Immutable.ImmutableArray.Create <byte>(peData)))
            {
                var mibcModule = EcmaModule.Create(tsc, peReader, null, null, new CustomCanonResolver(tsc));

                var assemblyDictionary = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod("AssemblyDictionary", null);
                IEnumerable <MethodProfileData> loadedMethodProfileData = Enumerable.Empty <MethodProfileData>();

                EcmaMethodIL ilBody        = EcmaMethodIL.Create(assemblyDictionary);
                byte[]       ilBytes       = ilBody.GetILBytes();
                int          currentOffset = 0;

                string mibcGroupName = "";
                while (currentOffset < ilBytes.Length)
                {
                    ILOpcode opcode = (ILOpcode)ilBytes[currentOffset];
                    if (opcode == ILOpcode.prefix1)
                    {
                        opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1];
                    }
                    switch (opcode)
                    {
                    case ILOpcode.ldstr:
                        if (mibcGroupName == "")
                        {
                            UInt32 userStringToken = (UInt32)(ilBytes[currentOffset + 1] + (ilBytes[currentOffset + 2] << 8) + (ilBytes[currentOffset + 3] << 16) + (ilBytes[currentOffset + 4] << 24));
                            mibcGroupName = (string)ilBody.GetObject((int)userStringToken);
                        }
                        break;

                    case ILOpcode.ldtoken:
                        if (String.IsNullOrEmpty(mibcGroupName))
                        {
                            break;
                        }

                        string[] assembliesByName = mibcGroupName.Split(';');

                        bool hasMatchingDefinition = (onlyDefinedInAssembly == null) || assembliesByName[0].Equals(onlyDefinedInAssembly);

                        if (!hasMatchingDefinition)
                        {
                            break;
                        }

                        bool areAllEntriesInVersionBubble = true;
                        foreach (string s in assembliesByName)
                        {
                            if (string.IsNullOrEmpty(s))
                            {
                                continue;
                            }

                            if (!assemblyNamesInVersionBubble.Contains(s))
                            {
                                areAllEntriesInVersionBubble = false;
                                break;
                            }
                        }

                        if (!areAllEntriesInVersionBubble)
                        {
                            break;
                        }

                        uint token = (uint)(ilBytes[currentOffset + 1] + (ilBytes[currentOffset + 2] << 8) + (ilBytes[currentOffset + 3] << 16) + (ilBytes[currentOffset + 4] << 24));
                        loadedMethodProfileData = loadedMethodProfileData.Concat(ReadMIbcGroup(tsc, (EcmaMethod)ilBody.GetObject((int)token)));
                        break;

                    case ILOpcode.pop:
                        mibcGroupName = "";
                        break;
                    }

                    // This isn't correct if there is a switch opcode, but since we won't do that, its ok
                    currentOffset += opcode.GetSize();
                }

                return(new IBCProfileData(false, loadedMethodProfileData));
            }
        }
Beispiel #13
0
        private EcmaModule AddModule(string filePath, string expectedSimpleName, bool useForBinding, ModuleData oldModuleData = null, bool throwOnFailureToLoad = true)
        {
            filePath = Path.GetFullPath(filePath);

            PEReader peReader = null;
            MemoryMappedViewAccessor mappedViewAccessor = null;
            PdbSymbolReader          pdbReader          = null;

            try
            {
                if (oldModuleData == null)
                {
                    peReader = OpenPEFile(filePath, out mappedViewAccessor);

#if !READYTORUN
                    if (peReader.HasMetadata && (peReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == 0)
                    {
                        throw new NotSupportedException($"Error: C++/CLI is not supported: '{filePath}'");
                    }
#endif

                    pdbReader = PortablePdbSymbolReader.TryOpenEmbedded(peReader, GetMetadataStringDecoder()) ?? OpenAssociatedSymbolFile(filePath, peReader);
                }
                else
                {
                    filePath           = oldModuleData.FilePath;
                    peReader           = oldModuleData.Module.PEReader;
                    mappedViewAccessor = oldModuleData.MappedViewAccessor;
                    pdbReader          = oldModuleData.Module.PdbReader;
                }

                if (!peReader.HasMetadata && !throwOnFailureToLoad)
                {
                    return(null);
                }

                EcmaModule module = EcmaModule.Create(this, peReader, containingAssembly: null, pdbReader);

                MetadataReader metadataReader = module.MetadataReader;
                string         simpleName     = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);

                if (expectedSimpleName != null && !simpleName.Equals(expectedSimpleName, StringComparison.OrdinalIgnoreCase))
                {
                    throw new FileNotFoundException("Assembly name does not match filename " + filePath);
                }

                ModuleData moduleData = new ModuleData()
                {
                    SimpleName         = simpleName,
                    FilePath           = filePath,
                    Module             = module,
                    MappedViewAccessor = mappedViewAccessor
                };

                lock (this)
                {
                    if (useForBinding)
                    {
                        ModuleData actualModuleData = _simpleNameHashtable.AddOrGetExisting(moduleData);
                        if (actualModuleData != moduleData)
                        {
                            if (actualModuleData.FilePath != filePath)
                            {
                                throw new FileNotFoundException("Module with same simple name already exists " + filePath);
                            }
                            return(actualModuleData.Module);
                        }
                    }
                    mappedViewAccessor = null; // Ownership has been transfered
                    pdbReader          = null; // Ownership has been transferred

                    _moduleHashtable.AddOrGetExisting(moduleData);
                }

                return(module);
            }
            finally
            {
                if (mappedViewAccessor != null)
                {
                    mappedViewAccessor.Dispose();
                }
                if (pdbReader != null)
                {
                    pdbReader.Dispose();
                }
            }
        }
Beispiel #14
0
        /// <summary>
        /// Parse an MIBC file for the methods that are interesting.
        /// The version bubble must be specified and will describe the restrict the set of methods parsed to those relevant to the compilation
        /// The onlyDefinedInAssembly parameter is used to restrict the set of types parsed to include only those which are defined in a specific module. Specify null to allow definitions from all modules.
        /// This limited parsing is not necessarily an exact set of prevention, so detailed algorithms that work at the individual method level are still necessary, but this allows avoiding excessive parsing.
        ///
        /// The format of the Mibc file is that of a .NET dll, with a global method named "AssemblyDictionary". Inside of that file are a series of references that are broken up by which assemblies define the individual methods.
        /// These references are encoded as IL code that represents the details.
        /// The format of these IL instruction is as follows.
        ///
        /// ldstr mibcGroupName
        /// ldtoken mibcGroupMethod
        /// pop
        /// {Repeat the above pattern N times, once per Mibc group}
        ///
        /// See comment above ReadMIbcGroup for details of the group format
        ///
        /// The mibcGroupName is in the following format "Assembly_{definingAssemblyName};{OtherAssemblyName};{OtherAssemblyName};...; (OtherAssemblyName is ; delimited)
        ///
        /// </summary>
        /// <returns></returns>
        public static ProfileData ParseMIbcFile(TypeSystemContext tsc, PEReader peReader, HashSet <string> assemblyNamesInVersionBubble, string onlyDefinedInAssembly)
        {
            var mibcModule = EcmaModule.Create(tsc, peReader, null, null, new CustomCanonResolver(tsc));

            var assemblyDictionary = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod("AssemblyDictionary", null);
            IEnumerable <MethodProfileData> loadedMethodProfileData = Enumerable.Empty <MethodProfileData>();

            EcmaMethodIL ilBody   = EcmaMethodIL.Create(assemblyDictionary);
            ILReader     ilReader = new ILReader(ilBody.GetILBytes());

            string mibcGroupName = "";

            while (ilReader.HasNext)
            {
                ILOpcode opcode = ilReader.ReadILOpcode();
                switch (opcode)
                {
                case ILOpcode.ldstr:
                    int userStringToken = ilReader.ReadILToken();
                    Debug.Assert(mibcGroupName == "");
                    if (mibcGroupName == "")
                    {
                        mibcGroupName = (string)ilBody.GetObject(userStringToken);
                    }
                    break;

                case ILOpcode.ldtoken:
                    int token = ilReader.ReadILToken();

                    if (String.IsNullOrEmpty(mibcGroupName))
                    {
                        break;
                    }

                    string[] assembliesByName = mibcGroupName.Split(';');

                    bool hasMatchingDefinition = (onlyDefinedInAssembly == null) || assembliesByName[0].Equals(onlyDefinedInAssembly);

                    if (!hasMatchingDefinition)
                    {
                        break;
                    }

                    if (assemblyNamesInVersionBubble != null)
                    {
                        bool areAllEntriesInVersionBubble = true;
                        foreach (string s in assembliesByName)
                        {
                            if (string.IsNullOrEmpty(s))
                            {
                                continue;
                            }

                            if (!assemblyNamesInVersionBubble.Contains(s))
                            {
                                areAllEntriesInVersionBubble = false;
                                break;
                            }
                        }

                        if (!areAllEntriesInVersionBubble)
                        {
                            break;
                        }
                    }

                    loadedMethodProfileData = loadedMethodProfileData.Concat(ReadMIbcGroup(tsc, (EcmaMethod)ilBody.GetObject(token)));
                    break;

                case ILOpcode.pop:
                    mibcGroupName = "";
                    break;

                default:
                    ilReader.Skip(opcode);
                    break;
                }
            }

            return(new IBCProfileData(false, loadedMethodProfileData));
        }
Beispiel #15
0
        /// <summary>
        /// Parse an MIBC file for the methods that are interesting.
        /// The version bubble must be specified and will describe the restrict the set of methods parsed to those relevant to the compilation
        /// The onlyDefinedInAssembly parameter is used to restrict the set of types parsed to include only those which are defined in a specific module. Specify null to allow definitions from all modules.
        /// This limited parsing is not necessarily an exact set of prevention, so detailed algorithms that work at the individual method level are still necessary, but this allows avoiding excessive parsing.
        ///
        /// The format of the Mibc file is that of a .NET dll, with a global method named "AssemblyDictionary". Inside of that file are a series of references that are broken up by which assemblies define the individual methods.
        /// These references are encoded as IL code that represents the details.
        /// The format of these IL instruction is as follows.
        ///
        /// ldstr mibcGroupName
        /// ldtoken mibcGroupMethod
        /// pop
        /// {Repeat the above pattern N times, once per Mibc group}
        ///
        /// See comment above ReadMIbcGroup for details of the group format
        ///
        /// The mibcGroupName is in the following format "Assembly_{definingAssemblyName};{OtherAssemblyName};{OtherAssemblyName};...; (OtherAssemblyName is ; delimited)
        ///
        /// </summary>
        /// <returns></returns>
        public static ProfileData ParseMIbcFile(CompilerTypeSystemContext tsc, string filename, HashSet <string> assemblyNamesInVersionBubble, string onlyDefinedInAssembly)
        {
            byte[]   peData = null;
            PEReader unprotectedPeReader = null;

            {
                FileStream fsMibcFile         = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false);
                bool       disposeOnException = true;

                try
                {
                    byte firstByte  = (byte)fsMibcFile.ReadByte();
                    byte secondByte = (byte)fsMibcFile.ReadByte();
                    fsMibcFile.Seek(0, SeekOrigin.Begin);
                    if (firstByte == 0x4d && secondByte == 0x5a)
                    {
                        // Uncompressed Mibc format, starts with 'MZ' prefix like all other PE files
                        unprotectedPeReader = new PEReader(fsMibcFile, PEStreamOptions.Default);
                        disposeOnException  = false;
                    }
                    else
                    {
                        using (var zipFile = new ZipArchive(fsMibcFile, ZipArchiveMode.Read, leaveOpen: false, entryNameEncoding: null))
                        {
                            disposeOnException = false;
                            var mibcDataEntry = zipFile.GetEntry(Path.GetFileName(filename) + ".dll");
                            using (var mibcDataStream = mibcDataEntry.Open())
                            {
                                peData = new byte[mibcDataEntry.Length];
                                using (BinaryReader br = new BinaryReader(mibcDataStream))
                                {
                                    peData = br.ReadBytes(checked ((int)mibcDataEntry.Length));
                                }
                            }
                        }
                    }
                }
                finally
                {
                    if (disposeOnException)
                    {
                        fsMibcFile.Dispose();
                    }
                }
            }

            if (peData != null)
            {
                unprotectedPeReader = new PEReader(System.Collections.Immutable.ImmutableArray.Create <byte>(peData));
            }

            using (var peReader = unprotectedPeReader)
            {
                var mibcModule = EcmaModule.Create(tsc, peReader, null, null, new CustomCanonResolver(tsc));

                var assemblyDictionary = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod("AssemblyDictionary", null);
                IEnumerable <MethodProfileData> loadedMethodProfileData = Enumerable.Empty <MethodProfileData>();

                EcmaMethodIL ilBody        = EcmaMethodIL.Create(assemblyDictionary);
                byte[]       ilBytes       = ilBody.GetILBytes();
                int          currentOffset = 0;

                string mibcGroupName = "";
                while (currentOffset < ilBytes.Length)
                {
                    ILOpcode opcode = (ILOpcode)ilBytes[currentOffset];
                    if (opcode == ILOpcode.prefix1)
                    {
                        opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1];
                    }
                    switch (opcode)
                    {
                    case ILOpcode.ldstr:
                        if (mibcGroupName == "")
                        {
                            UInt32 userStringToken = BinaryPrimitives.ReadUInt32LittleEndian(ilBytes.AsSpan(currentOffset + 1));
                            mibcGroupName = (string)ilBody.GetObject((int)userStringToken);
                        }
                        break;

                    case ILOpcode.ldtoken:
                        if (String.IsNullOrEmpty(mibcGroupName))
                        {
                            break;
                        }

                        string[] assembliesByName = mibcGroupName.Split(';');

                        bool hasMatchingDefinition = (onlyDefinedInAssembly == null) || assembliesByName[0].Equals(onlyDefinedInAssembly);

                        if (!hasMatchingDefinition)
                        {
                            break;
                        }

                        bool areAllEntriesInVersionBubble = true;
                        foreach (string s in assembliesByName)
                        {
                            if (string.IsNullOrEmpty(s))
                            {
                                continue;
                            }

                            if (!assemblyNamesInVersionBubble.Contains(s))
                            {
                                areAllEntriesInVersionBubble = false;
                                break;
                            }
                        }

                        if (!areAllEntriesInVersionBubble)
                        {
                            break;
                        }

                        uint token = BinaryPrimitives.ReadUInt32LittleEndian(ilBytes.AsSpan(currentOffset + 1));
                        loadedMethodProfileData = loadedMethodProfileData.Concat(ReadMIbcGroup(tsc, (EcmaMethod)ilBody.GetObject((int)token)));
                        break;

                    case ILOpcode.pop:
                        mibcGroupName = "";
                        break;
                    }

                    // This isn't correct if there is a switch opcode, but since we won't do that, its ok
                    currentOffset += opcode.GetSize();
                }

                return(new IBCProfileData(false, loadedMethodProfileData));
            }
        }
Beispiel #16
0
        public static ProfileData ParseMIbcFile(TypeSystemContext tsc, PEReader peReader, HashSet <string> assemblyNamesInVersionBubble, string onlyDefinedInAssembly, MibcGroupParseRules parseRule = MibcGroupParseRules.VersionBubble, HashSet <string> crossModuleInlineModules = null)
        {
            if (parseRule == MibcGroupParseRules.VersionBubble)
            {
                crossModuleInlineModules = s_EmptyHash;
            }

            if (parseRule == MibcGroupParseRules.AllGroups)
            {
                assemblyNamesInVersionBubble = null;
            }

            if (crossModuleInlineModules == null)
            {
                crossModuleInlineModules = s_EmptyHash;
            }

            var mibcModule = EcmaModule.Create(tsc, peReader, null, null, new CustomCanonResolver(tsc));

            var assemblyDictionary = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod("AssemblyDictionary", null);
            IEnumerable <MethodProfileData> loadedMethodProfileData = Enumerable.Empty <MethodProfileData>();

            EcmaMethodIL ilBody   = EcmaMethodIL.Create(assemblyDictionary);
            ILReader     ilReader = new ILReader(ilBody.GetILBytes());

            string mibcGroupName = "";

            while (ilReader.HasNext)
            {
                ILOpcode opcode = ilReader.ReadILOpcode();
                switch (opcode)
                {
                case ILOpcode.ldstr:
                    int userStringToken = ilReader.ReadILToken();
                    Debug.Assert(mibcGroupName == "");
                    if (mibcGroupName == "")
                    {
                        mibcGroupName = (string)ilBody.GetObject(userStringToken);
                    }
                    break;

                case ILOpcode.ldtoken:
                    int token = ilReader.ReadILToken();

                    if (String.IsNullOrEmpty(mibcGroupName))
                    {
                        break;
                    }

                    string[] assembliesByName = mibcGroupName.Split(';');

                    bool hasMatchingDefinition = (onlyDefinedInAssembly == null) || assembliesByName[0].Equals(onlyDefinedInAssembly);

                    if (!hasMatchingDefinition)
                    {
                        break;
                    }

                    if (assemblyNamesInVersionBubble != null)
                    {
                        bool mibcGroupUseable         = true;
                        bool someEntryInVersionBubble = false;
                        foreach (string s in assembliesByName)
                        {
                            if (string.IsNullOrEmpty(s))
                            {
                                continue;
                            }

                            bool entryInVersionBubble = assemblyNamesInVersionBubble.Contains(s);
                            someEntryInVersionBubble = someEntryInVersionBubble || entryInVersionBubble;

                            if (!entryInVersionBubble && !crossModuleInlineModules.Contains(s))
                            {
                                // If the group references a module that isn't in the version bubble and isn't cross module inlineable, its not useful.
                                mibcGroupUseable = false;
                                break;
                            }
                        }

                        if (!someEntryInVersionBubble && (parseRule == MibcGroupParseRules.VersionBubbleWithCrossModule1))
                        {
                            mibcGroupUseable = false;
                        }

                        if (!mibcGroupUseable)
                        {
                            break;
                        }
                    }

                    loadedMethodProfileData = loadedMethodProfileData.Concat(ReadMIbcGroup(tsc, (EcmaMethod)ilBody.GetObject(token)));
                    break;

                case ILOpcode.pop:
                    mibcGroupName = "";
                    break;

                default:
                    ilReader.Skip(opcode);
                    break;
                }
            }

            return(new IBCProfileData(ParseMibcConfig(tsc, peReader), false, loadedMethodProfileData));
        }