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:
parent
c9047de455
commit
21a739e3b1
2 changed files with 110 additions and 6 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue