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");
}
}
}
|