-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
The version of libffi bundled with JNA appears to incorrectly pass structs on the stack on arm64 devices.
Consider a function that takes struct arguments by value, like this example which just returns a field from its ninth struct argument:
typedef struct WrappedLong {
int64_t inner;
} WrappedLong;
int64_t return_the_ninth_argument(WrappedLong arg1, WrappedLong arg2, WrappedLong arg3, WrappedLong arg4, WrappedLong arg5, WrappedLong arg6, WrappedLong arg7, WrappedLong arg8, WrappedLong arg9) {
return arg9.inner;
}
When I try to call this function via JNA on an arm64 device, is returns a large and random-looking integer rather than the value passed in the ninth argument. I've published a minimal example to show how I'm calling this code via JNA, since the declarations seem like they would be a bit unwieldy copy-pasted into a github comment:
The linked file is part of a tiny Android application that I have been using to investigate this issue and which can be used to reproduce it in full: rfk/arm64-jna-structbug.
Running this example app works as expected on an x86_64 device, and it works as expected on an arm64 device if I modify it to return e.g. the eight argument rather than the ninth.
I believe this to be a bug in the libffi bundled with JNA:
- The first 8 arguments to this function are passed in registers, but subsequent arguments are passed on the stack.
- The code for passing small structs on the stack appears to copy a pointer to the argument value rather than copying the argument value itself.
If I change the linked line of code to memcpy from ecif->avalue[i] instead of ecif->avalue + i then my example app seems to work as expected, correctly returning the value passed as the ninth argument.
I took a quick look in the equivalent code in upstream libffi and the issue seems to be fixed there, although as far as I can tell it was fixed as a side-effect of this significant refactor/cleanup, so I'm not sure if it was ever reported upstream as a bug.
Since upstream libffi seems to have diverged quite a bit from the version in this repo, I wanted to ask for advice before proceeding any further here. Would this be best approached by trying to update the bundled version of libffi, or as a standalone fix?
For completeness: I reproduced this issue on a Samsung Galaxy S8 running Android 9, which is an arm64-v8a device, using both the released JNA 5.6.0 and a local build of the latest JNA master from github.