Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Varargs support on Apple Silicon #105

Closed
headius opened this issue May 5, 2021 · 6 comments · Fixed by #121
Closed

Varargs support on Apple Silicon #105

headius opened this issue May 5, 2021 · 6 comments · Fixed by #121
Milestone

Comments

@headius
Copy link
Member

headius commented May 5, 2021

The ABI for varargs on Apple Silicon is different from other platforms, though the specifics are not clear.

https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms

The issue manifests in the tests for jnr-ffi, and appears to be alignment problems with the values passed into snprintf. For example, formatting a 5-digit integer ends up writing 10 bytes, failing the test and producing a bogus result.

My exploration of this has led around in circles. The logic we have for varargs lives entirely in jnr-ffi and mostly just turns the variable arguments into part of the same argument buffer passed to native code, which is then converted into arguments using alloca or similar before doing the FFI call. It may be possible to fix the issue by changing how these arguments are structured, but I suspect that we will need to modify the logic in jffi that calls into libffi. Specifically, I believe we need to replicate this piece of code from the FFI gem:

https://github.com/ffi/ffi/blob/master/ext/ffi_c/Variadic.c#L244-L248

This logic to use ffi_prep_cif_var appears to have been added to FFI by Wayne Meissner, the original author of the JNR libraries, but never translated to jffi. This function appears to be the proper way to tell FFI that you are doing a varargs call so it can set up the stack properly for the current platform.

In order to use this, we will need to separate the variadic function call logic into a separate path, perhaps in CallContext.c.

I am hoping some folks that have worked on the FFI gem more than I can help clarify this a bit. Pinging @larskanis @ahorek. At this point I am tempted to reach out to Wayne and see if I can bribe him into one last bit of work on jffi, since this is a bit outside my expertise.

I am on the fence about releasing preliminary Apple Silicon support without fixing this. Everything except varargs is working, but unfortunately varargs are hard to avoid even within basic operations in JRuby.

@headius headius added this to the 1.4.0 milestone May 5, 2021
@JornVernee
Copy link

JornVernee commented May 6, 2021

(chiming in from twitter)

The ABI for varargs on Apple Silicon is different from other platforms, though the specifics are not clear.

We recently had a patch against the project Panama FFI as well to address this. AFAIK the only difference is that, where Linux/AArch64 passes variadic arguments in the 'usual' registers compared to a regular call, MacOS/AArch64 switches this up by always passing variadic arguments on the stack. (this greatly simplifies the underlying implementation of va_list and related macros, so I can see why it was done).

A good place to look for more info on the ABI is probably, unfortunately, the implementation. I.e. how va_list and the va_start, va_arg, and va_end macros are defined in the standard C library headers. This is what really made it click for me when looking into va_list on Windows.

I'd expect some form of special handling of variadic arguments to exist already, since on Windows/x64 variadic floating point arguments require special handling as well (need to be passed in both GP and FP regs). So, that might also be something to look for. I guess this is what ffi_prep_cif_var is doing, since it needs to know the number of fixed args.

Though, I don't really know about the jffi specifics. Hope that helps, at all.

@headius
Copy link
Member Author

headius commented May 13, 2021

@JornVernee Thank you! I am hoping the "simple" answer would be to split the variadic call path off from the fixed arg path and use this ffi_prep_cif_var function to set up varargs rather than trying to stitch it together from the Java side.

I will have a look at that patch and the va_list impl on Darwin to see if there's any simple fix I can do before going to the heavier lifting work in C.

@charleskorn
Copy link
Contributor

I hate to be the person that asks this question... are there any updates on this? Without support for varargs, functions like ioctl can't be used. (I get either crashes or errors about bad memory addresses when I try.)

Alternatively, are there any workarounds?

@headius
Copy link
Member Author

headius commented Jan 5, 2022

See #121 and related PRs! Should be able to get releases out this week with greatly improved M1 support.

@charleskorn
Copy link
Contributor

Awesome, thanks @headius!

@headius headius modified the milestones: 1.4.0, 1.3.9 Jan 6, 2022
@headius
Copy link
Member Author

headius commented Jan 6, 2022

Fixed by #121. Releasing today!

@headius headius closed this as completed Jan 6, 2022
@headius headius linked a pull request Jan 6, 2022 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants