From 50c65cf90b6c60043d81700f32b750cd4f3bf69f Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 12:14:34 +0200 Subject: [PATCH 01/22] [runtime] Extract logic in xamarin_unhandled_exception_handler to collect exception information to a separate function. So that we can later re-use this logic somewhere else. --- runtime/runtime.m | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index 81a37f214095..553bc95764cb 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -858,13 +858,13 @@ -(void) xamarinSetGCHandle: (int) gc_handle; xamarin_free (type_name); } -void -xamarin_unhandled_exception_handler (MonoObject *exc, gpointer user_data) +static NSMutableString * +print_all_exceptions (MonoObject *exc) { - int counter = 0; + NSMutableString *str = [[NSMutableString alloc] init]; // fetch the field, since the property might have been linked away. + int counter = 0; MonoClassField *inner_exception = mono_class_get_field_from_name (mono_object_get_class (exc), "inner_exception"); - NSMutableString *str = [[NSMutableString alloc] init]; do { print_exception (exc, counter > 0, str); @@ -876,7 +876,14 @@ -(void) xamarinSetGCHandle: (int) gc_handle; } } while (counter++ < 10 && exc); - NSLog (@"%@", str); + [str autorelease]; + return str; +} + +void +xamarin_unhandled_exception_handler (MonoObject *exc, gpointer user_data) +{ + NSLog (@"%@", print_all_exceptions (exc)); abort (); } From ac705ce505f8dba4a554b882a5b2a658fc11da43 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 12:06:37 +0200 Subject: [PATCH 02/22] [runtime] Make the existing trampolines exception-aware. This is required so that the Objective-C exception unwinder can properly unwind through these trampolines. --- runtime/trampolines-i386-asm.s | 9 +++++++++ runtime/trampolines-x86_64-asm.s | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/runtime/trampolines-i386-asm.s b/runtime/trampolines-i386-asm.s index a097d05556bd..c847e0a63d22 100644 --- a/runtime/trampolines-i386-asm.s +++ b/runtime/trampolines-i386-asm.s @@ -18,9 +18,16 @@ #if __i386__ +.subsections_via_symbols +.text + _xamarin_i386_common_trampoline: +.cfi_startproc pushl %ebp +.cfi_def_cfa_offset 8 +.cfi_offset %ebp, -8 movl %esp, %ebp +.cfi_def_cfa_register %ebp pushl %esi # we use %esi as a pointer to our CallState struct. It's a preserved register, so we need to save it. @@ -85,6 +92,8 @@ L_no_floating_point: L_double_stret_return: retl $0x4 +.cfi_endproc + # # trampolines # diff --git a/runtime/trampolines-x86_64-asm.s b/runtime/trampolines-x86_64-asm.s index d055700f1e3b..8cd31a15543c 100644 --- a/runtime/trampolines-x86_64-asm.s +++ b/runtime/trampolines-x86_64-asm.s @@ -16,9 +16,16 @@ #if __x86_64__ +.subsections_via_symbols +.text + _xamarin_x86_64_common_trampoline: +.cfi_startproc pushq %rbp +.cfi_def_cfa_offset 16 +.cfi_offset %rbp, -16 movq %rsp, %rbp +.cfi_def_cfa_register %rbp subq $0xC0, %rsp # allocate 192 bytes from the stack # todo: verify alignment. movq %r11, (%rsp) @@ -52,6 +59,7 @@ _xamarin_x86_64_common_trampoline: popq %rbp ret +.cfi_endproc # # trampolines From b8abf3db01efe55e05ffabf5cf40fc2575c78ea1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 12:24:55 +0200 Subject: [PATCH 03/22] Add runtime variable to specify if the Coop GC should be used, and a mmp/mtouch option to select it. The mmp/mtouch option is hidden for now, since it's not actually possible to select the Coop GC yet. --- runtime/runtime.m | 5 +++++ runtime/xamarin/main.h | 1 + tools/common/Application.cs | 10 ++++++++++ tools/common/Driver.cs | 5 +++++ tools/mmp/driver.cs | 4 ++++ tools/mmp/error.cs | 1 + tools/mtouch/Application.cs | 2 ++ tools/mtouch/error.cs | 1 + tools/mtouch/mtouch.cs | 2 ++ 9 files changed, 31 insertions(+) diff --git a/runtime/runtime.m b/runtime/runtime.m index 553bc95764cb..2289e3d3d8f5 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -74,6 +74,11 @@ const char *xamarin_arch_name = NULL; #endif +#if TARGET_OS_WATCH +bool xamarin_is_gc_coop = true; +#else +bool xamarin_is_gc_coop = false; +#endif /* Callbacks */ diff --git a/runtime/xamarin/main.h b/runtime/xamarin/main.h index 28ac23e64fd8..a9dfb06932ca 100644 --- a/runtime/xamarin/main.h +++ b/runtime/xamarin/main.h @@ -31,6 +31,7 @@ extern bool xamarin_compact_seq_points; extern int xamarin_log_level; extern const char *xamarin_executable_name; extern const char *xamarin_arch_name; +extern bool xamarin_is_gc_coop; #ifdef MONOTOUCH extern NSString* xamarin_crashlytics_api_key; diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 697252c41fde..d52e3d7bf522 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -43,6 +43,8 @@ public partial class Application public List Definitions = new List (); public Mono.Linker.I18nAssemblies I18n; + public bool? EnableCoopGC; + public string PlatformName { get { switch (Platform) { @@ -329,5 +331,13 @@ public static void TryDelete (string path) } } + public void InitializeCommon () + { + if (Platform == ApplePlatform.WatchOS && EnableCoopGC.HasValue && !EnableCoopGC.Value) + throw ErrorHelper.CreateError (88, "Cannot disable the Coop GC for watchOS apps. Please remove the --coop:false argument to mtouch."); + + if (!EnableCoopGC.HasValue) + EnableCoopGC = Platform == ApplePlatform.WatchOS; + } } } diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index 6c10dca2a80d..a0a2baa0e3e4 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -17,6 +17,11 @@ namespace Xamarin.Bundler { public partial class Driver { + static void AddSharedOptions (Mono.Options.OptionSet options) + { + options.Add ("coop:", "If the Coop GC should be used.", v => { App.EnableCoopGC = ParseBool (v, "coop"); }, hidden: true); + } + #if MONOMAC #pragma warning disable 0414 static string userTargetFramework = TargetFramework.Default.ToString (); diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs index 9342d76afaba..51eba6ac00cc 100644 --- a/tools/mmp/driver.cs +++ b/tools/mmp/driver.cs @@ -280,6 +280,8 @@ static void Main2 (string [] args) { "http-message-handler=", "Specify the default HTTP Message Handler", v => { http_message_provider = v; }}, }; + AddSharedOptions (os); + IList unprocessed; try { unprocessed = os.Parse (args); @@ -378,6 +380,8 @@ static void Main2 (string [] args) if (!IsUnifiedMobile && tls_provider != null) throw new MonoMacException (2011, true, "Selecting a TLS Provider is only supported in the Unified Mobile profile"); + App.InitializeCommon (); + Log ("Xamarin.Mac {0}{1}", Constants.Version, verbose > 0 ? "." + Constants.Revision : string.Empty); if (verbose > 0) diff --git a/tools/mmp/error.cs b/tools/mmp/error.cs index e8688b58c095..512db7b56117 100644 --- a/tools/mmp/error.cs +++ b/tools/mmp/error.cs @@ -33,6 +33,7 @@ namespace Xamarin.Bundler { // MM0068 Invalid value for target framework: {0}. // MM0079 Internal Error - No executable was copied into the app bundle. Please contact 'support@xamarin.com' // Warning MT0080 Disabling NewRefCount, --new-refcount:false, is deprecated. + // MM0088 ** Reserved mtouch ** // MM1xxx file copy / symlinks (project related) // MM14xx Product assemblies // MM1401 The required '{0}' assembly is missing from the references diff --git a/tools/mtouch/Application.cs b/tools/mtouch/Application.cs index 2b8783494636..f31b47dc82a6 100644 --- a/tools/mtouch/Application.cs +++ b/tools/mtouch/Application.cs @@ -847,6 +847,8 @@ void Initialize () Namespaces.Initialize (); + InitializeCommon (); + Driver.Watch ("Resolve References", 1); } diff --git a/tools/mtouch/error.cs b/tools/mtouch/error.cs index e09c306b68fd..512374ba73fc 100644 --- a/tools/mtouch/error.cs +++ b/tools/mtouch/error.cs @@ -95,6 +95,7 @@ namespace Xamarin.Bundler { // MT0085 No reference to '{0}' was found. It will be added automatically. // MT0086 A target framework (--target-framework) must be specified when building for TVOS or WatchOS. // Warning MT0087 + // MT0088 Cannot disable the Coop GC for watchOS apps. Please remove the --coop:false argument to mtouch. // MT0091 This version of Xamarin.iOS requires the {0} {1} SDK (shipped with Xcode {2}) when the managed linker is disabled. Either upgrade Xcode, or enable the managed linker. // MT0092 The option '{0}' is required. // MT0093 Could not find 'mlaunch'. diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs index 00477a7ce691..408242453ad5 100644 --- a/tools/mtouch/mtouch.cs +++ b/tools/mtouch/mtouch.cs @@ -1350,6 +1350,8 @@ static int Main2 (string [] args) { "xamarin-framework-directory=", "The framework directory", v => { mtouch_dir = v; }, true }, }; + AddSharedOptions (os); + try { assemblies = os.Parse (args); } From 9b4f7532abebb01c558ee8af82f86f1903dec955 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 12:47:26 +0200 Subject: [PATCH 04/22] Add runtime variable to specify the exception marshalling modes, and mmp/mtouch options to select it. --- runtime/runtime.m | 2 ++ runtime/xamarin/main.h | 20 ++++++++++++++ src/ObjCRuntime/ExceptionMode.cs | 30 ++++++++++++++++++++ src/frameworks.sources | 1 + tools/common/Application.cs | 39 ++++++++++++++++++++++++++ tools/common/Driver.cs | 47 ++++++++++++++++++++++++++++++++ tools/mmp/Makefile | 1 + tools/mmp/driver.cs | 3 ++ tools/mmp/error.cs | 1 + tools/mmp/mmp.csproj | 3 ++ tools/mtouch/Makefile | 1 + tools/mtouch/error.cs | 1 + tools/mtouch/mtouch.cs | 6 ++++ tools/mtouch/mtouch.csproj | 3 ++ tools/mtouch/simlauncher.m | 6 ++++ 15 files changed, 164 insertions(+) create mode 100644 src/ObjCRuntime/ExceptionMode.cs diff --git a/runtime/runtime.m b/runtime/runtime.m index 2289e3d3d8f5..80a17f17c57f 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -79,6 +79,8 @@ #else bool xamarin_is_gc_coop = false; #endif +enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeDefault; +enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode = MarshalManagedExceptionModeDefault; /* Callbacks */ diff --git a/runtime/xamarin/main.h b/runtime/xamarin/main.h index a9dfb06932ca..4aa8b38e2921 100644 --- a/runtime/xamarin/main.h +++ b/runtime/xamarin/main.h @@ -19,6 +19,24 @@ extern "C" { #endif +/* This enum must always match the identical enum in src/ObjCRuntime/ExceptionMode.cs */ +enum MarshalObjectiveCExceptionMode : int { + MarshalObjectiveCExceptionModeDefault = 0, + MarshalObjectiveCExceptionModeUnwindManagedCode = 1, + MarshalObjectiveCExceptionModeThrowManagedException = 2, + MarshalObjectiveCExceptionModeAbort = 3, + MarshalObjectiveCExceptionModeDisable = 4, +}; + +/* This enum must always match the identical enum in src/ObjCRuntime/ExceptionMode.cs */ +enum MarshalManagedExceptionMode : int { + MarshalManagedExceptionModeDefault = 0, + MarshalManagedExceptionModeUnwindNativeCode = 1, + MarshalManagedExceptionModeThrowObjectiveCException = 2, + MarshalManagedExceptionModeAbort = 3, + MarshalManagedExceptionModeDisable = 4, +}; + extern bool mono_use_llvm; // this is defined inside mono extern bool xamarin_use_new_assemblies; @@ -32,6 +50,8 @@ extern int xamarin_log_level; extern const char *xamarin_executable_name; extern const char *xamarin_arch_name; extern bool xamarin_is_gc_coop; +extern enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode; +extern enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode; #ifdef MONOTOUCH extern NSString* xamarin_crashlytics_api_key; diff --git a/src/ObjCRuntime/ExceptionMode.cs b/src/ObjCRuntime/ExceptionMode.cs new file mode 100644 index 000000000000..557f3d58fe31 --- /dev/null +++ b/src/ObjCRuntime/ExceptionMode.cs @@ -0,0 +1,30 @@ +// +// ExceptionMode.cs: +// +// Authors: +// Rolf Bjarne Kvinge +// +// Copyright 2016 Xamarin Inc. + +using System; +using XamCore.Foundation; + +namespace XamCore.ObjCRuntime { + /* This enum must always match the identical enum in runtime/xamarin/main.h */ + public enum MarshalObjectiveCExceptionMode { + Default = 0, + UnwindManagedCode = 1, // not available for watchOS/COOP, default for the other platforms + ThrowManagedException = 2, // default for watchOS/COOP + Abort = 3, + Disable = 4, // this will also prevent the corresponding event from working + } + + /* This enum must always match the identical enum in runtime/xamarin/main.h */ + public enum MarshalManagedExceptionMode { + Default = 0, + UnwindNativeCode = 1, // not available for watchOS/COOP, default for the other platforms + ThrowObjectiveCException = 2, // default for watchOS/COOP + Abort = 3, + Disable = 4, // this will also prevent the corresponding event from working + } +} diff --git a/src/frameworks.sources b/src/frameworks.sources index b97fd8caa61d..08850f9fe936 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -1393,6 +1393,7 @@ SHARED_SOURCES = \ ObjCRuntime/Dlfcn.cs \ ObjCRuntime/DynamicRegistrar.cs \ ObjCRuntime/ErrorHelper.cs \ + ObjCRuntime/ExceptionMode.cs \ ObjCRuntime/IDynamicRegistrar.cs \ ObjCRuntime/Method.cs \ ObjCRuntime/MethodDescription.cs \ diff --git a/tools/common/Application.cs b/tools/common/Application.cs index d52e3d7bf522..06faad4b0325 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -9,6 +9,8 @@ using Xamarin.Utils; +using XamCore.ObjCRuntime; + namespace Xamarin.Bundler { [Flags] @@ -44,6 +46,8 @@ public partial class Application public Mono.Linker.I18nAssemblies I18n; public bool? EnableCoopGC; + public MarshalObjectiveCExceptionMode MarshalObjectiveCExceptions; + public MarshalManagedExceptionMode MarshalManagedExceptions; public string PlatformName { get { @@ -338,6 +342,41 @@ public void InitializeCommon () if (!EnableCoopGC.HasValue) EnableCoopGC = Platform == ApplePlatform.WatchOS; + + if (EnableCoopGC.Value) { + switch (MarshalObjectiveCExceptions) { + case MarshalObjectiveCExceptionMode.UnwindManagedCode: + case MarshalObjectiveCExceptionMode.Disable: + throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when the Coop GC is enabled.", "--marshal-objectivec-exceptions", MarshalObjectiveCExceptions.ToString ().ToLowerInvariant ()); + } + switch (MarshalManagedExceptions) { + case MarshalManagedExceptionMode.UnwindNativeCode: + case MarshalManagedExceptionMode.Disable: + throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when the Coop GC is enabled.", "--marshal-managed-exceptions", MarshalManagedExceptions.ToString ().ToLowerInvariant ()); + } + } + + + bool isSimulatorOrDesktopDebug = EnableDebug; +#if MTOUCH + isSimulatorOrDesktopDebug &= IsSimulatorBuild; +#endif + + if (MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.Default) { + if (EnableCoopGC.Value) { + MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.ThrowManagedException; + } else { + MarshalObjectiveCExceptions = isSimulatorOrDesktopDebug ? MarshalObjectiveCExceptionMode.UnwindManagedCode : MarshalObjectiveCExceptionMode.Disable; + } + } + + if (MarshalManagedExceptions == MarshalManagedExceptionMode.Default) { + if (EnableCoopGC.Value) { + MarshalManagedExceptions = MarshalManagedExceptionMode.ThrowObjectiveCException; + } else { + MarshalManagedExceptions = isSimulatorOrDesktopDebug ? MarshalManagedExceptionMode.UnwindNativeCode : MarshalManagedExceptionMode.Disable; + } + } } } } diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index a0a2baa0e3e4..f0a0ba554ecc 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -14,12 +14,59 @@ using System.Text; using Xamarin.Utils; +using XamCore.ObjCRuntime; namespace Xamarin.Bundler { public partial class Driver { static void AddSharedOptions (Mono.Options.OptionSet options) { options.Add ("coop:", "If the Coop GC should be used.", v => { App.EnableCoopGC = ParseBool (v, "coop"); }, hidden: true); + options.Add ("marshal-objectivec-exceptions:", v => { + switch (v) { + case "default": + App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Default; + break; + case "unwindmanaged": + case "unwindmanagedcode": + App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.UnwindManagedCode; + break; + case "throwmanaged": + case "throwmanagedexception": + App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.ThrowManagedException; + break; + case "abort": + App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Abort; + break; + case "disable": + App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Disable; + break; + default: + throw ErrorHelper.CreateError (26, "Could not parse the command line argument '{0}': {1}", "--marshal-objective-exceptions", "Invalid value: " + v); + } + }); + options.Add ("marshal-managed-exceptions:", v => { + switch (v) { + case "default": + App.MarshalManagedExceptions = MarshalManagedExceptionMode.Default; + break; + case "unwindnative": + case "unwindnativecode": + App.MarshalManagedExceptions = MarshalManagedExceptionMode.UnwindNativeCode; + break; + case "throwobjectivec": + case "throwobjectivecexception": + App.MarshalManagedExceptions = MarshalManagedExceptionMode.ThrowObjectiveCException; + break; + case "abort": + App.MarshalManagedExceptions = MarshalManagedExceptionMode.Abort; + break; + case "disable": + App.MarshalManagedExceptions = MarshalManagedExceptionMode.Disable; + break; + default: + throw ErrorHelper.CreateError (26, "Could not parse the command line argument '{0}': {1}", "--marshal-managed-exceptions", "Invalid value: " + v); + } + }); } #if MONOMAC diff --git a/tools/mmp/Makefile b/tools/mmp/Makefile index d2b5e599261b..bcc577be9b0d 100644 --- a/tools/mmp/Makefile +++ b/tools/mmp/Makefile @@ -97,6 +97,7 @@ mmp_sources = \ $(TOP)/tools/common/cache.cs \ driver.cs \ $(TOP)/src/ObjCRuntime/ErrorHelper.cs \ + $(TOP)/src/ObjCRuntime/ExceptionMode.cs \ error.cs \ resolver.cs \ $(MONO_PATH)/mcs/class/Mono.Options/Mono.Options/Options.cs \ diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs index 51eba6ac00cc..481565c2621d 100644 --- a/tools/mmp/driver.cs +++ b/tools/mmp/driver.cs @@ -48,6 +48,7 @@ using MonoMac.Tuner; using Xamarin.Utils; using Xamarin.Linker; +using XamCore.ObjCRuntime; namespace Xamarin.Bundler { public enum RegistrarMode { @@ -877,6 +878,8 @@ static string GenerateMain () sw.WriteLine ("\txamarin_custom_bundle_name = @\"" + custom_bundle_name + "\";"); } sw.WriteLine ("\txamarin_use_il_registrar = {0};", registrar == RegistrarMode.IL ? "true" : "false"); + sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", App.MarshalManagedExceptions); + sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", App.MarshalObjectiveCExceptions); sw.WriteLine (); if (Driver.registrar == RegistrarMode.Static) sw.WriteLine ("\txamarin_create_classes ();"); diff --git a/tools/mmp/error.cs b/tools/mmp/error.cs index 512db7b56117..6211489ba6d5 100644 --- a/tools/mmp/error.cs +++ b/tools/mmp/error.cs @@ -34,6 +34,7 @@ namespace Xamarin.Bundler { // MM0079 Internal Error - No executable was copied into the app bundle. Please contact 'support@xamarin.com' // Warning MT0080 Disabling NewRefCount, --new-refcount:false, is deprecated. // MM0088 ** Reserved mtouch ** + // MM0089 ** Reserved mtouch ** // MM1xxx file copy / symlinks (project related) // MM14xx Product assemblies // MM1401 The required '{0}' assembly is missing from the references diff --git a/tools/mmp/mmp.csproj b/tools/mmp/mmp.csproj index 600c5c7324c4..40bb50d24275 100644 --- a/tools/mmp/mmp.csproj +++ b/tools/mmp/mmp.csproj @@ -263,6 +263,9 @@ external\ErrorHelper.cs + + external\ExceptionMode.cs + external\Registrar.cs diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index ff063f80c3cc..e77afca59440 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -91,6 +91,7 @@ COMMON_SOURCES = \ $(TOP)/src/build/ios/Constants.cs \ error.cs \ $(TOP)/src/ObjCRuntime/ErrorHelper.cs \ + $(TOP)/src/ObjCRuntime/ExceptionMode.cs \ $(MONO_DIR)/mcs/class/Mono.Options/Mono.Options/Options.cs MTOUCH_SOURCES = \ diff --git a/tools/mtouch/error.cs b/tools/mtouch/error.cs index 512374ba73fc..62389dcd06e8 100644 --- a/tools/mtouch/error.cs +++ b/tools/mtouch/error.cs @@ -96,6 +96,7 @@ namespace Xamarin.Bundler { // MT0086 A target framework (--target-framework) must be specified when building for TVOS or WatchOS. // Warning MT0087 // MT0088 Cannot disable the Coop GC for watchOS apps. Please remove the --coop:false argument to mtouch. + // MT0089 The option '{0}' cannot take the value '{1}' when the Coop GC is enabled. // MT0091 This version of Xamarin.iOS requires the {0} {1} SDK (shipped with Xcode {2}) when the managed linker is disabled. Either upgrade Xcode, or enable the managed linker. // MT0092 The option '{0}' is required. // MT0093 Could not find 'mlaunch'. diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs index 408242453ad5..02301448211c 100644 --- a/tools/mtouch/mtouch.cs +++ b/tools/mtouch/mtouch.cs @@ -63,6 +63,7 @@ using MonoTouch.Tuner; using XamCore.Registrar; +using XamCore.ObjCRuntime; using Xamarin.Linker; using Xamarin.Utils; @@ -651,6 +652,8 @@ public static string GenerateMain (IEnumerable assemblies, string asse sw.WriteLine ("\tmono_use_llvm = {0};", enable_llvm ? "TRUE" : "FALSE"); sw.WriteLine ("\txamarin_log_level = {0};", verbose); sw.WriteLine ("\txamarin_arch_name = \"{0}\";", abi.AsArchString ()); + 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)) @@ -907,6 +910,9 @@ public static bool CanWeSymlinkTheApplication () if (app.Registrar == RegistrarMode.Static || app.Registrar == RegistrarMode.LegacyStatic || app.Registrar == RegistrarMode.LegacyDynamic) return false; + if (app.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Default || app.Platform == ApplePlatform.WatchOS) + return false; + return true; } diff --git a/tools/mtouch/mtouch.csproj b/tools/mtouch/mtouch.csproj index 7b5e9644cfc8..4124bd5150eb 100644 --- a/tools/mtouch/mtouch.csproj +++ b/tools/mtouch/mtouch.csproj @@ -317,6 +317,9 @@ external\ErrorHelper.cs + + external\ExceptionMode.cs + diff --git a/tools/mtouch/simlauncher.m b/tools/mtouch/simlauncher.m index 79aa558c35e5..406ae9c58a9a 100644 --- a/tools/mtouch/simlauncher.m +++ b/tools/mtouch/simlauncher.m @@ -21,6 +21,12 @@ void xamarin_setup_impl () xamarin_create_classes_Xamarin_iOS (); #else xamarin_use_new_assemblies = FALSE; +#endif + xamarin_marshal_managed_exception_mode = MarshalManagedExceptionModeDisable; +#if DEBUG + xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeUnwindManagedCode; +#else + xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeDisabled; #endif } From 3903e01076dbaa955ef60c11b5e1d308d6cbe0b1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 13:00:20 +0200 Subject: [PATCH 05/22] [mmp/mtouch] Add a Driver.WriteIfDifferent overload that takes a byte array. --- tools/common/Driver.cs | 62 ++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index f0a0ba554ecc..f76b024c5f9d 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -196,6 +196,30 @@ static void FileMove (string source, string target) File.Move (source, target); } + static void MoveIfDifferent (string path, string tmp) + { + // Don't read the entire file into memory, it can be quite big in certain cases. + + bool move = false; + + using (var fs1 = new FileStream (path, FileMode.Open, FileAccess.Read)) { + using (var fs2 = new FileStream (tmp, FileMode.Open, FileAccess.Read)) { + if (fs1.Length != fs2.Length) { + Log (3, "New file '{0}' has different length, writing new file.", path); + move = true; + } else { + move = !Cache.CompareStreams (fs1, fs2); + } + } + } + + if (move) { + FileMove (tmp, path); + } else { + Log (3, "Target {0} is up-to-date.", path); + } + } + public static void WriteIfDifferent (string path, string contents) { var tmp = path + ".tmp"; @@ -207,29 +231,31 @@ public static void WriteIfDifferent (string path, string contents) return; } - // Don't read the entire file into memory, it can be quite big in certain cases. - - bool move = false; File.WriteAllText (tmp, contents); + MoveIfDifferent (path, tmp); + } catch (Exception e) { + File.WriteAllText (path, contents); + ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message); + } finally { + Application.TryDelete (tmp); + } + } - using (var fs1 = new FileStream (path, FileMode.Open, FileAccess.Read)) { - using (var fs2 = new FileStream (tmp, FileMode.Open, FileAccess.Read)) { - if (fs1.Length != fs2.Length) { - Log (3, "New file '{0}' has different length, writing new file.", path); - move = true; - } else { - move = !Cache.CompareStreams (fs1, fs2); - } - } - } + public static void WriteIfDifferent (string path, byte[] contents) + { + var tmp = path + ".tmp"; - if (move) { - FileMove (tmp, path); - } else { - Log (3, "Target {0} is up-to-date.", path); + try { + if (!File.Exists (path)) { + File.WriteAllBytes (path, contents); + Log (3, "File '{0}' does not exist, creating it.", path); + return; } + + File.WriteAllBytes (tmp, contents); + MoveIfDifferent (path, tmp); } catch (Exception e) { - File.WriteAllText (path, contents); + File.WriteAllBytes (path, contents); ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message); } finally { Application.TryDelete (tmp); From 86c73f8fc08fc60d03110b0cbfc565711c1057e3 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 13:10:35 +0200 Subject: [PATCH 06/22] Refactor static registrar to produce both a header and a source file. This way we can re-use the header in other files. --- tools/common/StaticRegistrar.cs | 100 +++++++++++++++++------------ tools/common/Target.cs | 9 +++ tools/mmp/driver.cs | 8 ++- tools/mtouch/.gitignore | 1 + tools/mtouch/Application.cs | 35 +++++++++- tools/mtouch/Assembly.cs | 4 +- tools/mtouch/Makefile | 34 +++++----- tools/mtouch/OldStaticRegistrar.cs | 26 +++++++- tools/mtouch/Target.cs | 54 ++++++++++------ tools/mtouch/mtouch.cs | 19 ------ 10 files changed, 185 insertions(+), 105 deletions(-) diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 87d8d0b10838..9f32e91d0b5b 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -498,45 +498,53 @@ public static bool IsNativeObject (TypeReference tr) } } - class StaticRegistrar : Registrar { - bool is_simulator; - bool? is_64_bits; + public interface IStaticRegistrar + { + void Generate (IEnumerable assemblies, string header_path, string source_path); + void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string assembly); + Mono.Linker.LinkContext LinkContext { get; set; } + } + + class StaticRegistrar : Registrar, IStaticRegistrar { + public Application App { get; private set; } + public Target Target { get; private set; } + public bool IsSingleAssembly { get { return !string.IsNullOrEmpty (single_assembly); } } + string single_assembly; IEnumerable input_assemblies; + Mono.Linker.LinkContext link_context; Dictionary availability_annotations; - StaticRegistrar (Application app) + public Mono.Linker.LinkContext LinkContext { + get { + return link_context; + } + set { + link_context = value; + availability_annotations = link_context?.Annotations.GetCustomAnnotations ("Availability"); + } + } + + void Init (Application app) { + this.App = app; trace = !LaxMode && (app.RegistrarOptions & RegistrarOptions.Trace) == RegistrarOptions.Trace; } - public StaticRegistrar (Application app, IEnumerable assemblies, bool is_simulator, string single_assembly = null) - : this (app) + public StaticRegistrar (Application app) { - this.is_simulator = is_simulator; - this.single_assembly = single_assembly; - this.input_assemblies = assemblies; - - foreach (var assembly in assemblies) - RegisterAssembly (assembly); + Init (app); } - public StaticRegistrar (Application app, IEnumerable assemblies, bool is_simulator, bool is_64_bits, Mono.Linker.LinkContext link_context) - : this (app) + public StaticRegistrar (Target target) { - this.is_simulator = is_simulator; - this.is_64_bits = is_64_bits; - this.input_assemblies = assemblies; - - availability_annotations = link_context?.Annotations.GetCustomAnnotations ("Availability"); - - foreach (var assembly in assemblies) - RegisterAssembly (assembly); + Init (target.App); + this.Target = target; } protected override bool LaxMode { get { - return !string.IsNullOrEmpty (single_assembly); + return IsSingleAssembly; } } @@ -606,19 +614,27 @@ protected override bool HasThisAttribute (MethodDefinition method) return SharedStatic.TryGetAttributeImpl (method, "System.Runtime.CompilerServices", "ExtensionAttribute", out attrib); } +#if MTOUCH + public bool IsSimulator { + get { return App.IsSimulatorBuild; } + } +#endif + protected override bool IsSimulatorOrDesktop { get { #if MONOMAC return true; #else - return is_simulator; + return App.IsSimulatorBuild; #endif } } protected override bool Is64Bits { get { - return is_64_bits.Value; + if (IsSingleAssembly) + throw new InvalidOperationException ("Can't emit size-specific code in single assembly mode."); + return Target.Is64Build; } } @@ -1735,8 +1751,10 @@ void CheckNamespace (TypeReference type, List exceptions) case "CoreAudioKit": // fatal error: 'CoreAudioKit/CoreAudioKit.h' file not found // radar filed with Apple - but that framework / header is not yet shipped with the iOS SDK simulator - if (is_simulator) +#if MTOUCH + if (IsSimulator) return; +#endif goto default; case "Metal": case "MetalKit": @@ -1744,7 +1762,7 @@ void CheckNamespace (TypeReference type, List exceptions) // #error Metal Simulator is currently unsupported // this framework is _officially_ not available on the simulator (in iOS8) #if !MONOMAC - if (is_simulator) + if (IsSimulator) return; #endif goto default; @@ -3352,24 +3370,23 @@ public override bool Equals (object obj) } } -#if MONOMAC - public static string Generate (Application app, IEnumerable list, bool is_64_bits, Mono.Linker.LinkContext link_context) + public void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string assembly) { - return new StaticRegistrar (app, list, true, is_64_bits, link_context).Generate (); - } -#else - public static string Generate (Application app, IEnumerable list, bool simulator, bool is_64_bits, Mono.Linker.LinkContext link_context) - { - return new StaticRegistrar (app, list, simulator, is_64_bits, link_context).Generate (); + single_assembly = assembly; + Generate (assemblies, header_path, source_path); } - public static string Generate (Application app, IEnumerable list, bool simulator, string single_assembly = null) + public void Generate (IEnumerable assemblies, string header_path, string source_path) { - return new StaticRegistrar (app, list, simulator, single_assembly).Generate (); + this.input_assemblies = assemblies; + + foreach (var assembly in assemblies) + RegisterAssembly (assembly); + + Generate (header_path, source_path); } -#endif - public string Generate () + void Generate (string header_path, string source_path) { using (var sb = new AutoIndentStringBuilder ()) { using (var hdr = new AutoIndentStringBuilder ()) { @@ -3391,6 +3408,8 @@ public string Generate () declarations = decls; methods = mthds; + mthds.WriteLine ($"#include \"{Path.GetFileName (header_path)}\""); + Specialize (sb); header = null; @@ -3399,7 +3418,8 @@ public string Generate () FlushTrace (); - return hdr.ToString () + "\n" + decls.ToString () + "\n" + mthds.ToString () + "\n" + sb.ToString (); + Driver.WriteIfDifferent (header_path, hdr.ToString () + "\n" + decls.ToString () + "\n"); + Driver.WriteIfDifferent (source_path, mthds.ToString () + "\n" + sb.ToString () + "\n"); } } } diff --git a/tools/common/Target.cs b/tools/common/Target.cs index bdfd5e32ce98..027b0159bf36 100644 --- a/tools/common/Target.cs +++ b/tools/common/Target.cs @@ -18,6 +18,7 @@ using Xamarin.Linker; using Xamarin.Utils; +using XamCore.Registrar; #if MONOTOUCH using MonoTouch; @@ -36,11 +37,19 @@ public partial class Target { public List Assemblies = new List (); public PlatformLinkContext LinkContext; + public LinkerOptions LinkerOptions; public PlatformResolver Resolver = new PlatformResolver (); public HashSet Frameworks = new HashSet (); public HashSet WeakFrameworks = new HashSet (); + public IStaticRegistrar StaticRegistrar { get; set; } + +#if MONOMAC + public bool Is32Build { get { return !Driver.Is64Bit; } } + public bool Is64Build { get { return Driver.Is64Bit; } } +#endif + public Target (Application app) { this.App = app; diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs index 481565c2621d..8b4a7d724b13 100644 --- a/tools/mmp/driver.cs +++ b/tools/mmp/driver.cs @@ -48,6 +48,7 @@ using MonoMac.Tuner; using Xamarin.Utils; using Xamarin.Linker; +using XamCore.Registrar; using XamCore.ObjCRuntime; namespace Xamarin.Bundler { @@ -579,6 +580,8 @@ static void Pack (IList unprocessed) ExtractNativeLinkInfo (); + BuildTarget.StaticRegistrar = new StaticRegistrar (BuildTarget); + if (!no_executable) { foreach (var nr in native_references) { if (!native_libs.ContainsKey (nr)) @@ -927,9 +930,10 @@ static int Compile (IEnumerable internalSymbols) SetSDKVersion (); if (registrar == RegistrarMode.Static) { - var code = XamCore.Registrar.StaticRegistrar.Generate (App, BuildTarget.Resolver.ResolverCache.Values, Is64Bit, BuildTarget.LinkContext); registrarPath = Path.Combine (Cache.Location, "registrar.m"); - File.WriteAllText (registrarPath, code); + var registrarH = Path.Combine (Cache.Location, "registrar.h"); + BuildTarget.StaticRegistrar.LinkContext = BuildTarget.LinkContext; + BuildTarget.StaticRegistrar.Generate (BuildTarget.Resolver.ResolverCache.Values, registrarH, registrarPath); var platform_assembly = BuildTarget.Resolver.ResolverCache.First ((v) => v.Value.Name.Name == XamCore.Registrar.Registrar.PlatformAssembly).Value; Frameworks.Gather (platform_assembly, BuildTarget.Frameworks, BuildTarget.WeakFrameworks); diff --git a/tools/mtouch/.gitignore b/tools/mtouch/.gitignore index fe01bb419d11..2673ba7899f7 100644 --- a/tools/mtouch/.gitignore +++ b/tools/mtouch/.gitignore @@ -3,6 +3,7 @@ SdkVersions.cs *.dylib *.dSYM *.registrar.*.m +*.registrar.*.h simlauncher-sgen simlauncher32-sgen simlauncher64-sgen diff --git a/tools/mtouch/Application.cs b/tools/mtouch/Application.cs index f31b47dc82a6..6a0692606465 100644 --- a/tools/mtouch/Application.cs +++ b/tools/mtouch/Application.cs @@ -7,6 +7,7 @@ using System.IO; using System.Text; using System.Threading; +using System.Threading.Tasks; using MonoTouch.Tuner; @@ -539,8 +540,11 @@ public void RunRegistrar () } break; } - var registrar = XamCore.Registrar.StaticRegistrar.Generate (this, resolvedAssemblies, simulator: true, single_assembly: Path.GetFileNameWithoutExtension (RootAssembly)); - Driver.WriteIfDifferent (registrar_m, registrar); + + BuildTarget = BuildTarget.Simulator; + + var registrar = new XamCore.Registrar.StaticRegistrar (this); + registrar.GenerateSingleAssembly (resolvedAssemblies, Path.ChangeExtension (registrar_m, "h"), registrar_m, Path.GetFileNameWithoutExtension (RootAssembly)); } public void Build () @@ -878,6 +882,9 @@ void SelectRegistrar () Registrar = RegistrarMode.LegacyDynamic; } } + + foreach (var target in Targets) + target.SelectStaticRegistrar (); } // Select all abi from the list matching the specified mask. @@ -1539,6 +1546,24 @@ public static void ProcessFrameworkForArguments (StringBuilder args, string fw, } } + public class BuildTasks : List + { + static void Execute (BuildTask v) + { + var next = v.Execute (); + if (next != null) + Parallel.ForEach (next, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, Execute); + } + + public void ExecuteInParallel () + { + if (Count == 0) + return; + + Parallel.ForEach (this, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, Execute); + } + } + public abstract class BuildTask { public IEnumerable NextTasks; @@ -1672,6 +1697,12 @@ protected override void Build () } internal class RegistrarTask : CompileTask { + public static void Create (List tasks, IEnumerable abis, Target target, string ifile) + { + foreach (var abi in abis) + Create (tasks, abi, target, ifile); + } + public static void Create (List tasks, Abi abi, Target target, string ifile) { var arch = abi.AsArchString (); diff --git a/tools/mtouch/Assembly.cs b/tools/mtouch/Assembly.cs index 0483dd523481..fec9b1f614cc 100644 --- a/tools/mtouch/Assembly.cs +++ b/tools/mtouch/Assembly.cs @@ -153,7 +153,7 @@ public bool CopyToDirectory (string directory, bool reload = true, bool check_ca IEnumerable CreateCompileTasks (string s, string asm_infile, string llvm_infile, Abi abi) { - var compile_tasks = new List (); + var compile_tasks = new BuildTasks (); if (asm_infile != null) { var task = CreateCompileTask (s, asm_infile, abi); if (task != null) @@ -372,7 +372,7 @@ public bool Symlink () return symlink_failed; } - public void CreateCompilationTasks (List tasks, string build_dir, IEnumerable abis) + public void CreateCompilationTasks (BuildTasks tasks, string build_dir, IEnumerable abis) { var assembly = Path.Combine (build_dir, FileName); diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index e77afca59440..2fd374b41087 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -315,32 +315,32 @@ bin/Makefile/Mono.Cecil.dll: $(MONO_CECIL_DLL) | bin/Makefile bin/Makefile/Mono.Cecil.Mdb.dll: $(MONO_CECIL_MDB_DLL) | bin/Makefile $(Q) cp $<* $(dir $@) -%.registrar.ios.m: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.iOS/%.dll $(LOCAL_MTOUCH) +%.registrar.ios.m %.registrar.ios.h: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.iOS/%.dll $(LOCAL_MTOUCH) $(Q_GEN) $(SYSTEM_MONO) --debug bin/Makefile/mtouch.exe --xamarin-framework-directory=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(IOS_SDK_VERSION) $< --registrar:static --target-framework Xamarin.iOS,v1.0 --abi i386,x86_64 - $(Q) touch $@ + $(Q) touch $*.registrar.ios.m $*.registrar.ios.h -%.registrar.watchos.m: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.WatchOS/%.dll $(LOCAL_MTOUCH) +%.registrar.watchos.m %.registrar.watchos.h: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.WatchOS/%.dll $(LOCAL_MTOUCH) $(Q_GEN) $(SYSTEM_MONO) --debug bin/Makefile/mtouch.exe --xamarin-framework-directory=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(WATCH_SDK_VERSION) $< --registrar:static --target-framework Xamarin.WatchOS,1.0 --abi i386 - $(Q) touch $@ + $(Q) touch $*.registrar.watchos.m $*.registrar.watchos.h -%.registrar.tvos.m: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.TVOS/%.dll $(LOCAL_MTOUCH) +%.registrar.tvos.m %.registrar.tvos.h: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.TVOS/%.dll $(LOCAL_MTOUCH) $(Q_GEN) $(SYSTEM_MONO) --debug bin/Makefile/mtouch.exe --xamarin-framework-directory=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(TVOS_SDK_VERSION) $< --registrar:static --target-framework Xamarin.TVOS,1.0 --abi x86_64 - $(Q) touch $@ + $(Q) touch $*.registrar.tvos.m $*.registrar.tvos.h %.registrar.ios.a: %.registrar.ios.i386.a %.registrar.ios.x86_64.a $(Q_LIPO) $(DEVICE_BIN_PATH)/lipo -create -output $@ $^ -%.registrar.ios.i386.a: %.registrar.ios.m - $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR86_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include +%.registrar.ios.i386.a: %.registrar.ios.m %.registrar.ios.h + $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR86_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include -%.registrar.ios.x86_64.a: %.registrar.ios.m - $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR64_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include +%.registrar.ios.x86_64.a: %.registrar.ios.m %.registrar.ios.h + $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR64_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include -%.registrar.watchos.a: %.registrar.watchos.m - $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORWATCH_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(XAMARIN_WATCHSIMULATOR_SDK)/usr/include +%.registrar.watchos.a: %.registrar.watchos.m %.registrar.watchos.h + $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORWATCH_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(XAMARIN_WATCHSIMULATOR_SDK)/usr/include -%.registrar.tvos.a: %.registrar.tvos.m - $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORTV_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(XAMARIN_TVSIMULATOR_SDK)/usr/include +%.registrar.tvos.a: %.registrar.tvos.m %.registrar.tvos.h + $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORTV_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(XAMARIN_TVSIMULATOR_SDK)/usr/include TARGETS = \ $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/bin/mtouch \ @@ -423,6 +423,6 @@ include $(TOP)/mk/rules.mk # make will automatically consider files created in chained implicit rules as temporary files, and delete them afterwards # marking those files as .SECONDARY will prevent that deletion. -.SECONDARY: $(foreach file,Xamarin.iOS MonoTouch.Dialog-1 MonoTouch.NUnitLite,$(file).registrar.ios.a $(file).registrar.ios.i386.a $(file).registrar.ios.x86_64.a $(file).registrar.ios.m) -.SECONDARY: $(foreach file,Xamarin.WatchOS,$(file).registrar.watchos.a $(file).registrar.watchos.m) -.SECONDARY: $(foreach file,Xamarin.TVOS MonoTouch.Dialog-1,$(file).registrar.tvos.a $(file).registrar.tvos.m) +.SECONDARY: $(foreach file,Xamarin.iOS MonoTouch.Dialog-1 MonoTouch.NUnitLite,$(file).registrar.ios.a $(file).registrar.ios.i386.a $(file).registrar.ios.x86_64.a $(file).registrar.ios.m $(file).registrar.ios.h) +.SECONDARY: $(foreach file,Xamarin.WatchOS,$(file).registrar.watchos.a $(file).registrar.watchos.m $(file).registrar.watchos.h) +.SECONDARY: $(foreach file,Xamarin.TVOS MonoTouch.Dialog-1,$(file).registrar.tvos.a $(file).registrar.tvos.m $(file).registrar.tvos.h) diff --git a/tools/mtouch/OldStaticRegistrar.cs b/tools/mtouch/OldStaticRegistrar.cs index 6987317df3fe..e4bb4cb965d1 100644 --- a/tools/mtouch/OldStaticRegistrar.cs +++ b/tools/mtouch/OldStaticRegistrar.cs @@ -17,6 +17,7 @@ using XamCore.Registrar; using XamCore.ObjCRuntime; using Xamarin.Linker; +using Mono.Linker; namespace XamCore.Registrar { @@ -777,7 +778,7 @@ public static IEnumerable Describe (IEnumerable asse } } - class OldStaticRegistrar { + class OldStaticRegistrar : IStaticRegistrar { static StringBuilder headers = new StringBuilder (); static StringBuilder structs = new StringBuilder (); static bool verbose = false; @@ -879,7 +880,12 @@ static string GetPrintfFormatSpecifier (TypeDefinition type, out bool unknown) static HashSet trampoline_names = new HashSet (); static HashSet namespaces = new HashSet (); static HashSet structures = new HashSet (); - + + public LinkContext LinkContext { + get { throw new NotImplementedException (); } + set { throw new NotImplementedException (); } + } + static void CheckNamespace (string ns) { if (!ns.StartsWith ("MonoTouch.")) @@ -1659,6 +1665,22 @@ static void Specialize (StringBuilder sb, Method method) method.SpecializedTrampoline = name; } + public void GeneratePInvokeWrappers (List marshal_exception_pinvokes, string header_path, string source_path) + { + throw new NotSupportedException (); + } + + public void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string single_assembly) + { + throw new NotSupportedException (); + } + + public void Generate (IEnumerable assemblies, string header_path, string source_path) + { + Driver.WriteIfDifferent (header_path, string.Empty); + Driver.WriteIfDifferent (source_path, Generate (assemblies)); + } + public static string Generate (IEnumerable assemblies) { var classes = TypeSystemDescriptor.Describe (assemblies); diff --git a/tools/mtouch/Target.cs b/tools/mtouch/Target.cs index 45cc9a8197bd..ef6d6dd1a673 100644 --- a/tools/mtouch/Target.cs +++ b/tools/mtouch/Target.cs @@ -16,6 +16,8 @@ using Xamarin.Utils; +using XamCore.Registrar; + namespace Xamarin.Bundler { public partial class Target { @@ -46,6 +48,8 @@ public partial class Target { // If any assemblies were updated (only set to false if the linker is disabled and no assemblies were modified). bool any_assembly_updated = true; + BuildTasks compile_tasks = new BuildTasks (); + // If we didn't link the final executable because the existing binary is up-to-date. public bool cached_executable; @@ -113,7 +117,7 @@ public void LoadSymbols () IEnumerable GetAssemblies () { if (App.LinkMode == LinkMode.None) - return Resolver.GetAssemblies (); + return ManifestResolver.GetAssemblies (); List assemblies = new List (); if (LinkContext == null) { @@ -352,7 +356,7 @@ public void LinkAssemblies (string main, ref List assemblies, string out resolver.AddSearchDirectory (Resolver.RootDirectory); resolver.AddSearchDirectory (Resolver.FrameworkDirectory); - var options = new LinkerOptions { + LinkerOptions = new LinkerOptions { MainAssembly = Resolver.Load (main), OutputDirectory = output_dir, LinkMode = App.LinkMode, @@ -375,7 +379,7 @@ public void LinkAssemblies (string main, ref List assemblies, string out RuntimeOptions = App.RuntimeOptions }; - MonoTouch.Tuner.Linker.Process (options, out link_context, out assemblies); + MonoTouch.Tuner.Linker.Process (LinkerOptions, out link_context, out assemblies); // reset resolver foreach (var file in assemblies) { @@ -621,22 +625,40 @@ public void ProcessAssemblies () Frameworks.ExceptWith (WeakFrameworks); } - public void Compile () + public void SelectStaticRegistrar () { - var tasks = new List (); + switch (App.Registrar) { + case RegistrarMode.LegacyStatic: + case RegistrarMode.Legacy: + case RegistrarMode.LegacyDynamic: + StaticRegistrar = new OldStaticRegistrar (); + break; + case RegistrarMode.Static: + case RegistrarMode.Dynamic: + case RegistrarMode.Default: + StaticRegistrar = new StaticRegistrar (this) + { + LinkContext = LinkContext, + }; + break; + } + } + public void Compile () + { // Compile the managed assemblies into object files or shared libraries if (App.IsDeviceBuild) { foreach (var a in Assemblies) - a.CreateCompilationTasks (tasks, BuildDirectory, Abis); + a.CreateCompilationTasks (compile_tasks, BuildDirectory, Abis); } // The static registrar. List registration_methods = null; if (App.Registrar == RegistrarMode.Static || App.Registrar == RegistrarMode.LegacyStatic) { var registrar_m = Path.Combine (ArchDirectory, "registrar.m"); - if (!Application.IsUptodate (Assemblies.Select (v => v.FullPath), new string[] { registrar_m })) { - registrar_m = Driver.RunRegistrar (this, Assemblies, BuildDirectory, BuildDirectory, App.Registrar == RegistrarMode.LegacyStatic, Is64Build, registrar_m); + var registrar_h = Path.Combine (ArchDirectory, "registrar.h"); + if (!Application.IsUptodate (Assemblies.Select (v => v.FullPath), new string[] { registrar_m, registrar_h })) { + StaticRegistrar.Generate (Assemblies.Select ((a) => a.AssemblyDefinition), registrar_h, registrar_m); registration_methods = new List (); registration_methods.Add ("xamarin_create_classes"); Driver.Watch ("Registrar", 1); @@ -644,8 +666,7 @@ public void Compile () Driver.Log (3, "Target '{0}' is up-to-date.", registrar_m); } - foreach (var abi in Abis) - RegistrarTask.Create (tasks, abi, this, registrar_m); + RegistrarTask.Create (compile_tasks, Abis, this, registrar_m); } if (App.Registrar == RegistrarMode.Dynamic && App.IsSimulatorBuild && App.LinkMode == LinkMode.None && App.IsUnified) { @@ -698,19 +719,10 @@ public void Compile () // The main method. foreach (var abi in Abis) - MainTask.Create (tasks, this, abi, Assemblies, App.AssemblyName, registration_methods); + MainTask.Create (compile_tasks, this, abi, Assemblies, App.AssemblyName, registration_methods); // Start compiling. - if (tasks.Count > 0) { - Action action = null; - action = (v) => { - var next = v.Execute (); - if (next != null) - Parallel.ForEach (next, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, action); - }; - - Parallel.ForEach (tasks, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, action); - } + compile_tasks.ExecuteInParallel (); if (App.FastDev) { foreach (var a in Assemblies) { diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs index 02301448211c..a152a0018b52 100644 --- a/tools/mtouch/mtouch.cs +++ b/tools/mtouch/mtouch.cs @@ -1726,25 +1726,6 @@ static bool IsBoundAssembly (Assembly s) return false; } - public static string RunRegistrar (Target target, List assemblies, string assemblies_path, string output_dir, bool old, bool is_64_bits, string out_file = null) - { - const string registrar_file = "registrar.m"; - - var resolvedAssemblies = assemblies.Select (asm => asm.AssemblyDefinition); - var output_file = out_file ?? Path.Combine (output_dir, registrar_file); - var code = string.Empty; - - if (old) { - code = OldStaticRegistrar.Generate (resolvedAssemblies); - } else { - code = StaticRegistrar.Generate (app, resolvedAssemblies, target.App.IsSimulatorBuild, is_64_bits, target.LinkContext); - } - - WriteIfDifferent (output_file, code); - - return output_file; - } - struct timespec { public IntPtr tv_sec; public IntPtr tv_nsec; From 1f1f6991a38be0eefc72e27021c079b523b9b728 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 11 May 2016 13:27:51 +0200 Subject: [PATCH 07/22] Implement support for exception marshalling. --- runtime/Makefile | 4 +- runtime/delegates.t4 | 16 ++ runtime/exports.t4 | 16 ++ runtime/libmonotouch.csproj | 14 ++ runtime/runtime-internal.h | 15 ++ runtime/runtime.m | 131 +++++++++++++++++ runtime/shared.h | 1 + ...rampolines-i386-objc_msgSend-copyframe.inc | 53 +++++++ .../trampolines-i386-objc_msgSend-post.inc | 93 ++++++++++++ runtime/trampolines-i386-objc_msgSend-pre.inc | 27 ++++ runtime/trampolines-i386-objc_msgSend.s | 25 ++++ runtime/trampolines-i386-objc_msgSendSuper.s | 26 ++++ ...trampolines-i386-objc_msgSendSuper_stret.s | 27 ++++ runtime/trampolines-i386-objc_msgSend_stret.s | 26 ++++ runtime/trampolines-invoke.m | 10 +- ...mpolines-x86_64-objc_msgSend-copyframe.inc | 46 ++++++ .../trampolines-x86_64-objc_msgSend-post.inc | 73 ++++++++++ .../trampolines-x86_64-objc_msgSend-pre.inc | 34 +++++ runtime/trampolines-x86_64-objc_msgSend.s | 19 +++ .../trampolines-x86_64-objc_msgSendSuper.s | 22 +++ ...ampolines-x86_64-objc_msgSendSuper_stret.s | 23 +++ .../trampolines-x86_64-objc_msgSend_stret.s | 23 +++ runtime/trampolines.m | 2 - runtime/xamarin/runtime.h | 2 + src/ObjCRuntime/Exceptions.cs | 26 ++++ src/ObjCRuntime/Runtime.cs | 68 +++++++++ src/frameworks.sources | 1 + tests/bindings-test/ApiDefinition.cs | 19 +++ tests/test-libraries/libtest.h | 12 ++ tests/test-libraries/libtest.m | 36 +++++ tools/common/Application.cs | 13 ++ tools/common/PInvokeWrapperGenerator.cs | 70 +++++++++ tools/common/StaticRegistrar.cs | 137 +++++++++++++++++- .../MonoTouch.Tuner/ListExportedSymbols.cs | 30 +++- tools/mmp/Makefile | 1 + tools/mmp/Tuning.cs | 12 +- tools/mmp/driver.cs | 21 +++ tools/mmp/mmp.csproj | 3 + tools/mtouch/Application.cs | 2 +- tools/mtouch/Makefile | 1 + tools/mtouch/Target.cs | 35 ++++- tools/mtouch/Tuning.cs | 12 +- tools/mtouch/mtouch.csproj | 3 + 43 files changed, 1214 insertions(+), 16 deletions(-) create mode 100644 runtime/trampolines-i386-objc_msgSend-copyframe.inc create mode 100644 runtime/trampolines-i386-objc_msgSend-post.inc create mode 100644 runtime/trampolines-i386-objc_msgSend-pre.inc create mode 100644 runtime/trampolines-i386-objc_msgSend.s create mode 100644 runtime/trampolines-i386-objc_msgSendSuper.s create mode 100644 runtime/trampolines-i386-objc_msgSendSuper_stret.s create mode 100644 runtime/trampolines-i386-objc_msgSend_stret.s create mode 100644 runtime/trampolines-x86_64-objc_msgSend-copyframe.inc create mode 100644 runtime/trampolines-x86_64-objc_msgSend-post.inc create mode 100644 runtime/trampolines-x86_64-objc_msgSend-pre.inc create mode 100644 runtime/trampolines-x86_64-objc_msgSend.s create mode 100644 runtime/trampolines-x86_64-objc_msgSendSuper.s create mode 100644 runtime/trampolines-x86_64-objc_msgSendSuper_stret.s create mode 100644 runtime/trampolines-x86_64-objc_msgSend_stret.s create mode 100644 src/ObjCRuntime/Exceptions.cs create mode 100644 tools/common/PInvokeWrapperGenerator.cs diff --git a/runtime/Makefile b/runtime/Makefile index c2af5da9c071..ddd34c8f563c 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -20,8 +20,8 @@ SHIPPED_HEADERS += \ xamarin/runtime.h \ SHARED_SOURCES += mono-runtime.m bindings.m bindings-generated.m shared.m runtime.m trampolines.m trampolines-invoke.m xamarin-support.m nsstring-localization.m trampolines-varargs.m -SHARED_I386_SOURCES += trampolines-i386.m trampolines-i386-asm.s -SHARED_X86_64_SOURCES += trampolines-x86_64.m trampolines-x86_64-asm.s +SHARED_I386_SOURCES += trampolines-i386.m trampolines-i386-asm.s trampolines-i386-objc_msgSend.s trampolines-i386-objc_msgSendSuper.s trampolines-i386-objc_msgSend_stret.s trampolines-i386-objc_msgSendSuper_stret.s +SHARED_X86_64_SOURCES += trampolines-x86_64.m trampolines-x86_64-asm.s trampolines-x86_64-objc_msgSend.s trampolines-x86_64-objc_msgSendSuper.s trampolines-x86_64-objc_msgSend_stret.s trampolines-x86_64-objc_msgSendSuper_stret.s SHARED_HEADERS += shared.h product.h delegates.h runtime-internal.h $(SHARED_INC) $(SHIPPED_HEADERS) trampolines-internal.h slinked-list.h SHARED_FILES = $(SHARED_SOURCES) $(SHARED_HEADERS) $(SHARED_I386_SOURCES) $(SHARED_X86_64_SOURCES) diff --git a/runtime/delegates.t4 b/runtime/delegates.t4 index 658b85ed567a..4c90714aea4e 100644 --- a/runtime/delegates.t4 +++ b/runtime/delegates.t4 @@ -19,6 +19,14 @@ "NSException *", "IntPtr", "exc" ) { WrappedManagedFunction = "ThrowNSException" }, + new XDelegate ("int", "int", "xamarin_create_ns_exception", + "NSException *", "IntPtr", "exc" + ) { WrappedManagedFunction = "CreateNSException" }, + + new XDelegate ("NSException *", "IntPtr", "xamarin_unwrap_ns_exception", + "int", "int", "exc_handle" + ) { WrappedManagedFunction = "UnwrapNSException" }, + new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_block_wrapper_creator", "MonoObject *", "IntPtr", "method", "int", "int", "parameter" @@ -147,6 +155,14 @@ new XDelegate ("char *", "IntPtr", "xamarin_lookup_managed_type_name", "Class", "IntPtr", "klass" ) { WrappedManagedFunction = "LookupManagedTypeName" }, + + new XDelegate ("MarshalManagedExceptionMode", "MarshalManagedExceptionMode", "xamarin_on_marshal_managed_exception", + "int", "int", "exception" + ) { WrappedManagedFunction = "OnMarshalManagedException" }, + + new XDelegate ("MarshalObjectiveCExceptionMode", "MarshalObjectiveCExceptionMode", "xamarin_on_marshal_objectivec_exception", + "id", "IntPtr", "exception" + ) { WrappedManagedFunction = "OnMarshalObjectiveCException" }, }; delegates.CalculateLengths (); #><#+ diff --git a/runtime/exports.t4 b/runtime/exports.t4 index eacc81e0d90e..8805e5ab2b18 100644 --- a/runtime/exports.t4 +++ b/runtime/exports.t4 @@ -218,6 +218,14 @@ #endregion + #region metadata/object-internals.h + + new Export ("void", "mono_set_pending_exception", + "MonoException *", "exc" + ), + + #endregion + #region metadata/assembly.h new Export ("void", "mono_set_assemblies_path", @@ -288,6 +296,14 @@ "MonoMethod *", "method" ), + new Export ("void", "mono_dllmap_insert", + "MonoImage *", "assembly", + "const char *", "dll", + "const char *", "func", + "const char *", "tdll", + "const char *", "tfunc" + ), + #endregion #region metadata/appdomain.h diff --git a/runtime/libmonotouch.csproj b/runtime/libmonotouch.csproj index 425c726b59a4..35536d76b975 100644 --- a/runtime/libmonotouch.csproj +++ b/runtime/libmonotouch.csproj @@ -83,5 +83,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/runtime/runtime-internal.h b/runtime/runtime-internal.h index 52a995b7bc40..1bb3472b580c 100644 --- a/runtime/runtime-internal.h +++ b/runtime/runtime-internal.h @@ -26,4 +26,19 @@ void *xamarin_marshal_return_value (MonoType *mtype, const char *type, MonoObject *retval, bool retain, MonoMethod *method); +/* + * XamarinGCHandle + * + * This is an ObjC type that ties the lifetime of a GCHandle to the lifetime of itself. + * It stores a GCHandle, and frees the GCHandle in dealloc. + */ +@interface XamarinGCHandle : NSObject { +@public + int handle; +} ++(XamarinGCHandle *) createWithHandle: (int) handle; +-(void) dealloc; +-(int) getHandle; +@end + #endif /* __RUNTIME_INTERNAL_H__ */ diff --git a/runtime/runtime.m b/runtime/runtime.m index 80a17f17c57f..0e456238d8b8 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -148,6 +148,8 @@ struct Delegates Delegates; struct Trampolines Trampolines; RegistrationData* RegistrationData; + enum MarshalObjectiveCExceptionMode MarshalObjectiveCExceptionMode; + enum MarshalManagedExceptionMode MarshalManagedExceptionMode; }; static struct Trampolines trampolines = { @@ -981,6 +983,8 @@ -(void) xamarinSetGCHandle: (int) gc_handle; xamarin_initialize_dynamic_runtime (NULL); #endif + xamarin_insert_dllmap (); + mono_trace_set_log_handler (log_callback, NULL); mono_trace_set_print_handler (print_callback); mono_trace_set_printerr_handler (print_callback); @@ -1024,6 +1028,8 @@ -(void) xamarinSetGCHandle: (int) gc_handle; options.Trampolines = trampolines; options.RegistrationData = ®istration_data; + options.MarshalObjectiveCExceptionMode = xamarin_marshal_objectivec_exception_mode; + options.MarshalManagedExceptionMode = xamarin_marshal_managed_exception_mode; params [0] = &options; @@ -1701,6 +1707,107 @@ -(void) xamarinSetGCHandle: (int) gc_handle; } } +void +xamarin_process_nsexception (NSException *ns_exception) +{ + MarshalObjectiveCExceptionMode mode; + XamarinGCHandle *exc_handle; + + mode = xamarin_on_marshal_objectivec_exception (ns_exception); + + if (mode == MarshalObjectiveCExceptionModeDefault) + mode = xamarin_is_gc_coop ? MarshalObjectiveCExceptionModeThrowManagedException : MarshalObjectiveCExceptionModeUnwindManagedCode; + + switch (mode) { + case MarshalObjectiveCExceptionModeUnwindManagedCode: + if (xamarin_is_gc_coop) + xamarin_assertion_message ("Cannot unwind managed frames for Objective-C exceptions when using the COOP GC."); + @throw ns_exception; + break; + case MarshalObjectiveCExceptionModeThrowManagedException: + exc_handle = [[ns_exception userInfo] objectForKey: @"XamarinManagedExceptionHandle"]; + if (exc_handle != NULL) { + int handle = [exc_handle getHandle]; + MonoObject *exc = mono_gchandle_get_target (handle); + mono_set_pending_exception ((MonoException *) exc); + } else { + int handle = xamarin_create_ns_exception (ns_exception); + MonoObject *exc = mono_gchandle_get_target (handle); + mono_set_pending_exception ((MonoException *) exc); + mono_gchandle_free (handle); + } + break; + case MarshalObjectiveCExceptionModeAbort: + default: + xamarin_assertion_message ("Aborting due to unhandled Objective-C exception:\n%s\n", [[ns_exception description] UTF8String]); + break; + } +} + +void +xamarin_process_managed_exception (MonoObject *exception) +{ + if (exception == NULL) + return; + + MarshalManagedExceptionMode mode; + + int handle = mono_gchandle_new (exception, false); + mode = xamarin_on_marshal_managed_exception (handle); + mono_gchandle_free (handle); + + if (mode == MarshalManagedExceptionModeDefault) + mode = xamarin_is_gc_coop ? MarshalManagedExceptionModeThrowObjectiveCException : MarshalManagedExceptionModeUnwindNativeCode; + + switch (mode) { + case MarshalManagedExceptionModeUnwindNativeCode: + if (xamarin_is_gc_coop) + xamarin_assertion_message ("Cannot unwind native frames for managed exceptions when using the COOP GC."); + mono_raise_exception ((MonoException *) exception); + break; + case MarshalManagedExceptionModeThrowObjectiveCException: { + int handle = mono_gchandle_new (exception, false); + NSException *ns_exc = xamarin_unwrap_ns_exception (handle); + + if (ns_exc != NULL) { + mono_gchandle_free (handle); + @throw ns_exc; + } else { + NSString *name = [NSString stringWithUTF8String: xamarin_type_get_full_name (mono_class_get_type (mono_object_get_class (exception)))]; + char *message = fetch_exception_property_string (exception, "get_Message", true); + NSString *reason = [NSString stringWithUTF8String: message]; + mono_free (message); + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: [XamarinGCHandle createWithHandle: handle] forKey: @"XamarinManagedExceptionHandle"]; + @throw [[NSException alloc] initWithName: name reason: reason userInfo: userInfo]; + } + break; + } + case MarshalManagedExceptionModeAbort: + default: + xamarin_assertion_message ("Aborting due to:\n%s\n", [print_all_exceptions (exception) UTF8String]); + break; + } +} + +void +xamarin_insert_dllmap () +{ +#if defined (__i386__) || defined (__x86_64__) + if (xamarin_marshal_objectivec_exception_mode == MarshalObjectiveCExceptionModeDisable) + return; +#if DYNAMIC_MONO_RUNTIME + const char *lib = "libxammac.dylib"; +#else + const char *lib = "__Internal"; +#endif + mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSend", lib, "xamarin_dyn_objc_msgSend"); + mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSendSuper", lib, "xamarin_dyn_objc_msgSendSuper"); + mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSend_stret", lib, "xamarin_dyn_objc_msgSend_stret"); + mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSendSuper_stret", lib, "xamarin_dyn_objc_msgSendSuper_stret"); + LOG (PRODUCT ": Added dllmap for objc_msgSend"); +#endif // defined (__i386__) || defined (__x86_64__) +} + /* * Object unregistration: * @@ -1856,3 +1963,27 @@ -(int) xamarinGetGCHandle { return xamarin_debug_mode; } + +/* + * XamarinGCHandle + */ +@implementation XamarinGCHandle ++(XamarinGCHandle *) createWithHandle: (int) h +{ + XamarinGCHandle *rv = [[XamarinGCHandle alloc] init]; + rv->handle = h; + [rv autorelease]; + return rv; +} + +-(void) dealloc +{ + mono_gchandle_free (handle); + [super dealloc]; +} + +-(int) getHandle +{ + return handle; +} +@end diff --git a/runtime/shared.h b/runtime/shared.h index 62585b468760..2220c991a04e 100644 --- a/runtime/shared.h +++ b/runtime/shared.h @@ -22,6 +22,7 @@ void initialize_cocoa_threads (init_cocoa_func *func); void install_nsautoreleasepool_hooks (); id xamarin_init_nsthread (id obj, bool is_direct, id target, SEL sel, id arg); +void xamarin_insert_dllmap (); /* * Block support diff --git a/runtime/trampolines-i386-objc_msgSend-copyframe.inc b/runtime/trampolines-i386-objc_msgSend-copyframe.inc new file mode 100644 index 000000000000..b15afdf74a25 --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSend-copyframe.inc @@ -0,0 +1,53 @@ + + # + # input stack layout: + # %esp+20: ... + # %esp+16: second arg + # %esp+12: first arg + # %esp+8: sel + # %esp+4: this + # %esp: buffer + # and %ebp+8 = %esp + # + # We extend the stack (big enough for all the arguments again), + # and copy all the arguments as-is there, before + # calling objc_msgSend with those copied arguments. + # The only difference is that this method has an exception handler. + # + + call _xamarin_get_frame_length # get_frame_length (this, sel) + + # use eax to extend the stack, but it needs to be aligned to 16 bytes first + addl $15,%eax + shrl $4,%eax + sall $4,%eax + subl %eax,%esp + # store the number somewhere so we can restore the stack pointer later + movl %eax,-80(%ebp) + + # copy arguments from old location in the stack to new location in the stack + # %ecx will hold the amount of bytes left to copy + # %esi the current src location + # %edi the current dst location + + # %ecx will already be a multiple of 4, since the abi requires it + # (arguments smaller than 4 bytes are extended to 4 bytes according to + # http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4) + + movl -80(%ebp),%ecx # ecx = frame_length + leal 8(%ebp),%esi # esi = address of first argument we got (buffer) + movl %esp,%edi # edi = address of the bottom of the stack + +L_start: + cmpl $0,%ecx # + je L_end # while (left != 0) { + subl $4,%ecx # len -= 4 + movl (%esi,%ecx),%eax # tmp = src [len] + movl %eax,(%edi,%ecx) # dst [len] = tmp + jmp L_start # } +L_end: + + movaps -16(%ebp), %xmm0 + movaps -32(%ebp), %xmm1 + movaps -48(%ebp), %xmm2 + movaps -64(%ebp), %xmm3 diff --git a/runtime/trampolines-i386-objc_msgSend-post.inc b/runtime/trampolines-i386-objc_msgSend-post.inc new file mode 100644 index 000000000000..f5e07e42e771 --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSend-post.inc @@ -0,0 +1,93 @@ +Lafterinvoke: + addl -80(%ebp), %esp + addl $68, %esp + popl %ebx + popl %edi + popl %esi + popl %ebp + retl +Lcatchhandler: + cmpl $1, %edx + movl %eax, (%esp) + jne Lnomatchexception + + # check if xamarin_marshal_objectivec_exception_mode == disable, if so, just don't handle the exception + call Lloadpcrelative1 +Lloadpcrelative1: + popl %ecx + cmpl $4, L_xamarin_marshal_objectivec_exception_mode$non_lazy_ptr-Lloadpcrelative1(%ecx) + je Lnomatchexception + + calll _objc_begin_catch +Lcatchbegin: + movl %eax, (%esp) + calll _xamarin_process_nsexception +Lcatchend: + calll _objc_end_catch + jmp Lafterinvoke +Lcatchcatchhandler: + calll _objc_end_catch +Lnomatchexception: + calll __Unwind_Resume +Lfunc_end0: +.cfi_endproc + +# exception table + .section __TEXT,__gcc_except_tab + .align 2 +GCC_except_table0: +Lexception0: + .byte 255 ## @LPStart Encoding = omit + .byte 155 ## @TType Encoding = indirect pcrel sdata4 + .asciz "\274" ## @TType base offset + .byte 3 ## Call site Encoding = udata4 + .byte 52 ## Call site table length +Lset0 = Lbeforeinvoke-Lfunc_begin0 ## >> Call Site 1 << + .long Lset0 +Lset1 = Lafterinvoke-Lbeforeinvoke ## Call between Lbeforeinvoke and Lafterinvoke + .long Lset1 +Lset2 = Lcatchhandler-Lfunc_begin0 ## jumps to Lcatchhandler + .long Lset2 + .byte 1 ## On action: 1 +Lset3 = Lafterinvoke-Lfunc_begin0 ## >> Call Site 2 << + .long Lset3 +Lset4 = Lcatchbegin-Lafterinvoke ## Call between Lafterinvoke and Lcatchbegin + .long Lset4 + .long 0 ## has no landing pad + .byte 0 ## On action: cleanup +Lset5 = Lcatchbegin-Lfunc_begin0 ## >> Call Site 3 << + .long Lset5 +Lset6 = Lcatchend-Lcatchbegin ## Call between Lcatchbegin and Lcatchend + .long Lset6 +Lset7 = Lcatchcatchhandler-Lfunc_begin0 ## jumps to Lcatchcatchhandler + .long Lset7 + .byte 0 ## On action: cleanup +Lset8 = Lcatchend-Lfunc_begin0 ## >> Call Site 4 << + .long Lset8 +Lset9 = Lfunc_end0-Lcatchend ## Call between Lcatchend and Lfunc_end0 + .long Lset9 + .long 0 ## has no landing pad + .byte 0 ## On action: cleanup + .byte 1 ## >> Action Record 1 << + ## Catch TypeInfo 1 + .byte 0 ## No further actions + ## >> Catch TypeInfos << +Ltmp9: ## TypeInfo 1 + .long L_OBJC_EHTYPE_$_NSException$non_lazy_ptr-Ltmp9 + .align 2 + + .section __DATA,__objc_imageinfo,regular,no_dead_strip +L_OBJC_IMAGE_INFO: + .long 0 + .long 0 + + .section __IMPORT,__pointers,non_lazy_symbol_pointers +L_OBJC_EHTYPE_$_NSException$non_lazy_ptr: + .indirect_symbol _OBJC_EHTYPE_$_NSException + .long 0 +L___objc_personality_v0$non_lazy_ptr: + .indirect_symbol ___objc_personality_v0 + .long 0 +L_xamarin_marshal_objectivec_exception_mode$non_lazy_ptr: + .indirect_symbol _xamarin_marshal_objectivec_exception_mode + .long 0 diff --git a/runtime/trampolines-i386-objc_msgSend-pre.inc b/runtime/trampolines-i386-objc_msgSend-pre.inc new file mode 100644 index 000000000000..7ecdb732e075 --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSend-pre.inc @@ -0,0 +1,27 @@ + +# function start +# preserve some registers +# create stack space to call get_frame_length + +Lfunc_begin0: +.cfi_startproc +.cfi_personality 155, L___objc_personality_v0$non_lazy_ptr +.cfi_lsda 16, Lexception0 + + pushl %ebp +.cfi_def_cfa_offset 8 +.cfi_offset %ebp, -8 + movl %esp, %ebp +.cfi_def_cfa_register %ebp + + pushl %esi # %ebp-4 + pushl %edi # %ebp-8 + pushl %ebx # %ebp-12 + # we need 64 bytes to store xmm0-3 + # we need 4 bytes to store the result for get_frame_length, which we store in %ebp-80 + # then 4 more bytes for stack space for the call to get_frame_length (which takes 2 arguments) + subl $76, %esp # to store xmm0-3 + movaps %xmm0, -16(%ebp) + movaps %xmm1, -32(%ebp) + movaps %xmm2, -48(%ebp) + movaps %xmm3, -64(%ebp) diff --git a/runtime/trampolines-i386-objc_msgSend.s b/runtime/trampolines-i386-objc_msgSend.s new file mode 100644 index 000000000000..4b63e559ffbe --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSend.s @@ -0,0 +1,25 @@ +#if __i386__ && MONOTOUCH + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSend +_xamarin_dyn_objc_msgSend: + +#include "trampolines-i386-objc_msgSend-pre.inc" + + # int frame_length = get_frame_length (this, sel) + movl 12(%ebp), %eax + movl %eax, 4(%esp) # push sel + movl 8(%ebp), %eax + movl %eax, (%esp) # push this + +#include "trampolines-i386-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: + calll _objc_msgSend + +#include "trampolines-i386-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-i386-objc_msgSendSuper.s b/runtime/trampolines-i386-objc_msgSendSuper.s new file mode 100644 index 000000000000..785154340947 --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSendSuper.s @@ -0,0 +1,26 @@ +#if __i386__ && MONOTOUCH + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSendSuper +_xamarin_dyn_objc_msgSendSuper: + +#include "trampolines-i386-objc_msgSend-pre.inc" + + # int frame_length = get_frame_length (this, sel) + movl 12(%ebp), %eax + movl %eax, 4(%esp) # push sel + movl 8(%ebp), %eax + movl (%eax), %eax + movl %eax, (%esp) # push this + +#include "trampolines-i386-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: + calll _objc_msgSendSuper + +#include "trampolines-i386-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-i386-objc_msgSendSuper_stret.s b/runtime/trampolines-i386-objc_msgSendSuper_stret.s new file mode 100644 index 000000000000..1f5e42cb9678 --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSendSuper_stret.s @@ -0,0 +1,27 @@ +#if __i386__ && MONOTOUCH + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSendSuper_stret +_xamarin_dyn_objc_msgSendSuper_stret: + +#include "trampolines-i386-objc_msgSend-pre.inc" + + # int frame_length = get_frame_length (this, sel) + movl 16(%ebp), %eax + movl %eax, 4(%esp) # push sel + movl 12(%ebp), %eax + movl (%eax), %eax + movl %eax, (%esp) # push this + +#include "trampolines-i386-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: + calll _objc_msgSendSuper_stret + subl $4, %esp + +#include "trampolines-i386-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-i386-objc_msgSend_stret.s b/runtime/trampolines-i386-objc_msgSend_stret.s new file mode 100644 index 000000000000..6f78b19f5694 --- /dev/null +++ b/runtime/trampolines-i386-objc_msgSend_stret.s @@ -0,0 +1,26 @@ +#if __i386__ && MONOTOUCH + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSend_stret +_xamarin_dyn_objc_msgSend_stret: + +#include "trampolines-i386-objc_msgSend-pre.inc" + + # int frame_length = get_frame_length (this, sel) + movl 16(%ebp), %eax + movl %eax, 4(%esp) # push sel + movl 12(%ebp), %eax + movl %eax, (%esp) # push this + +#include "trampolines-i386-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: + calll _objc_msgSend_stret + subl $4, %esp + +#include "trampolines-i386-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-invoke.m b/runtime/trampolines-invoke.m index a7865783eb59..ac97803a5408 100644 --- a/runtime/trampolines-invoke.m +++ b/runtime/trampolines-invoke.m @@ -21,6 +21,8 @@ void xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_func iterator, marshal_return_value_func marshal_return_value, void *context) { + MonoObject *exception = NULL; + MonoObject **exception_ptr = xamarin_marshal_managed_exception_mode == MarshalManagedExceptionModeDisable ? NULL : &exception; bool is_static = (type & Tramp_Static) == Tramp_Static; bool is_ctor = type == Tramp_Ctor; @@ -353,7 +355,7 @@ xamarin_set_nsobject_handle (retval, self); xamarin_set_nsobject_flags (retval, NSObjectFlagsNativeRef); - mono_runtime_invoke (method, retval, (void **) arg_ptrs, NULL); + mono_runtime_invoke (method, retval, (void **) arg_ptrs, exception_ptr); xamarin_create_managed_ref (self, retval, true); xamarin_register_nsobject (retval, self); @@ -366,7 +368,7 @@ fprintf (stderr, "\n"); #endif - retval = mono_runtime_invoke (method, mthis, (void **) arg_ptrs, NULL); + retval = mono_runtime_invoke (method, mthis, (void **) arg_ptrs, exception_ptr); #ifdef TRACE fprintf (stderr, " called managed method with %i arguments: ", num_arg); @@ -451,4 +453,6 @@ } else if (*ret_type != 'v') { marshal_return_value (context, ret_type, [sig methodReturnLength], retval, mono_signature_get_return_type (msig), (desc.semantic & ArgumentSemanticRetainReturnValue) != 0, method); } -} \ No newline at end of file + + xamarin_process_managed_exception (exception); +} diff --git a/runtime/trampolines-x86_64-objc_msgSend-copyframe.inc b/runtime/trampolines-x86_64-objc_msgSend-copyframe.inc new file mode 100644 index 000000000000..10d36eeff642 --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSend-copyframe.inc @@ -0,0 +1,46 @@ + + call _xamarin_get_frame_length + + # r12 holds the amount of stack space we need + movq %rax, %r12 + # first align stack requirement to 16 bytes + addq $15,%r12 + shrq $4,%r12 + salq $4,%r12 + # then make space for the arguments + subq %r12, %rsp + + # copy arguments from old location in the stack to new location in the stack + # %rcx will hold the amount of bytes left to copy + # %rsi the current src location + # %rdi the current dst location + + movq %r12, %rcx # rcx = frame_length + leaq 16(%rbp),%rsi # rsi = address of first argument we got + movq %rsp,%rdi # rdi = address of the bottom of the stack + +L_start: + cmpq $0,%rcx # + je L_end # while (left != 0) { + subq $8,%rcx # len -= 8 + movq (%rsi,%rcx),%rax # tmp = src [len] + movq %rax,(%rdi,%rcx) # dst [len] = tmp + jmp L_start # } +L_end: + + movq -16(%rbp), %rdi + movq -24(%rbp), %rsi + movq -32(%rbp), %rdx + movq -40(%rbp), %rcx + movq -48(%rbp), %r8 + movq -56(%rbp), %r9 + movq -64(%rbp), %rax + /* skip 8 bytes so that this is 16-byte aligned */ + movaps -80(%rbp), %xmm0 + movaps -96(%rbp), %xmm1 + movaps -112(%rbp), %xmm2 + movaps -128(%rbp), %xmm3 + movaps -144(%rbp), %xmm4 + movaps -160(%rbp), %xmm5 + movaps -176(%rbp), %xmm6 + movaps -192(%rbp), %xmm7 diff --git a/runtime/trampolines-x86_64-objc_msgSend-post.inc b/runtime/trampolines-x86_64-objc_msgSend-post.inc new file mode 100644 index 000000000000..ebc7d2c1071b --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSend-post.inc @@ -0,0 +1,73 @@ + +Lafterinvoke: + addq $208, %rsp + addq %r12, %rsp + movq -8(%rbp), %r12 + popq %rbp + retq +Lcatchhandler: + cmpl $1, %edx + movq %rax, %rdi + jne Lnomatchexception + + # check if xamarin_marshal_objectivec_exception_mode == disable, if so, just don't handle the exception + leal _xamarin_marshal_objectivec_exception_mode(%rip), %edx + cmpl $4, %edx + je Lnomatchexception + + callq _objc_begin_catch +Lcatchbegin: + movq %rax, %rdi + callq _xamarin_process_nsexception +Lcatchend: + callq _objc_end_catch + jmp Lafterinvoke +Lcatchcatchhandler: + callq _objc_end_catch +Lnomatchexception: + callq __Unwind_Resume +Lfunc_end2: + .cfi_endproc + +# exception table + .section __TEXT,__gcc_except_tab + .align 2 +GCC_except_table2: +Lexception2: + .byte 255 ## @LPStart Encoding = omit + .byte 155 ## @TType Encoding = indirect pcrel sdata4 + .asciz "\274" ## @TType base offset + .byte 3 ## Call site Encoding = udata4 + .byte 52 ## Call site table length +Lset0 = Lbeforeinvoke-Lfunc_begin2 ## >> Call Site 1 << + .long Lset0 +Lset1 = Lafterinvoke-Lbeforeinvoke ## Call between Lbeforeinvoke and Lafterinvoke + .long Lset1 +Lset2 = Lcatchhandler-Lfunc_begin2 ## jumps to Lcatchhandler + .long Lset2 + .byte 1 ## On action: 1 +Lset3 = Lafterinvoke-Lfunc_begin2 ## >> Call Site 2 << + .long Lset3 +Lset4 = Lcatchbegin-Lafterinvoke ## Call between Lafterinvoke and Lcatchbegin + .long Lset4 + .long 0 ## has no landing pad + .byte 0 ## On action: cleanup +Lset5 = Lcatchbegin-Lfunc_begin2 ## >> Call Site 3 << + .long Lset5 +Lset6 = Lcatchend-Lcatchbegin ## Call between Lcatchbegin and Lcatchend + .long Lset6 +Lset7 = Lcatchcatchhandler-Lfunc_begin2 ## jumps to Lcatchcatchhandler + .long Lset7 + .byte 0 ## On action: cleanup +Lset8 = Lcatchend-Lfunc_begin2 ## >> Call Site 4 << + .long Lset8 +Lset9 = Lfunc_end2-Lcatchend ## Call between Lcatchend and Lfunc_end2 + .long Lset9 + .long 0 ## has no landing pad + .byte 0 ## On action: cleanup + .byte 1 ## >> Action Record 1 << + ## Catch TypeInfo 1 + .byte 0 ## No further actions + ## >> Catch TypeInfos << + .long _OBJC_EHTYPE_$_NSException@GOTPCREL+4 ## TypeInfo 1 + .align 2 diff --git a/runtime/trampolines-x86_64-objc_msgSend-pre.inc b/runtime/trampolines-x86_64-objc_msgSend-pre.inc new file mode 100644 index 000000000000..ee324a25fedb --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSend-pre.inc @@ -0,0 +1,34 @@ + +# function start +# preserve some registers + +Lfunc_begin2: +.cfi_startproc +.cfi_personality 155, ___objc_personality_v0 +.cfi_lsda 16, Lexception2 + + pushq %rbp +.cfi_def_cfa_offset 16 +.cfi_offset %rbp, -16 + movq %rsp, %rbp +.cfi_def_cfa_register %rbp + + # calculate the amount of stack space we need + subq $208,%rsp + movq %r12, -8(%rbp) + movq %rdi, -16(%rbp) + movq %rsi, -24(%rbp) + movq %rdx, -32(%rbp) + movq %rcx, -40(%rbp) + movq %r8, -48(%rbp) + movq %r9, -56(%rbp) + movq %rax, -64(%rbp) + /* skip 8 bytes so that this is 16-byte aligned */ + movaps %xmm0, -80(%rbp) + movaps %xmm1, -96(%rbp) + movaps %xmm2, -112(%rbp) + movaps %xmm3, -128(%rbp) + movaps %xmm4, -144(%rbp) + movaps %xmm5, -160(%rbp) + movaps %xmm6, -176(%rbp) + movaps %xmm7, -192(%rbp) diff --git a/runtime/trampolines-x86_64-objc_msgSend.s b/runtime/trampolines-x86_64-objc_msgSend.s new file mode 100644 index 000000000000..3f7d3a71252e --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSend.s @@ -0,0 +1,19 @@ +#if __x86_64__ + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSend +_xamarin_dyn_objc_msgSend: + +#include "trampolines-x86_64-objc_msgSend-pre.inc" + +#include "trampolines-x86_64-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: +call _objc_msgSend + +#include "trampolines-x86_64-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-x86_64-objc_msgSendSuper.s b/runtime/trampolines-x86_64-objc_msgSendSuper.s new file mode 100644 index 000000000000..7eeddaf2a3d9 --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSendSuper.s @@ -0,0 +1,22 @@ +#if __x86_64__ + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSendSuper +_xamarin_dyn_objc_msgSendSuper: + +#include "trampolines-x86_64-objc_msgSend-pre.inc" + + # resolve objc_super* [handle, class_handle] to the actual instance + movq (%rdi), %rdi + +#include "trampolines-x86_64-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: +call _objc_msgSendSuper + +#include "trampolines-x86_64-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-x86_64-objc_msgSendSuper_stret.s b/runtime/trampolines-x86_64-objc_msgSendSuper_stret.s new file mode 100644 index 000000000000..67122004aa4c --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSendSuper_stret.s @@ -0,0 +1,23 @@ +#if __x86_64__ + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSendSuper_stret +_xamarin_dyn_objc_msgSendSuper_stret: + +#include "trampolines-x86_64-objc_msgSend-pre.inc" + + # put (id, SEL) where xamarin_get_frame_length expects it + movq (%rsi), %rdi + movq %rdx, %rsi + +#include "trampolines-x86_64-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: +call _objc_msgSendSuper_stret + +#include "trampolines-x86_64-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines-x86_64-objc_msgSend_stret.s b/runtime/trampolines-x86_64-objc_msgSend_stret.s new file mode 100644 index 000000000000..411add4773ad --- /dev/null +++ b/runtime/trampolines-x86_64-objc_msgSend_stret.s @@ -0,0 +1,23 @@ +#if __x86_64__ + +.subsections_via_symbols +.text + +.align 4 +.globl _xamarin_dyn_objc_msgSend_stret +_xamarin_dyn_objc_msgSend_stret: + +#include "trampolines-x86_64-objc_msgSend-pre.inc" + + # put (id, SEL) where xamarin_get_frame_length expects it + movq %rsi, %rdi + movq %rdx, %rsi + +#include "trampolines-x86_64-objc_msgSend-copyframe.inc" + +Lbeforeinvoke: +call _objc_msgSend_stret + +#include "trampolines-x86_64-objc_msgSend-post.inc" + +#endif diff --git a/runtime/trampolines.m b/runtime/trampolines.m index 7380e744ac6f..58e0042f3612 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -123,7 +123,6 @@ } } -#if defined (__i386__) int xamarin_get_frame_length (id self, SEL sel) { @@ -131,7 +130,6 @@ return [sig frameLength]; } -#endif static inline void find_objc_method_implementation (struct objc_super *sup, id self, SEL sel, IMP xamarin_impl) diff --git a/runtime/xamarin/runtime.h b/runtime/xamarin/runtime.h index aa15dfb9af2d..208d2de5f7b7 100644 --- a/runtime/xamarin/runtime.h +++ b/runtime/xamarin/runtime.h @@ -144,6 +144,8 @@ void xamarin_set_gc_pump_enabled (bool value); typedef void (*XamarinUnhandledExceptionFunc) (MonoObject *exc, const char *type_name, const char *message, const char *trace); void xamarin_install_unhandled_exception_hook (XamarinUnhandledExceptionFunc func); +void xamarin_process_nsexception (NSException *exc); +void xamarin_process_managed_exception (MonoObject *exc); id xamarin_invoke_objc_method_implementation (id self, SEL sel, IMP xamarin_impl); diff --git a/src/ObjCRuntime/Exceptions.cs b/src/ObjCRuntime/Exceptions.cs new file mode 100644 index 000000000000..6125e4548d94 --- /dev/null +++ b/src/ObjCRuntime/Exceptions.cs @@ -0,0 +1,26 @@ +// +// Exceptions.cs: +// +// Authors: +// Rolf Bjarne Kvinge +// +// Copyright 2016 Xamarin Inc. + +using System; +using XamCore.Foundation; + +namespace XamCore.ObjCRuntime { + public delegate void MarshalObjectiveCExceptionHandler (object sender, MarshalObjectiveCExceptionEventArgs args); + + public class MarshalObjectiveCExceptionEventArgs { + public NSException Exception { get; set; } + public MarshalObjectiveCExceptionMode ExceptionMode { get; set; } + } + + public delegate void MarshalManagedExceptionHandler (object sender, MarshalManagedExceptionEventArgs args); + + public class MarshalManagedExceptionEventArgs { + public Exception Exception { get; set; } + public MarshalManagedExceptionMode ExceptionMode { get; set; } + } +} diff --git a/src/ObjCRuntime/Runtime.cs b/src/ObjCRuntime/Runtime.cs index eef00c57030b..a35408cc0982 100644 --- a/src/ObjCRuntime/Runtime.cs +++ b/src/ObjCRuntime/Runtime.cs @@ -110,6 +110,8 @@ internal unsafe struct InitializationOptions { public Delegates Delegates; public Trampolines Trampolines; public RegistrationData *RegistrationData; + public MarshalObjectiveCExceptionMode MarshalObjectiveCExceptionMode; + public MarshalManagedExceptionMode MarshalManagedExceptionMode; public bool UseOldDynamicRegistrar { get { @@ -209,6 +211,9 @@ unsafe static void Initialize (ref InitializationOptions options) UseAutoreleasePoolInThreadPool = true; #endif + objc_exception_mode = options.MarshalObjectiveCExceptionMode; + managed_exception_mode = options.MarshalManagedExceptionMode; + initialized = true; #if PROFILE Console.WriteLine ("Runtime.Initialize completed in {0} ms", watch.ElapsedMilliseconds); @@ -234,6 +239,43 @@ static bool ThreadPoolDispatcher (Func callback) } #endif + static MarshalObjectiveCExceptionMode objc_exception_mode; + static MarshalManagedExceptionMode managed_exception_mode; + + public static event MarshalObjectiveCExceptionHandler MarshalObjectiveCException; + public static event MarshalManagedExceptionHandler MarshalManagedException; + + static MarshalObjectiveCExceptionMode OnMarshalObjectiveCException (IntPtr exception_handle) + { + if (MarshalObjectiveCException != null) { + var exception = GetNSObject (exception_handle); + var args = new MarshalObjectiveCExceptionEventArgs () + { + Exception = exception, + ExceptionMode = objc_exception_mode, + }; + + MarshalObjectiveCException (null, args); + return args.ExceptionMode; + } + return objc_exception_mode; + } + + static MarshalManagedExceptionMode OnMarshalManagedException (int exception_handle) + { + if (MarshalManagedException != null) { + var exception = GCHandle.FromIntPtr (new IntPtr (exception_handle)).Target as Exception; + var args = new MarshalManagedExceptionEventArgs () + { + Exception = exception, + ExceptionMode = managed_exception_mode, + }; + MarshalManagedException (null, args); + return args.ExceptionMode; + } + return managed_exception_mode; + } + #if !COREBUILD && (XAMARIN_APPLETLS || XAMARIN_NO_TLS) // This method is rewritten by the linker in CoreTlsProviderStep. static MonoTlsProvider TlsProviderFactoryCallback () @@ -273,6 +315,32 @@ static void ThrowNSException (IntPtr ns_exception) #endif } + static int CreateNSException (IntPtr ns_exception) + { + Exception ex; +#if MONOMAC + ex = new ObjCException (Runtime.GetNSObject (ns_exception)); +#else + ex = new MonoTouchException (Runtime.GetNSObject (ns_exception)); +#endif + return GCHandle.ToIntPtr (GCHandle.Alloc (ex)).ToInt32 (); + } + + static IntPtr UnwrapNSException (int exc_handle) + { + var obj = GCHandle.FromIntPtr (new IntPtr (exc_handle)).Target; +#if MONOMAC + var exc = obj as ObjCException; +#else + var exc = obj as MonoTouchException; +#endif + if (exc != null) { + return exc.NSException.DangerousRetain ().DangerousAutorelease ().Handle; + } else { + return IntPtr.Zero; + } + } + static IntPtr GetBlockWrapperCreator (IntPtr method, int parameter) { return ObjectWrapper.Convert (GetBlockWrapperCreator ((MethodInfo) ObjectWrapper.Convert (method), parameter)); diff --git a/src/frameworks.sources b/src/frameworks.sources index 08850f9fe936..83a2e59b4575 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -1393,6 +1393,7 @@ SHARED_SOURCES = \ ObjCRuntime/Dlfcn.cs \ ObjCRuntime/DynamicRegistrar.cs \ ObjCRuntime/ErrorHelper.cs \ + ObjCRuntime/Exceptions.cs \ ObjCRuntime/ExceptionMode.cs \ ObjCRuntime/IDynamicRegistrar.cs \ ObjCRuntime/Method.cs \ diff --git a/tests/bindings-test/ApiDefinition.cs b/tests/bindings-test/ApiDefinition.cs index 30784ff83544..eac8f11564dd 100644 --- a/tests/bindings-test/ApiDefinition.cs +++ b/tests/bindings-test/ApiDefinition.cs @@ -202,6 +202,25 @@ interface ObjCRegistrarTest { [Export ("testBlocks")] bool TestBlocks (); + + } + + [BaseType (typeof (NSObject))] + interface ObjCExceptionTest { + [Export ("throwObjCException")] + void ThrowObjCException (); + + [Export ("throwManagedException")] + void ThrowManagedException (); + + [Export ("invokeManagedExceptionThrower")] + void InvokeManagedExceptionThrower (); + + [Export ("invokeManagedExceptionThrowerAndRethrow")] + void InvokeManagedExceptionThrowerAndRethrow (); + + [Export ("invokeManagedExceptionThrowerAndCatch")] + void InvokeManagedExceptionThrowerAndCatch (); } [BaseType (typeof (NSObject))] diff --git a/tests/test-libraries/libtest.h b/tests/test-libraries/libtest.h index 746506bdb7fb..a9c34cdabc29 100644 --- a/tests/test-libraries/libtest.h +++ b/tests/test-libraries/libtest.h @@ -111,6 +111,18 @@ typedef unsigned int (^RegistrarTestBlock) (unsigned int magic); -(bool) testBlocks; @end +/* + * ObjC test class used for exception tests. + */ +@interface ObjCExceptionTest : NSObject { +} + -(void) throwObjCException; + -(void) throwManagedException; + -(void) invokeManagedExceptionThrower; + -(void) invokeManagedExceptionThrowerAndRethrow; + -(void) invokeManagedExceptionThrowerAndCatch; +@end + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tests/test-libraries/libtest.m b/tests/test-libraries/libtest.m index 14fd264d52b7..7c85a121602c 100644 --- a/tests/test-libraries/libtest.m +++ b/tests/test-libraries/libtest.m @@ -187,6 +187,42 @@ -(bool) testBlocks } @end +@implementation ObjCExceptionTest +{ +} + -(void) throwObjCException + { + [NSException raise:@"Some exception" format:@"exception was thrown"]; + } + + -(void) throwManagedException + { + abort (); // this method should be overridden in managed code. + } + + -(void) invokeManagedExceptionThrower + { + [self throwManagedException]; + } + + -(void) invokeManagedExceptionThrowerAndRethrow + { + @try { + [self throwManagedException]; + } @catch (id exc) { + [NSException raise:@"Caught exception" format:@"exception was rethrown"]; + } + } + -(void) invokeManagedExceptionThrowerAndCatch + { + @try { + [self throwManagedException]; + } @catch (id exc) { + // do nothing + } + } +@end + @interface CtorChaining1 : NSObject @property BOOL initCalled; @property BOOL initCallsInitCalled; diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 06faad4b0325..fc5f62b50248 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -49,6 +49,19 @@ public partial class Application public MarshalObjectiveCExceptionMode MarshalObjectiveCExceptions; public MarshalManagedExceptionMode MarshalManagedExceptions; + public bool RequiresPInvokeWrappers { + get { +#if MTOUCH + if (IsSimulatorBuild) + return false; +#else + if (Driver.Is64Bit) + return false; +#endif + return MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.ThrowManagedException || MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.Abort; + } + } + public string PlatformName { get { switch (Platform) { diff --git a/tools/common/PInvokeWrapperGenerator.cs b/tools/common/PInvokeWrapperGenerator.cs new file mode 100644 index 000000000000..800f5ceebb4e --- /dev/null +++ b/tools/common/PInvokeWrapperGenerator.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using Mono.Cecil; + +using Xamarin.Bundler; +using XamCore.Registrar; + +namespace Xamarin.Bundler +{ + class PInvokeWrapperGenerator + { + public Dictionary signatures = new Dictionary (); + public List exceptions = new List (); + public StringBuilder signature = new StringBuilder (); + public HashSet names = new HashSet (); + + public AutoIndentStringBuilder sb = new AutoIndentStringBuilder (); + AutoIndentStringBuilder hdr = new AutoIndentStringBuilder (); + AutoIndentStringBuilder decls = new AutoIndentStringBuilder (); + AutoIndentStringBuilder mthds = new AutoIndentStringBuilder (); + + public StaticRegistrar Registrar; + public string HeaderPath; + public string SourcePath; + + bool first; + + public void Start () + { + if (Driver.EnableDebug) + hdr.WriteLine ("#define DEBUG 1"); + + hdr.WriteLine ("#include "); + hdr.WriteLine ("#include "); + hdr.WriteLine ("#include "); + hdr.WriteLine ("#include "); + hdr.WriteLine ("#include "); + + Registrar.GeneratePInvokeWrappersStart (hdr, decls, mthds); + + mthds.WriteLine ($"#include \"{Path.GetFileName (HeaderPath)}\""); + + sb.WriteLine ("extern \"C\" {"); + } + + public void End () + { + sb.WriteLine ("}"); + + Registrar.GeneratePInvokeWrappersEnd (); + + Driver.WriteIfDifferent (HeaderPath, hdr.ToString () + "\n" + decls.ToString () + "\n"); + Driver.WriteIfDifferent (SourcePath, mthds.ToString () + "\n" + sb.ToString () + "\n"); + } + + public void ProcessMethod (MethodDefinition method) + { + if (!first) { + Start (); + first = true; + } + + Registrar.GeneratePInvokeWrapper (this, method); + } + } +} + diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 9f32e91d0b5b..13a8202853a6 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -3054,10 +3054,16 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List invoke.AppendLine ("xamarin_set_nsobject_flags (mthis, flags);"); } + var marshal_exception = "NULL"; + if (App.MarshalManagedExceptions != MarshalManagedExceptionMode.Disable) { + invoke.AppendLine ("MonoObject *exception = NULL;"); + marshal_exception = "&exception"; + } + if (!isVoid) invoke.AppendFormat ("{0} retval = ", "MonoObject *"); - invoke.AppendLine ("mono_runtime_invoke (managed_method, {0}, arg_ptrs, NULL);", isStatic ? "NULL" : "mthis"); + invoke.AppendLine ("mono_runtime_invoke (managed_method, {0}, arg_ptrs, {1});", isStatic ? "NULL" : "mthis", marshal_exception); if (isCtor) invoke.AppendLine ("xamarin_create_managed_ref (self, mthis, true);"); @@ -3252,6 +3258,9 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List if (trace ) body.AppendLine (nslog_end); + if (App.MarshalManagedExceptions != MarshalManagedExceptionMode.Disable) + body.WriteLine ("xamarin_process_managed_exception (exception);"); + if (isCtor) { body.WriteLine ("return self;"); } else if (isVoid) { @@ -3370,6 +3379,132 @@ public override bool Equals (object obj) } } + public void GeneratePInvokeWrappersStart (AutoIndentStringBuilder hdr, AutoIndentStringBuilder decls, AutoIndentStringBuilder mthds) + { + header = hdr; + declarations = decls; + methods = mthds; + } + + public void GeneratePInvokeWrappersEnd () + { + header = null; + declarations = null; + methods = null; + namespaces.Clear (); + structures.Clear (); + + FlushTrace (); + } + + public void GeneratePInvokeWrapper (PInvokeWrapperGenerator state, MethodDefinition method) + { + var signatures = state.signatures; + var exceptions = state.exceptions; + var signature = state.signature; + var names = state.names; + var sb = state.sb; + var pinfo = method.PInvokeInfo; + var is_stret = pinfo.EntryPoint.EndsWith ("_stret"); + var isVoid = method.ReturnType.FullName == "System.Void"; + var descriptiveMethodName = method.DeclaringType.FullName + "." + method.Name; + + signature.Clear (); + + string native_return_type; + int first_parameter = 0; + + if (is_stret) { + native_return_type = ToObjCParameterType (method.Parameters [0].ParameterType.GetElementType (), descriptiveMethodName, exceptions, method); + first_parameter = 1; + } else { + native_return_type = ToObjCParameterType (method.ReturnType, descriptiveMethodName, exceptions, method); + } + + signature.Append (native_return_type); + signature.Append (" "); + signature.Append (pinfo.EntryPoint); + signature.Append (" ("); + for (int i = 0; i < method.Parameters.Count; i++) { + if (i > 0) + signature.Append (", "); + signature.Append (ToObjCParameterType (method.Parameters[i].ParameterType, descriptiveMethodName, exceptions, method)); + } + signature.Append (")"); + + string wrapperName; + if (!signatures.TryGetValue (signature.ToString (), out wrapperName)) { + var name = "xamarin_pinvoke_wrapper_" + method.Name; + var counter = 0; + while (names.Contains (name)) { + name = "xamarin_pinvoke_wrapper_" + method.Name + (++counter).ToString (); + } + names.Add (name); + signatures [signature.ToString ()] = wrapperName = name; + + sb.WriteLine ("// EntryPoint: {0}", pinfo.EntryPoint); + sb.WriteLine ("// Managed method: {0}.{1}", method.DeclaringType.FullName, method.Name); + sb.WriteLine ("// Signature: {0}", signature.ToString ()); + + sb.Write ("typedef "); + sb.Write (native_return_type); + sb.Write ("(*func_"); + sb.Write (name); + sb.Write (") ("); + for (int i = first_parameter; i < method.Parameters.Count; i++) { + if (i > first_parameter) + sb.Write (", "); + sb.Write (ToObjCParameterType (method.Parameters[i].ParameterType, descriptiveMethodName, exceptions, method)); + sb.Write (" "); + sb.Write (method.Parameters[i].Name); + } + sb.WriteLine (");"); + + sb.WriteLine (native_return_type); + sb.Write (name); + sb.Write (" (", method.Name); + for (int i = first_parameter; i < method.Parameters.Count; i++) { + if (i > first_parameter) + sb.Write (", "); + sb.Write (ToObjCParameterType (method.Parameters[i].ParameterType, descriptiveMethodName, exceptions, method)); + sb.Write (" "); + sb.Write (method.Parameters [i].Name); + } + sb.WriteLine (")"); + sb.WriteLine ("{"); + sb.WriteLine ("@try {"); + if (!isVoid || is_stret) + sb.Write ("return "); + sb.Write ("((func_{0}) {1}) (", name, pinfo.EntryPoint); + for (int i = first_parameter; i < method.Parameters.Count; i++) { + if (i > first_parameter) + sb.Write (", "); + sb.Write (method.Parameters [i].Name); + } + sb.WriteLine (");"); + sb.WriteLine ("} @catch (NSException *exc) {"); + sb.WriteLine ("xamarin_process_nsexception (exc);"); + sb.WriteLine ("}"); + sb.WriteLine ("}"); + sb.WriteLine (); + } else { + Console.WriteLine ("Signature already processed: {0} for {1}.{2}", signature.ToString (), method.DeclaringType.FullName, method.Name); + } + + // find the module reference to __Internal + ModuleReference mr = null; + foreach (var mref in method.Module.ModuleReferences) { + if (mref.Name == "__Internal") { + mr = mref; + break; + } + } + if (mr == null) + method.Module.ModuleReferences.Add (mr = new ModuleReference ("__Internal")); + pinfo.Module = mr; + pinfo.EntryPoint = wrapperName; + } + public void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string assembly) { single_assembly = assembly; diff --git a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs index f1e780198a30..bcec3b380847 100644 --- a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs +++ b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs @@ -5,6 +5,8 @@ using Mono.Linker; using Mono.Linker.Steps; using Mono.Tuner; + +using Xamarin.Bundler; using Xamarin.Linker; #if MONOMAC @@ -17,6 +19,13 @@ namespace MonoTouch.Tuner { public class ListExportedSymbols : BaseStep { + PInvokeWrapperGenerator state; + + internal ListExportedSymbols (PInvokeWrapperGenerator state) + { + this.state = state; + } + protected override void ProcessAssembly (AssemblyDefinition assembly) { base.ProcessAssembly (assembly); @@ -55,8 +64,25 @@ void ProcessType (TypeDefinition type) void ProcessMethod (MethodDefinition method) { - if (method.IsPInvokeImpl && method.HasPInvokeInfo && method.PInvokeInfo.Module.Name == "__Internal") - ((DerivedLinkContext) Context).RequiredSymbols[method.PInvokeInfo.EntryPoint] = method; + if (method.IsPInvokeImpl && method.HasPInvokeInfo) { + var pinfo = method.PInvokeInfo; + if (pinfo.Module.Name == "__Internal") + ((DerivedLinkContext) Context).RequiredSymbols [pinfo.EntryPoint] = method; + + if (state != null) { + switch (pinfo.EntryPoint) { + case "objc_msgSend": + case "objc_msgSendSuper": + case "objc_msgSend_stret": + case "objc_msgSendSuper_stret": + case "objc_msgSend_fpret": + state.ProcessMethod (method); + break; + default: + return; + } + } + } if (MarkStep.IsPropertyMethod (method)) { var property = MarkStep.GetProperty (method); diff --git a/tools/mmp/Makefile b/tools/mmp/Makefile index bcc577be9b0d..fd5157bacb1d 100644 --- a/tools/mmp/Makefile +++ b/tools/mmp/Makefile @@ -102,6 +102,7 @@ mmp_sources = \ resolver.cs \ $(MONO_PATH)/mcs/class/Mono.Options/Mono.Options/Options.cs \ Tuning.cs \ + $(TOP)/tools/common/PInvokeWrapperGenerator.cs \ $(TOP)/tools/common/TargetFramework.cs \ $(TOP)/tools/common/Driver.cs \ $(TOP)/tools/common/Frameworks.cs \ diff --git a/tools/mmp/Tuning.cs b/tools/mmp/Tuning.cs index 80164970916c..4e284e92a757 100644 --- a/tools/mmp/Tuning.cs +++ b/tools/mmp/Tuning.cs @@ -30,6 +30,7 @@ public class LinkerOptions { public IList ExtraDefinitions { get; set; } public TargetFramework TargetFramework { get; set; } public string Architecture { get; set; } + internal PInvokeWrapperGenerator MarshalNativeExceptionsState { get; set; } internal RuntimeOptions RuntimeOptions { get; set; } public static I18nAssemblies ParseI18nAssemblies (string i18n) @@ -56,6 +57,7 @@ public class MonoMacLinkContext : LinkContext { Dictionary> pinvokes = new Dictionary> (); public Dictionary RequiredSymbols = new Dictionary (); + List marshal_exception_pinvokes; public MonoMacLinkContext (Pipeline pipeline, AssemblyResolver resolver) : base (pipeline, resolver) { @@ -64,6 +66,14 @@ public MonoMacLinkContext (Pipeline pipeline, AssemblyResolver resolver) : base public IDictionary> PInvokeModules { get { return pinvokes; } } + + public List MarshalExceptionPInvokes { + get { + if (marshal_exception_pinvokes == null) + marshal_exception_pinvokes = new List (); + return marshal_exception_pinvokes; + } + } } class Linker { @@ -168,7 +178,7 @@ static Pipeline CreatePipeline (LinkerOptions options) pipeline.AppendStep (new RegenerateGuidStep ()); } - pipeline.AppendStep (new ListExportedSymbols ()); + pipeline.AppendStep (new ListExportedSymbols (options.MarshalNativeExceptionsState)); pipeline.AppendStep (new OutputStep ()); diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs index 8b4a7d724b13..859b02ae9a7a 100644 --- a/tools/mmp/driver.cs +++ b/tools/mmp/driver.cs @@ -80,6 +80,7 @@ public static partial class Driver { static bool? profiling = false; static bool? thread_check = null; static string link_flags = null; + static LinkerOptions linker_options; static bool arch_set = false; static string arch = "i386"; @@ -604,6 +605,12 @@ static void Pack (IList unprocessed) Watch (string.Format ("Linking (mode: '{0}')", App.LinkMode), 1); } + if (App.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Disable && !App.RequiresPInvokeWrappers) { + internalSymbols.Add ("xamarin_dyn_objc_msgSend"); + internalSymbols.Add ("xamarin_dyn_objc_msgSendSuper"); + internalSymbols.Add ("xamarin_dyn_objc_msgSend_stret"); + internalSymbols.Add ("xamarin_dyn_objc_msgSendSuper_stret"); + } CopyDependencies (native_libs); Watch ("Copy Dependencies", 1); @@ -1062,6 +1069,12 @@ static int Compile (IEnumerable internalSymbols) if (!string.IsNullOrEmpty (DeveloperDirectory)) args.Append ("-isysroot ").Append (Quote (Path.Combine (DeveloperDirectory, "Platforms", "MacOSX.platform", "Developer", "SDKs", "MacOSX" + sdk_version + ".sdk"))).Append (' '); + if (App.RequiresPInvokeWrappers) { + var state = linker_options.MarshalNativeExceptionsState; + state.End (); + args.Append (Quote (state.SourcePath)).Append (' '); + } + var main = Path.Combine (Cache.Location, "main.m"); File.WriteAllText (main, mainSource); args.Append (Quote (main)); @@ -1153,8 +1166,16 @@ static IDictionary> Link () // by default we keep the code to ensure we're executing on the UI thread (for UI code) for debug builds // but this can be overridden to either (a) remove it from debug builds or (b) keep it in release builds EnsureUIThread = thread_check.HasValue ? thread_check.Value : App.EnableDebug, + MarshalNativeExceptionsState = !App.RequiresPInvokeWrappers ? null : new PInvokeWrapperGenerator () + { + SourcePath = Path.Combine (Cache.Location, "pinvokes.m"), + HeaderPath = Path.Combine (Cache.Location, "pinvokes.h"), + Registrar = (StaticRegistrar) BuildTarget.StaticRegistrar, + }, }; + linker_options = options; + Mono.Linker.LinkContext context; MonoMac.Tuner.Linker.Process (options, out context, out resolved_assemblies); BuildTarget.LinkContext = (context as MonoMacLinkContext); diff --git a/tools/mmp/mmp.csproj b/tools/mmp/mmp.csproj index 40bb50d24275..6cbce1125e19 100644 --- a/tools/mmp/mmp.csproj +++ b/tools/mmp/mmp.csproj @@ -317,6 +317,9 @@ external\PlatformAvailability2.cs + + external\PInvokeWrapperGenerator.cs + diff --git a/tools/mtouch/Application.cs b/tools/mtouch/Application.cs index 6a0692606465..71e8ae48a81b 100644 --- a/tools/mtouch/Application.cs +++ b/tools/mtouch/Application.cs @@ -1706,7 +1706,7 @@ public static void Create (List tasks, IEnumerable abis, Target public static void Create (List tasks, Abi abi, Target target, string ifile) { var arch = abi.AsArchString (); - var ofile = Path.Combine (Cache.Location, "registrar." + arch + ".o"); + var ofile = Path.Combine (Cache.Location, Path.GetFileNameWithoutExtension (ifile) + "." + arch + ".o"); if (!Application.IsUptodate (ifile, ofile)) { tasks.Add (new RegistrarTask () diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index 2fd374b41087..29d9094e7d94 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -110,6 +110,7 @@ MTOUCH_SOURCES = \ $(TOP)/tools/common/MachO.cs \ mtouch.cs \ OldStaticRegistrar.cs \ + $(TOP)/tools/common/PInvokeWrapperGenerator.cs \ $(TOP)/tools/common/TargetFramework.cs \ $(TOP)/tools/common/StaticRegistrar.cs \ $(TOP)/src/ObjCRuntime/Registrar.core.cs \ diff --git a/tools/mtouch/Target.cs b/tools/mtouch/Target.cs index ef6d6dd1a673..235cfde02f3c 100644 --- a/tools/mtouch/Target.cs +++ b/tools/mtouch/Target.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text; using System.Threading.Tasks; +using System.Xml; using MonoTouch.Tuner; @@ -163,19 +164,29 @@ public IEnumerable GetRequiredSymbols () foreach (var ep in File.ReadAllLines (cache_location)) entry_points.Add (ep, null); } else { + List marshal_exception_pinvokes; 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. entry_points = new Dictionary (); + marshal_exception_pinvokes = new List (); } else { entry_points = LinkContext.RequiredSymbols; + marshal_exception_pinvokes = LinkContext.MarshalExceptionPInvokes; } - + // keep the debugging helper in debugging binaries only if (App.EnableDebug && !App.EnableBitCode) entry_points.Add ("mono_pmip", null); + if (App.IsSimulatorBuild) { + entry_points.Add ("xamarin_dyn_objc_msgSend", null); + entry_points.Add ("xamarin_dyn_objc_msgSendSuper", null); + entry_points.Add ("xamarin_dyn_objc_msgSend_stret", null); + entry_points.Add ("xamarin_dyn_objc_msgSendSuper_stret", null); + } + File.WriteAllText (cache_location, string.Join ("\n", entry_points.Keys.ToArray ())); } return entry_points.Keys; @@ -376,7 +387,13 @@ public void LinkAssemblies (string main, ref List assemblies, string out IsDualBuild = App.IsDualBuild, Unified = App.IsUnified, DumpDependencies = App.LinkerDumpDependencies, - RuntimeOptions = App.RuntimeOptions + RuntimeOptions = App.RuntimeOptions, + MarshalNativeExceptionsState = !App.RequiresPInvokeWrappers ? null : new PInvokeWrapperGenerator () + { + SourcePath = Path.Combine (ArchDirectory, "pinvokes.m"), + HeaderPath = Path.Combine (ArchDirectory, "pinvokes.h"), + Registrar = (StaticRegistrar) StaticRegistrar, + }, }; MonoTouch.Tuner.Linker.Process (LinkerOptions, out link_context, out assemblies); @@ -520,6 +537,8 @@ public void ProcessAssemblies () // * Linking // Copy assemblies to LinkDirectory // Link and save to PreBuildDirectory + // If marshalling native exceptions: + // * Generate/calculate P/Invoke wrappers and save to PreBuildDirectory // * Has resourced to be removed: // Remove resource and save to NoResDirectory // Copy to BuildDirectory. [Why not save directly to BuildDirectory? Because otherwise if we're rebuilding @@ -532,7 +551,10 @@ public void ProcessAssemblies () // Strip managed code save to TargetDirectory (or just copy the file if stripping is disabled). // // * No linking - // Copy assembly to PreBuildDirectory. + // If marshalling native exceptions: + // Generate/calculate P/Invoke wrappers and save to PreBuildDirectory. + // If not marshalling native exceptions: + // Copy assemblies to PreBuildDirectory // * Has resourced to be removed: // Remove resource and save to NoResDirectory // Copy to BuildDirectory. @@ -592,6 +614,13 @@ public void ProcessAssemblies () ManagedLink (); + if (App.RequiresPInvokeWrappers) { + // Write P/Invokes + var state = LinkerOptions.MarshalNativeExceptionsState; + state.End (); + RegistrarTask.Create (compile_tasks, Abis, this, state.SourcePath); + } + // Now the assemblies are in PreBuildDirectory. // diff --git a/tools/mtouch/Tuning.cs b/tools/mtouch/Tuning.cs index 745aadfdb303..e1004cc4ada3 100644 --- a/tools/mtouch/Tuning.cs +++ b/tools/mtouch/Tuning.cs @@ -33,6 +33,7 @@ public class LinkerOptions { public bool IsDualBuild { get; set; } public bool Unified { get; set; } public bool DumpDependencies { get; set; } + internal PInvokeWrapperGenerator MarshalNativeExceptionsState { get; set; } internal RuntimeOptions RuntimeOptions { get; set; } public MonoTouchLinkContext LinkContext { get; set; } @@ -183,7 +184,7 @@ static Pipeline CreatePipeline (LinkerOptions options) pipeline.AppendStep (new FixModuleFlags ()); } - pipeline.AppendStep (new ListExportedSymbols ()); + pipeline.AppendStep (new ListExportedSymbols (options.MarshalNativeExceptionsState)); pipeline.AppendStep (new OutputStep ()); @@ -228,6 +229,7 @@ static ResolveFromXmlStep GetResolveStep (string filename) public class MonoTouchLinkContext : LinkContext { Dictionary required_symbols; + List marshal_exception_pinvokes; public Dictionary RequiredSymbols { get { @@ -237,6 +239,14 @@ public Dictionary RequiredSymbols { } } + public List MarshalExceptionPInvokes { + get { + if (marshal_exception_pinvokes == null) + marshal_exception_pinvokes = new List (); + return marshal_exception_pinvokes; + } + } + public MonoTouchLinkContext (Pipeline pipeline, AssemblyResolver resolver) : base (pipeline, resolver) { diff --git a/tools/mtouch/mtouch.csproj b/tools/mtouch/mtouch.csproj index 4124bd5150eb..1e2778a1294e 100644 --- a/tools/mtouch/mtouch.csproj +++ b/tools/mtouch/mtouch.csproj @@ -320,6 +320,9 @@ external\ExceptionMode.cs + + common\PInvokeWrapperGenerator.cs + From 1ef9dfb8113f1b496db4cda0a2d8ab4a68f56ce9 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 10:50:38 +0200 Subject: [PATCH 08/22] [runtime] Make sure the movaps instruction only accesses 16-byte aligned memory addresses. --- .../trampolines-i386-objc_msgSend-copyframe.inc | 12 ++++++------ runtime/trampolines-i386-objc_msgSend-post.inc | 4 ++-- runtime/trampolines-i386-objc_msgSend-pre.inc | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/runtime/trampolines-i386-objc_msgSend-copyframe.inc b/runtime/trampolines-i386-objc_msgSend-copyframe.inc index b15afdf74a25..18fa16c2fff2 100644 --- a/runtime/trampolines-i386-objc_msgSend-copyframe.inc +++ b/runtime/trampolines-i386-objc_msgSend-copyframe.inc @@ -23,7 +23,7 @@ sall $4,%eax subl %eax,%esp # store the number somewhere so we can restore the stack pointer later - movl %eax,-80(%ebp) + movl %eax,-16(%ebp) # copy arguments from old location in the stack to new location in the stack # %ecx will hold the amount of bytes left to copy @@ -34,7 +34,7 @@ # (arguments smaller than 4 bytes are extended to 4 bytes according to # http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4) - movl -80(%ebp),%ecx # ecx = frame_length + movl -16(%ebp),%ecx # ecx = frame_length leal 8(%ebp),%esi # esi = address of first argument we got (buffer) movl %esp,%edi # edi = address of the bottom of the stack @@ -47,7 +47,7 @@ L_start: jmp L_start # } L_end: - movaps -16(%ebp), %xmm0 - movaps -32(%ebp), %xmm1 - movaps -48(%ebp), %xmm2 - movaps -64(%ebp), %xmm3 + movaps -24(%ebp), %xmm0 + movaps -40(%ebp), %xmm1 + movaps -56(%ebp), %xmm2 + movaps -72(%ebp), %xmm3 diff --git a/runtime/trampolines-i386-objc_msgSend-post.inc b/runtime/trampolines-i386-objc_msgSend-post.inc index f5e07e42e771..e70712e2520e 100644 --- a/runtime/trampolines-i386-objc_msgSend-post.inc +++ b/runtime/trampolines-i386-objc_msgSend-post.inc @@ -1,6 +1,6 @@ Lafterinvoke: - addl -80(%ebp), %esp - addl $68, %esp + addl -16(%ebp), %esp + addl $92, %esp popl %ebx popl %edi popl %esi diff --git a/runtime/trampolines-i386-objc_msgSend-pre.inc b/runtime/trampolines-i386-objc_msgSend-pre.inc index 7ecdb732e075..b757462d567c 100644 --- a/runtime/trampolines-i386-objc_msgSend-pre.inc +++ b/runtime/trampolines-i386-objc_msgSend-pre.inc @@ -17,11 +17,11 @@ Lfunc_begin0: pushl %esi # %ebp-4 pushl %edi # %ebp-8 pushl %ebx # %ebp-12 - # we need 64 bytes to store xmm0-3 - # we need 4 bytes to store the result for get_frame_length, which we store in %ebp-80 + # we need 64 bytes to store xmm0-3, and those 64 bytes need to be 16-byte aligned. + # we need 4 bytes to store the result for get_frame_length, which we store in %ebp-16 # then 4 more bytes for stack space for the call to get_frame_length (which takes 2 arguments) - subl $76, %esp # to store xmm0-3 - movaps %xmm0, -16(%ebp) - movaps %xmm1, -32(%ebp) - movaps %xmm2, -48(%ebp) - movaps %xmm3, -64(%ebp) + subl $92, %esp # to store xmm0-3 + movaps %xmm0, -24(%ebp) + movaps %xmm1, -40(%ebp) + movaps %xmm2, -56(%ebp) + movaps %xmm3, -72(%ebp) From 515ed012c100a0221dd690898e4d7341a233a41d Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 10:50:57 +0200 Subject: [PATCH 09/22] Add libmonotouch.csproj / xamios.csproj to Xamarin.iOS.sln. --- Xamarin.iOS.sln | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/Xamarin.iOS.sln b/Xamarin.iOS.sln index 2fe3708e9b4d..2d650fb19122 100644 --- a/Xamarin.iOS.sln +++ b/Xamarin.iOS.sln @@ -11,6 +11,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2BFA13F8 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "introspection-ios", "tests\introspection\iOS\introspection-ios.csproj", "{208744BD-504E-47D7-9A98-1CF02454A6DA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libmonotouch", "runtime\libmonotouch.csproj", "{8A5B637C-E4FF-4145-B887-9347020100F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xamios", "src\xamios.csproj", "{E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -168,6 +172,74 @@ Global {208744BD-504E-47D7-9A98-1CF02454A6DA}.Release|iPhone.Build.0 = Release|iPhone {208744BD-504E-47D7-9A98-1CF02454A6DA}.DebugStaticRegistrar|iPhone.ActiveCfg = DebugStaticRegistrar|iPhone {208744BD-504E-47D7-9A98-1CF02454A6DA}.DebugStaticRegistrar|iPhone.Build.0 = DebugStaticRegistrar|iPhone + {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|Any CPU.Build.0 = Release|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Debug|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Release|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Release|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Debug|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Release|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Release|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Debug|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Release|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Release|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Debug|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Release|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Release|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Debug|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Release|Any CPU.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Release|Any CPU.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhone.Build.0 = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhone.ActiveCfg = Release|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhone.Build.0 = Release|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.DebugStaticRegistrar|iPhone.ActiveCfg = Debug|Any CPU + {8A5B637C-E4FF-4145-B887-9347020100F4}.DebugStaticRegistrar|iPhone.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|Any CPU.Build.0 = Release|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Release|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Release|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Release|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Release|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Release|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Release|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Release|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Release|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Release|Any CPU.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Release|Any CPU.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhone.Build.0 = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhone.ActiveCfg = Release|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhone.Build.0 = Release|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.DebugStaticRegistrar|iPhone.ActiveCfg = Debug|Any CPU + {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.DebugStaticRegistrar|iPhone.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {208744BD-504E-47D7-9A98-1CF02454A6DA} = {2BFA13F8-B568-48BF-9A70-9D43F718C523} From 3ac40ab0074de35f47f0b32cc5d91bb13a74c718 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 10:52:58 +0200 Subject: [PATCH 10/22] [runtime] Improve a few assertion messages to include more information. --- runtime/runtime.m | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index 0e456238d8b8..a71f1fa2fd9c 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1157,12 +1157,11 @@ -(void) xamarinSetGCHandle: (int) gc_handle; case _C_ATOM: case _C_VECTOR: case _C_CONST: - assert (0); case _C_ARY_E: case _C_UNION_E: case _C_STRUCT_E: - assert (0); - + xamarin_assertion_message ("Unhandled type encoding: %s", type); + break; case _C_ARY_B: { do { type++; @@ -1193,10 +1192,10 @@ -(void) xamarinSetGCHandle: (int) gc_handle; return ++type; } - + default: + xamarin_assertion_message ("Unsupported type encoding: %s", type); + break; } - - assert (0); } int @@ -1218,13 +1217,16 @@ -(void) xamarinSetGCHandle: (int) gc_handle; case _C_ULNG_LNG: return sizeof (unsigned long long); case _C_FLT: return sizeof (float); case _C_DBL: return sizeof (double); - case _C_BFLD: assert (0); case _C_BOOL: return sizeof (BOOL); case _C_VOID: return 0; - case _C_UNDEF: assert (0); case _C_PTR: return sizeof (void *); case _C_CHARPTR: return sizeof (char *); - case _C_ATOM: assert (0); + case _C_BFLD: { + case _C_UNDEF: + case _C_ATOM: + case _C_VECTOR: + xamarin_assertion_message ("Unhandled type encoding: %s", type); + break; case _C_ARY_B: { int size = 0; int len = atoi (type+1); @@ -1272,10 +1274,8 @@ -(void) xamarinSetGCHandle: (int) gc_handle; return size; } - case _C_VECTOR: assert (0); - case _C_CONST: assert (0); // The following are from table 6-2 here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html - // case 'r': _C_CONST + case 'r': // _C_CONST case 'n': case 'N': case 'o': From 79c68238ce8c95b7bcd3d4871e829ad152591763 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 11:36:36 +0200 Subject: [PATCH 11/22] [runtime] Don't use NSMethodSignature to compute frame size, it can't handle all method signature encodings. In particular NSMethodSignature chokes on encodings like this: [NSDecimalNumber initWithDecimal:] = "@36@0:8{?=b8b4b1b1b18[8S]}16" with an exception: NSInvalidArgumentException Reason: +[NSMethodSignature signatureWithObjCTypes:]: unsupported type encoding spec '{?}' so implement our own code to calculate the frame size. --- runtime/runtime.m | 15 ++++ runtime/trampolines.m | 205 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 218 insertions(+), 2 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index a71f1fa2fd9c..846df02b2643 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1154,6 +1154,10 @@ -(void) xamarinSetGCHandle: (int) gc_handle; case _C_PTR: return objc_skip_type (++type); case _C_BFLD: + type++; + while (*type && *type >= '0' && *type <= '9') + type++; + return type; case _C_ATOM: case _C_VECTOR: case _C_CONST: @@ -1222,6 +1226,17 @@ -(void) xamarinSetGCHandle: (int) gc_handle; case _C_PTR: return sizeof (void *); case _C_CHARPTR: return sizeof (char *); case _C_BFLD: { + // Example: [NSDecimalNumberPlaceholder initWithDecimal:] = @28@0:4{?=b8b4b1b1b18[8S]}8 + int bits = 0; + int bc = 1; + while (type [bc] >= '0' && type [bc] <= '9') { + bits = bits * 10 + (type [bc] - '0'); + bc++; + } + if (bits % sizeof (void *) == 0) + return bits / sizeof (void *); + return 1 + (bits / sizeof (void *)); + } case _C_UNDEF: case _C_ATOM: case _C_VECTOR: diff --git a/runtime/trampolines.m b/runtime/trampolines.m index 58e0042f3612..d656f50d108c 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -14,6 +14,7 @@ #include +#include "product.h" #include "delegates.h" #include "xamarin/xamarin.h" #include "slinked-list.h" @@ -123,12 +124,212 @@ } } +static const char * +get_method_description (Class cls, SEL sel) +{ + Protocol **protocols; + unsigned int p_count; + Class p_cls = cls; + struct objc_method_description desc; + + while (p_cls) { + protocols = class_copyProtocolList (p_cls, &p_count); + for (unsigned int i = 0; i < p_count; i++) { + desc = protocol_getMethodDescription (protocols [i], sel, YES, !class_isMetaClass (p_cls)); + if (desc.types != NULL) { + free (protocols); + return desc.types; + } + } + free (protocols); + p_cls = class_getSuperclass (p_cls); + } + + Method method = class_getInstanceMethod (cls, sel); + if (!method) + return NULL; + struct objc_method_description* m_desc; + m_desc = method_getDescription (method); + return m_desc ? m_desc->types : NULL; +} + +static int +count_until (const char *desc, char start, char end) +{ + // Counts the number of characters until a certain character is found: 'end' + // If the 'start' character is found, nesting is assumed, and an additional + // 'end' character must be found before the function returns. + int i = 1; + int sub = 0; + while (*desc) { + if (start == *desc) { + sub++; + } else if (end == *desc) { + sub--; + if (sub == 0) + return i; + } + i++; + // This is not multi-byte safe... + desc++; + } + + fprintf (stderr, PRODUCT ": Unexpected type encoding, did not find end character '%c' in '%s'.", end, desc); + + return i; +} + +static int +get_type_description_length (const char *desc) +{ + int length = 0; + // This function returns the length of the first encoded type string in desc. + switch (desc [0]) { + case _C_ID: + if (desc [1] == '?') { + // Example: [AVAssetImageGenerator generateCGImagesAsynchronouslyForTimes:completionHandler:] = 'v16@0:4@8@?12' + length = 2; + } else { + length = 1; + } + break; + case _C_CLASS: + case _C_SEL: + case _C_CHR: + case _C_UCHR: + case _C_SHT: + case _C_USHT: + case _C_INT: + case _C_UINT: + case _C_LNG: + case _C_ULNG: + case _C_LNG_LNG: + case _C_ULNG_LNG: + case _C_FLT: + case _C_DBL: + case _C_BOOL: + case _C_VOID: + case _C_CHARPTR: + length = 1; + break; + case _C_PTR: + length = 1; + + // handle broken encoding where simd types don't show up at all + // Example: [GKPath pathWithPoints:count:radius:cyclical:] = '@24@0:4^8L12f16c20' + // Here we assume that we're pointing to a simd type if we find + // a number (i.e. only get the size of what we're pointing to + // if the next character isn't a number). + if (desc [1] < '0' || desc [1] > '9') + length += get_type_description_length (desc + 1); + + break; + case _C_ARY_B: + length = count_until (desc, _C_ARY_B, _C_ARY_E); + break; + case _C_UNION_B: + length = count_until (desc, _C_UNION_B, _C_UNION_E); + break; + case _C_STRUCT_B: + length = count_until (desc, _C_STRUCT_B, _C_STRUCT_E); + break; + // The following are from table 6-2 here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html + case 'r': // _C_CONST + case 'n': + case 'N': + case 'o': + case 'O': + case 'R': + case 'V': + length = 1 + get_type_description_length (desc + 1); + break; + case _C_BFLD: + length = 1; + break; + case _C_UNDEF: + case _C_ATOM: + case _C_VECTOR: + xamarin_assertion_message ("Unhandled type encoding: %s", desc); + break; + default: + xamarin_assertion_message ("Unsupported type encoding: %s", desc); + break; + } + + // Every type encoding _may_ be followed by the stack frame offset for that type + while (desc [length] >= '0' && desc [length] <= '9') + length++; + + return length; +} + int xamarin_get_frame_length (id self, SEL sel) { - NSMethodSignature *sig = [self methodSignatureForSelector: sel]; + if (self == NULL) + return sizeof (void *) * 3; // we might be in objc_msgStret, in which case we'll need to copy three arguments. + + // [NSDecimalNumber initWithDecimal:] has this descriptor: "@36@0:8{?=b8b4b1b1b18[8S]}16" + // which NSMethodSignature chokes on: NSInvalidArgumentException Reason: +[NSMethodSignature signatureWithObjCTypes:]: unsupported type encoding spec '{?}' + // So instead parse the description ourselves. + + int length = 0; + Class cls = object_getClass (self); + const char *method_description = get_method_description (cls, sel); + const char *desc = method_description; + if (desc == NULL) { + // This happens with [[UITableViewCell appearance] backgroundColor] + @try { + NSMethodSignature *sig = [self methodSignatureForSelector: sel]; + length = [sig frameLength]; + } @catch (NSException *ex) { + length = sizeof (void *) * 64; // some high-ish number. + fprintf (stderr, PRODUCT ": Failed to calculate the frame size for the method [%s %s] (%s). Using a value of %i instead.\n", class_getName (cls), sel_getName (sel), [[ex description] UTF8String], length); + } + } else { + // The format of the method type encoding is described here: http://stackoverflow.com/a/11492151/183422 + // the return type might have a number after it, which is the size of the argument frame + // first get this number (if it's there), and use it as a minimum value for the frame length + int rvlength = get_type_description_length (desc); + int min_length = 0; + if (rvlength > 0) { + const char *min_start = desc + rvlength; + // the number is at the end of the return type encoding, so find any numbers + // at the end of the type encoding. + while (min_start > desc && min_start [-1] >= '0' && min_start [-1] <= '9') + min_start--; + if (min_start < desc + rvlength) { + for (int i = 0; i < desc + rvlength - min_start; i++) + min_length = min_length * 10 + (min_start [i] - '0'); + } + } + + // fprintf (stderr, "Found desc '%s' for [%s %s] with min frame length %i\n", desc, class_getName (cls), sel_getName (sel), min_length); + + // skip the return value. + desc += rvlength; + while (*desc) { + int tl = xamarin_objc_type_size (desc); + // round up to pointer size + if (tl % sizeof (void *) != 0) + tl += sizeof (void *) - (tl % sizeof (void *)); + length += tl; + // fprintf (stderr, " argument=%s length=%i totallength=%i\n", desc, tl, length); + desc += get_type_description_length (desc); + } + + if (min_length > length) { + // this might happen for methods that take simd types, since those arguments don't show up in the + // method signature encoding at all, but they're still added to the frame size. + // fprintf (stderr, " min length: %i is higher than calculated length: %i for [%s %s] with description %s\n", min_length, length, class_getName (cls), sel_getName (sel), method_description); + length = min_length; + } + } + + // we can't detect varargs, so just add 16 more pointer sized arguments to be on the safe-ish side. + length += sizeof (void *) * 16; - return [sig frameLength]; + return length; } static inline void From e8e8d6ea4364d67aa11b202b40056bc6cfb6662f Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 12:03:52 +0200 Subject: [PATCH 12/22] [runtime] Fix lookup of the inner exception when printing exception messages. --- runtime/runtime.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index 846df02b2643..5be5f057cad7 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -873,14 +873,14 @@ -(void) xamarinSetGCHandle: (int) gc_handle; NSMutableString *str = [[NSMutableString alloc] init]; // fetch the field, since the property might have been linked away. int counter = 0; - MonoClassField *inner_exception = mono_class_get_field_from_name (mono_object_get_class (exc), "inner_exception"); + MonoClassField *inner_exception = mono_class_get_field_from_name (mono_object_get_class (exc), "_innerException"); do { print_exception (exc, counter > 0, str); if (inner_exception) { mono_field_get_value (exc, inner_exception, &exc); } else { - LOG ("Could not find the field inner_exception in System.Exception\n"); + LOG ("Could not find the field _innerException in System.Exception\n"); break; } } while (counter++ < 10 && exc); From e51c48cda56bc0e4d10484b23cef820ed68386e3 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 13:34:49 +0200 Subject: [PATCH 13/22] [runtime] Don't store xmm registers where we store other registers. We store $ebx at $ebp-12: pushl %ebx # %ebp-12 so then storing xmm0 at $ebp-24: movaps %xmm0, -24(%ebp) would write 16 bytes between $ebp-24 and $ebp-8, thus overwriting the place where we stored $ebx. So allocate a bit more stack space and store the xmm registers further 16 bytes down. --- runtime/trampolines-i386-objc_msgSend-copyframe.inc | 8 ++++---- runtime/trampolines-i386-objc_msgSend-post.inc | 2 +- runtime/trampolines-i386-objc_msgSend-pre.inc | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/trampolines-i386-objc_msgSend-copyframe.inc b/runtime/trampolines-i386-objc_msgSend-copyframe.inc index 18fa16c2fff2..4bb4823c875a 100644 --- a/runtime/trampolines-i386-objc_msgSend-copyframe.inc +++ b/runtime/trampolines-i386-objc_msgSend-copyframe.inc @@ -47,7 +47,7 @@ L_start: jmp L_start # } L_end: - movaps -24(%ebp), %xmm0 - movaps -40(%ebp), %xmm1 - movaps -56(%ebp), %xmm2 - movaps -72(%ebp), %xmm3 + movaps -40(%ebp), %xmm0 + movaps -56(%ebp), %xmm1 + movaps -72(%ebp), %xmm2 + movaps -88(%ebp), %xmm3 diff --git a/runtime/trampolines-i386-objc_msgSend-post.inc b/runtime/trampolines-i386-objc_msgSend-post.inc index e70712e2520e..2b0ef6bfb2c3 100644 --- a/runtime/trampolines-i386-objc_msgSend-post.inc +++ b/runtime/trampolines-i386-objc_msgSend-post.inc @@ -1,6 +1,6 @@ Lafterinvoke: addl -16(%ebp), %esp - addl $92, %esp + addl $96, %esp popl %ebx popl %edi popl %esi diff --git a/runtime/trampolines-i386-objc_msgSend-pre.inc b/runtime/trampolines-i386-objc_msgSend-pre.inc index b757462d567c..162863926742 100644 --- a/runtime/trampolines-i386-objc_msgSend-pre.inc +++ b/runtime/trampolines-i386-objc_msgSend-pre.inc @@ -20,8 +20,8 @@ Lfunc_begin0: # we need 64 bytes to store xmm0-3, and those 64 bytes need to be 16-byte aligned. # we need 4 bytes to store the result for get_frame_length, which we store in %ebp-16 # then 4 more bytes for stack space for the call to get_frame_length (which takes 2 arguments) - subl $92, %esp # to store xmm0-3 - movaps %xmm0, -24(%ebp) - movaps %xmm1, -40(%ebp) - movaps %xmm2, -56(%ebp) - movaps %xmm3, -72(%ebp) + subl $96, %esp # to store xmm0-3 + movaps %xmm0, -40(%ebp) + movaps %xmm1, -56(%ebp) + movaps %xmm2, -72(%ebp) + movaps %xmm3, -88(%ebp) From feb12f61b5a00839dbd07d312d8baf75042accf0 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 14:24:47 +0200 Subject: [PATCH 14/22] [runtime] mono_set_pending_exception is a private function in mono, so make it optional. Otherwise Xamarin.Mac apps using the system mono will fail. --- runtime/exports.t4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/exports.t4 b/runtime/exports.t4 index 8805e5ab2b18..d6d2314fd493 100644 --- a/runtime/exports.t4 +++ b/runtime/exports.t4 @@ -220,7 +220,7 @@ #region metadata/object-internals.h - new Export ("void", "mono_set_pending_exception", + new Export (true, "void", "mono_set_pending_exception", "MonoException *", "exc" ), From f478657298a05dcb76a1927547811f537d621e62 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 14:29:50 +0200 Subject: [PATCH 15/22] [runtime] Only insert dllmap for exception marshalling when the ObjC runtime supports zero-cost exceptions. --- runtime/runtime.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index 5be5f057cad7..e7515620669e 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1807,7 +1807,7 @@ -(void) xamarinSetGCHandle: (int) gc_handle; void xamarin_insert_dllmap () { -#if defined (__i386__) || defined (__x86_64__) +#if defined (OBJC_ZEROCOST_EXCEPTIONS) && (defined (__i386__) || defined (__x86_64__)) if (xamarin_marshal_objectivec_exception_mode == MarshalObjectiveCExceptionModeDisable) return; #if DYNAMIC_MONO_RUNTIME From 53582429ec58b592b17c44b92cddcc0f884426f4 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 14:30:23 +0200 Subject: [PATCH 16/22] [runtime] Use the right condition when to reference libxammac.dylib vs __Internal. --- runtime/runtime.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index e7515620669e..861c7ac88243 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1810,7 +1810,7 @@ -(void) xamarinSetGCHandle: (int) gc_handle; #if defined (OBJC_ZEROCOST_EXCEPTIONS) && (defined (__i386__) || defined (__x86_64__)) if (xamarin_marshal_objectivec_exception_mode == MarshalObjectiveCExceptionModeDisable) return; -#if DYNAMIC_MONO_RUNTIME +#if DYLIB const char *lib = "libxammac.dylib"; #else const char *lib = "__Internal"; From 7c1b81eb9d1676371c3733015d759467c4b51c55 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 18:18:03 +0200 Subject: [PATCH 17/22] [runtime] Make sure the stack is correctly aligned to 16 bytes. --- runtime/trampolines-i386-objc_msgSend-post.inc | 2 +- runtime/trampolines-i386-objc_msgSend-pre.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/trampolines-i386-objc_msgSend-post.inc b/runtime/trampolines-i386-objc_msgSend-post.inc index 2b0ef6bfb2c3..d40088bec3b7 100644 --- a/runtime/trampolines-i386-objc_msgSend-post.inc +++ b/runtime/trampolines-i386-objc_msgSend-post.inc @@ -1,6 +1,6 @@ Lafterinvoke: addl -16(%ebp), %esp - addl $96, %esp + addl $108, %esp popl %ebx popl %edi popl %esi diff --git a/runtime/trampolines-i386-objc_msgSend-pre.inc b/runtime/trampolines-i386-objc_msgSend-pre.inc index 162863926742..0fe9994eee6f 100644 --- a/runtime/trampolines-i386-objc_msgSend-pre.inc +++ b/runtime/trampolines-i386-objc_msgSend-pre.inc @@ -20,7 +20,7 @@ Lfunc_begin0: # we need 64 bytes to store xmm0-3, and those 64 bytes need to be 16-byte aligned. # we need 4 bytes to store the result for get_frame_length, which we store in %ebp-16 # then 4 more bytes for stack space for the call to get_frame_length (which takes 2 arguments) - subl $96, %esp # to store xmm0-3 + subl $108, %esp # to store xmm0-3 movaps %xmm0, -40(%ebp) movaps %xmm1, -56(%ebp) movaps %xmm2, -72(%ebp) From f631069bbddcab845f54ac6aa441504643c4e4e9 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 May 2016 18:38:37 +0200 Subject: [PATCH 18/22] [mmp] Only 64-bit apps have the dynamic objc_msgSend wrappers. --- tools/mmp/driver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs index 859b02ae9a7a..306393d03116 100644 --- a/tools/mmp/driver.cs +++ b/tools/mmp/driver.cs @@ -605,7 +605,7 @@ static void Pack (IList unprocessed) Watch (string.Format ("Linking (mode: '{0}')", App.LinkMode), 1); } - if (App.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Disable && !App.RequiresPInvokeWrappers) { + if (App.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Disable && !App.RequiresPInvokeWrappers && BuildTarget.Is64Build) { internalSymbols.Add ("xamarin_dyn_objc_msgSend"); internalSymbols.Add ("xamarin_dyn_objc_msgSendSuper"); internalSymbols.Add ("xamarin_dyn_objc_msgSend_stret"); From 62cbe7e1f67aeac8296399216c691bde57eb532a Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 13 May 2016 13:16:37 +0200 Subject: [PATCH 19/22] [mtouch] Fix logic to detect if simlauncher can be used. --- tools/mtouch/Makefile | 4 ++++ tools/mtouch/mtouch.cs | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index 29d9094e7d94..b1e20217c449 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -153,6 +153,10 @@ monotouch-fixes.dylib: monotouch-fixes.i386.dylib monotouch-fixes.x86_64.dylib # keep in sync with mtouch.cs (but keep current differences too) UNREFERENCED_SYMBOLS = \ + _xamarin_dyn_objc_msgSend \ + _xamarin_dyn_objc_msgSendSuper \ + _xamarin_dyn_objc_msgSend_stret \ + _xamarin_dyn_objc_msgSendSuper_stret \ _xamarin_init_nsthread \ _xamarin_get_block_descriptor \ _xamarin_get_locale_country_code \ diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs index a152a0018b52..7d479eedd6fc 100644 --- a/tools/mtouch/mtouch.cs +++ b/tools/mtouch/mtouch.cs @@ -910,9 +910,15 @@ public static bool CanWeSymlinkTheApplication () if (app.Registrar == RegistrarMode.Static || app.Registrar == RegistrarMode.LegacyStatic || app.Registrar == RegistrarMode.LegacyDynamic) return false; - if (app.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Default || app.Platform == ApplePlatform.WatchOS) + // The default exception marshalling differs between release and debug mode, but we + // only have one simlauncher, so to use the simlauncher we'd have to chose either + // debug or release mode. Debug is more frequent, so make that the fast path. + if (!app.EnableDebug) return false; + if (app.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.UnwindManagedCode) + return false; // UnwindManagedCode is the default for debug builds. + return true; } From 2f2ac89da7ee441f1cecadbd730bd90dd9237558 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 13 May 2016 13:26:44 +0200 Subject: [PATCH 20/22] [mtouch] Remove misleading comment. mtouch calculates the exact set of native functions to reference now, so there's no need to sync anything. --- tools/mtouch/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index b1e20217c449..f1ae91aabf19 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -151,7 +151,6 @@ monotouch-fixes.x86_64.dylib: monotouch-fixes.c Makefile monotouch-fixes.dylib: monotouch-fixes.i386.dylib monotouch-fixes.x86_64.dylib $(Q_LIPO) lipo -create -output $@ $^ -# keep in sync with mtouch.cs (but keep current differences too) UNREFERENCED_SYMBOLS = \ _xamarin_dyn_objc_msgSend \ _xamarin_dyn_objc_msgSendSuper \ From f7a5b7fb70b9544be757b016b0429ad1b5ebecfe Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 13 May 2016 14:35:28 +0200 Subject: [PATCH 21/22] [runtime] Add docs about exception marshaling. --- runtime/EXCEPTIONS.md | 127 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 runtime/EXCEPTIONS.md diff --git a/runtime/EXCEPTIONS.md b/runtime/EXCEPTIONS.md new file mode 100644 index 000000000000..b9b0aa591077 --- /dev/null +++ b/runtime/EXCEPTIONS.md @@ -0,0 +1,127 @@ +History +==================== + +Historically we've attempted to convert Objective-C exceptions to managed exceptions +by adding a handler to be notified of uncaught Objective-C exceptions [1], but that one +major problem: it did not handle *caught* Objective-C exceptions, where the exception +handler would just abort. + +This is common in iOS apps, where the main runloop adds an Objective-C @try/@catch +handler that just aborts, preventing us from being notified of the Objectice-C exception. + +This approach also has another problem: the Objective-C exception handling mechanism +could unwind through managed code, which would result in all kinds of inexplicable +behavior (if there was an Objective-C exception handler that _didn't_ abort the app): + + try { + doSomethingThatCausesAnObjectiveCException (); + } finally { + Console.WriteLine ("Done!"); + } + +If there happened to be an Objective-C exception handler in a stack frame further +up, the Console.WriteLine would never be executed. + +Another frequent problem would occur in an IDE debugger if it tried to evaluate +every property for an object. If there were properties not supported on the +executing platform, it would throw an Objective-C exception (selector not found) +that would terminate the app. + +The reverse problem also existed: throwing a managed exception that the native-to-managed +boundary didn't catch, would make the mono runtime unwind through native frames, +causing a similar set of inexplicable behavior: + + [obj selectorThatManagedCodeHasOverriddenAndThrowAManagedException]; + NSLog (@"done!"); + +The NSLog statement would never be executed. + +[1] By using [NSSetUncaughtExceptionHandler](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSSetUncaughtExceptionHandler). + +Exception marshaling +==================== + +There are two parts to exception marshaling: Objective-C exceptions +and managed exceptions. + +In both cases we offer a managed event the app can subscribe to to +be notified, and then the app can choose what to do: + +1. Convert the managed/Objective-C exception to its counterpart + (i.e. managed exceptions are converted to an Objective-C exception, + and Objective-C exceptions are converted to a managed exception). + +2. Abort. + +3. Rethrow the original exception (this is not available when + using the Coop GC, which is the only option on watchOS). + +Managed exception marshaling +---------------------------- + +This is the simplest case, we just make sure to catch any managed +exceptions that reaches native code. There are a few sources: + +1. When we call mono_runtime_invoke in our trampolines. + + In this case mono_runtime_invoke will catch any exceptions + and return them to us (without unwinding anything else). + +2. When managed code gives a delegate to native code, and + native code calls that delegate. + + This is still a TODO, we need to use this new API to be + notified when the mono runtime detects this case: + https://github.com/mono/mono/pull/2948 + +Objective-C exception marshaling +-------------------------------- + +This is more complicated, because there must be an Objective-C +exception handler on the managed-to-native boundary frame, +catching the Objective-C exception. + +We have two approaches for this: + +1. Custom wrappers of the objc_msgSend functions with an + Objective-C exception handler. These have to be written + in assembly code, since it's not possible to do it in C. + At runtime we inject a dllmap into the process, which + redirects any P/invoke to the objc_msgSend functions to + these custom wrappers. + + Since we can't write assembly code for bitcode targets + (watchOS), another approach is still required though. + + This is the used for iOS/tvOS/watchOS simulator builds + (both 32-bit and 64-bit) and Mac/64 bits. It does not + work on Mac/32 bits because that platform does not use + 0-cost exceptions like the other platforms (it uses + setjmp/longjmp instead, and would require a very + different implementation in assembly code). + + The assembly code for these wrappers is in the + trampolines-[arch]-objc_msgSend* files. + + The documentation about 0-cost exceptions is scarce, + but here are a few documents which were helpful: + + https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Exceptions/Articles/Exceptions64Bit.html + https://sourceware.org/binutils/docs/as/CFI-directives.html + http://mentorembedded.github.io/cxx-abi/abi-eh.html + http://llvm.org/docs/ExceptionHandling.html + http://www.opensource.apple.com/source/llvmCore/llvmCore-3425.0.33/docs/ExceptionHandling.rst + http://stackoverflow.com/a/7535848 + http://www.darlinghq.org/for-developers/exception-handling-on-os-x + + The wrappers themselves were first created by writing + something similar in C, and then telling clang to + dump the corresponding assembly code (by passing + `--save-temps -fverbose-asm` to clang). + +2. At build time generate custom wrappers of every P/Invoke + to the objc_msgSend functions, and modify the P/Invoke to + call these generated wrappers. + + This is used for iOS/tvOS/watchOS device builds, and + Mac/32 bits. From cffc2e9181e9cdec2c828a69ba8004ecf8a19aea Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 17 May 2016 11:17:47 +0200 Subject: [PATCH 22/22] Rewrite user-visible messages to use 'cooperative mode' instead of 'coop'. This seems to be how we describe it publicly: http://tirania.org/blog/archive/2015/Dec-22.html --- runtime/runtime.m | 4 ++-- tools/common/Application.cs | 6 +++--- tools/common/Driver.cs | 2 +- tools/mtouch/error.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index 861c7ac88243..5d0559e32b33 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1736,7 +1736,7 @@ -(void) xamarinSetGCHandle: (int) gc_handle; switch (mode) { case MarshalObjectiveCExceptionModeUnwindManagedCode: if (xamarin_is_gc_coop) - xamarin_assertion_message ("Cannot unwind managed frames for Objective-C exceptions when using the COOP GC."); + xamarin_assertion_message ("Cannot unwind managed frames for Objective-C exceptions when the GC is in cooperative mode."); @throw ns_exception; break; case MarshalObjectiveCExceptionModeThrowManagedException: @@ -1777,7 +1777,7 @@ -(void) xamarinSetGCHandle: (int) gc_handle; switch (mode) { case MarshalManagedExceptionModeUnwindNativeCode: if (xamarin_is_gc_coop) - xamarin_assertion_message ("Cannot unwind native frames for managed exceptions when using the COOP GC."); + xamarin_assertion_message ("Cannot unwind native frames for managed exceptions when the GC is in cooperative mode."); mono_raise_exception ((MonoException *) exception); break; case MarshalManagedExceptionModeThrowObjectiveCException: { diff --git a/tools/common/Application.cs b/tools/common/Application.cs index fc5f62b50248..7239a2b2d4c5 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -351,7 +351,7 @@ public static void TryDelete (string path) public void InitializeCommon () { if (Platform == ApplePlatform.WatchOS && EnableCoopGC.HasValue && !EnableCoopGC.Value) - throw ErrorHelper.CreateError (88, "Cannot disable the Coop GC for watchOS apps. Please remove the --coop:false argument to mtouch."); + throw ErrorHelper.CreateError (88, "The GC must be in cooperative mode for watchOS apps. Please remove the --coop:false argument to mtouch."); if (!EnableCoopGC.HasValue) EnableCoopGC = Platform == ApplePlatform.WatchOS; @@ -360,12 +360,12 @@ public void InitializeCommon () switch (MarshalObjectiveCExceptions) { case MarshalObjectiveCExceptionMode.UnwindManagedCode: case MarshalObjectiveCExceptionMode.Disable: - throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when the Coop GC is enabled.", "--marshal-objectivec-exceptions", MarshalObjectiveCExceptions.ToString ().ToLowerInvariant ()); + throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when cooperative mode is enabled for the GC.", "--marshal-objectivec-exceptions", MarshalObjectiveCExceptions.ToString ().ToLowerInvariant ()); } switch (MarshalManagedExceptions) { case MarshalManagedExceptionMode.UnwindNativeCode: case MarshalManagedExceptionMode.Disable: - throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when the Coop GC is enabled.", "--marshal-managed-exceptions", MarshalManagedExceptions.ToString ().ToLowerInvariant ()); + throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when cooperative mode is enabled for the GC.", "--marshal-managed-exceptions", MarshalManagedExceptions.ToString ().ToLowerInvariant ()); } } diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index f76b024c5f9d..ec9dbf27d6e6 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -20,7 +20,7 @@ namespace Xamarin.Bundler { public partial class Driver { static void AddSharedOptions (Mono.Options.OptionSet options) { - options.Add ("coop:", "If the Coop GC should be used.", v => { App.EnableCoopGC = ParseBool (v, "coop"); }, hidden: true); + options.Add ("coop:", "If the GC should run in cooperative mode.", v => { App.EnableCoopGC = ParseBool (v, "coop"); }, hidden: true); options.Add ("marshal-objectivec-exceptions:", v => { switch (v) { case "default": diff --git a/tools/mtouch/error.cs b/tools/mtouch/error.cs index 62389dcd06e8..756f22f21794 100644 --- a/tools/mtouch/error.cs +++ b/tools/mtouch/error.cs @@ -95,8 +95,8 @@ namespace Xamarin.Bundler { // MT0085 No reference to '{0}' was found. It will be added automatically. // MT0086 A target framework (--target-framework) must be specified when building for TVOS or WatchOS. // Warning MT0087 - // MT0088 Cannot disable the Coop GC for watchOS apps. Please remove the --coop:false argument to mtouch. - // MT0089 The option '{0}' cannot take the value '{1}' when the Coop GC is enabled. + // MT0088 The GC must be in cooperative mode for watchOS apps. Please remove the --coop:false argument to mtouch. + // MT0089 The option '{0}' cannot take the value '{1}' when cooperative mode is enabled for the GC. // MT0091 This version of Xamarin.iOS requires the {0} {1} SDK (shipped with Xcode {2}) when the managed linker is disabled. Either upgrade Xcode, or enable the managed linker. // MT0092 The option '{0}' is required. // MT0093 Could not find 'mlaunch'.