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

Problem passing Union ByValue w/ leading int #1118

Closed
jeog opened this issue Jul 26, 2019 · 3 comments
Closed

Problem passing Union ByValue w/ leading int #1118

jeog opened this issue Jul 26, 2019 · 3 comments
Labels

Comments

@jeog
Copy link

jeog commented Jul 26, 2019

Apologies if this is something obvious but I've looked through the docs, google forum, and stack overflow and couldn't find anything.

I'm having an issue passing a union by value whose first field is an int. I've simplified the issue to the following:

(Note - the library is implemented in C++ and is accessed through a stable ABI layer in C; details below the code sections)

C/C++

typedef union{
    int a;
    double b;
    int c;
} test_union;

EXTERN_C_SPEC_ DLL_SPEC_ int
UnionTest_ABI( int a, int b, test_union test, int c, int d);

int
UnionTest_ABI( int a, int b, test_union test, int c, int d)
{
    switch(b){
    case 0:
        std::cout<< "UNIONTEST: 0 : a=" << a << " b=" << b << " VAL="
                 << test.a << " c=" << c << " d=" << d << std::endl;
        break;
    case 1:
        std::cout<< "UNIONTEST: 1 : a=" << a << " b=" << b << " VAL="
                 << test.b << " c=" << c << " d=" << d << std::endl;
        break;
    case 2:
        std::cout<< "UNIONTEST: 2 : a=" << a << " b=" << b << " VAL="
        << test.c << " c=" << c << " d=" << d << std::endl;
    }
    return 0;
}

Java

public interface CLib extends Library {
   public static class TestUnion extends Union {
        public int a;
        public double b;
        public int c;
      
        public static class ByValue extends TestUnion implements Union.ByValue {            
        };
    }

  int UnionTest_ABI( int a, int b, TestUnion.ByValue v, int c, int d);
}

Test

        CLib.TestUnion.ByValue testUnion = new CLib.TestUnion.ByValue();
        testUnion.c = 33; 
        testUnion.setType(Integer.TYPE);

        getCLib().UnionTest_ABI( 1, 2, testUnion, 7,8);

C Output

UNIONTEST: 2 : a=1 b=2 VAL=7 c=8 d=-737409248

The int/union is not being passed in and args 'c' and 'd' are one position to the 'left'. Some type of alignment issue I guess? As you can imagine this does or does not crash in different ways depending on the function.

But, if I change the Java Union to the following, it works:

    public static class TestUnion extends Union {
        public long a;
        public double b;
        public int c;
      
        public static class ByValue extends TestUnion implements Union.ByValue {            
        };
    }

JRE

# JRE version: OpenJDK Runtime Environment (8.0_212-b03) (build 1.8.0_212-8u212-b03-2~deb9u1-b03)
# Java VM: OpenJDK 64-Bit Server VM (25.212-b03 mixed mode linux-amd64 compressed oops)

JNA

Java Native Access (JNA) API Version 5
Version: 5.3.1 (b0)
 Native: 6.0.0 (147a998f0cbc89681a1ae6c0dd121629)
 Prefix: linux-x86-64

System

Linux debian 4.19.0-0.bpo.4-amd64 #1 SMP Debian 4.19.28-2~bpo9+1 (2019-03-27) x86_64 GNU/Linux

Lib

file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
g++ -std=c++0x -DTHIS_EXPORTS_INTERFACE -O0 -g3 -Wall -c --DDEBUG -fPIC 

Thanks,
jon

@matthiasblaesing
Copy link
Member

I think I can explain the problem - and indeed this is a bug. If I read the ABI document correctly https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf , Page 21+22), the union should be passed as an INTEGER class. But JNA sets the ffi_type of the union to double, which is class SSE. The size and also the alignment will be right, but the argument will be passed wrong.

INTEGER and SSE parameter are passed via different registers. And this also explains the visible effect, the integer parameter seem to be shifted, because native expects five parameters in INTEGER class registers, while ffi only passes four in integer registers.

I'm not yet sure how to fix this.

@jeog
Copy link
Author

jeog commented Jul 28, 2019

FWIW, by replacing the ints with longs in the (Java) union I was able to get it to work on x86 and x64, on windows and linux; but given my limited understanding of JNA/ffi I have no idea how fragile that temporary workaround is.

@matthiasblaesing
Copy link
Member

Fix was merged to master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants