C#+iOS: Fix P/Invoke symbols being stripped by the linker

We use `Mono.Cecil` to search for P/Invoke methods in assemblies in
order to collect symbols that we must prevent from being stripped.

We could pass the symbols as `-u` linker arguments (`-Wl,-u,symbol`)
for the native target (not for the project), but it was simpler to
generate referencing code and avoid changes to Godot's iOS exporter.
This commit is contained in:
Ignacio Roldán Etchevery 2021-05-21 06:24:15 +02:00
parent c9047de455
commit 21a739e3b1
2 changed files with 110 additions and 6 deletions

View file

@ -4,7 +4,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Godot;
using GodotTools.Core;
using GodotTools.Internals;
using Mono.Cecil;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
@ -415,6 +418,30 @@ MONO_AOT_MODE_LAST = 1000,
cppCode.AppendLine($"\tmono_jit_set_aot_mode({aotModeStr});");
cppCode.AppendLine("} // gd_mono_setup_aot");
// Prevent symbols from being stripped
var symbols = CollectSymbols(assemblies);
foreach (string symbol in symbols)
{
cppCode.Append("extern void *");
cppCode.Append(symbol);
cppCode.AppendLine(";");
}
cppCode.AppendLine("__attribute__((used)) __attribute__((optnone)) static void __godot_symbol_referencer() {");
cppCode.AppendLine("\tvoid *aux;");
foreach (string symbol in symbols)
{
cppCode.Append("\taux = ");
cppCode.Append(symbol);
cppCode.AppendLine(";");
}
cppCode.AppendLine("} // __godot_symbol_referencer");
cppCode.AppendLine("} // extern \"C\"");
cppCode.AppendLine("#endif // !TARGET_OS_SIMULATOR");
@ -456,13 +483,89 @@ MONO_AOT_MODE_LAST = 1000,
exporter.AddIosFramework("libiconv.tbd");
exporter.AddIosFramework("GSS.framework");
exporter.AddIosFramework("CFNetwork.framework");
}
// Force load and export dynamic are needed for the linker to not strip required symbols.
// In theory we shouldn't be relying on this for P/Invoked functions (as is the case with
// functions in System.Native/libmono-native). Instead, we should use cecil to search for
// DllImports in assemblies and pass them to 'ld' as '-u/--undefined {pinvoke_symbol}'.
exporter.AddIosLinkerFlags("-rdynamic");
exporter.AddIosLinkerFlags($"-force_load \"$(SRCROOT)/{MonoFrameworkFile("libmono-native")}\"");
private static List<string> CollectSymbols(IDictionary<string, string> assemblies)
{
var symbols = new List<string>();
var resolver = new DefaultAssemblyResolver();
foreach (var searchDir in resolver.GetSearchDirectories())
resolver.RemoveSearchDirectory(searchDir);
foreach (var searchDir in assemblies
.Select(a => a.Value.GetBaseDir().NormalizePath()).Distinct())
{
resolver.AddSearchDirectory(searchDir);
}
AssemblyDefinition ReadAssembly(string fileName)
=> AssemblyDefinition.ReadAssembly(fileName,
new ReaderParameters {AssemblyResolver = resolver});
foreach (var assembly in assemblies)
{
using (var assemblyDef = ReadAssembly(assembly.Value))
CollectSymbolsFromAssembly(assemblyDef, symbols);
}
return symbols;
}
private static void CollectSymbolsFromAssembly(AssemblyDefinition assembly, ICollection<string> symbols)
{
if (!assembly.MainModule.HasTypes)
return;
foreach (var type in assembly.MainModule.Types)
{
CollectSymbolsFromType(type, symbols);
}
}
private static void CollectSymbolsFromType(TypeDefinition type, ICollection<string> symbols)
{
if (type.HasNestedTypes)
{
foreach (var nestedType in type.NestedTypes)
CollectSymbolsFromType(nestedType, symbols);
}
if (type.Module.HasModuleReferences)
CollectPInvokeSymbols(type, symbols);
}
private static void CollectPInvokeSymbols(TypeDefinition type, ICollection<string> symbols)
{
if (!type.HasMethods)
return;
foreach (var method in type.Methods)
{
if (!method.IsPInvokeImpl || !method.HasPInvokeInfo)
continue;
var pInvokeInfo = method.PInvokeInfo;
if (pInvokeInfo == null)
continue;
switch (pInvokeInfo.Module.Name)
{
case "__Internal":
case "libSystem.Net.Security.Native":
case "System.Net.Security.Native":
case "libSystem.Security.Cryptography.Native.Apple":
case "System.Security.Cryptography.Native.Apple":
case "libSystem.Native":
case "System.Native":
case "libSystem.Globalization.Native":
case "System.Globalization.Native":
{
symbols.Add(pInvokeInfo.EntryPoint);
break;
}
}
}
}
/// Converts an assembly name to a valid symbol name in the same way the AOT compiler does

View file

@ -20,6 +20,7 @@
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>