From 1c56c7e7507dc24ae1ed2f693c793d94df814c76 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Sun, 14 Feb 2010 20:04:59 +0000 Subject: [PATCH] Add finer grained control of what register information is printed out git-svn-id: https://smali.googlecode.com/svn/trunk@625 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../baksmali/Adaptors/MethodDefinition.java | 253 ++++++++++++++++-- .../main/java/org/jf/baksmali/baksmali.java | 8 +- .../src/main/java/org/jf/baksmali/main.java | 61 ++++- .../Code/Analysis/AnalyzedInstruction.java | 47 ++++ .../jf/dexlib/Code/Analysis/ClassPath.java | 6 +- .../dexlib/Code/Analysis/MethodAnalyzer.java | 6 +- 6 files changed, 342 insertions(+), 39 deletions(-) diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index 75824300..6ee76ecc 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -30,15 +30,14 @@ package org.jf.baksmali.Adaptors; import org.jf.baksmali.Adaptors.Format.*; import org.jf.baksmali.baksmali; +import org.jf.baksmali.main; import org.jf.dexlib.*; +import org.jf.dexlib.Code.*; import org.jf.dexlib.Code.Analysis.AnalyzedInstruction; import org.jf.dexlib.Code.Analysis.MethodAnalyzer; import org.jf.dexlib.Code.Analysis.RegisterType; import org.jf.dexlib.Code.Analysis.ValidationException; import org.jf.dexlib.Debug.DebugInstructionIterator; -import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.OffsetInstruction; import org.jf.dexlib.Util.AccessFlags; import org.antlr.stringtemplate.StringTemplateGroup; import org.antlr.stringtemplate.StringTemplate; @@ -57,6 +56,8 @@ public class MethodDefinition { private final SparseIntArray sparseSwitchMap; private final SparseIntArray instructionMap; + private final int registerCount; + public MethodDefinition(StringTemplateGroup stg, ClassDataItem.EncodedMethod encodedMethod) { this.stg = stg; this.encodedMethod = encodedMethod; @@ -71,6 +72,8 @@ public class MethodDefinition { sparseSwitchMap = new SparseIntArray(1); instructionMap = new SparseIntArray(instructions.length); + registerCount = encodedMethod.codeItem.getRegisterCount(); + int currentCodeAddress = 0; for (int i=0; i 1 || i == 0) { - methodItems.add(new CommentMethodItem(stg, getPreInstructionRegisterString(instruction), - currentCodeAddress, Integer.MIN_VALUE)); - } - methodItems.add(new CommentMethodItem(stg, getPostInstructionRegisterString(instruction), - currentCodeAddress, Integer.MAX_VALUE-1)); - } + if (baksmali.registerInfo != 0) { + printPreRegister.clear(); + printPostRegister.clear(); + if ((baksmali.registerInfo & main.ALL) != 0) { + printPreRegister.set(0, registerCount); + printPostRegister.set(0, registerCount); + } else { + if ((baksmali.registerInfo & main.ALLPRE) != 0) { + printPreRegister.set(0, registerCount); + } else { + if ((baksmali.registerInfo & main.ARGS) != 0) { + addArgsRegs(printPreRegister, instruction); + } + if ((baksmali.registerInfo & main.MERGE) != 0) { + addMergeRegs(printPreRegister, instructions, i); + } else if ((baksmali.registerInfo & main.FULLMERGE) != 0 && + (i == 0 || instruction.isBeginningInstruction())) { + addParamRegs(printPreRegister); + } + } + + if ((baksmali.registerInfo & main.ALLPOST) != 0) { + printPostRegister.set(0, registerCount); + } else if ((baksmali.registerInfo & main.DEST) != 0) { + addDestRegs(printPostRegister, instruction); + } + } + + if ((baksmali.registerInfo & main.FULLMERGE) != 0) { + addFullMergeRegs(printPreRegister, methodItems, methodAnalyzer, i, currentCodeAddress); + } + + if (!printPreRegister.isEmpty()) { + methodItems.add(new CommentMethodItem(stg, + getPreInstructionRegisterString(instruction, printPreRegister), + currentCodeAddress, 99.9)); + } + + if (!printPostRegister.isEmpty()) { + methodItems.add(new CommentMethodItem(stg, + getPostInstructionRegisterString(instruction, printPostRegister), + currentCodeAddress, 100.1)); + } + } currentCodeAddress += instruction.instruction.getSize(currentCodeAddress); } @@ -300,13 +344,168 @@ public class MethodDefinition { return methodItems; } - private String getPreInstructionRegisterString(AnalyzedInstruction instruction) { + private void addArgsRegs(BitSet printPreRegister, AnalyzedInstruction analyzedInstruction) { + if (analyzedInstruction.instruction instanceof RegisterRangeInstruction) { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; + + printPreRegister.set(instruction.getStartRegister(), + instruction.getStartRegister() + instruction.getRegCount()); + } else if (analyzedInstruction.instruction instanceof FiveRegisterInstruction) { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; + int regCount = instruction.getRegCount(); + switch (regCount) { + case 5: + printPreRegister.set(instruction.getRegisterA()); + //fall through + case 4: + printPreRegister.set(instruction.getRegisterG()); + //fall through + case 3: + printPreRegister.set(instruction.getRegisterF()); + //fall through + case 2: + printPreRegister.set(instruction.getRegisterE()); + //fall through + case 1: + printPreRegister.set(instruction.getRegisterD()); + } + } else if (analyzedInstruction.instruction instanceof ThreeRegisterInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + printPreRegister.set(instruction.getRegisterA()); + printPreRegister.set(instruction.getRegisterB()); + printPreRegister.set(instruction.getRegisterC()); + } else if (analyzedInstruction.instruction instanceof TwoRegisterInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + printPreRegister.set(instruction.getRegisterA()); + printPreRegister.set(instruction.getRegisterB()); + } else if (analyzedInstruction.instruction instanceof SingleRegisterInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + printPreRegister.set(instruction.getRegisterA()); + } + } + + private void addFullMergeRegs(BitSet printPreRegister, List methodItems, MethodAnalyzer methodAnalyzer, + int instructionNum, int currentCodeAddress) { + if (instructionNum == 0) { + return; + } + + //MethodAnalyzer cached the analyzedInstructions, so we're not actually re-analyzing, just getting the + //instructions that have already been analyzed + AnalyzedInstruction[] instructions = methodAnalyzer.analyze(); + AnalyzedInstruction instruction = instructions[instructionNum]; + + if (instruction.getPredecessorCount() <= 1) { + return; + } + + StringBuffer sb = new StringBuffer(); + + for (int registerNum=0; registerNum= 0; + registerNum = registers.nextSetBit(registerNum + 1)) { + + RegisterType registerType = instruction.getPreInstructionRegisterType(registerNum); + sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum)); sb.append("="); if (registerType == null) { sb.append("null"); @@ -319,22 +518,26 @@ public class MethodDefinition { return sb.toString(); } - private String getPostInstructionRegisterString(AnalyzedInstruction instruction) { + private String getPostInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) { StringBuilder sb = new StringBuilder(); - for (int i=0; i= 0; + registerNum = registers.nextSetBit(registerNum + 1)) { + + RegisterType registerType = instruction.getPostInstructionRegisterType(registerNum); + sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum)); sb.append("="); - sb.append(registerType.toString()); + if (registerType == null) { + sb.append("null"); + } else { + sb.append(registerType.toString()); + } sb.append(";"); } return sb.toString(); } - private void addTries(List methodItems) { if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) { return; diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index ff590256..4cab033c 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -45,23 +45,23 @@ public class baksmali { public static boolean useLocalsDirective = false; public static boolean useSequentialLabels = false; public static boolean outputDebugInfo = true; - public static boolean verboseRegisterInfo = false; + public static int registerInfo = 0; public static String bootClassPath; public static DeodexUtil deodexUtil = null; public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory, String bootClassPath, boolean noParameterRegisters, boolean useLocalsDirective, boolean useSequentialLabels, - boolean outputDebugInfo, boolean verboseRegisterInfo) + boolean outputDebugInfo, int registerInfo) { baksmali.noParameterRegisters = noParameterRegisters; baksmali.useLocalsDirective = useLocalsDirective; baksmali.useSequentialLabels = useSequentialLabels; baksmali.outputDebugInfo = outputDebugInfo; - baksmali.verboseRegisterInfo = verboseRegisterInfo; + baksmali.registerInfo = registerInfo; baksmali.bootClassPath = bootClassPath; - if (verboseRegisterInfo) { + if (registerInfo != 0) { ClassPath.InitializeClassPath(bootClassPath==null?null:bootClassPath.split(":"), dexFile); } diff --git a/baksmali/src/main/java/org/jf/baksmali/main.java b/baksmali/src/main/java/org/jf/baksmali/main.java index 82600295..1a19f1ba 100644 --- a/baksmali/src/main/java/org/jf/baksmali/main.java +++ b/baksmali/src/main/java/org/jf/baksmali/main.java @@ -31,6 +31,14 @@ public class main { private static final Options options; + public static final int ALL = 1; + public static final int ALLPRE = 2; + public static final int ALLPOST = 4; + public static final int ARGS = 8; + public static final int DEST = 16; + public static final int MERGE = 32; + public static final int FULLMERGE = 64; + static { options = new Options(); buildOptions(); @@ -75,7 +83,8 @@ public class main { boolean useLocalsDirective = false; boolean useSequentialLabels = false; boolean outputDebugInfo = true; - boolean verboseRegisterInfo = false; + + int registerInfo = 0; String outputDirectory = "out"; String dumpFileName = null; @@ -147,7 +156,36 @@ public class main { } if (commandLine.hasOption("r")) { - verboseRegisterInfo = true; + String[] values = commandLine.getOptionValues('r'); + + if (values == null || values.length == 0) { + registerInfo = ARGS | DEST | MERGE; + } else { + for (String value: values) { + if (value.equalsIgnoreCase("ALL")) { + registerInfo |= ALL; + } else if (value.equalsIgnoreCase("ALLPRE")) { + registerInfo |= ALLPRE; + } else if (value.equalsIgnoreCase("ALLPOST")) { + registerInfo |= ALLPOST; + } else if (value.equalsIgnoreCase("ARGS")) { + registerInfo |= ARGS; + } else if (value.equalsIgnoreCase("DEST")) { + registerInfo |= DEST; + } else if (value.equalsIgnoreCase("MERGE")) { + registerInfo |= MERGE; + } else if (value.equalsIgnoreCase("FULLMERGE")) { + registerInfo |= FULLMERGE; + } else { + usage(); + return; + } + } + + if ((registerInfo & FULLMERGE) != 0) { + registerInfo &= ~MERGE; + } + } } if (commandLine.hasOption("c")) { @@ -210,7 +248,7 @@ public class main { if (disassemble) { baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters, - useLocalsDirective, useSequentialLabels, outputDebugInfo, verboseRegisterInfo); + useLocalsDirective, useSequentialLabels, outputDebugInfo, registerInfo); } if ((doDump || write) && !dexFile.isOdex()) { @@ -238,6 +276,7 @@ public class main { */ private static void usage() { HelpFormatter formatter = new HelpFormatter(); + formatter.setWidth(100); formatter.printHelp("java -jar baksmali.jar [options] ", "disassembles and/or dumps a dex file", options, ""); } @@ -319,8 +358,18 @@ public class main { .withDescription("don't write out debug info (.local, .param, .line, etc.)") .create("b"); - Option verboseRegisterInfoOption = OptionBuilder.withLongOpt("verbose-registers") - .withDescription("print verbose register type information for each instruction") + Option registerInfoOption = OptionBuilder.withLongOpt("register-info") + .hasOptionalArgs() + .withArgName("REGISTER_INFO_TYPES") + .withValueSeparator(',') + .withDescription("print the specificed type(s) of register information for each instruction. " + + "\"ARGS,DEST,MERGE\" is the default if no types are specified.\nValid values are:\nALL: all " + + "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " + + "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " + + "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " + + "pre-instruction register has been merged from more than 1 different post-instruction " + + "register from its predecessors\nFULLMERGE: For each register that would be printed by " + + "MERGE, also show the incoming register types that were merged") .create("r"); Option classPathOption = OptionBuilder.withLongOpt("bootclasspath") @@ -342,7 +391,7 @@ public class main { options.addOption(useLocalsOption); options.addOption(sequentialLabelsOption); options.addOption(noDebugInfoOption); - options.addOption(verboseRegisterInfoOption); + options.addOption(registerInfoOption); options.addOption(classPathOption); } } \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java index 7dd57cd4..e6201d7b 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java @@ -6,7 +6,9 @@ import org.jf.dexlib.ItemType; import org.jf.dexlib.MethodIdItem; import org.jf.dexlib.Util.ExceptionWithContext; +import java.util.Collections; import java.util.LinkedList; +import java.util.List; public class AnalyzedInstruction { /** @@ -63,7 +65,24 @@ public class AnalyzedInstruction { return predecessors.size(); } + public List getPredecessors() { + return Collections.unmodifiableList(predecessors); + } + + private boolean checkPredecessorSorted(AnalyzedInstruction predecessor) { + if (predecessors.size() == 0) { + return true; + } + + if (predecessor.getInstructionIndex() <= predecessors.getLast().getInstructionIndex()) { + return false; + } + + return true; + } + protected void addPredecessor(AnalyzedInstruction predecessor) { + assert checkPredecessorSorted(predecessor); predecessors.add(predecessor); } @@ -80,6 +99,34 @@ public class AnalyzedInstruction { return true; } + public int getSuccessorCount() { + return successors.size(); + } + + public List getSuccesors() { + return Collections.unmodifiableList(successors); + } + + /** + * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction + * that can be the first successfully executed instruction in the method. The first instruction is always a + * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then + * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise, + * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the + * corresponding exception handler is a beginning instruction, etc. + * @return a boolean value indicating whether this instruction is a beginning instruction + */ + public boolean isBeginningInstruction() { + if (predecessors.size() == 0) { + return false; + } + + if (predecessors.getFirst().instructionIndex == -1) { + return true; + } + return false; + } + /* * Sets the "post-instruction" register type as indicated. This should only be used to set * the method parameter types for the "start of method" instruction, or to set the register diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java index 7cb49266..f73e0957 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java @@ -94,7 +94,7 @@ public class ClassPath { if (classType.charAt(0) == '[') { return theClassPath.createArrayClassDef(classType); } else { - //TODO: we should output a warning + //TODO: we should output a warning return theClassPath.createUnresolvedClassDef(classType); } } @@ -571,12 +571,12 @@ public class ClassPath { if (interfaces != null) { for (TypeIdItem interfaceType: interfaces.getTypes()) { ClassDef interfaceDef = ClassPath.getClassDef(interfaceType.getTypeDescriptor()); - assert interfaceDef.isInterface; + assert interfaceDef.isInterface(); implementedInterfaceSet.add(interfaceDef); interfaceDef = interfaceDef.getSuperclass(); while (!interfaceDef.getClassType().equals("Ljava/lang/Object;")) { - assert interfaceDef.isInterface; + assert interfaceDef.isInterface(); implementedInterfaceSet.add(interfaceDef); interfaceDef = interfaceDef.getSuperclass(); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java index 89f43d44..c1b9c49c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -183,6 +183,10 @@ public class MethodAnalyzer { (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; } + public AnalyzedInstruction getStartOfMethod() { + return startOfMethod; + } + public AnalyzedInstruction[] makeInstructionArray() { AnalyzedInstruction[] instructionArray = new AnalyzedInstruction[instructions.size()]; for (int i=0; i