Welcome to mirror list, hosted at ThFree Co, Russian Federation.

PeVerifier.cs « TestCasesRunner « Tests « linker - github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 388fa1e5d80bdaa5e579ad48eb67535ca5416346 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Win32;
using Mono.Cecil;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Extensions;
using NUnit.Framework;

namespace Mono.Linker.Tests.TestCasesRunner {
	public class PeVerifier
	{
		private readonly string _peExecutable;

		public PeVerifier ()
		{
			_peExecutable = Environment.OSVersion.Platform == PlatformID.Win32NT ? FindPeExecutableFromRegistry ().ToString () : "pedump";
		}

		public PeVerifier (string peExecutable)
		{
			_peExecutable = peExecutable;
		}

		public virtual void Check (LinkedTestCaseResult linkResult, AssemblyDefinition original)
		{
			bool skipCheckEntirely;
			HashSet<string> assembliesToSkip;
			ProcessSkipAttributes (linkResult, original, out skipCheckEntirely, out assembliesToSkip);

			if (skipCheckEntirely)
				return;

			foreach (var file in linkResult.OutputAssemblyPath.Parent.Files ()) {
				if (file.ExtensionWithDot != ".exe" && file.ExtensionWithDot != ".dll")
					continue;

				// Always skip the I18N assemblies, for some reason they end up in the output directory on OSX.
				// verification of these fails due to native pointers
				if (file.FileName.StartsWith ("I18N"))
					continue;

				if (assembliesToSkip.Contains (file.FileName))
					continue;

				CheckAssembly (file);
			}
		}

		private void ProcessSkipAttributes (LinkedTestCaseResult linkResult, AssemblyDefinition original, out bool skipCheckEntirely, out HashSet<string> assembliesToSkip)
		{
			var peVerifyAttrs = original.MainModule.GetType (linkResult.TestCase.ReconstructedFullTypeName).CustomAttributes.Where (attr => attr.AttributeType.Name == nameof (SkipPeVerifyAttribute));
			skipCheckEntirely = false;
			assembliesToSkip = new HashSet<string> ();
			foreach (var attr in peVerifyAttrs) {
				var ctorArg = attr.ConstructorArguments.FirstOrDefault ();

				if (!attr.HasConstructorArguments) {
					skipCheckEntirely = true;
				} else if (ctorArg.Type.Name == nameof (SkipPeVerifyForToolchian)) {
					var skipToolchain = (SkipPeVerifyForToolchian)ctorArg.Value;

					if (skipToolchain == SkipPeVerifyForToolchian.Pedump) {
						if (Environment.OSVersion.Platform != PlatformID.Win32NT)
							skipCheckEntirely = true;
					}
					else
						throw new ArgumentException($"Unhandled platform and toolchain values of {Environment.OSVersion.Platform} and {skipToolchain}");
				} else if (ctorArg.Type.Name == nameof (String)) {
					assembliesToSkip.Add ((string)ctorArg.Value);
				} else {
					throw new ArgumentException($"Unhandled constructor argument type of {ctorArg.Type} on {nameof (SkipPeVerifyAttribute)}");
				}
			}
		}

		private void CheckAssembly (NPath assemblyPath)
		{
			var capturedOutput = new List<string> ();
			var exeArgs = Environment.OSVersion.Platform == PlatformID.Win32NT ? $"/nologo {assemblyPath.InQuotes ()}" : $"--verify metadata,code {assemblyPath.InQuotes ()}";
			var process = new Process ();
			process.StartInfo.FileName = _peExecutable;
			process.StartInfo.Arguments = exeArgs;
			process.StartInfo.UseShellExecute = false;
			process.StartInfo.CreateNoWindow = true;
			process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
			process.StartInfo.RedirectStandardOutput = true;
			process.OutputDataReceived += (sender, args) => capturedOutput.Add (args.Data);
			process.Start ();
			process.BeginOutputReadLine ();
			process.WaitForExit ();

			if (process.ExitCode != 0) {
				Assert.Fail ($"Invalid IL detected in {assemblyPath}\n{capturedOutput.Aggregate ((buff, s) => buff + Environment.NewLine + s)}");
			}
		}

		public static NPath FindPeExecutableFromRegistry ()
		{
			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
				throw new InvalidOperationException ("This method should only be called on windows");

			var key = Registry.LocalMachine.OpenSubKey (@"SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows");
			foreach (var sdkKeyName in key.GetSubKeyNames ().OrderBy (name => new Version (name.TrimStart ('v').TrimEnd ('A'))).Reverse ()) {
				var sdkKey = Registry.LocalMachine.OpenSubKey ($"SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft SDKs\\Windows\\{sdkKeyName}");

				var sdkDir = (string)sdkKey.GetValue ("InstallationFolder");
				if (string.IsNullOrEmpty (sdkDir))
					continue;

				var binDir = sdkDir.ToNPath ().Combine ("bin");

				if (!binDir.Exists ())
					continue;

				foreach (var netSdkDirs in binDir.Directories ().OrderBy (dir => dir.FileName)) {
					var peVerifyPath = netSdkDirs.Combine ("PEVerify.exe");

					if (peVerifyPath.FileExists ())
						return peVerifyPath;
				}
			}

			throw new InvalidOperationException ("Could not locate a peverify.exe executable");
		}
	}
}