Skip to content

Passing structs by value on the stack is incorrect on arm64 #1259

@rfk

Description

@rfk

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions