Add ability to list methods/fields/types when assembling with smali

This can be useful in analyzing why you are going over the 64k
method/field/type limit
This commit is contained in:
Ben Gruver 2015-09-05 16:10:26 -07:00 committed by Connor Tumbleson
parent 52fd17a436
commit 7a3d09cc47
2 changed files with 113 additions and 0 deletions

View File

@ -58,6 +58,7 @@ import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.iface.reference.TypeReference;
import org.jf.dexlib2.util.InstructionUtil;
import org.jf.dexlib2.util.MethodUtil;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.dexlib2.writer.io.DeferredOutputStream;
import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory;
import org.jf.dexlib2.writer.io.DexDataStore;
@ -196,6 +197,33 @@ public abstract class DexWriter<
classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
}
@Nonnull
public List<String> getMethodReferences() {
List<String> methodReferences = Lists.newArrayList();
for (Entry<? extends MethodRefKey, Integer> methodReference: methodSection.getItems()) {
methodReferences.add(ReferenceUtil.getMethodDescriptor(methodReference.getKey()));
}
return methodReferences;
}
@Nonnull
public List<String> getFieldReferences() {
List<String> fieldReferences = Lists.newArrayList();
for (Entry<? extends FieldRefKey, Integer> fieldReference: fieldSection.getItems()) {
fieldReferences.add(ReferenceUtil.getFieldDescriptor(fieldReference.getKey()));
}
return fieldReferences;
}
@Nonnull
public List<String> getTypeReferences() {
List<String> classReferences = Lists.newArrayList();
for (Entry<? extends TypeKey, Integer> typeReference: typeSection.getItems()) {
classReferences.add(typeReference.getKey().toString());
}
return classReferences;
}
public void writeTo(@Nonnull DexDataStore dest) throws IOException {
this.writeTo(dest, MemoryDeferredOutputStream.getFactory());
}

View File

@ -28,7 +28,9 @@
package org.jf.smali;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenSource;
@ -113,6 +115,15 @@ public class main {
boolean printTokens = false;
boolean experimental = false;
boolean listMethods = false;
String methodListFilename = null;
boolean listFields = false;
String fieldListFilename = null;
boolean listTypes = false;
String typeListFilename = null;
int apiLevel = 15;
String outputDexFile = "out.dex";
@ -153,6 +164,18 @@ public class main {
case 'j':
jobs = Integer.parseInt(commandLine.getOptionValue("j"));
break;
case 'm':
listMethods = true;
methodListFilename = commandLine.getOptionValue("m");
break;
case 'f':
listFields = true;
fieldListFilename = commandLine.getOptionValue("f");
break;
case 't':
listTypes = true;
typeListFilename = commandLine.getOptionValue("t");
break;
case 'V':
verboseErrors = true;
break;
@ -232,6 +255,27 @@ public class main {
System.exit(1);
}
if (listMethods) {
if (Strings.isNullOrEmpty(methodListFilename)) {
methodListFilename = outputDexFile + ".methods";
}
writeReferences(dexBuilder.getMethodReferences(), methodListFilename);
}
if (listFields) {
if (Strings.isNullOrEmpty(fieldListFilename)) {
fieldListFilename = outputDexFile + ".fields";
}
writeReferences(dexBuilder.getFieldReferences(), fieldListFilename);
}
if (listTypes) {
if (Strings.isNullOrEmpty(typeListFilename)) {
typeListFilename = outputDexFile + ".types";
}
writeReferences(dexBuilder.getTypeReferences(), typeListFilename);
}
dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
} catch (RuntimeException ex) {
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
@ -244,6 +288,23 @@ public class main {
}
}
private static void writeReferences(List<String> references, String filename) {
PrintWriter writer = null;
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
for (String reference: Ordering.natural().sortedCopy(references)) {
writer.println(reference);
}
} catch (IOException ex) {
throw new RuntimeException(ex);
} finally {
if (writer != null) {
writer.close();
}
}
}
private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) {
File[] files = dir.listFiles();
if (files != null) {
@ -378,6 +439,27 @@ public class main {
.withArgName("API_LEVEL")
.create("a");
Option listMethodsOption = OptionBuilder.withLongOpt("list-methods")
.withDescription("Lists all the method references to FILE" +
" (<output_dex_filename>.methods by default)")
.hasOptionalArg()
.withArgName("FILE")
.create("m");
Option listFieldsOption = OptionBuilder.withLongOpt("list-fields")
.withDescription("Lists all the field references to FILE" +
" (<output_dex_filename>.fields by default)")
.hasOptionalArg()
.withArgName("FILE")
.create("f");
Option listClassesOption = OptionBuilder.withLongOpt("list-types")
.withDescription("Lists all the type references to FILE" +
" (<output_dex_filename>.types by default)")
.hasOptionalArg()
.withArgName("FILE")
.create("t");
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
.withDescription("enable experimental opcodes to be assembled, even if they " +
" aren't necessarily supported by the Android runtime yet")
@ -405,6 +487,9 @@ public class main {
basicOptions.addOption(apiLevelOption);
basicOptions.addOption(experimentalOption);
basicOptions.addOption(jobsOption);
basicOptions.addOption(listMethodsOption);
basicOptions.addOption(listFieldsOption);
basicOptions.addOption(listClassesOption);
debugOptions.addOption(verboseErrorsOption);
debugOptions.addOption(printTokensOption);