Bug 46452 - SIGSEV while executing native code when P/Invoking into objc_msgSend to access NSString Selector initWithFormat:arguments: when building for 64 bit
Summary: SIGSEV while executing native code when P/Invoking into objc_msgSend to acces...
Status: RESOLVED ANSWERED
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: XI 10.2 (iOS 10.1)
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-11-03 23:36 UTC by Jon Goldberger [MSFT]
Modified: 2016-11-07 16:24 UTC (History)
4 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Test Project (12.80 KB, application/zip)
2016-11-03 23:36 UTC, Jon Goldberger [MSFT]
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Jon Goldberger [MSFT] 2016-11-03 23:36:56 UTC
Created attachment 18322 [details]
Test Project

## Description

SIGSEV while executing native code when P/Invoking into objc_msgSend to access NSString Selector initWithFormat:arguments: when building for 64 bit, whether ARM64 or X86_64. If you build only for 32 bit, the SIGSEV error does not occur.

Code:

>public override void ViewDidLoad()
>        {
>            base.ViewDidLoad();
>
>            var str = Alloc(new Class(typeof(NSString)));
>            var format = new NSString("%s");
>
>            var handle = NSStringInitWithFormatArguments(str.Handle, Selector.GetHandle("initWithFormat:arguments:"), format.Handle, new NSString("a").Handle);
>
>            Debug.WriteLine(NSString.FromHandle(handle));
>        }
>
>        [DllImport(Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
>        private static extern IntPtr NSStringInitWithFormatArguments(IntPtr target, IntPtr selector, IntPtr key, IntPtr args);

Stack trace for ARM64: https://gist.github.com/jgold6/1c6c4b2a4b8de4a4f1a637d7cf629413

Stack trace for X86_64: https://gist.github.com/jgold6/36f8516765db440e68b9632fa834b9dc



## Steps to reproduce

1. Open the attached test project and set to Debug configuration.

2. Deploy app to simulator or device

Expected result: No SIGSEV error and the console will display "Success:" with some characters after.

Actual result: SIGSEV crash. 

## Environment

=== Xamarin Studio Enterprise ===

Version 6.1.1 (build 17)
Installation UUID: ceaba76c-db06-4fbd-b326-f69ea53c3e01
Runtime:
	Mono 4.6.1 (mono-4.6.0-branch-c8sr0/ef43c15) (64-bit)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 406010005

=== NuGet ===

Version: 3.4.3.0

=== Xamarin.Profiler ===

Version: 0.33.1
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

=== Xamarin.Android ===

Version: 7.0.1.3 (Visual Studio Enterprise)
Android SDK: /Users/jongoldberger/Library/Developer/Xamarin/android-sdk-macosx
	Supported Android versions:
		4.0.3 (API level 15)
		4.1   (API level 16)
		4.2   (API level 17)
		4.3   (API level 18)
		4.4   (API level 19)
		5.0   (API level 21)
		5.1   (API level 22)
		6.0   (API level 23)
		7.0   (API level 24)

SDK Tools Version: 25.2.2
SDK Platform Tools Version: 24.0.3
SDK Build Tools Version: 24.0.3

Java SDK: /usr
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Xamarin Android Player ===

Version: 0.6.5
Location: /Applications/Xamarin Android Player.app

=== Xamarin Inspector ===

Version: 0.10.0.0
Hash: e931a52
Branch: master
Build date: Thu, 18 Aug 2016 17:46:46 GMT

=== Apple Developer Tools ===

Xcode 8.1 (11544)
Build 8B62

=== Xamarin.iOS ===

Version: 10.2.0.4 (Visual Studio Enterprise)
Hash: b638977
Branch: xcode8.1
Build date: 2016-10-25 14:38:48-0400

=== Xamarin.Mac ===

Version: 2.10.0.105 (Visual Studio Enterprise)

=== Build Information ===

Release ID: 601010017
Git revision: 44d481a9be9cf2bf7c01bfb99c59b77dfa08e712
Build date: 2016-10-25 14:27:24-04
Xamarin addins: 19cdb28081bf28d9688698030abb96e04e391f69
Build lane: monodevelop-lion-cycle8-sr0

=== Operating System ===

Mac OS X 10.12.1
Darwin Jons-MacBook-Pro.local 16.1.0 Darwin Kernel Version 16.1.0
    Thu Oct 13 21:26:57 PDT 2016
    root:xnu-3789.21.3~60/RELEASE_X86_64 x86_64

=== Enabled user installed addins ===

Xamarin Inspector 0.10.0.0
Comment 2 Sebastien Pouliot 2016-11-04 00:30:05 UTC
You're just lucky it does not crash on 32 bits as the signature is incorrect.

> - (instancetype)initWithFormat:(NSString *)format 
>                     arguments:(va_list)argList;

The 2nd argument is not an `NSString` (like used in the sample) but a `va_list`. You cannot substitute the list for the first (and only) item you would like in that list.


Binding variadic functions is quite tricky because it's ABI specific. As much as possible use the similar .NET methods for formatting (e.g. String.Format) and then convert the string into an NSString.
Comment 3 Ruben Buniatyan 2016-11-04 16:00:53 UTC
I don't think it's a matter of being lucky. It doesn't seem to be a signature problem here. Otherwise, why does it work on devices or on simulator with 32 bits? I want to stress that the problem is with simulator only when architecture is set to x86_64. This problem appeared after updating to the latest Xamarin which brings support for Xcode 8.1. Before, everything was just fine. There's definitely another reason for this crash.

Regarding the signature, see these:
- https://github.com/xamarin/xamarin-macios/blob/fc55e4306f79491fd269ca2495c6a859799cb1c6/src/Foundation/NSString.cs
- https://github.com/xamarin/xamarin-macios/blob/fc55e4306f79491fd269ca2495c6a859799cb1c6/runtime/nsstring-localization.m

Exactly the same signature. I know that the Xamarin's code above doesn't work and for that I reported the issue #41292 a while ago.

As I was able to find workaround for #41292, I was pretty happy with it. But after updating to the latest Xamarin, I started facing this issue on the simulator. Regardless of what you pass as 2nd argument, it always crashes. Of course, in my real code I don't pass a handle of NSString. This is for demo purposes only, so you can see the issue. It just crashes when there's a 2nd argument no matter what.

And here is my actual code that works on devices but not on 64 bit simulator:
https://github.com/rubo/missinkit/blob/develop/Utilities/NSStringUtility.cs:40

And I'm pretty sure there's nothing wrong with signature -- IntPtr to arg list or whatever.
Comment 4 Ruben Buniatyan 2016-11-04 16:06:58 UTC
I don't think it's a matter of being lucky. It doesn't seem to be a signature problem here. Otherwise, why does it work on devices or on simulator with 32 bits? I want to stress that the problem is with simulator only when architecture is set to x86_64. This problem appeared after updating to the latest Xamarin which brings support for Xcode 8.1. Before, everything was just fine. There's definitely another reason for this crash.

Regarding the signature, see these:
- https://github.com/xamarin/xamarin-macios/blob/fc55e4306f79491fd269ca2495c6a859799cb1c6/src/Foundation/NSString.cs
- https://github.com/xamarin/xamarin-macios/blob/fc55e4306f79491fd269ca2495c6a859799cb1c6/runtime/nsstring-localization.m

Exactly the same signature. I know that the Xamarin's code above doesn't work and for that I reported the issue #41292 a while ago.

As I was able to find workaround for #41292, I was pretty happy with it. But after updating to the latest Xamarin, I started facing this issue on the simulator. Regardless of what you pass as 2nd argument, it always crashes. Of course, in my real code I don't pass a handle of NSString. This is for demo purposes only, so you can see the issue. It just crashes when there's a 2nd argument no matter what.

And here is my actual code that works on devices but not on 64 bit simulator:
https://github.com/rubo/missinkit/blob/develop/Utilities/NSStringUtility.cs (line 40) And I'm pretty sure there's nothing wrong with signature -- IntPtr to arg list or whatever.
Comment 5 Rolf Bjarne Kvinge [MSFT] 2016-11-04 16:20:23 UTC
On ARM64 the calling convention is different for varargs methods, so you have to do something ugly like this: https://github.com/xamarin/xamarin-macios/blob/fc55e4306f79491fd269ca2495c6a859799cb1c6/src/UIKit/UIAppearance.cs#L112

In any case you're calling the P/Invoke wrong, you're passing an NSString as the argument when the format is %s (i.e. "char *"), which is likely why it's failing on X86_64.
Comment 6 Ruben Buniatyan 2016-11-04 17:38:45 UTC
Hey Rolf. Well, my problem is with x86_64, not ARM64. I mean on the simulator for 64-bit app. It works as it should on devices. So I don't need the trick you used for appearanceWhenContainedIn: method. You perhaps missed the last part of my comments where I indicated that actually I don't use NSString but another object to handle variadic arguments. NSString was just an example.

Nevertheless, I'll be very grateful if someone provide me with working example for 64-bit app on simulator.
Comment 7 Ruben Buniatyan 2016-11-04 17:56:24 UTC
Even if I change the format %d and pass a number instead of NSString, it won't work. Something is different with va_list handling on simulator for 64-bit apps and I need you help me to understand what exactly.
Comment 8 Rolf Bjarne Kvinge [MSFT] 2016-11-07 12:29:41 UTC
Sorry, I though you were calling a varargs method, (...), not a va_list method.

That in fact makes it much more complicated, because you can't construct a va_list in managed code. From the code you link to it looks like you try to create a native array of pointers, but that's not how va_list is defined on x86_64 (from this stack overflow post: http://stackoverflow.com/a/4958507/183422 you can see the actual format, and it looks like you have to put some values into specific registers).

A simple native example crashes too: https://gist.github.com/rolfbjarne/783a83b9a04f7e167439c81c1e384305

It would probably be a lot easier to use [NSString initWithFormat:] instead, and create a P/Invoke for each signature you need to call.
Comment 9 Ruben Buniatyan 2016-11-07 16:24:43 UTC
Thank you, Rolf. That's what I needed to know.