java proguard: library doesnt work after optimization - java

i am optimizing a jar with proguard, but it crashes after optimization.
here is my proguard task:
<proguard>
-injars ${dist}/${jarname}
-outjars ${dist}-proguard/${jarname}
-target 5
-libraryjars '${java.home}/lib/rt.jar'
-dontobfuscate
-optimizationpasses 4
-overloadaggressively
-repackageclasses ''
-allowaccessmodification
-keep public class * {
public static void main(java.lang.String[]);
}
</proguard>
as soon as i put in the -dontoptimize option, it works.
according to the stack of the exception it crashes when accessing a static public member of a class with a nullpointer. here is the code:
public static Texture ring, dust, spikering, thinring, crystal, clouds;
public static void init() {
Field [] fields = TexturePool.class.getDeclaredFields();
for (Field field : fields) {
if(field.getType() == Texture.class) {
field.set( null, /*imagine new object here*/ );
}
}
}
thanks!

ok, i just found out myself. i think the optimization completely optimized that classmembers away, since they are not directly accessed in this class. if i specify the option:
-keepclassmembers public class com.package.** {
public static * ;
}
it works even with optimization.

According to Best Java Obfuscation Application For Size Reduction:
"I was always able to fix the problem by not using the Proguard argument "-overloadaggressively"."
Perhaps you should try the same?
EDIT: The problem could easily be that an assignment is optimized away. The initializations happening in the source code, where a field is defined, is actually done by the compiler in a static code blokc. Appears that the optimizations tinker with that. What happens with fewer optimization passes?

I had the same issue with ProGuard optimizing away class fields that were modified using reflection API only. However, the suggested answer didn't work for me as there were too many classes scattered throughout the code base to specify class filter.
Instead, disabling field optimization did the trick for me:
-optimizations !field/*

Related

how to keep private members in pro-guard rules

I am trying to use Proguard to keep my private fields, but it won't work.
I stole most of this from Proguard keep classmembers because that question is similar to what I'm asking, and also followed this link How to tell ProGuard to keep private fields without specifying each field
But it still doesn't work.
I want to make a library for another company and still keep my access level modifiers fields and methods.
Proguard:
-keepclassmembers class com.example.mylibrary.Bedika {
private <fields>;
}
-keep class com.example.mylibrary.Bedika {
*;
}
My AAR library
public class Bedika {
private String stam;
public Bedika(String stam) {
this.stam = stam;
}
public void print() {
System.out.println(stam);
}
}
output after Proguard:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.example.mylibrary;
public class Bedika {
public String stam;
public Bedika(String var1) {
this.stam = var1;
}
public void print() {
System.out.println(this.stam);
}
}
It seems like R8 is causing this issue and not Proguard.
Go into gradle.properties, and set android.enableR8=false. Next time you build, it will use Proguard.
R8 is Google's answer to Proguard and in the recent versions of The Android Gradle Plugin (3.4.0+) it defaults to R8's code shrinker/obfuscator. There are some pros and cons to using Google's version instead of Guardsquare's technology.
For more information, look at the documentation
You can usually add the #Keep annotation above the class or object (in Java or Kotlin). This is pretty self-explanatory, and will work.

Proguard does not remove Log.d in internal classes?

I use
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
public static java.lang.String getStackTraceString(java.lang.Throwable);
}
However when I decompile, I see the one and only custom string I used in a Log.d line in decompiled jar file. How can that be possible? Doesn't Proguard remove Log.d lines completely?
This is relevant if proguard optimization is enabled i.e. your proguard.config is this
proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
Just for clarification, it's my understanding optimisation 'has' to been enabled for the log calls to be stripped.
You could try using a Log wrapper class (like this one) to wrap android.util.Log. I used this approach due to a note from Eric (a.k.a Mr Proguard) in this answer about how Proguard inspects the code. So this ends up with a simpler call to Log.d(tag, string) rather than Log.d(tag, string + value+ String)
Or use DexGuard IIRC is has some explicit function for log removal. --
[update: sorry my bad it's not a explicit function DexGuard uses the same config as you noted. I guess I was thinking the fact the decompiled code is more mangled with DexGuard.]

Running signed android app, NPE occurs, possibly due to proguard?

I have an android app I have been developing, and it's now ready to launch. It works great running from eclipse unsigned. When I export a signed copy and run it, an NPE occurs. I've tried creating the signed version with the latest supplied proguard files (proguard-android.txt and proguard-android-optimize.txt). No difference with either. The only mods I've made via my local proguard config file is to leave line numbers so I can get a decent stack trace.
The NPE is what has me scratching my head. I use a lock object for synchronization, and I initialize it in the definition of the object and also during stream initialization:
public class HttpRequest extends NetworkRequest {
private transient Object lock = new Object();
<snip>
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
lock = new Object();
}
In my debugging version, lock is never null, but in my signed version, when I go to use the lock object, it's null, consistently. I can only assume proguard is doing something I don't want, but this is the first time I've delved into proguard so I'm a bit out of my element.
Can anyone suggest how I can debug this further?
Proguard does three things: shrink, optimize, obfuscate.
In the shrink step, it cuts out any unused program code. What may be happening to you is that Proguard may be stripping away the readObject method because it appears to be unused. I would expect there to be a keep-rule for that method out of the box, but apparently there isn't, or at least not in the Android SDK.
One way to verify that would be to assemble your app with -dontobfuscate in the proguard config, then extracting the classes.dex file from the APK package and running dexdump classes.dex and looking for the presence or absence of the readObject method.
Ultimately, to ensure the readObject method is preserved, you would add the following rule to your ProGuard config:
-keepclassmembers class * implements java.io.Serializable {
private void readObject(java.io.ObjectInputStream);
}
Updated by mvsjes2 to add the config that actually worked for me:
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

How to remove log calls in J2SE/J2EE application (not Android)

I know that removing logger calls with Proguard works for Android applications.
How can one accomplish this in standard Java application?
import java.util.logging.Logger;
public class Clazz {
private static final Logger LOGGER = Logger.getLogger(Clazz.class.getName());
public void foo() {
LOGGER.info("bar");
}
}
in my Proguard configuration I have the following:
-assumenosideeffects class java.util.logging.Logger { *; }
-whyareyoukeeping class java.util.logging.Logger
which gives the following output when running:
[proguard] java.util.logging.Logger
[proguard] is a library class.
I understand that it's a library but I want to strip all calls to it anyway. Is this possible with Proguard? If not, why? How come this works so conveniently for Android, does the logger field or lack of it have something to do with this?
You should be able to remove logging calls like this, assuming you haven't disabled optimization -- it's the optimization step that removes unnecessary and unwanted calls. ProGuard can't remove the Logger class itself, since it is a run-time library class, as you've seen.
You mustn't use a wildcard for matching the methods though, since this includes essential methods like wait() and finalize() (affecting all classes). You'll have to enumerate the methods that you want to remove. For instance:
-assumenosideeffects class java.util.logging.Logger {
void info(java.lang.String);
}

Overloaded package-private method causes compilation failure - Is this a JLS oddity or javac bug?

I've come across an oddity of the JLS, or a JavaC bug (not sure which). Please read the following and provide an explanation, citing JLS passage or Sun Bug ID, as appropriate.
Suppose I have a contrived project with code in three "modules" -
API - defines the framework API - think Servlet API
Impl - defines the API implementation - think Tomcat Servlet container
App - the application I wrote
Here are the classes in each module:
API - MessagePrinter.java
package api;
public class MessagePrinter {
public void print(String message) {
System.out.println("MESSAGE: " + message);
}
}
API - MessageHolder.java (yes, it references an "impl" class - more on this later)
package api;
import impl.MessagePrinterInternal;
public class MessageHolder {
private final String message;
public MessageHolder(String message) {
this.message = message;
}
public void print(MessagePrinter printer) {
printer.print(message);
}
/**
* NOTE: Package-Private visibility.
*/
void print(MessagePrinterInternal printer) {
printer.print(message);
}
}
Impl - MessagePrinterInternal.java - This class depends on an API class. As the name suggests, it is intended for "internal" use elsewhere in my little framework.
package impl;
import api.MessagePrinter;
/**
* An "internal" class, not meant to be added to your
* application classpath. Think the Tomcat Servlet API implementation classes.
*/
public class MessagePrinterInternal extends MessagePrinter {
public void print(String message) {
System.out.println("INTERNAL: " + message);
}
}
Finally, the sole class in the App module...MyApp.java
import api.MessageHolder;
import api.MessagePrinter;
public class MyApp {
public static void main(String[] args) {
MessageHolder holder = new MessageHolder("Hope this compiles");
holder.print(new MessagePrinter());
}
}
So, now I attempt to compile my little application, MyApp.java. Suppose my API jars are exported via a jar, say api.jar, and being a good citizen I only referencd that jar in my classpath - not the Impl class shiped in impl.jar.
Now, obviously there is a flaw in my framework design in that the API classes shouldn't have any dependency on "internal" implementation classes. However, what came as a surprise is that MyApp.java didn't compile at all.
javac -cp api.jar src\MyApp.java
src\MyApp.java:11: cannot access impl.MessagePrinterInternal class file for impl.MessagePrinterInternal not found
holder.print(new MessagePrinter());
^
1 error
The problem is that the compiler is trying to resolve the version print() to use, due to method overloading. However, the compilation error is somewhat unexpected, as one of the methods is package-private, and therefore not visible to MyApp.
So, is this a javac bug, or some oddity of the JLS?
Compiler: Sun javac 1.6.0_14
There is is nothing wrong with JLS or javac. Of course this doesn't compile, because your class MessageHolder references MessagePrinterInternal which is not on the compile classpath if I understand your explanation right. You have to break this reference into the implementation, for example with an interface in your API.
EDIT 1: For clarification: This has nothing to do with the package-visible method as you seem to think. The problem is that the type MessagePrinterInternal is needed for compilation, but you don't have it on the classpath. You cannot expect javac to compile source code when it doesn't have access to referenced classes.
EDIT 2: I reread the code again and this is what seems to be happening: When MyApp is compiled, it tries to load class MessageHolder. Class MessageHolder references MessagePrinterInternal, so it tries to load that also and fails. I am not sure that is specified in the JLS, it might also depend on the JVM. In my experience with the Sun JVM, you need to have at least all statically referenced classes available when a class is loaded; that includes the types of fields, anything in the method signatures, extended classses and implemented interfaces. You could argue that this is counter-intuitive, but I would respond that in general there is very little you do with a class where such information is missing: you cannot instantiate objects, you cannot use the metadata (the Class object) etc. With that background knowledge, I would say the behavior you see is expected.
First off I would expect the things in the api package to be interfaces rather than classes (based on the name). Once you do this the problem will go away since you cannot have package access in interfaces.
The next thing is that, AFAIK, this is a Java oddity (in that it doesn't do what you would want). If you get rid of the public method and make the package on private you will get the same thing.
Changing everything in the api package to be interfaces will fix your problem and give you a cleaner separation in your code.
I guess you can always argue that javac can be a little bit smarter, but it has to stop somewhere. it's not human, human can always be smarter than a compiler, you can always find examples that make perfect sense for a human but dumbfound a compiler.
I don't know the exact spec on this matter, and I doubt javac authors made any mistake here. but who cares? why not put all dependencies in the classpath, even if some of them are superficial? doing that consistently makes our lives a lot easier.

Categories

Resources