diff options
33 files changed, 947 insertions, 330 deletions
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java index b3f9ae17..fc43d6f1 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java @@ -76,8 +76,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem { return false; } - return opcode.isOdexedInstanceVolatile() || opcode.isOdexedStaticVolatile() || - opcode == Opcode.THROW_VERIFICATION_ERROR; + return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR; } @Override 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 6e009fb7..ef2110a8 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -320,7 +320,7 @@ public class MethodDefinition { String parameterType = parameter.getType(); String parameterName = parameter.getName(); Collection<? extends Annotation> annotations = parameter.getAnnotations(); - if (parameterName != null || annotations.size() != 0) { + if ((options.outputDebugInfo && parameterName != null) || annotations.size() != 0) { writer.write(".param p"); writer.printSignedIntAsDec(registerNumber); diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java index fa54f091..f5329388 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java @@ -156,7 +156,8 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem { RegisterType mergedRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNum); for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) { - RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum); + RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType( + predecessor, registerNum); if (predecessorRegisterType.category != RegisterType.UNKNOWN && !predecessorRegisterType.equals(mergedRegisterType)) { registers.set(registerNum); @@ -179,7 +180,8 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem { boolean first = true; for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) { - RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum); + RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType( + predecessor, registerNum); if (!first) { writer.write(','); diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index 1e297029..50607340 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -135,7 +135,7 @@ public class baksmali { List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses()); if (!options.noAccessorComments) { - options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs); + options.syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getOpcodes(), classDefs); } final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali"); diff --git a/baksmali/src/test/java/org/jf/baksmali/DexTest.java b/baksmali/src/test/java/org/jf/baksmali/DexTest.java new file mode 100644 index 00000000..5a4db658 --- /dev/null +++ b/baksmali/src/test/java/org/jf/baksmali/DexTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.baksmali; + +import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.junit.Assert; + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; + +/** + * A base test class for performing a test using a dex file as input + */ +/** + * A base test class for performing a disassembly on a dex file and verifying the results + * + * The test accepts a single-class dex file as input. By default, the input dex file should be a resource at + * [testDir]/[testName]Input.dex + */ +public abstract class DexTest { + protected final String testDir; + + protected DexTest(@Nonnull String testDir) { + this.testDir = testDir; + } + + protected DexTest() { + this.testDir = this.getClass().getSimpleName(); + } + + @Nonnull + protected String getInputFilename(@Nonnull String testName) { + return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName); + } + + @Nonnull + protected DexBackedDexFile getInputDexFile(@Nonnull String testName, @Nonnull baksmaliOptions options) { + try { + // Load file from resources as a stream + byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName)); + return new DexBackedDexFile(Opcodes.forApi(options.apiLevel), inputBytes); + } catch (IOException ex) { + Assert.fail(); + } + return null; + } +} diff --git a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java index 1fbafeab..1a34e8c3 100644 --- a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java +++ b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java @@ -32,7 +32,6 @@ package org.jf.baksmali; import com.google.common.collect.Iterables; -import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.iface.ClassDef; import org.junit.Assert; @@ -50,21 +49,7 @@ import java.io.IOException; * By default, the input and output files should be resources at [testDir]/[testName]Input.dex * and [testDir]/[testName]Output.smali respectively */ -public class DisassemblyTest { - protected final String testDir; - - protected DisassemblyTest(@Nonnull String testDir) { - this.testDir = testDir; - } - - protected DisassemblyTest() { - this.testDir = this.getClass().getSimpleName(); - } - - @Nonnull - protected String getInputFilename(@Nonnull String testName) { - return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName); - } +public class DisassemblyTest extends DexTest { @Nonnull protected String getOutputFilename(@Nonnull String testName) { @@ -77,22 +62,13 @@ public class DisassemblyTest { protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) { try { - // Load file from resources as a stream - String inputFilename = getInputFilename(testName); - byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName)); - - DexBackedDexFile inputDex = new DexBackedDexFile(Opcodes.forApi(options.apiLevel), inputBytes); + DexBackedDexFile inputDex = getInputDexFile(testName, options); Assert.assertEquals(1, inputDex.getClassCount()); ClassDef inputClass = Iterables.getFirst(inputDex.getClasses(), null); Assert.assertNotNull(inputClass); String input = BaksmaliTestUtils.getNormalizedSmali(inputClass, options, true); - String output; - if (getOutputFilename(testName).equals(inputFilename)) { - output = input; - } else { - output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName)); - } + String output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName)); output = BaksmaliTestUtils.normalizeSmali(output, true); // Run smali, baksmali, and then compare strings are equal (minus comments/whitespace) diff --git a/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java b/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java new file mode 100644 index 00000000..06841310 --- /dev/null +++ b/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.baksmali; + +import com.google.common.collect.Lists; +import org.jf.dexlib2.analysis.ClassPath; +import org.jf.dexlib2.analysis.ClassProto; +import org.jf.dexlib2.iface.DexFile; +import org.junit.Assert; +import org.junit.Test; + +public class FieldGapOrderTest extends DexTest { + @Test + public void testOldOrder() { + DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions()); + Assert.assertEquals(3, dexFile.getClasses().size()); + + ClassPath classPath = new ClassPath(Lists.newArrayList(dexFile), false, 66); + ClassProto classProto = (ClassProto)classPath.getClass("LGapOrder;"); + Assert.assertEquals("r1", classProto.getFieldByOffset(12).getName()); + Assert.assertEquals("r2", classProto.getFieldByOffset(16).getName()); + Assert.assertEquals("d", classProto.getFieldByOffset(24).getName()); + Assert.assertEquals("s", classProto.getFieldByOffset(36).getName()); + Assert.assertEquals("i", classProto.getFieldByOffset(32).getName()); + } + + @Test + public void testNewOrder() { + DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions()); + Assert.assertEquals(3, dexFile.getClasses().size()); + + ClassPath classPath = new ClassPath(Lists.newArrayList(dexFile), false, 67); + ClassProto classProto = (ClassProto)classPath.getClass("LGapOrder;"); + Assert.assertEquals("s", classProto.getFieldByOffset(10).getName()); + Assert.assertEquals("r1", classProto.getFieldByOffset(12).getName()); + Assert.assertEquals("r2", classProto.getFieldByOffset(16).getName()); + Assert.assertEquals("i", classProto.getFieldByOffset(20).getName()); + Assert.assertEquals("d", classProto.getFieldByOffset(24).getName()); + } +} diff --git a/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex b/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex Binary files differnew file mode 100644 index 00000000..4e593516 --- /dev/null +++ b/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex diff --git a/build.gradle b/build.gradle index 5cc2aaa5..88101e6b 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ apply plugin: 'idea' -version = '2.1.0' +version = '2.1.1' def jarVersion = version diff --git a/dexlib2/OatVersions.txt b/dexlib2/OatVersions.txt index e64eccf6..8aa9ea96 100644 --- a/dexlib2/OatVersions.txt +++ b/dexlib2/OatVersions.txt @@ -12,4 +12,13 @@ d7cbf8a6629942e7bd315ffae7e1c77b082f3e11 - 60 07785bb98dc8bbe192970e0f4c2cafd338a8dc68 - 64 fa2c054b28d4b540c1b3651401a7a091282a015f - 65 7070ccd8b6439477eafeea7ed3736645d78e003f - 64 (revert of fa2c054b) -7bf2b4f1d08050f80782217febac55c8cfc5e4ef - 65 (re-commit of fa2c054b)
\ No newline at end of file +7bf2b4f1d08050f80782217febac55c8cfc5e4ef - 65 (re-commit of fa2c054b) +0b71357fb52be9bb06d35396a3042b4381b01041 - 66 +fab6788358dfb64e5c370611ddbbbffab0ed0553 - 67 +- Change in FieldGap priority queue ordering +1aee900d5a0b3a8d78725a7551356bda0d8554e1 - 68 +54b62480636ae846d705fc180c7bd6cd08ec1e42 - 69 +6e2d5747d00697a25251d25dd33b953e54709507 - 68 (revert of 54b62480) +0747466fca310eedea5fc49e37d54f240a0b3c0f - 69 (re-commit of 54b62480) +501fd635a557645ab05f893c56e1f358e21bab82 - 70 +99170c636dfae4908b102347cfe9f92bad1881cc - 71
\ No newline at end of file diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java index 5fb2a8c0..3a642358 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java @@ -131,20 +131,20 @@ public enum Opcode IPUT_BYTE(0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), IPUT_CHAR(0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), IPUT_SHORT(0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SGET(0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SGET_WIDE(0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), - SGET_OBJECT(0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SGET_BOOLEAN(0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SGET_BYTE(0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SGET_CHAR(0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SGET_SHORT(0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SPUT(0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SPUT_WIDE(0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SPUT_OBJECT(0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SPUT_BOOLEAN(0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SPUT_BYTE(0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SPUT_CHAR(0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SPUT_SHORT(0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET(0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SGET_WIDE(0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SGET_OBJECT(0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SGET_BOOLEAN(0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SGET_BYTE(0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SGET_CHAR(0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SGET_SHORT(0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SPUT(0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_WIDE(0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_OBJECT(0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_BOOLEAN(0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_BYTE(0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_CHAR(0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_SHORT(0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), INVOKE_VIRTUAL(0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_SUPER(0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_DIRECT(0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE), @@ -260,15 +260,15 @@ public enum Opcode SHR_INT_LIT8(0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), USHR_INT_LIT8(0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IGET_VOLATILE(firstApi(0xe3, 9), "iget-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IPUT_VOLATILE(firstApi(0xe4, 9), "iput-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SGET_VOLATILE(firstApi(0xe5, 9), "sget-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SPUT_VOLATILE(firstApi(0xe6, 9), "sput-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - IGET_OBJECT_VOLATILE(firstApi(0xe7, 9), "iget-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IGET_WIDE_VOLATILE(firstApi(0xe8, 9), "iget-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), - IPUT_WIDE_VOLATILE(firstApi(0xe9, 9), "iput-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SGET_WIDE_VOLATILE(firstApi(0xea, 9), "sget-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), - SPUT_WIDE_VOLATILE(firstApi(0xeb, 9), "sput-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IGET_VOLATILE(firstApi(0xe3, 9), "iget-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IPUT_VOLATILE(firstApi(0xe4, 9), "iput-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET_VOLATILE(firstApi(0xe5, 9), "sget-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_VOLATILE(firstApi(0xe6, 9), "sput-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), + IGET_OBJECT_VOLATILE(firstApi(0xe7, 9), "iget-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_WIDE_VOLATILE(firstApi(0xe8, 9), "iget-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + IPUT_WIDE_VOLATILE(firstApi(0xe9, 9), "iput-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET_WIDE_VOLATILE(firstApi(0xea, 9), "sget-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_WIDE_VOLATILE(firstApi(0xeb, 9), "sput-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), THROW_VERIFICATION_ERROR(firstApi(0xed, 5), "throw-verification-error", ReferenceType.NONE, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW), EXECUTE_INLINE(allApis(0xee), "execute-inline", ReferenceType.NONE, Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), @@ -277,29 +277,29 @@ public enum Opcode INVOKE_OBJECT_INIT_RANGE(firstApi(0xf0, 14), "invoke-object-init/range", ReferenceType.METHOD, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE), RETURN_VOID_BARRIER(combine(firstApi(0xf1, 11), lastArtVersion(0x73, 59)), "return-void-barrier", ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY), RETURN_VOID_NO_BARRIER(firstArtVersion(0x73, 60), "return-void-no-barrier", ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY), - IGET_QUICK(combine(allApis(0xf2), allArtVersions(0xe3)), "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IGET_WIDE_QUICK(combine(allApis(0xf3), allArtVersions(0xe4)), "iget-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), - IGET_OBJECT_QUICK(combine(allApis(0xf4), allArtVersions(0xe5)), "iget-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IPUT_QUICK(combine(allApis(0xf5), allArtVersions(0xe6)), "iput-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - IPUT_WIDE_QUICK(combine(allApis(0xf6), allArtVersions(0xe7)), "iput-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - IPUT_OBJECT_QUICK(combine(allApis(0xf7), allArtVersions(0xe8)), "iput-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - IPUT_BOOLEAN_QUICK(allArtVersions(0xeb), "iput-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK), - IPUT_BYTE_QUICK(allArtVersions(0xec), "iput-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK), - IPUT_CHAR_QUICK(allArtVersions(0xed), "iput-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK), - IPUT_SHORT_QUICK(allArtVersions(0xee), "iput-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK), - IGET_BOOLEAN_QUICK(allArtVersions(0xef), "iget-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IGET_BYTE_QUICK(allArtVersions(0xf0), "iget-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IGET_CHAR_QUICK(allArtVersions(0xf1), "iget-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - IGET_SHORT_QUICK(allArtVersions(0xf2), "iget-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_QUICK(combine(allApis(0xf2), allArtVersions(0xe3)), "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_WIDE_QUICK(combine(allApis(0xf3), allArtVersions(0xe4)), "iget-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + IGET_OBJECT_QUICK(combine(allApis(0xf4), allArtVersions(0xe5)), "iget-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IPUT_QUICK(combine(allApis(0xf5), allArtVersions(0xe6)), "iput-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_WIDE_QUICK(combine(allApis(0xf6), allArtVersions(0xe7)), "iput-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_OBJECT_QUICK(combine(allApis(0xf7), allArtVersions(0xe8)), "iput-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_BOOLEAN_QUICK(allArtVersions(0xeb), "iput-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR), + IPUT_BYTE_QUICK(allArtVersions(0xec), "iput-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR), + IPUT_CHAR_QUICK(allArtVersions(0xed), "iput-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR), + IPUT_SHORT_QUICK(allArtVersions(0xee), "iput-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR), + IGET_BOOLEAN_QUICK(allArtVersions(0xef), "iget-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_BYTE_QUICK(allArtVersions(0xf0), "iget-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_CHAR_QUICK(allArtVersions(0xf1), "iget-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_SHORT_QUICK(allArtVersions(0xf2), "iget-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), INVOKE_VIRTUAL_QUICK(combine(allApis(0xf8), allArtVersions(0xe9)), "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_VIRTUAL_QUICK_RANGE(combine(allApis(0xf9), allArtVersions(0xea)), "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_SUPER_QUICK(allApis(0xfa), "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_SUPER_QUICK_RANGE(allApis(0xfb), "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), - IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), - SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), - SPUT_OBJECT_VOLATILE(firstApi(0xfe, 9), "sput-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), + SPUT_OBJECT_VOLATILE(firstApi(0xfe, 9), "sput-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR), PACKED_SWITCH_PAYLOAD(0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0), SPARSE_SWITCH_PAYLOAD(0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0), @@ -327,12 +327,12 @@ public enum Opcode public static final int SETS_REGISTER = 0x10; //if the instruction sets the value of it's first register to a wide type public static final int SETS_WIDE_REGISTER = 0x20; - //if the instruction is an odexed iget-quick/iput-quick instruction - public static final int ODEXED_INSTANCE_QUICK = 0x40; - //if the instruction is an odexed iget-volatile/iput-volatile instruction - public static final int ODEXED_INSTANCE_VOLATILE = 0x80; - //if the instruction is an odexed sget-volatile/sput-volatile instruction - public static final int ODEXED_STATIC_VOLATILE = 0x100; + //if the instruction is an iget-quick/iput-quick instruction + public static final int QUICK_FIELD_ACCESSOR = 0x40; + //if the instruction is a *get-volatile/*put-volatile instruction + public static final int VOLATILE_FIELD_ACCESSOR = 0x80; + //if the instruction is a static sget-*/sput-*instruction + public static final int STATIC_FIELD_ACCESSOR = 0x100; //if the instruction is a jumbo instruction public static final int JUMBO_OPCODE = 0x200; //if the instruction can initialize an uninitialized object reference @@ -451,16 +451,16 @@ public enum Opcode return (flags & SETS_WIDE_REGISTER) != 0; } - public final boolean isOdexedInstanceQuick() { - return (flags & ODEXED_INSTANCE_QUICK) != 0; + public final boolean isQuickFieldaccessor() { + return (flags & QUICK_FIELD_ACCESSOR) != 0; } - public final boolean isOdexedInstanceVolatile() { - return (flags & ODEXED_INSTANCE_VOLATILE) != 0; + public final boolean isVolatileFieldAccessor() { + return (flags & VOLATILE_FIELD_ACCESSOR) != 0; } - public final boolean isOdexedStaticVolatile() { - return (flags & ODEXED_STATIC_VOLATILE) != 0; + public final boolean isStaticFieldAccessor() { + return (flags & STATIC_FIELD_ACCESSOR) != 0; } public final boolean isJumboOpcode() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java index 30cc9067..f6fc95a1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java @@ -31,12 +31,15 @@ package org.jf.dexlib2.analysis; +import com.google.common.base.Objects; +import com.google.common.collect.Maps; import org.jf.dexlib2.iface.instruction.*; import org.jf.dexlib2.iface.reference.MethodReference; import org.jf.dexlib2.iface.reference.Reference; import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.*; public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { @@ -71,6 +74,12 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { protected final RegisterType[] postRegisterMap; /** + * This contains optional register type overrides for register types from predecessors + */ + @Nullable + protected Map<PredecessorOverrideKey, RegisterType> predecessorRegisterOverrides = null; + + /** * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again */ @@ -101,6 +110,17 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { return Collections.unmodifiableSortedSet(predecessors); } + public RegisterType getPredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber) { + if (predecessorRegisterOverrides != null) { + RegisterType override = predecessorRegisterOverrides.get( + new PredecessorOverrideKey(predecessor, registerNumber)); + if (override != null) { + return override; + } + } + return predecessor.postRegisterMap[registerNumber]; + } + protected boolean addPredecessor(AnalyzedInstruction predecessor) { return predecessors.add(predecessor); } @@ -169,12 +189,19 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { * register is a destination register for this instruction, or if the pre-instruction register type didn't change * after merging in the given register type */ - protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) { + protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions, + boolean override) { assert registerNumber >= 0 && registerNumber < postRegisterMap.length; assert registerType != null; RegisterType oldRegisterType = preRegisterMap[registerNumber]; - RegisterType mergedRegisterType = oldRegisterType.merge(registerType); + + RegisterType mergedRegisterType; + if (override) { + mergedRegisterType = getMergedPreRegisterTypeFromPredecessors(registerNumber); + } else { + mergedRegisterType = oldRegisterType.merge(registerType); + } if (mergedRegisterType.equals(oldRegisterType)) { return false; @@ -193,39 +220,82 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { /** * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the - * given register. Any dead, unreachable, or odexed predecessor is ignored + * given register. Any dead, unreachable, or odexed predecessor is ignored. This takes into account any overridden + * predecessor register types + * * @param registerNumber the register number * @return The register type resulting from merging the post-instruction register types from all predecessors */ - protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) { + protected RegisterType getMergedPreRegisterTypeFromPredecessors(int registerNumber) { RegisterType mergedRegisterType = null; for (AnalyzedInstruction predecessor: predecessors) { - RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber]; - assert predecessorRegisterType != null; - mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType); + RegisterType predecessorRegisterType = getPredecessorRegisterType(predecessor, registerNumber); + if (predecessorRegisterType != null) { + if (mergedRegisterType == null) { + mergedRegisterType = predecessorRegisterType; + } else { + mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType); + } + } } return mergedRegisterType; } + /** + * Sets the "post-instruction" register type as indicated. + * @param registerNumber Which register to set + * @param registerType The "post-instruction" register type + * @return true if the given register type is different than the existing post-instruction register type + */ + protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) { + assert registerNumber >= 0 && registerNumber < postRegisterMap.length; + assert registerType != null; - /* - * Sets the "post-instruction" register type as indicated. - * @param registerNumber Which register to set - * @param registerType The "post-instruction" register type - * @returns true if the given register type is different than the existing post-instruction register type - */ - protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) { - assert registerNumber >= 0 && registerNumber < postRegisterMap.length; - assert registerType != null; + RegisterType oldRegisterType = postRegisterMap[registerNumber]; + if (oldRegisterType.equals(registerType)) { + return false; + } + + postRegisterMap[registerNumber] = registerType; + return true; + } + + /** + * Adds an override for a register type from a predecessor. + * + * This is used to set the register type for only one branch from a conditional jump. + * + * @param predecessor Which predecessor is being overriden + * @param registerNumber The register number of the register being overriden + * @param registerType The overridden register type + * @param verifiedInstructions + * + * @return true if the post-instruction register type for this instruction changed as a result of this override + */ + protected boolean overridePredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber, + @Nonnull RegisterType registerType, BitSet verifiedInstructions) { + if (predecessorRegisterOverrides == null) { + predecessorRegisterOverrides = Maps.newHashMap(); + } + predecessorRegisterOverrides.put(new PredecessorOverrideKey(predecessor, registerNumber), registerType); + + RegisterType mergedType = getMergedPreRegisterTypeFromPredecessors(registerNumber); - RegisterType oldRegisterType = postRegisterMap[registerNumber]; - if (oldRegisterType.equals(registerType)) { - return false; - } + if (preRegisterMap[registerNumber].equals(mergedType)) { + return false; + } + + preRegisterMap[registerNumber] = mergedType; + verifiedInstructions.clear(instructionIndex); - postRegisterMap[registerNumber] = registerType; - return true; - } + if (!setsRegister(registerNumber)) { + if (!postRegisterMap[registerNumber].equals(mergedType)) { + postRegisterMap[registerNumber] = mergedType; + return true; + } + } + return false; + } protected boolean isInvokeInit() { if (instruction == null || !instruction.getOpcode().canInitializeReference()) { @@ -328,5 +398,27 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { return 1; } } + + private static class PredecessorOverrideKey { + public final AnalyzedInstruction analyzedInstruction; + public final int registerNumber; + + public PredecessorOverrideKey(AnalyzedInstruction analyzedInstruction, int registerNumber) { + this.analyzedInstruction = analyzedInstruction; + this.registerNumber = registerNumber; + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PredecessorOverrideKey that = (PredecessorOverrideKey)o; + return com.google.common.base.Objects.equal(registerNumber, that.registerNumber) && + Objects.equal(analyzedInstruction, that.analyzedInstruction); + } + + @Override public int hashCode() { + return Objects.hashCode(analyzedInstruction, registerNumber); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java index 4b8920f4..cf75dffe 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java @@ -40,6 +40,7 @@ import com.google.common.collect.*; import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.DexFileFactory.DexFileNotFound; import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.analysis.reflection.ReflectionClassDef; import org.jf.dexlib2.dexbacked.OatFile.OatDexFile; import org.jf.dexlib2.iface.ClassDef; @@ -60,8 +61,10 @@ import java.util.regex.Pattern; public class ClassPath { @Nonnull private final TypeProto unknownClass; @Nonnull private HashMap<String, ClassDef> availableClasses = Maps.newHashMap(); - private boolean checkPackagePrivateAccess; - public boolean isArt; + private final boolean checkPackagePrivateAccess; + public final int oatVersion; + + public static final int NOT_ART = -1; /** * Creates a new ClassPath instance that can load classes from the given dex files @@ -78,7 +81,7 @@ public class ClassPath { * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order * @param api API level */ - public ClassPath(@Nonnull Iterable<DexFile> classPath, int api) { + public ClassPath(@Nonnull Iterable<? extends DexFile> classPath, int api) { this(Lists.newArrayList(classPath), api == 17); } @@ -89,8 +92,8 @@ public class ClassPath { * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by * default */ - public ClassPath(@Nonnull Iterable<DexFile> classPath, boolean checkPackagePrivateAccess) { - this(classPath, checkPackagePrivateAccess, false); + public ClassPath(@Nonnull Iterable<? extends DexFile> classPath, boolean checkPackagePrivateAccess) { + this(classPath, checkPackagePrivateAccess, NOT_ART); } /** @@ -99,16 +102,17 @@ public class ClassPath { * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by * default - * @param isArt Whether this is ClassPath is for ART + * @param oatVersion The applicable oat version, or NOT_ART */ - public ClassPath(@Nonnull Iterable < DexFile > classPath, boolean checkPackagePrivateAccess, boolean isArt) { + public ClassPath(@Nonnull Iterable<? extends DexFile> classPath, boolean checkPackagePrivateAccess, + int oatVersion) { // add fallbacks for certain special classes that must be present Iterable<DexFile> dexFiles = Iterables.concat(classPath, Lists.newArrayList(getBasicClasses())); unknownClass = new UnknownClassProto(this); loadedClasses.put(unknownClass.getType(), unknownClass); this.checkPackagePrivateAccess = checkPackagePrivateAccess; - this.isArt = isArt; + this.oatVersion = oatVersion; loadPrimitiveType("Z"); loadPrimitiveType("B"); @@ -136,13 +140,18 @@ public class ClassPath { private static DexFile getBasicClasses() { // fallbacks for some special classes that we assume are present - return new ImmutableDexFile(ImmutableSet.of( - new ReflectionClassDef(Class.class), - new ReflectionClassDef(Cloneable.class), - new ReflectionClassDef(Object.class), - new ReflectionClassDef(Serializable.class), - new ReflectionClassDef(String.class), - new ReflectionClassDef(Throwable.class))); + return new ImmutableDexFile(Opcodes.forApi(19), + ImmutableSet.of( + new ReflectionClassDef(Class.class), + new ReflectionClassDef(Cloneable.class), + new ReflectionClassDef(Object.class), + new ReflectionClassDef(Serializable.class), + new ReflectionClassDef(String.class), + new ReflectionClassDef(Throwable.class))); + } + + public boolean isArt() { + return oatVersion != NOT_ART; } @Nonnull @@ -191,15 +200,15 @@ public class ClassPath { int api, boolean checkPackagePrivateAccess, boolean experimental) { ArrayList<DexFile> dexFiles = Lists.newArrayList(); - boolean isArt = false; + int oatVersion = NOT_ART; for (String classPathEntry: classPath) { List<? extends DexFile> classPathDexFiles = loadClassPathEntry(classPathDirs, classPathEntry, api, experimental); - if (!isArt) { + if (oatVersion == NOT_ART) { for (DexFile classPathDexFile: classPathDexFiles) { if (classPathDexFile instanceof OatDexFile) { - isArt = true; + oatVersion = ((OatDexFile)classPathDexFile).getOatVersion(); break; } } @@ -207,20 +216,20 @@ public class ClassPath { dexFiles.addAll(classPathDexFiles); } dexFiles.add(dexFile); - return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt); + return new ClassPath(dexFiles, checkPackagePrivateAccess, oatVersion); } @Nonnull public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile, int api, boolean checkPackagePrivateAccess, boolean experimental, - boolean isArt) { + int oatVersion) { ArrayList<DexFile> dexFiles = Lists.newArrayList(); for (String classPathEntry: classPath) { dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental)); } dexFiles.add(dexFile); - return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt); + return new ClassPath(dexFiles, checkPackagePrivateAccess, oatVersion); } private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); @@ -290,7 +299,7 @@ public class ClassPath { private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize( new Supplier<OdexedFieldInstructionMapper>() { @Override public OdexedFieldInstructionMapper get() { - return new OdexedFieldInstructionMapper(isArt); + return new OdexedFieldInstructionMapper(isArt()); } }); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index d66e8ebd..57aae115 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -374,7 +374,7 @@ public class ClassProto implements TypeProto { } @Nonnull SparseArray<FieldReference> getInstanceFields() { - if (classPath.isArt) { + if (classPath.isArt()) { return artInstanceFieldsSupplier.get(); } else { return dalvikInstanceFieldsSupplier.get(); @@ -548,21 +548,37 @@ public class ClassProto implements TypeProto { } }); - private static class FieldGap implements Comparable<FieldGap> { + private static abstract class FieldGap implements Comparable<FieldGap> { public final int offset; public final int size; - public FieldGap(int offset, int size) { - this.offset = offset; - this.size = size; + public static FieldGap newFieldGap(int offset, int size, int oatVersion) { + if (oatVersion >= 67) { + return new FieldGap(offset, size) { + @Override public int compareTo(FieldGap o) { + int result = Ints.compare(o.size, size); + if (result != 0) { + return result; + } + return Ints.compare(offset, o.offset); + } + }; + } else { + return new FieldGap(offset, size) { + @Override public int compareTo(FieldGap o) { + int result = Ints.compare(size, o.size); + if (result != 0) { + return result; + } + return Ints.compare(o.offset, offset); + } + }; + } } - @Override public int compareTo(@Nonnull FieldGap o) { - int result = Ints.compare(o.size, size); - if (result != 0) { - return result; - } - return Ints.compare(o.offset, offset); + private FieldGap(int offset, int size) { + this.offset = offset; + this.size = size; } } @@ -630,13 +646,13 @@ public class ClassProto implements TypeProto { int remaining = gapEnd - offset; if ((remaining >= 4) && (offset % 4 == 0)) { - gaps.add(new FieldGap(offset, 4)); + gaps.add(FieldGap.newFieldGap(offset, 4, classPath.oatVersion)); offset += 4; } else if (remaining >= 2 && (offset % 2 == 0)) { - gaps.add(new FieldGap(offset, 2)); + gaps.add(FieldGap.newFieldGap(offset, 2, classPath.oatVersion)); offset += 2; } else { - gaps.add(new FieldGap(offset, 1)); + gaps.add(FieldGap.newFieldGap(offset, 1, classPath.oatVersion)); offset += 1; } } @@ -703,14 +719,14 @@ public class ClassProto implements TypeProto { private int getNextFieldOffset() { SparseArray<FieldReference> instanceFields = getInstanceFields(); if (instanceFields.size() == 0) { - return classPath.isArt ? 0 : 8; + return classPath.isArt() ? 0 : 8; } int lastItemIndex = instanceFields.size()-1; int fieldOffset = instanceFields.keyAt(lastItemIndex); FieldReference lastField = instanceFields.valueAt(lastItemIndex); - if (classPath.isArt) { + if (classPath.isArt()) { return fieldOffset + getTypeSize(lastField.getType().charAt(0)); } else { switch (lastField.getType().charAt(0)) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java index e7ad17ca..eca9391f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java @@ -337,34 +337,60 @@ public class MethodAnalyzer { registerType); } - private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction, - int registerNumber, @Nonnull RegisterType registerType) { - - BitSet changedInstructions = new BitSet(analyzedInstructions.size()); - - if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) { - return; - } - - propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions); - + private void propagateChanges(@Nonnull BitSet changedInstructions, int registerNumber, boolean override) { //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on //the next iteration of the while loop. - //This could also be done recursively, but in large methods it would likely cause very deep recursion, - //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly - //annoying. + //This could also be done recursively, but in large methods it would likely cause very deep recursion. while (!changedInstructions.isEmpty()) { for (int instructionIndex=changedInstructions.nextSetBit(0); - instructionIndex>=0; - instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) { + instructionIndex>=0; + instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) { changedInstructions.clear(instructionIndex); propagateRegisterToSuccessors(analyzedInstructions.valueAt(instructionIndex), registerNumber, - changedInstructions); + changedInstructions, override); } } + } + + private void overridePredecessorRegisterTypeAndPropagateChanges( + @Nonnull AnalyzedInstruction analyzedInstruction, @Nonnull AnalyzedInstruction predecessor, + int registerNumber, @Nonnull RegisterType registerType) { + BitSet changedInstructions = new BitSet(analyzedInstructions.size()); + + if (!analyzedInstruction.overridePredecessorRegisterType( + predecessor, registerNumber, registerType, analyzedState)) { + return; + } + changedInstructions.set(analyzedInstruction.instructionIndex); + + propagateChanges(changedInstructions, registerNumber, true); + + if (registerType.category == RegisterType.LONG_LO) { + checkWidePair(registerNumber, analyzedInstruction); + overridePredecessorRegisterTypeAndPropagateChanges(analyzedInstruction, predecessor, registerNumber + 1, + RegisterType.LONG_HI_TYPE); + } else if (registerType.category == RegisterType.DOUBLE_LO) { + checkWidePair(registerNumber, analyzedInstruction); + overridePredecessorRegisterTypeAndPropagateChanges(analyzedInstruction, predecessor, registerNumber + 1, + RegisterType.DOUBLE_HI_TYPE); + } + } + + private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction, + int registerNumber, @Nonnull RegisterType registerType) { + + BitSet changedInstructions = new BitSet(analyzedInstructions.size()); + + if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) { + return; + } + + propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions, false); + + propagateChanges(changedInstructions, registerNumber, false); if (registerType.category == RegisterType.LONG_LO) { checkWidePair(registerNumber, analyzedInstruction); @@ -376,10 +402,10 @@ public class MethodAnalyzer { } private void propagateRegisterToSuccessors(@Nonnull AnalyzedInstruction instruction, int registerNumber, - @Nonnull BitSet changedInstructions) { + @Nonnull BitSet changedInstructions, boolean override) { RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber); for (AnalyzedInstruction successor: instruction.successors) { - if (successor.mergeRegister(registerNumber, postRegisterType, analyzedState)) { + if (successor.mergeRegister(registerNumber, postRegisterType, analyzedState, override)) { changedInstructions.set(successor.instructionIndex); } } @@ -1039,8 +1065,11 @@ public class MethodAnalyzer { } private void analyzeMoveResult(@Nonnull AnalyzedInstruction analyzedInstruction) { - AnalyzedInstruction previousInstruction = analyzedInstructions.valueAt(analyzedInstruction.instructionIndex-1); - if (!previousInstruction.instruction.getOpcode().setsResult()) { + AnalyzedInstruction previousInstruction = null; + if (analyzedInstruction.instructionIndex > 0) { + previousInstruction = analyzedInstructions.valueAt(analyzedInstruction.instructionIndex-1); + } + if (previousInstruction == null || !previousInstruction.instruction.getOpcode().setsResult()) { throw new AnalysisException(analyzedInstruction.instruction.getOpcode().name + " must occur after an " + "invoke-*/fill-new-array instruction"); } @@ -1134,6 +1163,44 @@ public class MethodAnalyzer { private void analyzeInstanceOf(@Nonnull AnalyzedInstruction analyzedInstruction) { setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BOOLEAN_TYPE); + + int instructionIndex = analyzedInstruction.getInstructionIndex(); + AnalyzedInstruction nextAnalyzedInstruction = analyzedInstructions.valueAt(instructionIndex + 1); + + Instruction nextInstruction = nextAnalyzedInstruction.instruction; + if (nextInstruction.getOpcode() == Opcode.IF_EQZ) { + if (((Instruction21t)nextInstruction).getRegisterA() == analyzedInstruction.getDestinationRegister()) { + Reference reference = ((Instruction22c)analyzedInstruction.getInstruction()).getReference(); + RegisterType registerType = RegisterType.getRegisterType(classPath, (TypeReference)reference); + + if (registerType.type != null && !registerType.type.isInterface()) { + int objectRegister = ((TwoRegisterInstruction)analyzedInstruction.getInstruction()).getRegisterB(); + + RegisterType existingType = nextAnalyzedInstruction.getPostInstructionRegisterType(objectRegister); + + if (existingType.type != null) { + boolean override = false; + + // Only override if we're going from an interface to a class, or are going to a narrower class + if (existingType.type.isInterface()) { + override = true; + } else { + TypeProto commonSuperclass = registerType.type.getCommonSuperclass(existingType.type); + // only if it's a narrowing conversion + if (commonSuperclass.getType().equals(existingType.type.getType())) { + override = true; + } + } + + if (override) { + overridePredecessorRegisterTypeAndPropagateChanges( + analyzedInstructions.valueAt(instructionIndex + 2), nextAnalyzedInstruction, + objectRegister, registerType); + } + } + } + } + } } private void analyzeArrayLength(@Nonnull AnalyzedInstruction analyzedInstruction) { @@ -1710,7 +1777,7 @@ public class MethodAnalyzer { } resolvedMethod = superType.getMethodByVtableIndex(methodIndex); - } else{ + } else { resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex); } @@ -1799,7 +1866,7 @@ public class MethodAnalyzer { Instruction deodexedInstruction; - if (originalOpcode.isOdexedStaticVolatile()) { + if (originalOpcode.isStaticFieldAccessor()) { OneRegisterInstruction instruction = (OneRegisterInstruction)analyzedInstruction.instruction; deodexedInstruction = new ImmutableInstruction21c(opcode, instruction.getRegisterA(), field); } else { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java index 6352f08c..0ed1fef1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java @@ -43,26 +43,40 @@ public class OdexedFieldInstructionMapper { private static final int GET = 0; private static final int PUT = 1; + private static final int INSTANCE = 0; + private static final int STATIC = 1; + private static final int PRIMITIVE = 0; private static final int WIDE = 1; private static final int REFERENCE = 2; private static class FieldOpcode { public final char type; + public final boolean isStatic; @Nonnull public final Opcode normalOpcode; - @Nonnull public final Opcode quickOpcode; + @Nullable public final Opcode quickOpcode; @Nullable public final Opcode volatileOpcode; - public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode, + public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode, @Nullable Opcode volatileOpcode) { this.type = type; + this.isStatic = false; this.normalOpcode = normalOpcode; this.quickOpcode = quickOpcode; this.volatileOpcode = volatileOpcode; } - public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode) { + public FieldOpcode(char type, boolean isStatic, @Nonnull Opcode normalOpcode, @Nullable Opcode volatileOpcode) { + this.type = type; + this.isStatic = isStatic; + this.normalOpcode = normalOpcode; + this.quickOpcode = null; + this.volatileOpcode = volatileOpcode; + } + + public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode) { this.type = type; + this.isStatic = false; this.normalOpcode = normalOpcode; this.quickOpcode = quickOpcode; this.volatileOpcode = null; @@ -91,6 +105,28 @@ public class OdexedFieldInstructionMapper { new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE), new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE), new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE), + + new FieldOpcode('Z', true, Opcode.SPUT_BOOLEAN, Opcode.SPUT_VOLATILE), + new FieldOpcode('B', true, Opcode.SPUT_BYTE, Opcode.SPUT_VOLATILE), + new FieldOpcode('S', true, Opcode.SPUT_SHORT, Opcode.SPUT_VOLATILE), + new FieldOpcode('C', true, Opcode.SPUT_CHAR, Opcode.SPUT_VOLATILE), + new FieldOpcode('I', true, Opcode.SPUT, Opcode.SPUT_VOLATILE), + new FieldOpcode('F', true, Opcode.SPUT, Opcode.SPUT_VOLATILE), + new FieldOpcode('J', true, Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE), + new FieldOpcode('D', true, Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE), + new FieldOpcode('L', true, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE), + new FieldOpcode('[', true, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE), + + new FieldOpcode('Z', true, Opcode.SGET_BOOLEAN, Opcode.SGET_VOLATILE), + new FieldOpcode('B', true, Opcode.SGET_BYTE, Opcode.SGET_VOLATILE), + new FieldOpcode('S', true, Opcode.SGET_SHORT, Opcode.SGET_VOLATILE), + new FieldOpcode('C', true, Opcode.SGET_CHAR, Opcode.SGET_VOLATILE), + new FieldOpcode('I', true, Opcode.SGET, Opcode.SGET_VOLATILE), + new FieldOpcode('F', true, Opcode.SGET, Opcode.SGET_VOLATILE), + new FieldOpcode('J', true, Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE), + new FieldOpcode('D', true, Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE), + new FieldOpcode('L', true, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE), + new FieldOpcode('[', true, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE), }; private static final FieldOpcode[] artFieldOpcodes = new FieldOpcode[] { @@ -117,7 +153,7 @@ public class OdexedFieldInstructionMapper { new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK) }; - private final FieldOpcode[][] opcodeMap = new FieldOpcode[2][10]; + private final FieldOpcode[][][] opcodeMap = new FieldOpcode[2][2][10]; private final Map<Opcode, Integer> opcodeValueTypeMap = new HashMap<Opcode, Integer>(30); private static int getValueType(char type) { @@ -169,6 +205,10 @@ public class OdexedFieldInstructionMapper { return (opcode.flags & Opcode.SETS_REGISTER) != 0; } + private static boolean isStatic(@Nonnull Opcode opcode) { + return (opcode.flags & Opcode.STATIC_FIELD_ACCESSOR) != 0; + } + public OdexedFieldInstructionMapper(boolean isArt) { FieldOpcode[] opcodes; if (isArt) { @@ -178,9 +218,13 @@ public class OdexedFieldInstructionMapper { } for (FieldOpcode fieldOpcode: opcodes) { - opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT][getTypeIndex(fieldOpcode.type)] = fieldOpcode; + opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT] + [isStatic(fieldOpcode.normalOpcode)?STATIC:INSTANCE] + [getTypeIndex(fieldOpcode.type)] = fieldOpcode; - opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type)); + if (fieldOpcode.quickOpcode != null) { + opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type)); + } if (fieldOpcode.volatileOpcode != null) { opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type)); } @@ -189,7 +233,9 @@ public class OdexedFieldInstructionMapper { @Nonnull public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) { - FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT][getTypeIndex(fieldType.charAt(0))]; + FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT] + [isStatic(odexedOpcode)?STATIC:INSTANCE] + [getTypeIndex(fieldType.charAt(0))]; if (!isCompatible(odexedOpcode, fieldOpcode.type)) { throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType, diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java index 84981221..b1e5dbbf 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java @@ -482,6 +482,49 @@ public class MutableMethodImplementation implements MethodImplementation { } while (true); } + private int mapCodeAddressToIndex(int codeAddress) { + float avgCodeUnitsPerInstruction = 1.9f; + + int index = (int)(codeAddress/avgCodeUnitsPerInstruction); + if (index >= instructionList.size()) { + index = instructionList.size() - 1; + } + + MethodLocation guessedLocation = instructionList.get(index); + + if (guessedLocation.codeAddress == codeAddress) { + return index; + } else if (guessedLocation.codeAddress > codeAddress) { + do { + index--; + } while (instructionList.get(index).codeAddress > codeAddress); + return index; + } else { + do { + index++; + } while (index < instructionList.size() && instructionList.get(index).codeAddress <= codeAddress); + return index-1; + } + } + + @Nonnull + public Label newLabelForAddress(int codeAddress) { + if (codeAddress < 0 || codeAddress > instructionList.get(instructionList.size()-1).codeAddress) { + throw new IndexOutOfBoundsException(String.format("codeAddress %d out of bounds", codeAddress)); + } + MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddress)); + return referent.addNewLabel(); + } + + @Nonnull + public Label newLabelForIndex(int instructionIndex) { + if (instructionIndex < 0 || instructionIndex >= instructionList.size()) { + throw new IndexOutOfBoundsException(String.format("instruction index %d out of bounds", instructionIndex)); + } + MethodLocation referent = instructionList.get(instructionIndex); + return referent.addNewLabel(); + } + @Nonnull private Label newLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) { MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress)); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java index 9cf49294..eeb28227 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java @@ -97,6 +97,23 @@ public class BaseDexBuffer { (((long)buf[offset+7]) << 56); } + public int readLongAsSmallUint(int offset) { + byte[] buf = this.buf; + offset += baseOffset; + long result = (buf[offset] & 0xff) | + ((buf[offset+1] & 0xff) << 8) | + ((buf[offset+2] & 0xff) << 16) | + ((buf[offset+3] & 0xffL) << 24) | + ((buf[offset+4] & 0xffL) << 32) | + ((buf[offset+5] & 0xffL) << 40) | + ((buf[offset+6] & 0xffL) << 48) | + (((long)buf[offset+7]) << 56); + if (result < 0 || result > Integer.MAX_VALUE) { + throw new ExceptionWithContext("Encountered out-of-range ulong at offset 0x%x", offset); + } + return (int)result; + } + public int readInt(int offset) { byte[] buf = this.buf; offset += baseOffset; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java index 638fd855..32505eec 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java @@ -46,7 +46,7 @@ import java.io.InputStream; import java.util.Set; public class DexBackedDexFile extends BaseDexBuffer implements DexFile { - private final Opcodes opcodes; + @Nonnull private final Opcodes opcodes; private final int stringCount; private final int stringStartOffset; @@ -61,7 +61,7 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile { private final int classCount; private final int classStartOffset; - private DexBackedDexFile(Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) { + private DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) { super(buf, offset); this.opcodes = opcodes; @@ -117,7 +117,7 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile { return new DexBackedDexFile(opcodes, buf, 0, false); } - public Opcodes getOpcodes() { + @Override @Nonnull public Opcodes getOpcodes() { return opcodes; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java index b5250d0d..dbeb67ce 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java @@ -49,29 +49,38 @@ import java.util.List; public class OatFile extends BaseDexBuffer { private static final byte[] ELF_MAGIC = new byte[] { 0x7f, 'E', 'L', 'F' }; private static final byte[] OAT_MAGIC = new byte[] { 'o', 'a', 't', '\n' }; - private static final int ELF_HEADER_SIZE = 52; + private static final int MIN_ELF_HEADER_SIZE = 52; // These are the "known working" versions that I have manually inspected the source for. // Later version may or may not work, depending on what changed. private static final int MIN_OAT_VERSION = 56; - private static final int MAX_OAT_VERSION = 65; + private static final int MAX_OAT_VERSION = 71; public static final int UNSUPPORTED = 0; public static final int SUPPORTED = 1; public static final int UNKNOWN = 2; + private final boolean is64bit; @Nonnull private final OatHeader oatHeader; @Nonnull private final Opcodes opcodes; public OatFile(@Nonnull byte[] buf) { super(buf); - if (buf.length < ELF_HEADER_SIZE) { + if (buf.length < MIN_ELF_HEADER_SIZE) { throw new NotAnOatFileException(); } verifyMagic(buf); + if (buf[4] == 1) { + is64bit = false; + } else if (buf[4] == 2) { + is64bit = true; + } else { + throw new InvalidOatFileException(String.format("Invalid word-size value: %x", buf[5])); + } + OatHeader oatHeader = null; SymbolTable symbolTable = getSymbolTable(); for (Symbol symbol: symbolTable.getSymbols()) { @@ -192,6 +201,10 @@ public class OatFile extends BaseDexBuffer { this.filename = filename; } + public int getOatVersion() { + return OatFile.this.getOatVersion(); + } + @Override public boolean hasOdexOpcodes() { return true; } @@ -254,9 +267,18 @@ public class OatFile extends BaseDexBuffer { @Nonnull private List<SectionHeader> getSections() { - final int offset = readSmallUint(32); - final int entrySize = readUshort(46); - final int entryCount = readUshort(48); + final int offset; + final int entrySize; + final int entryCount; + if (is64bit) { + offset = readLongAsSmallUint(40); + entrySize = readUshort(58); + entryCount = readUshort(60); + } else { + offset = readSmallUint(32); + entrySize = readUshort(46); + entryCount = readUshort(48); + } if (offset + (entrySize * entryCount) > buf.length) { throw new InvalidOatFileException("The ELF section headers extend past the end of the file"); @@ -267,7 +289,11 @@ public class OatFile extends BaseDexBuffer { if (index < 0 || index >= entryCount) { throw new IndexOutOfBoundsException(); } - return new SectionHeader(offset + (index * entrySize)); + if (is64bit) { + return new SectionHeader64Bit(offset + (index * entrySize)); + } else { + return new SectionHeader32Bit(offset + (index * entrySize)); + } } @Override public int size() { @@ -300,43 +326,35 @@ public class OatFile extends BaseDexBuffer { } } - private class SectionHeader { - private final int offset; - + private abstract class SectionHeader { + protected final int offset; public static final int TYPE_DYNAMIC_SYMBOL_TABLE = 11; + public SectionHeader(int offset) { this.offset = offset; } + @Nonnull public String getName() { return getSectionNameStringTable().getString(readSmallUint(offset)); } + public int getType() { return readInt(offset + 4); } + public abstract long getAddress(); + public abstract int getOffset(); + public abstract int getSize(); + public abstract int getLink(); + public abstract int getEntrySize(); + } - public SectionHeader(int offset) { - this.offset = offset; - } - - @Nonnull - public String getName() { - return getSectionNameStringTable().getString(readSmallUint(offset)); - } - - public int getType() { - return readInt(offset + 4); - } - - public long getAddress() { - return readInt(offset + 12) & 0xFFFFFFFFL; - } - - public int getOffset() { - return readSmallUint(offset + 16); - } - - public int getSize() { - return readSmallUint(offset + 20); - } - - public int getLink() { - return readSmallUint(offset + 24); - } + private class SectionHeader32Bit extends SectionHeader { + public SectionHeader32Bit(int offset) { super(offset); } + @Override public long getAddress() { return readInt(offset + 12) & 0xFFFFFFFFL; } + @Override public int getOffset() { return readSmallUint(offset + 16); } + @Override public int getSize() { return readSmallUint(offset + 20); } + @Override public int getLink() { return readSmallUint(offset + 24); } + @Override public int getEntrySize() { return readSmallUint(offset + 36); } + } - public int getEntrySize() { - return readSmallUint(offset + 36); - } + private class SectionHeader64Bit extends SectionHeader { + public SectionHeader64Bit(int offset) { super(offset); } + @Override public long getAddress() { return readLong(offset + 16); } + @Override public int getOffset() { return readLongAsSmallUint(offset + 24); } + @Override public int getSize() { return readLongAsSmallUint(offset + 32); } + @Override public int getLink() { return readSmallUint(offset + 40); } + @Override public int getEntrySize() { return readLongAsSmallUint(offset + 56); } } class SymbolTable { @@ -367,7 +385,11 @@ public class OatFile extends BaseDexBuffer { if (index < 0 || index >= entryCount) { throw new IndexOutOfBoundsException(); } - return new Symbol(offset + index * entrySize); + if (is64bit) { + return new Symbol64(offset + index * entrySize); + } else { + return new Symbol32(offset + index * entrySize); + } } @Override public int size() { @@ -376,29 +398,13 @@ public class OatFile extends BaseDexBuffer { }; } - public class Symbol { - private final int offset; - - public Symbol(int offset) { - this.offset = offset; - } - - @Nonnull - public String getName() { - return stringTable.getString(readSmallUint(offset)); - } - - public int getValue() { - return readSmallUint(offset + 4); - } - - public int getSize() { - return readSmallUint(offset + 8); - } - - public int getSectionIndex() { - return readUshort(offset + 14); - } + public abstract class Symbol { + protected final int offset; + public Symbol(int offset) { this.offset = offset; } + @Nonnull public abstract String getName(); + public abstract long getValue(); + public abstract int getSize(); + public abstract int getSectionIndex(); public int getFileOffset() { SectionHeader sectionHeader; @@ -423,6 +429,26 @@ public class OatFile extends BaseDexBuffer { return (int)fileOffset; } } + + public class Symbol32 extends Symbol { + public Symbol32(int offset) { super(offset); } + + @Nonnull + public String getName() { return stringTable.getString(readSmallUint(offset)); } + public long getValue() { return readSmallUint(offset + 4); } + public int getSize() { return readSmallUint(offset + 8); } + public int getSectionIndex() { return readUshort(offset + 14); } + } + + public class Symbol64 extends Symbol { + public Symbol64(int offset) { super(offset); } + + @Nonnull + public String getName() { return stringTable.getString(readSmallUint(offset)); } + public long getValue() { return readLong(offset + 8); } + public int getSize() { return readLongAsSmallUint(offset + 16); } + public int getSectionIndex() { return readUshort(offset + 6); } + } } private class StringTable { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java index 45707f03..474bfb13 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java @@ -31,6 +31,8 @@ package org.jf.dexlib2.iface; +import org.jf.dexlib2.Opcodes; + import javax.annotation.Nonnull; import java.util.Set; @@ -46,4 +48,11 @@ public interface DexFile { * @return A set of the classes defined in this dex file */ @Nonnull Set<? extends ClassDef> getClasses(); + + /** + * Get the Opcodes associated with this dex file + * + * @return The Opcodes instance representing the possible opcodes that can be encountered in this dex file + */ + @Nonnull Opcodes getOpcodes(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java index e911eb38..2112bd07 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java @@ -32,6 +32,7 @@ package org.jf.dexlib2.immutable; import com.google.common.collect.ImmutableSet; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.util.ImmutableUtils; @@ -42,21 +43,37 @@ import java.util.Collection; public class ImmutableDexFile implements DexFile { @Nonnull protected final ImmutableSet<? extends ImmutableClassDef> classes; + @Nonnull private final Opcodes opcodes; + @Deprecated public ImmutableDexFile(@Nullable Collection<? extends ClassDef> classes) { this.classes = ImmutableClassDef.immutableSetOf(classes); + this.opcodes = Opcodes.forApi(19); } + @Deprecated public ImmutableDexFile(@Nullable ImmutableSet<? extends ImmutableClassDef> classes) { this.classes = ImmutableUtils.nullToEmptySet(classes); + this.opcodes = Opcodes.forApi(19); + } + + public ImmutableDexFile(@Nonnull Opcodes opcodes, @Nullable Collection<? extends ClassDef> classes) { + this.classes = ImmutableClassDef.immutableSetOf(classes); + this.opcodes = opcodes; + } + + public ImmutableDexFile(@Nonnull Opcodes opcodes, @Nullable ImmutableSet<? extends ImmutableClassDef> classes) { + this.classes = ImmutableUtils.nullToEmptySet(classes); + this.opcodes = opcodes; } public static ImmutableDexFile of(DexFile dexFile) { if (dexFile instanceof ImmutableDexFile) { return (ImmutableDexFile)dexFile; } - return new ImmutableDexFile(dexFile.getClasses()); + return new ImmutableDexFile(dexFile.getOpcodes(), dexFile.getClasses()); } @Nonnull @Override public ImmutableSet<? extends ImmutableClassDef> getClasses() { return classes; } + @Nonnull @Override public Opcodes getOpcodes() { return opcodes; } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java index e482cd04..839ac096 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java @@ -31,6 +31,7 @@ package org.jf.dexlib2.rewriter; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.instruction.Instruction; @@ -116,6 +117,10 @@ public class DexRewriter implements Rewriters { @Override @Nonnull public Set<? extends ClassDef> getClasses() { return RewriterUtils.rewriteSet(getClassDefRewriter(), dexFile.getClasses()); } + + @Nonnull @Override public Opcodes getOpcodes() { + return dexFile.getOpcodes(); + } } @Nonnull @Override public Rewriter<ClassDef> getClassDefRewriter() { return classDefRewriter; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java index 1e8b010b..674d5180 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java @@ -33,11 +33,12 @@ package org.jf.dexlib2.util; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; import org.jf.dexlib2.iface.instruction.WideLiteralInstruction; -import org.jf.dexlib2.Opcodes; +import javax.annotation.Nonnull; import java.util.List; public class SyntheticAccessorFSM { @@ -212,7 +213,13 @@ static final int SyntheticAccessorFSM_en_main = 1; public static final int NEGATIVE_ONE = -1; public static final int OTHER = 0; - public static int test(List<? extends Instruction> instructions) { + @Nonnull private final Opcodes opcodes; + + public SyntheticAccessorFSM(@Nonnull Opcodes opcodes) { + this.opcodes = opcodes; + } + + public int test(List<? extends Instruction> instructions) { int accessorType = -1; int cs, p = 0; int pe = instructions.size(); @@ -231,15 +238,13 @@ static final int SyntheticAccessorFSM_en_main = 1; // The return register; int returnRegister = -1; - Opcodes opcodes = Opcodes.forApi(20); - -// line 238 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" +// line 242 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" { cs = SyntheticAccessorFSM_start; } -// line 243 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" +// line 247 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" { int _klen; int _trans = 0; @@ -320,19 +325,19 @@ case 1: switch ( _SyntheticAccessorFSM_actions[_acts++] ) { case 0: -// line 96 "SyntheticAccessorFSM.rl" +// line 100 "SyntheticAccessorFSM.rl" { putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA(); } break; case 1: -// line 103 "SyntheticAccessorFSM.rl" +// line 107 "SyntheticAccessorFSM.rl" { constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral(); } break; case 2: -// line 107 "SyntheticAccessorFSM.rl" +// line 111 "SyntheticAccessorFSM.rl" { mathType = INT; mathOp = ADD; @@ -340,146 +345,146 @@ case 1: } break; case 3: -// line 113 "SyntheticAccessorFSM.rl" +// line 117 "SyntheticAccessorFSM.rl" { mathType = INT; } break; case 4: -// line 114 "SyntheticAccessorFSM.rl" +// line 118 "SyntheticAccessorFSM.rl" { mathType = LONG; } break; case 5: -// line 115 "SyntheticAccessorFSM.rl" +// line 119 "SyntheticAccessorFSM.rl" { mathType = FLOAT; } break; case 6: -// line 116 "SyntheticAccessorFSM.rl" +// line 120 "SyntheticAccessorFSM.rl" {mathType = DOUBLE; } break; case 7: -// line 116 "SyntheticAccessorFSM.rl" +// line 120 "SyntheticAccessorFSM.rl" { mathOp = ADD; } break; case 8: -// line 119 "SyntheticAccessorFSM.rl" +// line 123 "SyntheticAccessorFSM.rl" { mathType = INT; } break; case 9: -// line 120 "SyntheticAccessorFSM.rl" +// line 124 "SyntheticAccessorFSM.rl" { mathType = LONG; } break; case 10: -// line 121 "SyntheticAccessorFSM.rl" +// line 125 "SyntheticAccessorFSM.rl" { mathType = FLOAT; } break; case 11: -// line 122 "SyntheticAccessorFSM.rl" +// line 126 "SyntheticAccessorFSM.rl" {mathType = DOUBLE; } break; case 12: -// line 122 "SyntheticAccessorFSM.rl" +// line 126 "SyntheticAccessorFSM.rl" { mathOp = SUB; } break; case 13: -// line 126 "SyntheticAccessorFSM.rl" +// line 130 "SyntheticAccessorFSM.rl" { mathOp = MUL; } break; case 14: -// line 130 "SyntheticAccessorFSM.rl" +// line 134 "SyntheticAccessorFSM.rl" { mathOp = DIV; } break; case 15: -// line 134 "SyntheticAccessorFSM.rl" +// line 138 "SyntheticAccessorFSM.rl" { mathOp = REM; } break; case 16: -// line 137 "SyntheticAccessorFSM.rl" +// line 141 "SyntheticAccessorFSM.rl" { mathOp = AND; } break; case 17: -// line 140 "SyntheticAccessorFSM.rl" +// line 144 "SyntheticAccessorFSM.rl" { mathOp = OR; } break; case 18: -// line 143 "SyntheticAccessorFSM.rl" +// line 147 "SyntheticAccessorFSM.rl" { mathOp = XOR; } break; case 19: -// line 146 "SyntheticAccessorFSM.rl" +// line 150 "SyntheticAccessorFSM.rl" { mathOp = SHL; } break; case 20: -// line 149 "SyntheticAccessorFSM.rl" +// line 153 "SyntheticAccessorFSM.rl" { mathOp = SHR; } break; case 21: -// line 152 "SyntheticAccessorFSM.rl" +// line 156 "SyntheticAccessorFSM.rl" { mathOp = USHR; } break; case 22: -// line 158 "SyntheticAccessorFSM.rl" +// line 162 "SyntheticAccessorFSM.rl" { returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA(); } break; case 23: -// line 164 "SyntheticAccessorFSM.rl" +// line 168 "SyntheticAccessorFSM.rl" { accessorType = SyntheticAccessorResolver.GETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; case 24: -// line 168 "SyntheticAccessorFSM.rl" +// line 172 "SyntheticAccessorFSM.rl" { accessorType = SyntheticAccessorResolver.SETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; case 25: -// line 172 "SyntheticAccessorFSM.rl" +// line 176 "SyntheticAccessorFSM.rl" { accessorType = SyntheticAccessorResolver.METHOD; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; case 26: -// line 176 "SyntheticAccessorFSM.rl" +// line 180 "SyntheticAccessorFSM.rl" { accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister); } break; case 27: -// line 180 "SyntheticAccessorFSM.rl" +// line 184 "SyntheticAccessorFSM.rl" { accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister); } break; case 28: -// line 188 "SyntheticAccessorFSM.rl" +// line 192 "SyntheticAccessorFSM.rl" { accessorType = mathOp; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 483 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" +// line 487 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" } } } @@ -499,7 +504,7 @@ case 5: break; } } -// line 201 "SyntheticAccessorFSM.rl" +// line 205 "SyntheticAccessorFSM.rl" return accessorType; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java index a46a18f0..7808f84d 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java @@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.MethodImplementation; @@ -68,10 +69,12 @@ public class SyntheticAccessorResolver { public static final int SHR_ASSIGNMENT = 16; public static final int USHR_ASSIGNMENT = 17; + private final SyntheticAccessorFSM syntheticAccessorFSM; private final Map<String, ClassDef> classDefMap; private final Map<String, AccessedMember> resolvedAccessors = Maps.newConcurrentMap(); - public SyntheticAccessorResolver(Iterable<? extends ClassDef> classDefs) { + public SyntheticAccessorResolver(@Nonnull Opcodes opcodes, @Nonnull Iterable<? extends ClassDef> classDefs) { + this.syntheticAccessorFSM = new SyntheticAccessorFSM(opcodes); ImmutableMap.Builder<String, ClassDef> builder = ImmutableMap.builder(); for (ClassDef classDef: classDefs) { @@ -124,7 +127,8 @@ public class SyntheticAccessorResolver { List<Instruction> instructions = ImmutableList.copyOf(matchedMethodImpl.getInstructions()); - int accessType = SyntheticAccessorFSM.test(instructions); + + int accessType = syntheticAccessorFSM.test(instructions); if (accessType >= 0) { AccessedMember member = diff --git a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl index 60d297bb..96ac5367 100644 --- a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl +++ b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl @@ -64,7 +64,13 @@ public class SyntheticAccessorFSM { public static final int NEGATIVE_ONE = -1; public static final int OTHER = 0; - public static int test(List<? extends Instruction> instructions) { + @Nonnull private final Opcodes opcodes; + + public SyntheticAccessorFSM(@Nonnull Opcodes opcodes) { + this.opcodes = opcodes; + } + + public int test(List<? extends Instruction> instructions) { int accessorType = -1; int cs, p = 0; int pe = instructions.size(); @@ -83,8 +89,6 @@ public class SyntheticAccessorFSM { // The return register; int returnRegister = -1; - Opcodes opcodes = Opcodes.forApi(20); - %%{ import "Opcodes.rl"; alphtype short; diff --git a/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java b/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java index 924d3fd3..4c8f85bf 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java @@ -81,7 +81,7 @@ public class AccessorTest { Assert.assertNotNull(url); DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15, false); - SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses()); + SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getOpcodes(), f.getClasses()); ClassDef accessorTypesClass = null; ClassDef accessorsClass = null; diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java index f3b1094e..521290f0 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java @@ -33,6 +33,7 @@ package org.jf.dexlib2.analysis; import com.google.common.collect.ImmutableSet; import junit.framework.Assert; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.immutable.ImmutableDexFile; import org.junit.Test; @@ -53,38 +54,41 @@ public class CommonSuperclassTest { private final ClassPath classPath; public CommonSuperclassTest() throws IOException { - classPath = new ClassPath(new ImmutableDexFile(ImmutableSet.of( - TestUtils.makeClassDef("Ljava/lang/Object;", null), - TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"), - TestUtils.makeClassDef("Ltest/two;", "Ljava/lang/Object;"), - TestUtils.makeClassDef("Ltest/onetwo;", "Ltest/one;"), - TestUtils.makeClassDef("Ltest/onetwothree;", "Ltest/onetwo;"), - TestUtils.makeClassDef("Ltest/onethree;", "Ltest/one;"), - TestUtils.makeClassDef("Ltest/fivetwo;", "Ltest/five;"), - TestUtils.makeClassDef("Ltest/fivetwothree;", "Ltest/fivetwo;"), - TestUtils.makeClassDef("Ltest/fivethree;", "Ltest/five;"), - TestUtils.makeInterfaceDef("Ljava/lang/Cloneable;"), - TestUtils.makeInterfaceDef("Ljava/io/Serializable;"), - - // basic class and interface - TestUtils.makeClassDef("Liface/classiface1;", "Ljava/lang/Object;", "Liface/iface1;"), - TestUtils.makeInterfaceDef("Liface/iface1;"), - - // a more complex interface tree - TestUtils.makeInterfaceDef("Liface/base1;"), - // implements undefined interface - TestUtils.makeInterfaceDef("Liface/sub1;", "Liface/base1;", "Liface/base2;"), - // this implements sub1, so that its interfaces can't be fully resolved either - TestUtils.makeInterfaceDef("Liface/sub2;", "Liface/base1;", "Liface/sub1;"), - TestUtils.makeInterfaceDef("Liface/sub3;", "Liface/base1;"), - TestUtils.makeInterfaceDef("Liface/sub4;", "Liface/base1;", "Liface/sub3;"), - TestUtils.makeClassDef("Liface/classsub1;", "Ljava/lang/Object;", "Liface/sub1;"), - TestUtils.makeClassDef("Liface/classsub2;", "Ljava/lang/Object;", "Liface/sub2;"), - TestUtils.makeClassDef("Liface/classsub3;", "Ljava/lang/Object;", "Liface/sub3;", "Liface/base;"), - TestUtils.makeClassDef("Liface/classsub4;", "Ljava/lang/Object;", "Liface/sub3;", "Liface/sub4;"), - TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"), - TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;", "Liface/sub2;", - "Liface/sub3;", "Liface/sub4;") + classPath = new ClassPath(new ImmutableDexFile(Opcodes.forApi(19), + ImmutableSet.of( + TestUtils.makeClassDef("Ljava/lang/Object;", null), + TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"), + TestUtils.makeClassDef("Ltest/two;", "Ljava/lang/Object;"), + TestUtils.makeClassDef("Ltest/onetwo;", "Ltest/one;"), + TestUtils.makeClassDef("Ltest/onetwothree;", "Ltest/onetwo;"), + TestUtils.makeClassDef("Ltest/onethree;", "Ltest/one;"), + TestUtils.makeClassDef("Ltest/fivetwo;", "Ltest/five;"), + TestUtils.makeClassDef("Ltest/fivetwothree;", "Ltest/fivetwo;"), + TestUtils.makeClassDef("Ltest/fivethree;", "Ltest/five;"), + TestUtils.makeInterfaceDef("Ljava/lang/Cloneable;"), + TestUtils.makeInterfaceDef("Ljava/io/Serializable;"), + + // basic class and interface + TestUtils.makeClassDef("Liface/classiface1;", "Ljava/lang/Object;", "Liface/iface1;"), + TestUtils.makeInterfaceDef("Liface/iface1;"), + + // a more complex interface tree + TestUtils.makeInterfaceDef("Liface/base1;"), + // implements undefined interface + TestUtils.makeInterfaceDef("Liface/sub1;", "Liface/base1;", "Liface/base2;"), + // this implements sub1, so that its interfaces can't be fully resolved either + TestUtils.makeInterfaceDef("Liface/sub2;", "Liface/base1;", "Liface/sub1;"), + TestUtils.makeInterfaceDef("Liface/sub3;", "Liface/base1;"), + TestUtils.makeInterfaceDef("Liface/sub4;", "Liface/base1;", "Liface/sub3;"), + TestUtils.makeClassDef("Liface/classsub1;", "Ljava/lang/Object;", "Liface/sub1;"), + TestUtils.makeClassDef("Liface/classsub2;", "Ljava/lang/Object;", "Liface/sub2;"), + TestUtils.makeClassDef("Liface/classsub3;", "Ljava/lang/Object;", "Liface/sub3;", + "Liface/base;"), + TestUtils.makeClassDef("Liface/classsub4;", "Ljava/lang/Object;", "Liface/sub3;", + "Liface/sub4;"), + TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"), + TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;", + "Liface/sub2;", "Liface/sub3;", "Liface/sub4;") ))); } diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java index 25f7778d..90a63590 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java @@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.instruction.Instruction; @@ -66,7 +67,7 @@ public class CustomMethodInlineTableTest { ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, null, null, null, null, ImmutableList.of(method)); - DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef)); + DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef)); ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile, 15, false); @@ -93,7 +94,7 @@ public class CustomMethodInlineTableTest { ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, null, null, null, ImmutableList.of(method), null); - DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef)); + DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef)); ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile, 15, false); @@ -120,7 +121,7 @@ public class CustomMethodInlineTableTest { ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, null, null, null, ImmutableList.of(method), null); - DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef)); + DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef)); ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile, 15, false); diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java index 59b0d276..c9a9af95 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java @@ -34,6 +34,7 @@ package org.jf.dexlib2.analysis.util; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import junit.framework.Assert; +import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.analysis.ClassPath; import org.jf.dexlib2.analysis.TestUtils; import org.jf.dexlib2.analysis.TypeProto; @@ -56,7 +57,7 @@ public class SuperclassChainTest { ImmutableSet<ClassDef> classes = ImmutableSet.<ClassDef>of( objectClassDef, oneClassDef, twoClassDef, threeClassDef); - ClassPath classPath = new ClassPath(new ImmutableDexFile(classes)); + ClassPath classPath = new ClassPath(new ImmutableDexFile(Opcodes.forApi(19), classes)); TypeProto objectClassProto = classPath.getClass("Ljava/lang/Object;"); TypeProto oneClassProto = classPath.getClass("Ltest/one;"); @@ -87,7 +88,7 @@ public class SuperclassChainTest { ClassDef twoClassDef = TestUtils.makeClassDef("Ltest/two;", "Ltest/one;"); ClassDef threeClassDef = TestUtils.makeClassDef("Ltest/three;", "Ltest/two;"); ImmutableSet<ClassDef> classes = ImmutableSet.<ClassDef>of(twoClassDef, threeClassDef); - ClassPath classPath = new ClassPath(new ImmutableDexFile(classes)); + ClassPath classPath = new ClassPath(new ImmutableDexFile(Opcodes.forApi(19), classes)); TypeProto unknownClassProto = classPath.getUnknownClass(); TypeProto oneClassProto = classPath.getClass("Ltest/one;"); diff --git a/dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java b/dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java new file mode 100644 index 00000000..0df5ab3d --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.builder.instruction.BuilderInstruction10x; +import org.jf.dexlib2.builder.instruction.BuilderInstruction32x; +import org.jf.dexlib2.iface.MethodImplementation; +import org.junit.Assert; +import org.junit.Test; + +public class MutableMethodImplementationTest { + + @Test + public void testTryEndAtEndOfMethod() { + MethodImplementationBuilder builder = new MethodImplementationBuilder(10); + + Label startLabel = builder.addLabel("start"); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction32x(Opcode.MOVE_16, 0, 0)); + Label endLabel = builder.addLabel("end"); + + builder.addCatch(startLabel, endLabel, startLabel); + + MethodImplementation methodImplementation = builder.getMethodImplementation(); + + Assert.assertEquals(0, methodImplementation.getTryBlocks().get(0).getStartCodeAddress()); + Assert.assertEquals(8, methodImplementation.getTryBlocks().get(0).getCodeUnitCount()); + + methodImplementation = new MutableMethodImplementation(methodImplementation); + + Assert.assertEquals(0, methodImplementation.getTryBlocks().get(0).getStartCodeAddress()); + Assert.assertEquals(8, methodImplementation.getTryBlocks().get(0).getCodeUnitCount()); + } + + @Test + public void testNewLabelByAddress() { + MethodImplementationBuilder builder = new MethodImplementationBuilder(10); + + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction32x(Opcode.MOVE_16, 0, 0)); + + MutableMethodImplementation mutableMethodImplementation = + new MutableMethodImplementation(builder.getMethodImplementation()); + + mutableMethodImplementation.addCatch( + mutableMethodImplementation.newLabelForAddress(0), + mutableMethodImplementation.newLabelForAddress(8), + mutableMethodImplementation.newLabelForAddress(1)); + + Assert.assertEquals(0, mutableMethodImplementation.getTryBlocks().get(0).getStartCodeAddress()); + Assert.assertEquals(8, mutableMethodImplementation.getTryBlocks().get(0).getCodeUnitCount()); + Assert.assertEquals(1, mutableMethodImplementation.getTryBlocks().get(0).getExceptionHandlers().get(0) + .getHandlerCodeAddress()); + } + + @Test + public void testNewLabelByIndex() { + MethodImplementationBuilder builder = new MethodImplementationBuilder(10); + + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + builder.addInstruction(new BuilderInstruction32x(Opcode.MOVE_16, 0, 0)); + + MutableMethodImplementation mutableMethodImplementation = + new MutableMethodImplementation(builder.getMethodImplementation()); + + mutableMethodImplementation.addCatch( + mutableMethodImplementation.newLabelForIndex(0), + mutableMethodImplementation.newLabelForIndex(6), + mutableMethodImplementation.newLabelForIndex(1)); + + Assert.assertEquals(0, mutableMethodImplementation.getTryBlocks().get(0).getStartCodeAddress()); + Assert.assertEquals(8, mutableMethodImplementation.getTryBlocks().get(0).getCodeUnitCount()); + Assert.assertEquals(1, mutableMethodImplementation.getTryBlocks().get(0).getExceptionHandlers().get(0) + .getHandlerCodeAddress()); + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java index 54372cf6..1a0a2892 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java @@ -72,7 +72,7 @@ public class DexWriterTest { MemoryDataStore dataStore = new MemoryDataStore(); try { - DexPool.writeTo(dataStore, new ImmutableDexFile(ImmutableSet.of(classDef))); + DexPool.writeTo(dataStore, new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of(classDef))); } catch (IOException ex) { throw new RuntimeException(ex); } @@ -112,7 +112,7 @@ public class DexWriterTest { MemoryDataStore dataStore = new MemoryDataStore(); try { - DexPool.writeTo(dataStore, new ImmutableDexFile(ImmutableSet.of(classDef))); + DexPool.writeTo(dataStore, new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of(classDef))); } catch (IOException ex) { throw new RuntimeException(ex); } diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java index b36b088d..bbf4ff37 100644 --- a/smali/src/main/java/org/jf/smali/main.java +++ b/smali/src/main/java/org/jf/smali/main.java @@ -327,7 +327,7 @@ public class main { LexerErrorInterface lexer; - FileInputStream fis = new FileInputStream(smaliFile.getAbsolutePath()); + FileInputStream fis = new FileInputStream(smaliFile); InputStreamReader reader = new InputStreamReader(fis, "UTF-8"); lexer = new smaliFlexLexer(reader); |