// Copyright 2013--2014 Xamarin Inc. All rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Mono.Cecil; using Mono.Tuner; using Mono.Linker; using Xamarin.Linker; using Xamarin.Utils; using Registrar; using ObjCRuntime; #if MONOTOUCH using MonoTouch; using MonoTouch.Tuner; using PlatformResolver = MonoTouch.Tuner.MonoTouchResolver; using PlatformLinkContext = MonoTouch.Tuner.MonoTouchLinkContext; #elif MMP using MonoMac.Tuner; using PlatformResolver = Xamarin.Bundler.MonoMacResolver; using PlatformLinkContext = MonoMac.Tuner.MonoMacLinkContext; #elif NET using LinkerOptions = Xamarin.Linker.LinkerConfiguration; using PlatformLinkContext = Xamarin.Tuner.DerivedLinkContext; using PlatformResolver = Xamarin.Linker.DotNetResolver; #else #error Invalid defines #endif namespace Xamarin.Bundler { public partial class Target { public Application App; public AssemblyCollection Assemblies = new AssemblyCollection (); // The root assembly is not in this list. public PlatformLinkContext LinkContext; public LinkerOptions LinkerOptions; public PlatformResolver Resolver = new PlatformResolver (); public HashSet Frameworks = new HashSet (); public HashSet WeakFrameworks = new HashSet (); internal StaticRegistrar StaticRegistrar { get; set; } // If we didn't link because the existing (cached) assemblyes are up-to-date. bool cached_link = false; Symbols dynamic_symbols; // Note that each 'Target' can have multiple abis: armv7+armv7s for instance. public List Abis; // If we're targetting a 32 bit arch for this target. bool? is32bits; public bool Is32Build { get { if (!is32bits.HasValue) is32bits = Application.IsArchEnabled (Abis, Abi.Arch32Mask); return is32bits.Value; } } // If we're targetting a 64 bit arch for this target. bool? is64bits; public bool Is64Build { get { if (!is64bits.HasValue) is64bits = Application.IsArchEnabled (Abis, Abi.Arch64Mask); return is64bits.Value; } } public Target (Application app) { this.App = app; this.StaticRegistrar = new StaticRegistrar (this); } // If this is an app extension, this returns the equivalent (32/64bit) target for the container app. // This may be null (it's possible to build an extension for 32+64bit, and the main app only for 64-bit, for instance. public Target ContainerTarget { get { return App.ContainerApp.Targets.FirstOrDefault ((v) => v.Is32Build == Is32Build); } } public Assembly AddAssembly (AssemblyDefinition assembly) { var asm = new Assembly (this, assembly); Assemblies.Add (asm); return asm; } // This will find the link context, possibly looking in container targets. public PlatformLinkContext GetLinkContext () { if (LinkContext != null) return LinkContext; if (App.IsExtension && App.IsCodeShared) return ContainerTarget.GetLinkContext (); return null; } public bool CachedLink { get { return cached_link; } } public void ExtractNativeLinkInfo (List exceptions) { foreach (var a in Assemblies) { try { a.ExtractNativeLinkInfo (); } catch (Exception e) { exceptions.Add (e); } } #if MTOUCH if (!App.OnlyStaticLibraries && Assemblies.Count ((v) => v.HasLinkWithAttributes) > 1) { ErrorHelper.Warning (127, Errors.MT0127); App.ClearAssemblyBuildTargets (); // the default is to compile to static libraries, so just revert to the default. } #endif } [DllImport (Constants.libSystemLibrary, SetLastError = true)] static extern string realpath (string path, IntPtr zero); public static string GetRealPath (string path, bool warnIfNoSuchPathExists = true) { // For some reason realpath doesn't always like filenames only, and will randomly fail. // Prepend the current directory if there's no directory specified. if (string.IsNullOrEmpty (Path.GetDirectoryName (path))) path = Path.Combine (Environment.CurrentDirectory, path); var rv = realpath (path, IntPtr.Zero); if (rv != null) return rv; var errno = Marshal.GetLastWin32Error (); if (warnIfNoSuchPathExists || (errno !=2)) ErrorHelper.Warning (54, Errors.MT0054, path, FileCopier.strerror (errno), errno); return path; } public void ValidateAssembliesBeforeLink () { if (App.AreAnyAssembliesTrimmed) { foreach (Assembly assembly in Assemblies) { if ((assembly.AssemblyDefinition.MainModule.Attributes & ModuleAttributes.ILOnly) == 0) throw ErrorHelper.CreateError (2014, Errors.MT2014, assembly.AssemblyDefinition.MainModule.FileName); } } } public void ComputeLinkerFlags () { foreach (var a in Assemblies) a.ComputeLinkerFlags (); } public void GatherFrameworks () { Assembly asm = null; foreach (var assembly in Assemblies) { if (assembly.AssemblyDefinition.Name.Name == Driver.GetProductAssembly (App)) { asm = assembly; break; } } if (asm == null) throw ErrorHelper.CreateError (99, Errors.MX0099, $"could not find the product assembly {Driver.GetProductAssembly(App)} in the list of assemblies referenced by the executable"); AssemblyDefinition productAssembly = asm.AssemblyDefinition; // *** make sure any change in the above lists (or new list) are also reflected in // *** Makefile so simlauncher-sgen does not miss any framework HashSet processed = new HashSet (); #if !MONOMAC Version v80 = new Version (8, 0); #endif foreach (ModuleDefinition md in productAssembly.Modules) { foreach (TypeDefinition td in md.Types) { // process only once each namespace (as we keep adding logic below) string nspace = td.Namespace; if (processed.Contains (nspace)) continue; processed.Add (nspace); Framework framework; if (Driver.GetFrameworks (App).TryGetValue (nspace, out framework)) { // framework specific processing switch (framework.Name) { #if MONOMAC && !NET case "QTKit": // we already warn in Frameworks.cs Gather method if (!Driver.LinkProhibitedFrameworks) continue; break; #else case "CoreAudioKit": // CoreAudioKit seems to be functional in the iOS 9 simulator. if (App.IsSimulatorBuild && App.SdkVersion.Major < 9) continue; break; case "Metal": case "MetalKit": case "MetalPerformanceShaders": case "CHIP": case "PHASE": case "ThreadNetwork": // some frameworks do not exists on simulators and will result in linker errors if we include them if (App.IsSimulatorBuild) continue; break; case "DeviceCheck": if (App.IsSimulatorBuild && App.SdkVersion.Major < 13) continue; break; case "PushKit": // in Xcode 6 beta 7 this became an (ld) error - it was a warning earlier :( // ld: embedded dylibs/frameworks are only supported on iOS 8.0 and later (@rpath/PushKit.framework/PushKit) for architecture armv7 // this was fixed in Xcode 6.2 (6.1 was still buggy) see #29786 if ((App.DeploymentTarget < v80) && (Driver.XcodeVersion < new Version (6, 2))) { ErrorHelper.Warning (49, Errors.MT0049, framework.Name); continue; } break; #if !NET case "WatchKit": // Xcode 11 doesn't ship WatchKit for iOS if (Driver.XcodeVersion.Major == 11 && App.Platform == ApplePlatform.iOS) { ErrorHelper.Warning (5219, Errors.MT5219); continue; } break; #endif default: if (App.IsSimulatorBuild && !App.IsFrameworkAvailableInSimulator (framework.Name)) { if (App.AreAnyAssembliesTrimmed) { ErrorHelper.Warning (5223, Errors.MX5223, framework.Name, App.PlatformName); } else { Driver.Log (3, Errors.MX5223, framework.Name, App.PlatformName); } continue; } break; #endif } if (framework.Unavailable) { ErrorHelper.Warning (181, Errors.MX0181 /* Not linking with the framework {0} (used by the type {1}) because it's not available on the current platform ({2}). */, framework.Name, td.FullName, App.PlatformName); continue; } if (App.SdkVersion >= framework.Version) { var add_to = framework.AlwaysWeakLinked || App.DeploymentTarget < framework.Version ? asm.WeakFrameworks : asm.Frameworks; add_to.Add (framework.Name); continue; } else { Driver.Log (3, "Not linking with the framework {0} (used by the type {1}) because it was introduced in {2} {3}, and we're using the {2} {4} SDK.", framework.Name, td.FullName, App.PlatformName, framework.Version, App.SdkVersion); } } } } // Make sure there are no duplicates between frameworks and weak frameworks. // Keep the weak ones. asm.Frameworks.ExceptWith (asm.WeakFrameworks); } internal static void PrintAssemblyReferences (AssemblyDefinition assembly) { if (Driver.Verbosity < 2) return; var main = assembly.MainModule; Driver.Log ($"Loaded assembly '{assembly.FullName}' from {StringUtils.Quote (assembly.MainModule.FileName)}"); foreach (var ar in main.AssemblyReferences) Driver.Log ($" References: '{ar.FullName}'"); } public Symbols GetAllSymbols () { CollectAllSymbols (); return dynamic_symbols; } public void CollectAllSymbols () { if (dynamic_symbols != null) return; var dyn_msgSend_functions = new [] { new { Name = "xamarin_dyn_objc_msgSend", ValidAbis = Abi.SimulatorArchMask | Abi.ARM64 }, new { Name = "xamarin_dyn_objc_msgSendSuper", ValidAbis = Abi.SimulatorArchMask | Abi.ARM64 }, new { Name = "xamarin_dyn_objc_msgSend_stret", ValidAbis = Abi.SimulatorArchMask }, new { Name = "xamarin_dyn_objc_msgSendSuper_stret", ValidAbis = Abi.SimulatorArchMask }, }; var cache_location = Path.Combine (App.Cache.Location, "entry-points.txt"); if (cached_link) { dynamic_symbols = new Symbols (); dynamic_symbols.Load (cache_location, this); } else { if (LinkContext == null) { // This happens when using the simlauncher and the msbuild tasks asked for a list // of symbols (--symbollist). In that case just produce an empty list, since the // binary shouldn't end up stripped anyway. dynamic_symbols = new Symbols (); } else { dynamic_symbols = LinkContext.RequiredSymbols; } // keep the debugging helper in debugging binaries only var has_mono_pmip = App.EnableDebug; #if MMP has_mono_pmip &= !Driver.IsUnifiedFullSystemFramework; #endif if (has_mono_pmip) dynamic_symbols.AddFunction ("mono_pmip"); bool has_dyn_msgSend; switch (App.Platform) { case ApplePlatform.iOS: case ApplePlatform.TVOS: case ApplePlatform.WatchOS: has_dyn_msgSend = App.IsSimulatorBuild; break; case ApplePlatform.MacCatalyst: case ApplePlatform.MacOSX: has_dyn_msgSend = App.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Disable && !App.RequiresPInvokeWrappers; break; default: throw ErrorHelper.CreateError (71, Errors.MX0071, App.Platform, App.ProductName); } if (has_dyn_msgSend) { foreach (var dyn_msgSend_function in dyn_msgSend_functions) dynamic_symbols.AddFunction (dyn_msgSend_function.Name); } #if MONOTOUCH if (App.EnableProfiling && App.LibProfilerLinkMode == AssemblyBuildTarget.StaticObject) dynamic_symbols.AddFunction ("mono_profiler_init_log"); #endif dynamic_symbols.Save (cache_location); } foreach (var dynamicFunction in dyn_msgSend_functions) { var symbol = dynamic_symbols.Find (dynamicFunction.Name); if (symbol != null) { symbol.ValidAbis = dynamicFunction.ValidAbis; } } foreach (var name in App.IgnoredSymbols) { var symbol = dynamic_symbols.Find (name); if (symbol == null) { ErrorHelper.Warning (5218, Errors.MT5218, StringUtils.Quote (name)); } else { symbol.Ignore = true; } } } bool IsRequiredSymbol (Symbol symbol, Assembly single_assembly = null, Abi? target_abis = null) { if (symbol.Ignore) return false; // If this symbol is only defined for certain abis, verify if there is an abi match if (target_abis.HasValue && symbol.ValidAbis.HasValue && (target_abis.Value & symbol.ValidAbis.Value) == 0) return false; // Check if this symbol is used in the assembly we're filtering to if (single_assembly != null && !symbol.Members.Any ((v) => v.Module.Assembly == single_assembly.AssemblyDefinition)) return false; // nope, this symbol is not used in the assembly we're using as filter. // If we're code-sharing, the managed linker might have found symbols // that are not in any of the assemblies in the current app. // This occurs because the managed linker processes all the // assemblies for all the apps together, but when linking natively // we're only linking with the assemblies that actually go into the app. if (App.Platform != ApplePlatform.MacOSX && App.IsCodeShared && symbol.Assemblies.Count > 0) { // So if this is a symbol related to any assembly, make sure // at least one of those assemblies are in the current app. if (!symbol.Assemblies.Any ((v) => Assemblies.Contains (v))) return false; } switch (symbol.Type) { case SymbolType.Field: return true; case SymbolType.Function: #if MTOUCH // functions are not required if they're used in an assembly which isn't using dlsym, and we're AOT-compiling. if (App.IsSimulatorBuild) return true; if (App.Platform == ApplePlatform.MacCatalyst) return true; if (single_assembly != null) return App.UseDlsym (single_assembly.FileName); if (symbol.Members?.Any () == true) { foreach (var member in symbol.Members) { if (App.UseDlsym (member.Module.FileName)) { // If any assembly uses dlsym to reference this symbol, it's a required symbol that must be preserved, // because otherwise stripping the binary will cause the symbol (but not the function itself) to be removed, // preventing any assembly using dlsym to find it. return true; } } // None of the members use dlsym (and we have at least one member), then we don't need to preserve the symbol. return false; } #endif return true; case SymbolType.ObjectiveCClass: // Objective-C classes are not required when we're using the static registrar and we're not compiling to shared libraries, // (because the registrar code is linked into the main app, but not each shared library, // so the registrar code won't keep symbols in the shared libraries). if (single_assembly != null) return true; return App.Registrar != RegistrarMode.Static; default: throw ErrorHelper.CreateError (99, Errors.MX0099, $"invalid symbol type {symbol.Type} for symbol {symbol.Name}"); } } public Symbols GetRequiredSymbols (Assembly assembly = null, Abi? target_abis = null) { CollectAllSymbols (); Symbols filtered = new Symbols (); foreach (var ep in dynamic_symbols) { if (IsRequiredSymbol (ep, assembly, target_abis)) { filtered.Add (ep); } } return filtered ?? dynamic_symbols; } #if MTOUCH IEnumerable GenerateReferencingSource (string reference_m, IEnumerable symbols) #else internal string GenerateReferencingSource (string reference_m, IEnumerable symbols) #endif { if (!symbols.Any ()) { if (File.Exists (reference_m)) File.Delete (reference_m); #if MTOUCH yield break; #else return null; #endif } var sb = new StringBuilder (); sb.AppendLine ("#import "); foreach (var symbol in symbols) { switch (symbol.Type) { case SymbolType.Function: case SymbolType.Field: sb.Append ("extern void * ").Append (symbol.Name).AppendLine (";"); break; case SymbolType.ObjectiveCClass: sb.AppendLine ($"@interface {symbol.ObjectiveCName} : NSObject @end"); break; default: throw ErrorHelper.CreateError (99, Errors.MX0099, $"invalid symbol type {symbol.Type} for symbol {symbol.Name}"); } } sb.AppendLine ("static void __xamarin_symbol_referencer () __attribute__ ((used)) __attribute__ ((optnone));"); sb.AppendLine ("void __xamarin_symbol_referencer ()"); sb.AppendLine ("{"); sb.AppendLine ("\tvoid *value;"); foreach (var symbol in symbols) { switch (symbol.Type) { case SymbolType.Function: case SymbolType.Field: sb.AppendLine ($"\tvalue = {symbol.Name};"); break; case SymbolType.ObjectiveCClass: sb.AppendLine ($"\tvalue = [{symbol.ObjectiveCName} class];"); break; default: throw ErrorHelper.CreateError (99, Errors.MX0099, $"invalid symbol type {symbol.Type} for symbol {symbol.Name}"); } } sb.AppendLine ("}"); sb.AppendLine (); Driver.WriteIfDifferent (reference_m, sb.ToString (), true); #if MTOUCH foreach (var abi in GetArchitectures (AssemblyBuildTarget.StaticObject)) { var arch = abi.AsArchString (); var reference_o = Path.Combine (Path.GetDirectoryName (reference_m), arch, Path.GetFileNameWithoutExtension (reference_m) + ".o"); var compile_task = new CompileTask { Target = this, Abi = abi, InputFile = reference_m, OutputFile = reference_o, SharedLibrary = false, Language = "objective-c", }; yield return compile_task; } #else return reference_m; #endif } // This is to load the symbols for all assemblies, so that we can give better error messages // (with file name / line number information). public void LoadSymbols () { foreach (var a in Assemblies) a.LoadSymbols (); } public void GenerateMain (ApplePlatform platform, Abi abi, string main_source, IList registration_methods) { var sb = new StringBuilder (); GenerateMain (sb, platform, abi, main_source, registration_methods); } public void GenerateMain (StringBuilder sb, ApplePlatform platform, Abi abi, string main_source, IList registration_methods) { try { using (var sw = new StringWriter (sb)) { if (registration_methods != null) { foreach (var method in registration_methods) { sw.Write ("extern \"C\" void "); sw.Write (method); sw.WriteLine ("();"); } sw.WriteLine (); } sw.WriteLine ("static void xamarin_invoke_registration_methods ()"); sw.WriteLine ("{"); if (registration_methods != null) { for (int i = 0; i < registration_methods.Count; i++) { sw.Write ("\t"); sw.Write (registration_methods [i]); sw.WriteLine ("();"); } } sw.WriteLine ("}"); sw.WriteLine (); switch (platform) { case ApplePlatform.iOS: case ApplePlatform.TVOS: case ApplePlatform.WatchOS: case ApplePlatform.MacCatalyst: GenerateIOSMain (sw, abi); break; case ApplePlatform.MacOSX: #if NET GenerateIOSMain (sw, abi); #else GenerateMacMain (sw); #endif break; default: throw ErrorHelper.CreateError (71, Errors.MX0071, platform, App.ProductName); } } Driver.WriteIfDifferent (main_source, sb.ToString (), true); } catch (ProductException) { throw; } catch (Exception e) { throw new ProductException (4001, true, e, Errors.MT4001, main_source); } } void GenerateMacMain (StringWriter sw) { sw.WriteLine ("#define MONOMAC 1"); sw.WriteLine ("#include "); #if !NET if (App.Registrar == RegistrarMode.PartialStatic) sw.WriteLine ("extern \"C\" void xamarin_create_classes_Xamarin_Mac ();"); #endif sw.WriteLine (); sw.WriteLine (); sw.WriteLine (); sw.WriteLine ("extern \"C\" int xammac_setup ()"); sw.WriteLine ("{"); if (App.CustomBundleName != null) { sw.WriteLine ("\textern NSString* xamarin_custom_bundle_name;"); sw.WriteLine ("\txamarin_custom_bundle_name = @\"" + App.CustomBundleName + "\";"); } sw.WriteLine ("\txamarin_executable_name = \"{0}\";", App.AssemblyName); if (!App.IsDefaultMarshalManagedExceptionMode) sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", App.MarshalManagedExceptions); sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", App.MarshalObjectiveCExceptions); if (App.DisableLldbAttach.HasValue ? App.DisableLldbAttach.Value : !App.EnableDebug) sw.WriteLine ("\txamarin_disable_lldb_attach = true;"); if (App.DisableOmitFramePointer ?? App.EnableDebug) sw.WriteLine ("\txamarin_disable_omit_fp = true;"); sw.WriteLine (); #if !NET if (App.Registrar == RegistrarMode.Static) sw.WriteLine ("\txamarin_create_classes ();"); else if (App.Registrar == RegistrarMode.PartialStatic) sw.WriteLine ("\txamarin_create_classes_Xamarin_Mac ();"); #endif if (App.EnableDebug) sw.WriteLine ("\txamarin_debug_mode = TRUE;"); sw.WriteLine ($"\tsetenv (\"MONO_GC_PARAMS\", \"{App.MonoGCParams}\", 1);"); sw.WriteLine ("\txamarin_supports_dynamic_registration = {0};", App.DynamicRegistrationSupported ? "TRUE" : "FALSE"); #if MMP // AOT for .NET/macOS needs some design to verify it's staying the same way as current Xamarin.Mac // for instance: we might decide to select which assemblies to AOT in a different way. if (App.AOTOptions != null && App.AOTOptions.IsHybridAOT) sw.WriteLine ("\txamarin_mac_hybrid_aot = TRUE;"); #endif if (Driver.IsUnifiedMobile) sw.WriteLine ("\txamarin_mac_modern = TRUE;"); sw.WriteLine ("\txamarin_invoke_registration_methods ();"); sw.WriteLine ("\treturn 0;"); sw.WriteLine ("}"); sw.WriteLine (); } // note: this is executed under Parallel.ForEach void GenerateIOSMain (StringWriter sw, Abi abi) { var app = App; var assemblies = Assemblies; var assembly_name = App.AssemblyName; var assembly_externs = new StringBuilder (); var assembly_aot_modules = new StringBuilder (); var register_assemblies = new StringBuilder (); var assembly_location = new StringBuilder (); var assembly_location_count = 0; var enable_llvm = (abi & Abi.LLVM) != 0; register_assemblies.AppendLine ("\tGCHandle exception_gchandle = INVALID_GCHANDLE;"); foreach (var s in assemblies) { if (!s.IsAOTCompiled) continue; var info = s.AssemblyDefinition.Name.Name; info = EncodeAotSymbol (info); assembly_externs.Append ("extern void *mono_aot_module_").Append (info).AppendLine ("_info;"); assembly_aot_modules.Append ("\tmono_aot_register_module (mono_aot_module_").Append (info).AppendLine ("_info);"); string sname = s.FileName; if (assembly_name != sname && IsBoundAssembly (s)) { register_assemblies.Append ("\txamarin_open_and_register (\"").Append (sname).Append ("\", &exception_gchandle);").AppendLine (); register_assemblies.AppendLine ("\txamarin_process_managed_exception_gchandle (exception_gchandle);"); } } var frameworks = assemblies.Where ((a) => a.BuildTarget == AssemblyBuildTarget.Framework) .OrderBy ((a) => a.Identity, StringComparer.Ordinal); foreach (var asm_fw in frameworks) { var asm_name = asm_fw.Identity; if (asm_fw.BuildTargetName == asm_name) continue; // this is deduceable var prefix = string.Empty; if (!app.HasFrameworksDirectory && asm_fw.IsCodeShared) prefix = "../../"; var suffix = string.Empty; if (app.IsSimulatorBuild) suffix = "/simulator"; assembly_location.AppendFormat ("\t{{ \"{0}\", \"{2}Frameworks/{1}.framework/MonoBundle{3}\" }},\n", asm_name, asm_fw.BuildTargetName, prefix, suffix); assembly_location_count++; } sw.WriteLine ("#include \"xamarin/xamarin.h\""); if (assembly_location.Length > 0) { sw.WriteLine (); sw.WriteLine ("struct AssemblyLocation assembly_location_entries [] = {"); sw.WriteLine (assembly_location); sw.WriteLine ("};"); sw.WriteLine (); sw.WriteLine ("struct AssemblyLocations assembly_locations = {{ {0}, assembly_location_entries }};", assembly_location_count); } sw.WriteLine (); sw.WriteLine (assembly_externs); sw.WriteLine ("void xamarin_register_modules_impl ()"); sw.WriteLine ("{"); sw.WriteLine (assembly_aot_modules); sw.WriteLine ("}"); sw.WriteLine (); sw.WriteLine ("void xamarin_register_assemblies_impl ()"); sw.WriteLine ("{"); sw.WriteLine (register_assemblies); sw.WriteLine ("}"); sw.WriteLine (); // Burn in a reference to the profiling symbol so that the native linker doesn't remove it // On iOS we can pass -u to the native linker, but that doesn't work on tvOS, where // we're building with bitcode (even when bitcode is disabled, we still build with the // bitcode marker, which makes the linker reject -u). if (app.EnableProfiling) { sw.WriteLine ("extern \"C\" { void mono_profiler_init_log (); }"); sw.WriteLine ("typedef void (*xamarin_profiler_symbol_def)();"); sw.WriteLine ("extern xamarin_profiler_symbol_def xamarin_profiler_symbol;"); sw.WriteLine ("xamarin_profiler_symbol_def xamarin_profiler_symbol = NULL;"); } if (app.UseInterpreter) { sw.WriteLine ("extern \"C\" { void mono_ee_interp_init (const char *); }"); sw.WriteLine ("extern \"C\" { void mono_icall_table_init (void); }"); sw.WriteLine ("extern \"C\" { void mono_marshal_ilgen_init (void); }"); sw.WriteLine ("extern \"C\" { void mono_method_builder_ilgen_init (void); }"); sw.WriteLine ("extern \"C\" { void mono_sgen_mono_ilgen_init (void); }"); } #if NET if (app.MonoNativeMode != MonoNativeMode.None) { sw.WriteLine ("static const char *xamarin_runtime_libraries_array[] = {"); foreach (var lib in app.MonoLibraries) sw.WriteLine ($"\t\"{Path.GetFileNameWithoutExtension (lib)}\","); sw.WriteLine ($"\tNULL"); sw.WriteLine ("};"); } #endif sw.WriteLine ("void xamarin_setup_impl ()"); sw.WriteLine ("{"); if (app.EnableProfiling) sw.WriteLine ("\txamarin_profiler_symbol = mono_profiler_init_log;"); if (app.EnableLLVMOnlyBitCode) sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY);"); else if (app.UseInterpreter) { sw.WriteLine ("\tmono_icall_table_init ();"); sw.WriteLine ("\tmono_marshal_ilgen_init ();"); sw.WriteLine ("\tmono_method_builder_ilgen_init ();"); sw.WriteLine ("\tmono_sgen_mono_ilgen_init ();"); #if !NET sw.WriteLine ("\tmono_ee_interp_init (NULL);"); #endif sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_INTERP);"); } else if (app.IsDeviceBuild) { sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_FULL);"); } else if (app.Platform == ApplePlatform.MacCatalyst && ((abi & Abi.ARM64) == Abi.ARM64)) { sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_FULL);"); } else if (app.IsSimulatorBuild && ((abi & Abi.ARM64) == Abi.ARM64)) { sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_FULL);"); } if (assembly_location.Length > 0) sw.WriteLine ("\txamarin_set_assembly_directories (&assembly_locations);"); sw.WriteLine ("\txamarin_invoke_registration_methods ();"); if (app.MonoNativeMode != MonoNativeMode.None) { #if NET // Mono doesn't support dllmaps for Mac Catalyst / macOS in .NET, so we're using an alternative: // the PINVOKE_OVERRIDE runtime option. Since we have to use it for Mac Catalyst + macOS, let's // just use it everywhere to simplify code. This means that at runtime we need to know how we // linked to mono, so store that in the xamarin_libmono_native_link_mode variable. // Ref: https://github.com/dotnet/runtime/issues/43204 (macOS) https://github.com/dotnet/runtime/issues/48110 (Mac Catalyst) sw.WriteLine ($"\txamarin_libmono_native_link_mode = XamarinNativeLinkMode{app.LibMonoNativeLinkMode};"); sw.WriteLine ($"\txamarin_runtime_libraries = xamarin_runtime_libraries_array;"); #else string mono_native_lib; if (app.LibMonoNativeLinkMode == AssemblyBuildTarget.StaticObject) { mono_native_lib = "__Internal"; } else { mono_native_lib = app.GetLibNativeName () + ".dylib"; } sw.WriteLine (); sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Native\", NULL, \"{mono_native_lib}\", NULL);"); sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Security.Cryptography.Native.Apple\", NULL, \"{mono_native_lib}\", NULL);"); sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Net.Security.Native\", NULL, \"{mono_native_lib}\", NULL);"); sw.WriteLine (); #endif } if (app.EnableDebug) sw.WriteLine ("\txamarin_gc_pump = {0};", app.DebugTrack.Value ? "TRUE" : "FALSE"); sw.WriteLine ("\txamarin_init_mono_debug = {0};", app.PackageManagedDebugSymbols ? "TRUE" : "FALSE"); sw.WriteLine ("\txamarin_executable_name = \"{0}\";", assembly_name); if (app.XamarinRuntime == XamarinRuntime.MonoVM) sw.WriteLine ("\tmono_use_llvm = {0};", enable_llvm ? "TRUE" : "FALSE"); sw.WriteLine ("\txamarin_log_level = {0};", Driver.Verbosity.ToString (CultureInfo.InvariantCulture)); sw.WriteLine ("\txamarin_arch_name = \"{0}\";", abi.AsArchString ()); if (!app.IsDefaultMarshalManagedExceptionMode) sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", app.MarshalManagedExceptions); sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", app.MarshalObjectiveCExceptions); if (app.EnableDebug) sw.WriteLine ("\txamarin_debug_mode = TRUE;"); if (!string.IsNullOrEmpty (app.MonoGCParams)) sw.WriteLine ("\tsetenv (\"MONO_GC_PARAMS\", \"{0}\", 1);", app.MonoGCParams); // Do this last, so that the app developer can override any other environment variable we set. foreach (var kvp in app.EnvironmentVariables) sw.WriteLine ("\tsetenv (\"{0}\", \"{1}\", 1);", kvp.Key.Replace ("\"", "\\\""), kvp.Value.Replace ("\"", "\\\"")); sw.WriteLine ("\txamarin_supports_dynamic_registration = {0};", app.DynamicRegistrationSupported ? "TRUE" : "FALSE"); #if NET sw.WriteLine ("\txamarin_runtime_configuration_name = {0};", string.IsNullOrEmpty (app.RuntimeConfigurationFile) ? "NULL" : $"\"{app.RuntimeConfigurationFile}\""); #endif sw.WriteLine ("}"); sw.WriteLine (); sw.Write ("int "); sw.Write (app.IsWatchExtension ? "xamarin_watchextension_main" : "main"); sw.WriteLine (" (int argc, char **argv)"); sw.WriteLine ("{"); sw.WriteLine ("\tNSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];"); if (app.IsExtension) { // the name of the executable must be the bundle id (reverse dns notation) // but we do not want to impose that (ugly) restriction to the managed .exe / project name / ... sw.WriteLine ("\targv [0] = (char *) \"{0}\";", Path.GetFileNameWithoutExtension (app.RootAssemblies [0])); sw.WriteLine ("\tint rv = xamarin_main (argc, argv, XamarinLaunchModeExtension);"); } else { sw.WriteLine ("\tint rv = xamarin_main (argc, argv, XamarinLaunchModeApp);"); } sw.WriteLine ("\t[pool drain];"); sw.WriteLine ("\treturn rv;"); sw.WriteLine ("}"); string extension_main = null; if (app.Platform == ApplePlatform.WatchOS && app.IsWatchExtension) { // We're building a watch extension, and we have multiple scenarios, depending on the watchOS version we're executing on: // // * watchOS 2.0 -> 5.*: we must call a `main` function provided in the WatchKit framework. // * watchOS 6.0 -> * : we must call a `WKExtensionMain` function provided in the WatchKit framework. // * watchOS 7.0 -> * : The `WKExtensionMain` function uses dlsym to find any `main` functions in the // main executable, and calls that function (otherwise WKExtensionMain will call // UIApplicationMain and normal startup occurs) // // * We can't call our entry point "main", because we call WKExtensionMain, and then we run into an // infinite loop on watchOS 7.0. So we call it xamarin_watch_extension_main. // * The watchOS 6+ SDK helpfully provides a static library (WKExtensionMainLegacy) that has a // WKExtensionMain function, which we use when the deployment target is earlier than watchOS 6.0. // This means that calling WKExtensionMain works everywhere (as long as we're using the // watchOS 6+ SDK to build; otherwise we just call "main" directly and don't link with the // WKExtensionMainLegacy library) if (app.SdkVersion.Major >= 6) { extension_main = "WKExtensionMain"; } else { extension_main = "main"; } } if (!string.IsNullOrEmpty (extension_main)) { sw.WriteLine ($"extern \"C\" {{ int {extension_main} (int argc, char* argv[]); }}"); sw.WriteLine (); } sw.WriteLine (); sw.WriteLine ("void xamarin_initialize_callbacks () __attribute__ ((constructor));"); sw.WriteLine ("void xamarin_initialize_callbacks ()"); sw.WriteLine ("{"); sw.WriteLine ("\txamarin_setup = xamarin_setup_impl;"); sw.WriteLine ("\txamarin_register_assemblies = xamarin_register_assemblies_impl;"); sw.WriteLine ("\txamarin_register_modules = xamarin_register_modules_impl;"); if (!string.IsNullOrEmpty (extension_main)) sw.WriteLine ($"\txamarin_extension_main = {extension_main};"); sw.WriteLine ("}"); } static string EncodeAotSymbol (string symbol) { var sb = new StringBuilder (); /* This mimics what the aot-compiler does */ foreach (var b in System.Text.Encoding.UTF8.GetBytes (symbol)) { char c = (char) b; if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { sb.Append (c); continue; } sb.Append ('_'); } return sb.ToString (); } static bool IsBoundAssembly (Assembly s) { if (s.IsFrameworkAssembly == true) return false; AssemblyDefinition ad = s.AssemblyDefinition; foreach (ModuleDefinition md in ad.Modules) foreach (TypeDefinition td in md.Types) if (td.IsNSObject (s.Target.LinkContext)) return true; return false; } } }