java-native-access / java-native-access/jna
Incorrect maximum alignment for linux-x86_64
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 8.9k
- Forks
- 1.7k
- PR merge metrics
- No merged PRs in 30d
Description
JNA does not support the long double. While it is rarely used or necessary by itself, it has importance in constructs such as max_align_t, which may be used in APIs and structures that attempt to encapsulate generic data.
I specifically tested everything on Ubuntu 17.04 with default packages. This is my environment:
kfyatek@kfyatek:/tmp$ uname -a
Linux kfyatek 4.10.0-33-generic #37-Ubuntu SMP Fri Aug 11 10:55:28 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
kfyatek@kfyatek:/tmp$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 6.3.0-12ubuntu2' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.3.0 20170406 (Ubuntu 6.3.0-12ubuntu2)
kfyatek@kfyatek:/tmp$ java -version
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2ubuntu1.17.04.3-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
On my system, the ABI defines the maximum alignment as 16 bytes:
kfyatek@kfyatek:/tmp$ cat test.c
#include <stddef.h>
#include <stdio.h>
typedef struct {
char ch;
max_align_t align;
} test_t;
int main() {
printf("%d\n", (int) _Alignof(max_align_t));
printf("%d\n", (int) offsetof(test_t, align));
}
kfyatek@kfyatek:/tmp$ gcc test.c -o test
kfyatek@kfyatek:/tmp$ ./test
16
16
The same result can be achieved by writing something like a "poor man's max_align_t" manually:
kfyatek@kfyatek:/tmp$ cat test2.c
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
typedef union {
void *data_pointer;
void (*code_pointer)();
long double largest_float;
intmax_t largest_integer;
} poor_mans_max_align_t;
typedef struct {
char ch;
poor_mans_max_align_t align;
} test_t;
int main() {
printf("%d\n", (int) _Alignof(poor_mans_max_align_t));
printf("%d\n", (int) offsetof(test_t, align));
}
kfyatek@kfyatek:/tmp$ gcc test2.c -o test2
kfyatek@kfyatek:/tmp$ ./test2
16
16
Or even just long double alone:
kfyatek@kfyatek:/tmp$ cat test3.c
#include <stddef.h>
#include <stdio.h>
typedef struct {
char ch;
long double ld;
} test_t;
int main() {
printf("%d\n", (int) _Alignof(long double));
printf("%d\n", (int) offsetof(test_t, ld));
}
kfyatek@kfyatek:/tmp$ gcc test3.c -o test3
kfyatek@kfyatek:/tmp$ ./test3
16
16
Now, while I cannot meaningfully access long double via JNA, I can encapsulate it in a structure with appropriate size and alignment. But when I try to use it in another structure, things doesn't go so smoothly:
kfyatek@kfyatek:/tmp$ cat Test.java
import com.sun.jna.*;
import java.util.*;
public class Test {
public static class NativeLongDouble extends Structure {
public byte[] data = new byte[16];
public NativeLongDouble() {
super();
}
public NativeLongDouble(Pointer peer) {
super(peer);
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("data");
}
@Override
protected int getNativeAlignment(Class<?> type, Object value, boolean isFirstElement) {
return 16;
}
}
public static class TestStruct extends Structure {
public byte ch;
public NativeLongDouble ld;
public TestStruct() {
super();
}
public TestStruct(Pointer peer) {
super(peer);
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("ch", "ld");
}
}
public static void main(String[] args) {
System.out.println(new TestStruct());
}
}
kfyatek@kfyatek:/tmp$ javac -cp jna-4.4.0.jar Test.java
kfyatek@kfyatek:/tmp$ java -cp jna-4.4.0.jar:. Test
Test$TestStruct(auto-allocated@0x7f02bc213f70 (24 bytes)) {
byte ch@0=0
Test$NativeLongDouble ld@8=Test$NativeLongDouble(native@0x0) (16 bytes) {
byte data[16]@0=[B@7ef20235
}
}
Note: for more accurate code, I should query some native library; I actually attempted accessing com.sun.jna.Structure$FFIType$FFITypes via reflection in my more production-ish attempt to solve this problem; I hardcoded 16 here for brevity.
As you can see, we cannot reliably reproduce layout of any struct that includes a long double (or anything derived from it, such as max_align_t) with JNA on Linux x86_64.
This seems to stem from two reasons:
Native.MAX_ALIGNMENTis hardcoded to 8 on most platforms, including all Linuxes. On other platforms it's defined toLONG_SIZE, which on Linux x86_64 is 8 anyway; even though real maximum alignment for Linux x86_64 is 16, as demonstrated above.Structure::getNativeAlignment()limits alignment toNative.MAX_ALIGNMENTviaMath.min(), so even if the structure's alignment requirements are properly calculated as 16, it gets clamped to 8 due to that, unlessgetNativeAlignment()is overloaded for all outer structures as well.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Native.MAX_ALIGNMENT and Structure.getNativeAlignment(), the two locations identified in the report, and compare their behavior for Linux x86_64 with the provided C alignment examples. Reproduce the nested TestStruct layout using the supplied Java example; done means structures containing a 16-byte-aligned value match the native offsets instead of being clamped to 8 bytes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- devtools, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100