Skip to content

Commit 2ebe0c9

Browse files
committed
Add System.Runtime.CompilerServices.Unsafe as a shimmed package
1 parent cf87339 commit 2ebe0c9

4 files changed

Lines changed: 100 additions & 45 deletions

File tree

src/GenApiCompatDll/Program.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,24 @@ .. var dotnetOobPackagePaths
4949
.ToDictionary(t => t.fwk, t => t.files)))
5050
.ToDictionary(t => t.name, t => (t.path, t.fwks));
5151

52-
// load available shims (by name)
53-
var shimsByName = Directory.EnumerateFiles(shimsDir, "*.dll", new EnumerationOptions() { RecurseSubdirectories = true })
52+
// load available shims
53+
var allShimDlls = Directory.EnumerateFiles(shimsDir, "*.dll", new EnumerationOptions() { RecurseSubdirectories = true })
5454
.Where(f => Path.GetDirectoryName(f) != shimsDir)
5555
.Select(f => (path: f, name: Path.GetFileName(f), fwk: NuGetFramework.ParseFolder(Path.GetFileName(Path.GetDirectoryName(f))!)))
56+
.ToArray();
57+
58+
var shimsByName = allShimDlls
5659
.GroupBy(t => t.name)
5760
.ToDictionary(
5861
g => g.Key,
5962
g => g.ToDictionary(t => t.fwk, t => t.path));
6063

64+
var shimsByTfm = allShimDlls
65+
.GroupBy(t => t.fwk)
66+
.ToDictionary(
67+
g => g.Key,
68+
g => g.ToDictionary(t => t.name, t => t.path));
69+
6170

6271
// load our tfm list
6372
var packageTfms = reducer.ReduceEquivalent(
@@ -235,6 +244,13 @@ void ExportType(TypeExport export, IImplementation impl, bool nested)
235244
impl = (IImplementation)module.CorLibTypeFactory.CorLibScope;
236245
}
237246
else
247+
if (export.FromPackage.Equals("system.runtime.compilerservices.unsafe", StringComparison.OrdinalIgnoreCase)
248+
&& tfm is { Framework: ".NETCoreApp" } && tfm.Version >= new Version(7, 0, 0))
249+
{
250+
// on .NET 7, System.Runtime.CompilerServices.Unsafe moved into System.Runtime
251+
impl = (IImplementation)module.CorLibTypeFactory.CorLibScope;
252+
}
253+
else
238254
{
239255
// the package provides something compatible with this TFM, reference the FromFile ref
240256
impl = assemblyRefsByName[export.FromFile];
@@ -282,10 +298,11 @@ string GetReferencePathForTfm(NuGetFramework framework, bool useShim)
282298
// if we want to use shims instead, remap all of the files to use the shims
283299
if (useShim)
284300
{
301+
var shimFilesDict = shimsByTfm[reducer.GetNearest(best, shimsByTfm.Keys)!];
285302
pkgFiles = pkgFiles
286303
.Select(f => Path.GetFileName(f))
287-
.Select(n => shimsByName[n])
288-
.Select(dict => dict[reducer.GetNearest(best, dict.Keys)!]);
304+
.Select(n => shimFilesDict.TryGetValue(n, out var v) ? v : null)
305+
.Where(v => v is not null)!;
289306
}
290307

291308
// then add them to the dll list

src/MonoMod.Backports.Shims/Directory.Build.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
<_PPArguments Include="%(_ShimmedPackages.PkgPath)" />
7878
</ItemGroup>
7979

80-
<Exec ConsoleToMsBuild="true" LogStandardErrorAsError="true"
80+
<Exec ConsoleToMsBuild="true" LogStandardErrorAsError="false"
8181
Command="&quot;$(_ShimgenExe)&quot; @(_PPArguments->'&quot;%(Identity)&quot;',' ')">
8282
<Output TaskParameter="ExitCode" PropertyName="_ExitCode" />
8383
<Output TaskParameter="ConsoleOutput" ItemName="_ShimGenOutput" />

src/MonoMod.Backports.Shims/MonoMod.Backports.Shims.csproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,17 @@
2121
<!-- Packages we're going to shim -->
2222
<ItemGroup>
2323
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" Shim="true" />
24-
<PackageReference Include="System.Memory" Version="4.6.0" Shim="true" />
25-
<PackageReference Include="System.Buffers" Version="4.6.0" Shim="true" />
24+
<PackageReference Include="System.Memory" Version="4.6.3" Shim="true" />
25+
<PackageReference Include="System.Buffers" Version="4.6.1" Shim="true" />
26+
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" Shim="true" />
2627
<PackageReference Include="System.ValueTuple" Version="4.6.1" Shim="true" />
2728

2829
<!-- It seems like we can't sign shims for this, and it's a .NET Standard package, so who cares, really -->
2930
<!--<PackageReference Include="System.Collections.Concurrent" Version="4.3.0" Shim="true" />-->
3031
</ItemGroup>
3132

3233
<!-- ApiCompat properly resolve backports -->
33-
<Target Name="AddBackportsRefForNonBuildingPack"
34-
BeforeTargets="AssignProjectConfiguration"
35-
Condition="'$(BuildProjectReferences)' == 'false' and '$(_IsPacking)' == 'true'">
34+
<Target Name="AddBackportsRefForNonBuildingPack" BeforeTargets="AssignProjectConfiguration" Condition="'$(BuildProjectReferences)' == 'false' and '$(_IsPacking)' == 'true'">
3635
<ItemGroup>
3736
<ProjectReference Include="$(MMSourcePath)MonoMod.Backports\MonoMod.Backports.csproj" ReferenceOutputAssembly="true" />
3837
</ItemGroup>

src/ShimGen/Program.cs

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
using AsmResolver.DotNet.Builder;
44
using AsmResolver.DotNet.Serialized;
55
using AsmResolver.IO;
6+
using AsmResolver.PE;
7+
using AsmResolver.PE.Builder;
68
using AsmResolver.PE.DotNet.Metadata.Tables;
79
using AsmResolver.PE.DotNet.StrongName;
10+
using AsmResolver.PE.File;
811
using NuGet.Frameworks;
912
using ShimGen;
13+
using System.Diagnostics.CodeAnalysis;
1014

1115
if (args is not [
1216
var outputRefDir,
@@ -57,7 +61,7 @@ .. var dotnetOobPackagePaths
5761
// first, arrange the lookups in a reasonable manner
5862
// pkgPath -> framework -> dllPaths
5963
var packageLayout = new Dictionary<string, Dictionary<NuGetFramework, List<(string dllPath, string dllName)>>>();
60-
var dllsByDllName = new Dictionary<string, List<string>>();
64+
var dllsByDllName = new Dictionary<string, Dictionary<NuGetFramework, string>>();
6165
foreach (var (pkgPath, libPath, framework, dllPath) in pkgList
6266
.SelectMany(ta
6367
=> ta.Item2.SelectMany(tb
@@ -86,7 +90,7 @@ .. var dotnetOobPackagePaths
8690
dllsByDllName.Add(dllName, dllPathList = new());
8791
}
8892

89-
dllPathList.Add(dllPath);
93+
dllPathList.Add(framework, dllPath);
9094
}
9195

9296
// collect the list of ALL target frameworks that we might care about
@@ -96,7 +100,7 @@ .. var dotnetOobPackagePaths
96100
.ToArray();
97101

98102
// then build up a mapping of the source files for all of those TFMs
99-
var frameworkGroupLayout = new Dictionary<NuGetFramework, List<string>>();
103+
var frameworkGroupLayout = new Dictionary<NuGetFramework, List<(string dllName, string assemblyFullName)>>();
100104
foreach (var tfm in targetTfms)
101105
{
102106
if (!frameworkGroupLayout.TryGetValue(tfm, out var list))
@@ -106,12 +110,22 @@ .. var dotnetOobPackagePaths
106110

107111
foreach (var (pkgPath, pkgFwks) in packageLayout)
108112
{
109-
var bestTfm = fwReducer.GetNearest(tfm, pkgFwks.Keys);
110-
if (bestTfm is not null)
113+
if (TryGetFrameworkKey(pkgFwks, tfm, fwReducer, out var dlls))
111114
{
112-
foreach (var (_, dllName) in pkgFwks[bestTfm])
115+
foreach (var (dllPath, _) in dlls)
113116
{
114-
list.Add(dllName);
117+
var assembly = AssemblyDefinition.FromFile(dllPath);
118+
if (assembly.Name == "System.Runtime.CompilerServices.Unsafe")
119+
{
120+
// For SRCS.Unsafe, we switch to using inbox on net6, BEFORE it's moved into System.Runtime (which happened net7).
121+
// As such, we want to make sure we DO NOT record SRCS.Unsafe for net6.
122+
if (tfm.Framework == ".NETCoreApp" && tfm.Version is { Major: 6, Minor: 0 })
123+
{
124+
continue;
125+
}
126+
}
127+
// We include FullName here so that if the string name key changes (for some reason) we handle it properly
128+
list.Add((Path.GetFileName(dllPath), assembly.FullName));
115129
}
116130
}
117131
}
@@ -126,7 +140,7 @@ .. var dotnetOobPackagePaths
126140
// for each (particularly net35), but if we just pick the overall minimum (netstandard2.0), net35 would be preferred
127141
// for all .NET Framework targets, even the ones that support NS2.0.
128142
var frameworkAssemblies = frameworkGroupLayout
129-
.GroupBy(kvp => kvp.Value, SequenceEqualityComparer<string>.Instance)
143+
.GroupBy(kvp => kvp.Value, SequenceEqualityComparer<(string, string)>.Instance)
130144
.SelectMany(g =>
131145
g.Select(kvp => kvp.Key)
132146
.GroupBy(tfm => tfm.Framework)
@@ -140,42 +154,43 @@ .. var dotnetOobPackagePaths
140154
var importedSet = new Dictionary<string, ExportedType>();
141155
foreach (var (targetTfm, assemblies) in frameworkAssemblies)
142156
{
143-
foreach (var dllName in assemblies)
157+
foreach (var (dllName, _) in assemblies)
144158
{
145159
importedSet.Clear();
146160

147161
ModuleDefinition? backportsShim = null;
148162
AssemblyDefinition? backportsShimAssembly = null;
149163
AssemblyReference? backportsReference = null;
150164

151-
foreach (var bclShimPath in dllsByDllName[dllName])
165+
var bclShimPath = GetFrameworkKey(dllsByDllName[dllName], targetTfm, fwReducer);
166+
167+
var bclShim = ModuleDefinition.FromFile(bclShimPath, readerParams);
168+
169+
var bcl = KnownCorLibs.FromRuntimeInfo(
170+
DotNetRuntimeInfo.Parse(
171+
targetTfm.GetDotNetFrameworkName(DefaultFrameworkNameProvider.Instance)));
172+
173+
// set up the new shim module
174+
backportsShim = new ModuleDefinition(bclShim.Name, bcl)
175+
{
176+
RuntimeVersion = bclShim.RuntimeVersion
177+
};
178+
backportsShimAssembly = new AssemblyDefinition(bclShim.Assembly!.Name,
179+
new(bclShim.Assembly!.Version.Major, 9999, 9999, 9999))
152180
{
153-
var bclShim = ModuleDefinition.FromFile(bclShimPath, readerParams);
181+
Modules = { backportsShim },
182+
HashAlgorithm = bclShim.Assembly!.HashAlgorithm,
183+
PublicKey = bclShim.Assembly!.PublicKey,
184+
HasPublicKey = bclShim.Assembly!.HasPublicKey,
185+
};
154186

155-
if (peImageBuilder is null || backportsShim is null || backportsShimAssembly is null || backportsReference is null)
156-
{
157-
var bcl = KnownCorLibs.FromRuntimeInfo(
158-
DotNetRuntimeInfo.Parse(
159-
targetTfm.GetDotNetFrameworkName(DefaultFrameworkNameProvider.Instance)));
187+
backportsReference =
188+
new AssemblyReference("MonoMod.Backports", new(1, 0, 0, 0))
189+
.ImportWith(backportsShim.DefaultImporter);
160190

161-
// set up the new shim module
162-
backportsShim = new ModuleDefinition(bclShim.Name, bcl)
163-
{
164-
RuntimeVersion = bclShim.RuntimeVersion
165-
};
166-
backportsShimAssembly = new AssemblyDefinition(bclShim.Assembly!.Name,
167-
new(bclShim.Assembly!.Version.Major, 9999, 9999, 9999))
168-
{
169-
Modules = { backportsShim },
170-
HashAlgorithm = bclShim.Assembly!.HashAlgorithm,
171-
PublicKey = bclShim.Assembly!.PublicKey,
172-
HasPublicKey = bclShim.Assembly!.HasPublicKey,
173-
};
174-
175-
backportsReference =
176-
new AssemblyReference("MonoMod.Backports", new(1, 0, 0, 0))
177-
.ImportWith(backportsShim.DefaultImporter);
178-
}
191+
foreach (var file in dllsByDllName[dllName].Values)
192+
{
193+
bclShim = ModuleDefinition.FromFile(file, readerParams);
179194

180195
// go through all public types, make sure they exist in the shim, and generate the forwarder
181196
foreach (var type in bclShim.TopLevelTypes)
@@ -223,7 +238,11 @@ void ExportType(TypeDefinition type, bool nested, IImplementation impl)
223238
var keyPath = Path.Combine(snkPath, name + ".snk");
224239
if (!File.Exists(keyPath))
225240
{
226-
Console.Error.WriteLine($"ShimGen : warning : Missing SNK file for key {name} (for {backportsShim.Name})");
241+
Console.Error.WriteLine($"ShimGen : warning : Missing SNK file for key {name} (for {backportsShim.Name}), fakesigning");
242+
var pefile = PEFile.FromFile(newShimPath);
243+
var image = PEImage.FromFile(pefile);
244+
image.DotNetDirectory!.Flags |= AsmResolver.PE.DotNet.DotNetDirectoryFlags.StrongNameSigned;
245+
image.ToPEFile(new ManagedPEFileBuilder(listener)).Write(newShimPath);
227246
}
228247
else
229248
{
@@ -239,3 +258,23 @@ void ExportType(TypeDefinition type, bool nested, IImplementation impl)
239258
}
240259

241260
return 0;
261+
262+
static bool TryGetFrameworkKey<T>(Dictionary<NuGetFramework, T> dict, NuGetFramework key, FrameworkReducer reducer, [MaybeNullWhen(false)] out T value)
263+
{
264+
var best = reducer.GetNearest(key, dict.Keys);
265+
if (best is null)
266+
{
267+
value = default;
268+
return false;
269+
}
270+
271+
return dict.TryGetValue(best, out value);
272+
}
273+
274+
275+
static T GetFrameworkKey<T>(Dictionary<NuGetFramework, T> dict, NuGetFramework key, FrameworkReducer reducer)
276+
{
277+
var best = reducer.GetNearest(key, dict.Keys);
278+
return dict[best!];
279+
}
280+

0 commit comments

Comments
 (0)