diff --git a/baksmali/src/main/java/org/jf/baksmali/main.java b/baksmali/src/main/java/org/jf/baksmali/main.java index 7e7c15a6..510e6505 100644 --- a/baksmali/src/main/java/org/jf/baksmali/main.java +++ b/baksmali/src/main/java/org/jf/baksmali/main.java @@ -29,6 +29,7 @@ package org.jf.baksmali; import org.apache.commons.cli.*; +import org.jf.dexlib.Code.Opcode; import org.jf.dexlib.DexFile; import org.jf.util.ConsoleUtil; import org.jf.util.smaliHelpFormatter; @@ -108,6 +109,8 @@ public class main { boolean verify = false; boolean ignoreErrors = false; + int apiLevel = 14; + int registerInfo = 0; String outputDirectory = "out"; @@ -208,6 +211,9 @@ public class main { case 'm': noAccessorComments = true; break; + case 'a': + apiLevel = Integer.parseInt(commandLine.getOptionValue("a")); + break; case 'N': disassemble = false; break; @@ -279,6 +285,8 @@ public class main { bootClassPathDirsArray[i] = bootClassPathDirs.get(i); } + Opcode.updateMapsForApiLevel(apiLevel); + baksmali.disassembleDexFile(dexFileFile.getPath(), dexFile, deodex, outputDirectory, bootClassPathDirsArray, bootClassPath, extraBootClassPathEntries.toString(), noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets, @@ -415,6 +423,13 @@ public class main { .withDescription("don't output helper comments for synthetic accessors") .create("m"); + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 14 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + Option dumpOption = OptionBuilder.withLongOpt("dump-to") .withDescription("dumps the given dex file into a single annotated dump file named FILE" + " (.dump by default), along with the normal disassembly") @@ -464,6 +479,7 @@ public class main { basicOptions.addOption(classPathDirOption); basicOptions.addOption(codeOffsetOption); basicOptions.addOption(noAccessorCommentsOption); + basicOptions.addOption(apiLevelOption); debugOptions.addOption(dumpOption); debugOptions.addOption(ignoreErrorsOption); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java index 44d37836..2c12a9b8 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java @@ -73,7 +73,12 @@ public class Instruction21c extends InstructionWithReference implements SingleRe protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { if(getReferencedItem().getIndex() > 0xFFFF) { - throw new RuntimeException(String.format("%s index is too large. Use the %s/jumbo instruction instead.", opcode.referenceType.name(), opcode.name)); + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large", opcode.referenceType.name())); + } } out.writeByte(opcode.value); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java index 91662793..5833a661 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java @@ -64,7 +64,12 @@ public class Instruction22c extends InstructionWithReference implements TwoRegis protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { if(getReferencedItem().getIndex() > 0xFFFF) { - throw new RuntimeException(String.format("%s index is too large. Use the %s/jumbo instruction instead.", opcode.referenceType.name(), opcode.name)); + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); + } } out.writeByte(opcode.value); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java index 3bb212b5..1fad2777 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java @@ -95,7 +95,12 @@ public class Instruction35c extends InstructionWithReference implements FiveRegi protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { if(getReferencedItem().getIndex() > 0xFFFF) { - throw new RuntimeException(String.format("%s index is too large. Use the %s/jumbo instruction instead.", opcode.referenceType.name(), opcode.name)); + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); + } } out.writeByte(opcode.value); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java index 057c9447..4beace5b 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java @@ -81,7 +81,12 @@ public class Instruction3rc extends InstructionWithReference implements Register protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { if(getReferencedItem().getIndex() > 0xFFFF) { - throw new RuntimeException(String.format("%s index is too large. Use the jumbo variant of the instruction instead.", opcode.referenceType.name())); + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); + } } out.writeByte(opcode.value); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java index d0259059..1c332e33 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java @@ -374,6 +374,47 @@ public enum Opcode } } + private static void removeOpcodes(Opcode... toRemove) { + for (Opcode opcode: toRemove) { + opcodesByName.remove(opcode.name.toLowerCase().hashCode()); + + if (((opcode.value >> 8) & 0xFF) == 0x00) { + opcodesByValue[opcode.value] = null; + } else { + expandedOpcodesByValue[opcode.value & 0xFF] = null; + } + } + } + + /** + * This will add/remove/replace various opcodes in the value/name maps as needed, + * based on the idiosyncrasies of that api level + * @param apiLevel + */ + public static void updateMapsForApiLevel(int apiLevel) { + if (apiLevel < 5) { + removeOpcodes(THROW_VERIFICATION_ERROR); + } + if (apiLevel < 8) { + removeOpcodes(EXECUTE_INLINE_RANGE); + } + if (apiLevel < 9) { + removeOpcodes(IGET_VOLATILE, IPUT_VOLATILE, SGET_VOLATILE, SPUT_VOLATILE, IGET_OBJECT_VOLATILE, + IGET_WIDE_VOLATILE, IPUT_WIDE_VOLATILE, SGET_WIDE_VOLATILE, SPUT_WIDE_VOLATILE, + IPUT_OBJECT_VOLATILE, SGET_OBJECT_VOLATILE, SPUT_OBJECT_VOLATILE); + } + if (apiLevel < 14) { + removeOpcodes(CONST_CLASS_JUMBO, CHECK_CAST_JUMBO, INSTANCE_OF_JUMBO, NEW_INSTANCE_JUMBO, + NEW_ARRAY_JUMBO, FILLED_NEW_ARRAY_JUMBO, IGET_JUMBO, IGET_WIDE_JUMBO, IGET_OBJECT_JUMBO, + IGET_BOOLEAN_JUMBO, IGET_BYTE_JUMBO, IGET_CHAR_JUMBO, IGET_SHORT_JUMBO, IPUT_JUMBO, IPUT_WIDE_JUMBO, + IPUT_OBJECT_JUMBO, IPUT_BOOLEAN_JUMBO, IPUT_BYTE_JUMBO, IPUT_CHAR_JUMBO, IPUT_SHORT_JUMBO, + SGET_JUMBO, SGET_WIDE_JUMBO, SGET_OBJECT_JUMBO, SGET_BOOLEAN_JUMBO, SGET_BYTE_JUMBO, + SGET_CHAR_JUMBO, SGET_SHORT_JUMBO, SPUT_JUMBO, SPUT_WIDE_JUMBO, SPUT_OBJECT_JUMBO, + SPUT_BOOLEAN_JUMBO, SPUT_BYTE_JUMBO, SPUT_CHAR_JUMBO, SPUT_SHORT_JUMBO, INVOKE_VIRTUAL_JUMBO, + INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO); + } + } + public final short value; public final String name; public final ReferenceType referenceType; @@ -435,7 +476,7 @@ public enum Opcode } public final boolean hasJumboOpcode() { - return jumboOpcode != -1; + return jumboOpcode != -1 && Opcode.getOpcodeByValue(jumboOpcode) != null; } public final Opcode getJumboOpcode() { diff --git a/dexlib/src/main/java/org/jf/dexlib/CodeItem.java b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java index 2528ada5..97801bd3 100644 --- a/dexlib/src/main/java/org/jf/dexlib/CodeItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java @@ -458,10 +458,13 @@ public class CodeItem extends Item { InstructionWithJumboVariant instructionWithJumboVariant = (InstructionWithJumboVariant)referenceInstruction; - replaceInstructionAtAddress(currentCodeAddress, - instructionWithJumboVariant.makeJumbo()); - didSomething = true; - break; + Instruction jumboInstruction = instructionWithJumboVariant.makeJumbo(); + if (jumboInstruction != null) { + replaceInstructionAtAddress(currentCodeAddress, + instructionWithJumboVariant.makeJumbo()); + didSomething = true; + break; + } } } diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java index ff72a449..39765a92 100644 --- a/smali/src/main/java/org/jf/smali/main.java +++ b/smali/src/main/java/org/jf/smali/main.java @@ -32,6 +32,7 @@ import org.antlr.runtime.*; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.apache.commons.cli.*; +import org.jf.dexlib.Code.Opcode; import org.jf.dexlib.CodeItem; import org.jf.dexlib.DexFile; import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; @@ -106,6 +107,8 @@ public class main { boolean oldLexer = false; boolean printTokens = false; + int apiLevel = 14; + String outputDexFile = "out.dex"; String dumpFileName = null; @@ -136,6 +139,9 @@ public class main { case 'x': allowOdex = true; break; + case 'a': + apiLevel = Integer.parseInt(commandLine.getOptionValue("a")); + break; case 'D': dumpFileName = commandLine.getOptionValue("D", outputDexFile + ".dump"); break; @@ -184,6 +190,8 @@ public class main { } } + Opcode.updateMapsForApiLevel(apiLevel); + DexFile dexFile = new DexFile(); boolean errors = false; @@ -383,6 +391,13 @@ public class main { " cause dalvik to reject the class") .create("x"); + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file to generate, e.g. 14 for ICS. If not " + + "specified, it defaults to 14 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + Option dumpOption = OptionBuilder.withLongOpt("dump-to") .withDescription("additionally writes a dump of written dex file to FILE (.dump by default)") .hasOptionalArg() @@ -417,6 +432,7 @@ public class main { basicOptions.addOption(helpOption); basicOptions.addOption(outputOption); basicOptions.addOption(allowOdexOption); + basicOptions.addOption(apiLevelOption); debugOptions.addOption(dumpOption); debugOptions.addOption(sortOption);