javaproguardjnaoshi

Exception in getFieldOrder() when obfuscating JNA Structure using progaurd


My un-obfuscated code works..

When run through Proguard obfuscation -- just 1 pass optimization I am getting the following error:

Exception in thread "main" java.lang.Error: Structure.getFieldOrder() on class com.sun.jna.platform.mac.SystemB$Timeval does not provide enough names [0] ([]) to match declared fields [2] ([tv_sec, tv_usec])
    at com.sun.jna.Structure.getFields(Unknown Source)
    at com.sun.jna.Structure.deriveLayout(Unknown Source)
    at com.sun.jna.Structure.calculateSize(Unknown Source)
    at com.sun.jna.Structure.allocateMemory(Unknown Source)
    at com.sun.jna.Structure.ensureAllocated(Unknown Source)
    at com.sun.jna.Structure.ensureAllocated(Unknown Source)
    at com.sun.jna.Structure.getPointer(Unknown Source)
    at oshi.util.platform.mac.SysctlUtil.sysctl(Unknown Source)
    at oshi.software.os.mac.MacOperatingSystem.<clinit>(Unknown Source)
    at oshi.SystemInfo.createOperatingSystem(Unknown Source)
    at oshi.util.Memoizer$1.get(Unknown Source)
    at oshi.SystemInfo.getOperatingSystem(Unknown Source)

I tried several on keep directive as follows:

-keep class com.sun.** { *; }

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class com.sun.jna.**{ *; }
-keep class * implements  com.sun.jna.**{ *; }

-keep class oshi.**{ *; }
-keep class * implements  oshi.**{ *; }

-keepclassmembers class * extends com.sun.jna.** {
    <fields>;
   <methods>;
}

-keepclassmembers class * extends oshi.** {
    <fields>;
   <methods>;
}

How can I configure Proguard to avoid this exception?


Solution

  • Beginning with JNA 5.x, the Structure class fields are identified with the @FieldOrder annotation. For the Timeval structure causing your problem, it's defined like this:

    @Structure.FieldOrder({ "tv_sec", "tv_usec" })
    class Timeval extends Structure {
        public NativeLong tv_sec; // seconds
        public int tv_usec; // microseconds
    }
    

    The stack trace indicates it's finding the public fields (correctly) but not finding the field order declared by the annotation.

    This question indicates that Proguard will obfuscate annotations, which is likely the source of the problem.

    JNA's FAQ suggests the following syntax. It's possible the more broad wildcard definition is necessary rather than explicitly defining <fields> and <methods>.

    -dontwarn java.awt.*
    -keep class com.sun.jna.* { *; }
    -keepclassmembers class * extends com.sun.jna.* { public *; }
    

    Since the annotation is defined in JNA's Structure class as an @interface, the solution suggested by answers to this question indicate the following should fix the problem:

    # Annotated interfaces (including methods which are also kept in implementing classes)
    -keep @com.sun.jna.Structure.FieldOrder interface * {
        *;
    }
    

    Other solutions that may work, more broadly: