[macios-devel] [android-devel] Signal-chaining & crash reporters

Jonathan Pryor jonpryor at vt.edu
Fri Sep 16 16:43:59 UTC 2016


Kumpera: Please see Option #5, at end.

On Sep 16, 2016, at 10:59 AM, Sebastien Pouliot via android-devel <android-devel at lists.dot.net> wrote:
> The current sequence of events is this:
>  
> a) The app launches, and Xamarin.iOS initializes the Mono runtime.

Which raises Plausible Solution #0: structure things such that the 3rd party lib sets the default signal handler *before* mono is initialized.

This could be done by e.g. using a C++ global object in an archive linked into the main binary: the constructor will be executed before `main()` executes, so by the time mono has initialized it will see the 3rd party SIGSEGV handler, not the system handler.

There are problems with this:

1. Before `main()`, execution order of global constructors is undefined. This solution is “scalable”, in that any number of libraries can participate, but the order between those libraries can’t be controlled.

2. It requires that the 3rd party library be create-able in this fashion. For example, if it uses a license key which is only available in managed code, this can’t work.

3. It only works for *app* projects. This can thus work for Xamarin.iOS, but it *can’t* work for Xamarin.Android, which uses shared libraries.

However, we can apply similar Judo to make things work in Xamarin.Android. Mono is initialized on Android through a ContentProvider with a high //provider/@android:initOrder value; the `initOrder` value controls the order of loading ContentProviders. You could thus set things up so that the 3rd party lib uses a Java ContentProvider with a *higher* `initOrder` value, thus ensuring that the 3rd party lib is initialized *before* Xamarin.Android. Mono will thus see the 3rd party’s SIGSEGV handler, not the system one.

The immediate problem here is that Xamarin.Android sets the initOrder value to int.MaxValue, but we can certainly lower the value we use to allow code to execute before the Xamarin.Android bootstrap code…

Pro: works/can be made to work *now*, with minimal/no changes to mono and Xamarin.

Con: Requires modifying process state before Mono starts executing, which isn’t always easy or straightforward (requires native code).

> b) Managed code starts running, and the actual app initializes any third-party libraries. At this point third-party libraries typipcally do something like this:

This is assuming/requiring that the 3rd party code is managed code. When it comes to signal handlers, I don’t think that’s valid; any reasonably sane SIGSEGV-handling code *must* be native. Thus, we don’t necessarily need to support initializing it *during* execution of managed code.

That might still be a good idea… I’m not sure, myself.

> A couple of ideas:
>  
> 1) Add an API to select/set the signal handler Mono chains to:
>  
> namespace ObjCRuntime {
>       public class Runtime {
>             public struct SigAction
>             {
>                   public IntPtr Handler; //
>                   public uint Mask; // sa_mask => sigset_t => __darwin_sigset_t => uint32_t
>                   public int Flags; // sa_flags => int
>             }
>  
>             public static bool InstallSignalHandler (int signal, SigAction handler, out SigAction previous_handler);
>       }
> }

I would prefer to *not* expose a managed binding of `struct sigaction`, in part to reduce portability issues. On Android, `sigset_t` is `unsigned long`, so on 64-bit platforms this will be a 64-bit value, not a 32-bit value, so this wouldn’t be compatible.

Granted, we could have “parallel” definitions for Android vs. iOS, but if we’re doing to define a new API, we should try to minimize such things. :-)

> 2) Add an attribute that gives the name of the signal handler, and then Xamarin.iOS generates the required code to make sure these signal handlers are called for signals Mono doesn't handle:

Such an attribute would need to be iOS/Mac-specific; there’s no Android equivalent. It would also require that `third_party_signal_handler` be a public symbol that the linker can find, in addition to the “use before initialized” issue that Rolf notes.

I think Solution #0 (global C++ objects) overlaps with this general idea, and is cleaner to boot.

> 3) A mix of both of the above:
>  
>       ObjCRuntime.Runtime.EnableCustomSignalHandler (Signals.SIGSEGV, "third_party_signal_handler");
>  
>    The problem here is that we'd have to use dlsym to lookup the function pointer for third_party_signal_handler, which can be problematic sometimes (#1 just gets the function pointer from the signal handler the native third-party library installed, and for #2 generate native code that references the function instead).

Another issue: the signals that are specified are signals that Mono needs to handle *first*. I’m not sure that this list of signals is/should be set in stone; what if Mono needs to be first for processing SIGILL in the future? Then this code will be out-of-date, possibly resulting on obscure errors.

This doesn’t feel “safe” to me

>  4) A mix of all of the above:
..
> Any other ideas? Suggestions for better naming for the API?

Option 5: a “new" Mono function.

The fundamental problem is that Mono *must* be the first signal handler for e.g. SIGSEGV, but ~anybody else can replace the SIGSEGV handler at any time.

What if the Mono team made one of the following function public:

	// in: https://github.com/mono/mono/blob/master/mono/mini/mini-posix.c
	void mono_runtime_install_handlers (void);
	void mono_runtime_posix_install_handlers (void);

My quick reading of `mono_runtime_posix_install_handlers()` (line ~470) suggests that it registers those signals. It’s currently “internal”, i.e. you can’t `dlsym()` that function from `libmonosgen-2.0.dylib`, but if it were public, you could then register all your third party library code, then call `mono_runtime_install_handlers()`, and the app would be fine.

There would be a problem if e.g. a null value was used in between 3rd party crash handler registration and use of `mono_runtime_install_handlers()`, but such issues should be minimized with appropriate coding, and these issues exist in e.g. current practice.

 - Jon



More information about the macios-devel mailing list