未验证 提交 7706e344 编写于 作者: A Alexander Köplinger 提交者: GitHub

Add ILStrip task to trim method bodies (#57359)

This is used for iOS when assemblies are FullAOT compiled since the method body IL is unnecessary there.
上级 3042bd7a
......@@ -122,6 +122,10 @@
<Uri>https://github.com/dotnet/runtime-assets</Uri>
<Sha>cc70e46d909f35a576abe245219c1df31160bbd6</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.CilStrip.Sources" Version="6.0.0-beta.21411.3">
<Uri>https://github.com/dotnet/runtime-assets</Uri>
<Sha>cc70e46d909f35a576abe245219c1df31160bbd6</Sha>
</Dependency>
<Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="11.1.0-alpha.1.21409.1">
<Uri>https://github.com/dotnet/llvm-project</Uri>
<Sha>30296e71234ffb3eb8da789d36adf4db146e6602</Sha>
......
......@@ -126,6 +126,7 @@
<SystemRuntimeTimeZoneDataVersion>6.0.0-beta.21411.3</SystemRuntimeTimeZoneDataVersion>
<SystemSecurityCryptographyX509CertificatesTestDataVersion>6.0.0-beta.21411.3</SystemSecurityCryptographyX509CertificatesTestDataVersion>
<SystemWindowsExtensionsTestDataVersion>6.0.0-beta.21411.3</SystemWindowsExtensionsTestDataVersion>
<MicrosoftDotNetCilStripSourcesVersion>6.0.0-beta.21411.3</MicrosoftDotNetCilStripSourcesVersion>
<!-- dotnet-optimization dependencies -->
<optimizationwindows_ntx64MIBCRuntimeVersion>1.0.0-prerelease.21411.3</optimizationwindows_ntx64MIBCRuntimeVersion>
<optimizationwindows_ntx86MIBCRuntimeVersion>1.0.0-prerelease.21411.3</optimizationwindows_ntx86MIBCRuntimeVersion>
......
......@@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(RepoTasksDir)ILStripTask\ILStrip.csproj" />
<ProjectReference Include="$(RepoTasksDir)RuntimeConfigParser\RuntimeConfigParser.csproj" />
<ProjectReference Include="$(RepoTasksDir)JsonToItemsTaskFactory\JsonToItemsTaskFactory.csproj" />
</ItemGroup>
......@@ -14,6 +15,7 @@
<PackageFile Include="Sdk\Sdk.props" TargetPath="Sdk" />
<PackageFile Include="Sdk\Sdk.targets" TargetPath="Sdk" />
<PackageFile Include="build\$(MSBuildProjectName).props" TargetPath="build" />
<PackageFile Include="Sdk\ILStripTask.props" TargetPath="Sdk" />
<PackageFile Include="Sdk\RuntimeConfigParserTask.props" TargetPath="Sdk" />
<PackageFile Include="Sdk\RuntimeComponentManifest.props" TargetPath="Sdk" />
<PackageFile Include="Sdk\RuntimeComponentManifest.targets" TargetPath="Sdk" />
......
<Project>
<PropertyGroup>
<ILStripTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)..\tasks\net6.0\ILStrip.dll</ILStripTasksAssemblyPath>
<ILStripTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$(MSBuildThisFileDirectory)..\tasks\net472\ILStrip.dll</ILStripTasksAssemblyPath>
</PropertyGroup>
<UsingTask TaskName="ILStrip" AssemblyFile="$(ILStripTasksAssemblyPath)" />
</Project>
<Project>
<Import Project="$(MSBuildThisFileDirectory)\ILStripTask.props" />
<Import Project="$(MSBuildThisFileDirectory)\RuntimeConfigParserTask.props" />
<Import Project="$(MSBuildThisFileDirectory)\RuntimeComponentManifest.props" />
</Project>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Cecil.Binary;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
public class ILStrip : Microsoft.Build.Utilities.Task
{
/// <summary>
/// Assemblies to be stripped.
/// The assemblies will be modified in place if OutputPath metadata is not set.
/// </summary>
[Required]
public ITaskItem[] Assemblies { get; set; } = Array.Empty<ITaskItem>();
/// <summary>
/// Disable parallel stripping
/// </summary>
public bool DisableParallelStripping { get; set; }
public override bool Execute()
{
if (Assemblies.Length == 0)
{
throw new ArgumentException($"'{nameof(Assemblies)}' is required.", nameof(Assemblies));
}
if (DisableParallelStripping)
{
foreach (var assemblyItem in Assemblies)
{
if (!StripAssembly(assemblyItem))
return !Log.HasLoggedErrors;
}
}
else
{
Parallel.ForEach(Assemblies,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
assemblyItem => StripAssembly(assemblyItem));
}
return !Log.HasLoggedErrors;
}
private bool StripAssembly(ITaskItem assemblyItem)
{
string assemblyFile = assemblyItem.ItemSpec;
var outputPath = assemblyItem.GetMetadata("OutputPath");
if (String.IsNullOrWhiteSpace(outputPath))
{
outputPath = assemblyFile;
Log.LogMessage(MessageImportance.Low, $"[ILStrip] {assemblyFile}");
}
else
{
Log.LogMessage(MessageImportance.Low, $"[ILStrip] {assemblyFile} to {outputPath}");
}
try
{
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyFile);
AssemblyStripper.StripAssembly(assembly, outputPath);
}
catch (Exception ex)
{
Log.LogMessage(MessageImportance.Low, ex.ToString());
Log.LogError($"ILStrip failed for {assemblyFile}: {ex.Message}");
return false;
}
return true;
}
private class AssemblyStripper
{
AssemblyDefinition assembly;
BinaryWriter writer;
Image original;
Image stripped;
ReflectionWriter reflection_writer;
MetadataWriter metadata_writer;
TablesHeap original_tables;
TablesHeap stripped_tables;
AssemblyStripper(AssemblyDefinition assembly, BinaryWriter writer)
{
this.assembly = assembly;
this.writer = writer;
}
void Strip()
{
FullLoad();
ClearMethodBodies();
CopyOriginalImage();
PatchMethods();
PatchFields();
PatchResources();
Write();
}
void FullLoad()
{
assembly.MainModule.FullLoad();
}
void ClearMethodBodies()
{
foreach (TypeDefinition type in assembly.MainModule.Types)
{
ClearMethodBodies(type.Constructors);
ClearMethodBodies(type.Methods);
}
}
static void ClearMethodBodies(ICollection methods)
{
foreach (MethodDefinition method in methods)
{
if (!method.HasBody)
continue;
MethodBody body = new MethodBody(method);
body.CilWorker.Emit(OpCodes.Ret);
method.Body = body;
}
}
void CopyOriginalImage()
{
original = assembly.MainModule.Image;
stripped = Image.CreateImage();
stripped.Accept(new CopyImageVisitor(original));
assembly.MainModule.Image = stripped;
original_tables = original.MetadataRoot.Streams.TablesHeap;
stripped_tables = stripped.MetadataRoot.Streams.TablesHeap;
TableCollection tables = original_tables.Tables;
foreach (IMetadataTable table in tables)
stripped_tables.Tables.Add(table);
stripped_tables.Valid = original_tables.Valid;
stripped_tables.Sorted = original_tables.Sorted;
reflection_writer = new ReflectionWriter(assembly.MainModule);
reflection_writer.StructureWriter = new StructureWriter(assembly, writer);
reflection_writer.CodeWriter.Stripped = true;
metadata_writer = reflection_writer.MetadataWriter;
PatchHeap(metadata_writer.StringWriter, original.MetadataRoot.Streams.StringsHeap);
PatchHeap(metadata_writer.GuidWriter, original.MetadataRoot.Streams.GuidHeap);
PatchHeap(metadata_writer.UserStringWriter, original.MetadataRoot.Streams.UserStringsHeap);
PatchHeap(metadata_writer.BlobWriter, original.MetadataRoot.Streams.BlobHeap);
if (assembly.EntryPoint != null)
metadata_writer.EntryPointToken = assembly.EntryPoint.MetadataToken.ToUInt();
}
static void PatchHeap(MemoryBinaryWriter heap_writer, MetadataHeap heap)
{
if (heap == null)
return;
heap_writer.BaseStream.Position = 0;
heap_writer.Write(heap.Data);
}
void PatchMethods()
{
MethodTable methodTable = (MethodTable)stripped_tables[MethodTable.RId];
if (methodTable == null)
return;
RVA method_rva = RVA.Zero;
for (int i = 0; i < methodTable.Rows.Count; i++)
{
MethodRow methodRow = methodTable[i];
methodRow.ImplFlags |= MethodImplAttributes.NoInlining;
MetadataToken methodToken = MetadataToken.FromMetadataRow(TokenType.Method, i);
MethodDefinition method = (MethodDefinition)assembly.MainModule.LookupByToken(methodToken);
if (method.HasBody)
{
method_rva = method_rva != RVA.Zero
? method_rva
: reflection_writer.CodeWriter.WriteMethodBody(method);
methodRow.RVA = method_rva;
}
else
methodRow.RVA = RVA.Zero;
}
}
void PatchFields()
{
FieldRVATable fieldRvaTable = (FieldRVATable)stripped_tables[FieldRVATable.RId];
if (fieldRvaTable == null)
return;
for (int i = 0; i < fieldRvaTable.Rows.Count; i++)
{
FieldRVARow fieldRvaRow = fieldRvaTable[i];
MetadataToken fieldToken = new MetadataToken(TokenType.Field, fieldRvaRow.Field);
FieldDefinition field = (FieldDefinition)assembly.MainModule.LookupByToken(fieldToken);
fieldRvaRow.RVA = metadata_writer.GetDataCursor();
metadata_writer.AddData(field.InitialValue.Length + 3 & (~3));
metadata_writer.AddFieldInitData(field.InitialValue);
}
}
void PatchResources()
{
ManifestResourceTable resourceTable = (ManifestResourceTable)stripped_tables[ManifestResourceTable.RId];
if (resourceTable == null)
return;
for (int i = 0; i < resourceTable.Rows.Count; i++)
{
ManifestResourceRow resourceRow = resourceTable[i];
if (resourceRow.Implementation.RID != 0)
continue;
foreach (Resource resource in assembly.MainModule.Resources)
{
EmbeddedResource er = resource as EmbeddedResource;
if (er == null)
continue;
if (resource.Name != original.MetadataRoot.Streams.StringsHeap[resourceRow.Name])
continue;
resourceRow.Offset = metadata_writer.AddResource(er.Data);
}
}
}
void Write()
{
stripped.MetadataRoot.Accept(metadata_writer);
}
public static void StripAssembly(AssemblyDefinition assembly, string file)
{
using (FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
{
new AssemblyStripper(assembly, new BinaryWriter(fs)).Strip();
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks)</TargetFrameworks>
<OutputType>Library</OutputType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<NoWarn>$(NoWarn),CA1050,CS0618,CS0649,CS8604,CS8602,CS8632,SYSLIB0003</NoWarn>
<RunAnalyzers>false</RunAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(RefOnlyMicrosoftBuildTasksCoreVersion)" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(RefOnlyMicrosoftBuildUtilitiesCoreVersion)" />
<PackageReference Include="Microsoft.DotNet.CilStrip.Sources" Version="$(MicrosoftDotNetCilStripSourcesVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="ILStrip.cs" />
<Compile Include="..\Common\Utils.cs" />
<Compile Include="$(RepoRoot)src\libraries\System.Private.CoreLib\src\System\Diagnostics\CodeAnalysis\NullableAttributes.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
</ItemGroup>
<!-- GetFilesToPackage assists to place `ILStrip.dll` in a NuGet package in Microsoft.NET.Runtime.MonoTargets.Sdk.pkgproj for external use -->
<Target Name="GetFilesToPackage" Returns="@(FilesToPackage)">
<ItemGroup>
<_PublishFramework Remove="@(_PublishFramework)" />
<_PublishFramework Include="$(TargetFrameworks)" />
<FilesToPackage Include="$(OutputPath)%(_PublishFramework.Identity)\$(AssemblyName).dll" TargetPath="tasks\%(_PublishFramework.Identity)" />
</ItemGroup>
</Target>
</Project>
......@@ -26,7 +26,7 @@
<Compile Include="$(RepoRoot)src\libraries\System.Private.CoreLib\src\System\Diagnostics\CodeAnalysis\NullableAttributes.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
</ItemGroup>
<!-- GetFilesToPackage assists to place `RuntimeConfigParser.dll` in a NuGet package in Microsoft.NET.Runtime.RuntimeConfigParser.Task.pkgproj for external use -->
<!-- GetFilesToPackage assists to place `RuntimeConfigParser.dll` in a NuGet package in Microsoft.NET.Runtime.MonoTargets.Sdk.pkgproj for external use -->
<Target Name="GetFilesToPackage" Returns="@(FilesToPackage)">
<ItemGroup>
<_PublishFramework Remove="@(_PublishFramework)" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册