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

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/tasks
diff options
context:
space:
mode:
authorAlexander Köplinger <alex.koeplinger@outlook.com>2021-08-14 00:21:34 +0300
committerGitHub <noreply@github.com>2021-08-14 00:21:34 +0300
commit7706e3445b0b7378317aea45afeea3edd372581f (patch)
tree71c49f1f7ae990e845a7404dcfdfdb50689a660a /src/tasks
parent3042bd7ae65b92f2968c115c11c8c76bdbd6b336 (diff)
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.
Diffstat (limited to 'src/tasks')
-rw-r--r--src/tasks/ILStripTask/ILStrip.cs277
-rw-r--r--src/tasks/ILStripTask/ILStrip.csproj32
-rw-r--r--src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj2
3 files changed, 310 insertions, 1 deletions
diff --git a/src/tasks/ILStripTask/ILStrip.cs b/src/tasks/ILStripTask/ILStrip.cs
new file mode 100644
index 00000000000..7ce5a5ecc07
--- /dev/null
+++ b/src/tasks/ILStripTask/ILStrip.cs
@@ -0,0 +1,277 @@
+// 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();
+ }
+ }
+ }
+}
diff --git a/src/tasks/ILStripTask/ILStrip.csproj b/src/tasks/ILStripTask/ILStrip.csproj
new file mode 100644
index 00000000000..879c56f26ae
--- /dev/null
+++ b/src/tasks/ILStripTask/ILStrip.csproj
@@ -0,0 +1,32 @@
+<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>
diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj
index c19669c373c..2fb35e649ee 100644
--- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj
+++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj
@@ -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)" />