Thanks to visit codestin.com
Credit goes to github.com

Skip to content

interpreter: bool arguments aren't 64-bit zero-extended in 64-bit registers #20092

@rolfbjarne

Description

@rolfbjarne

Steps to Reproduce

  1. Download the test case, unzip, open solution and run on an iOS device.
  2. Tap the hamburger menu -> Browse -> First item

Current Behavior

An Objective-C exception which crashes the application:

Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[Xamarin_Forms_Platform_iOS_ShellSectionRenderer_NavDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]: unrecognized selector sent to instance 0x123412300

Expected Behavior

No crash.

Version Used:

Xamarin.iOS master (dotnet/macios@1b09465) or 13.20.1.18 (haven't tried earlier versions).

This is mono's 2020-02 branch.

Problem

The problem is that mono's interpreter doesn't zero-extend bool arguments to fill 64-bit registers.

lldb session: https://gist.github.com/rolfbjarne/2bbd8b5971e37752b6a8098b23f9ea03

note how the x2 register isn't 0x1, but 0x0000000100000001 at the entry to the function: https://gist.github.com/rolfbjarne/2bbd8b5971e37752b6a8098b23f9ea03#file-lldb-session-L31

This is the P/Invoke in question:

[DllImport (LIBOBJC_DYLIB, EntryPoint="objc_msgSend")]
public extern static void void_objc_msgSend_bool (IntPtr receiver, IntPtr selector, bool arg1);

and it's called like something like this:

void_objc_msgSend_bool (ptr1, ptr2, true);

I tried figuring out if the ARM64 spec actually requires parameters in registers to be zero-extended to fill the entire 64-bit register, and I couldn't find any supporting statement. Arm's calling convention document (https://developer.arm.com/documentation/ihi0055/c/) seems to indicate any unused bits are undefined.

Apple has a few changes to the ARM64 calling convention (https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html), one seems particularly relevant:

The general ABI specifies that it is the callee’s responsibility to sign or zero-extend arguments having fewer than 32 bits, and that unused bits in a register are unspecified. In iOS, however, the caller must perform such extensions, up to 32 bits.

but that's only to extend to 32-bit, not 64-bit.

That being said, it doesn't matter much what the spec says, what matters is what clang thinks, and clang certainly seems to think that the bool arguments should be zero-extended to 64-bits.

The following test case:

void x (BOOL b);
void x (BOOL b)
{
    NSUInteger u = 0;
    u |= b;
    NSLog (@"u: %llx", (unsigned long long) u);
}

typedef void (y2) (NSUInteger b);
typedef void (x2) (BOOL b);

void testbool ()
{
    y2* z = (y2*) x;
    
    z (0x1111111111111111);
}

prints "u: 0x1" in Debug mode and "u: 0x1111111111111111" in release mode when added to an Xcode project and run on an iOS device.

Ref: xamarin/Xamarin.Forms#10519

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions