Home | History | Annotate | Download | only in Format
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.baksmali.Adaptors.Format;
     30 
     31 import org.jf.baksmali.Adaptors.MethodDefinition;
     32 import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
     33 import org.jf.baksmali.Adaptors.MethodItem;
     34 import org.jf.baksmali.Renderers.LongRenderer;
     35 import org.jf.baksmali.baksmaliOptions;
     36 import org.jf.dexlib2.Opcode;
     37 import org.jf.dexlib2.ReferenceType;
     38 import org.jf.dexlib2.VerificationError;
     39 import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
     40 import org.jf.dexlib2.iface.instruction.*;
     41 import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
     42 import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
     43 import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
     44 import org.jf.dexlib2.iface.reference.Reference;
     45 import org.jf.dexlib2.util.ReferenceUtil;
     46 import org.jf.util.ExceptionWithContext;
     47 import org.jf.util.IndentingWriter;
     48 import org.jf.util.NumberUtils;
     49 
     50 import javax.annotation.Nonnull;
     51 import java.io.IOException;
     52 import java.util.Map;
     53 
     54 public class InstructionMethodItem<T extends Instruction> extends MethodItem {
     55     @Nonnull protected final MethodDefinition methodDef;
     56     @Nonnull protected final T instruction;
     57 
     58     public InstructionMethodItem(@Nonnull MethodDefinition methodDef, int codeAddress, @Nonnull T instruction) {
     59         super(codeAddress);
     60         this.methodDef = methodDef;
     61         this.instruction = instruction;
     62     }
     63 
     64     public double getSortOrder() {
     65         //instructions should appear after everything except an "end try" label and .catch directive
     66         return 100;
     67     }
     68 
     69     private boolean isAllowedOdex(@Nonnull Opcode opcode) {
     70         baksmaliOptions options = methodDef.classDef.options;
     71         if (options.allowOdex) {
     72             return true;
     73         }
     74 
     75         if (methodDef.classDef.options.apiLevel >= 14) {
     76             return false;
     77         }
     78 
     79         return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR;
     80     }
     81 
     82     @Override
     83     public boolean writeTo(IndentingWriter writer) throws IOException {
     84         Opcode opcode = instruction.getOpcode();
     85         String verificationErrorName = null;
     86         String referenceString = null;
     87 
     88         boolean commentOutInstruction = false;
     89 
     90         if (instruction instanceof Instruction20bc) {
     91             int verificationError = ((Instruction20bc)instruction).getVerificationError();
     92             verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
     93             if (verificationErrorName == null) {
     94                 writer.write("#was invalid verification error type: ");
     95                 writer.printSignedIntAsDec(verificationError);
     96                 writer.write("\n");
     97                 verificationErrorName = "generic-error";
     98             }
     99         }
    100 
    101         if (instruction instanceof ReferenceInstruction) {
    102             ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
    103             try {
    104                 Reference reference = referenceInstruction.getReference();
    105 
    106                 String classContext = null;
    107                 if (methodDef.classDef.options.useImplicitReferences) {
    108                     classContext = methodDef.method.getDefiningClass();
    109                 }
    110 
    111                 referenceString = ReferenceUtil.getReferenceString(reference, classContext);
    112                 assert referenceString != null;
    113             } catch (InvalidItemIndex ex) {
    114                 writer.write("#");
    115                 writer.write(ex.getMessage());
    116                 writer.write("\n");
    117                 commentOutInstruction = true;
    118 
    119                 referenceString = String.format("%s@%d",
    120                     ReferenceType.toString(referenceInstruction.getReferenceType()),
    121                     ex.getInvalidIndex());
    122             } catch (ReferenceType.InvalidReferenceTypeException ex) {
    123                 writer.write("#invalid reference type: ");
    124                 writer.printSignedIntAsDec(ex.getReferenceType());
    125                 commentOutInstruction = true;
    126 
    127                 referenceString = "invalid_reference";
    128             }
    129         }
    130 
    131         if (instruction instanceof Instruction31t) {
    132             boolean validPayload = true;
    133 
    134             switch (instruction.getOpcode()) {
    135                 case PACKED_SWITCH:
    136                     int baseAddress = methodDef.getPackedSwitchBaseAddress(
    137                             this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
    138                     if (baseAddress == -1) {
    139                         validPayload = false;
    140                     }
    141                     break;
    142                 case SPARSE_SWITCH:
    143                     baseAddress = methodDef.getSparseSwitchBaseAddress(
    144                             this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
    145                     if (baseAddress == -1) {
    146                         validPayload = false;
    147                     }
    148                     break;
    149                 case FILL_ARRAY_DATA:
    150                     try {
    151                         methodDef.findPayloadOffset(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
    152                                 Opcode.ARRAY_PAYLOAD);
    153                     } catch (InvalidSwitchPayload ex) {
    154                         validPayload = false;
    155                     }
    156                     break;
    157                 default:
    158                     throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
    159             }
    160 
    161             if (!validPayload) {
    162                 writer.write("#invalid payload reference\n");
    163                 commentOutInstruction = true;
    164             }
    165         }
    166 
    167         if (opcode.odexOnly()) {
    168             if (!isAllowedOdex(opcode)) {
    169                 writer.write("#disallowed odex opcode\n");
    170                 commentOutInstruction = true;
    171             }
    172         }
    173 
    174         if (commentOutInstruction) {
    175             writer.write("#");
    176         }
    177 
    178         switch (instruction.getOpcode().format) {
    179             case Format10t:
    180                 writeOpcode(writer);
    181                 writer.write(' ');
    182                 writeTargetLabel(writer);
    183                 break;
    184             case Format10x:
    185                 if (instruction instanceof UnknownInstruction) {
    186                     writer.write("#unknown opcode: 0x");
    187                     writer.printUnsignedLongAsHex(((UnknownInstruction)instruction).getOriginalOpcode());
    188                     writer.write('\n');
    189                 }
    190                 writeOpcode(writer);
    191                 break;
    192             case Format11n:
    193                 writeOpcode(writer);
    194                 writer.write(' ');
    195                 writeFirstRegister(writer);
    196                 writer.write(", ");
    197                 writeLiteral(writer);
    198                 break;
    199             case Format11x:
    200                 writeOpcode(writer);
    201                 writer.write(' ');
    202                 writeFirstRegister(writer);
    203                 break;
    204             case Format12x:
    205                 writeOpcode(writer);
    206                 writer.write(' ');
    207                 writeFirstRegister(writer);
    208                 writer.write(", ");
    209                 writeSecondRegister(writer);
    210                 break;
    211             case Format20bc:
    212                 writeOpcode(writer);
    213                 writer.write(' ');
    214                 writer.write(verificationErrorName);
    215                 writer.write(", ");
    216                 writer.write(referenceString);
    217                 break;
    218             case Format20t:
    219             case Format30t:
    220                 writeOpcode(writer);
    221                 writer.write(' ');
    222                 writeTargetLabel(writer);
    223                 break;
    224             case Format21c:
    225             case Format31c:
    226                 writeOpcode(writer);
    227                 writer.write(' ');
    228                 writeFirstRegister(writer);
    229                 writer.write(", ");
    230                 writer.write(referenceString);
    231                 break;
    232             case Format21ih:
    233             case Format21lh:
    234             case Format21s:
    235             case Format31i:
    236             case Format51l:
    237                 writeOpcode(writer);
    238                 writer.write(' ');
    239                 writeFirstRegister(writer);
    240                 writer.write(", ");
    241                 writeLiteral(writer);
    242                 if (instruction.getOpcode().setsWideRegister()) {
    243                     writeCommentIfLikelyDouble(writer);
    244                 } else {
    245                     boolean isResourceId = writeCommentIfResourceId(writer);
    246                     if (!isResourceId) writeCommentIfLikelyFloat(writer);
    247                 }
    248                 break;
    249             case Format21t:
    250             case Format31t:
    251                 writeOpcode(writer);
    252                 writer.write(' ');
    253                 writeFirstRegister(writer);
    254                 writer.write(", ");
    255                 writeTargetLabel(writer);
    256                 break;
    257             case Format22b:
    258             case Format22s:
    259                 writeOpcode(writer);
    260                 writer.write(' ');
    261                 writeFirstRegister(writer);
    262                 writer.write(", ");
    263                 writeSecondRegister(writer);
    264                 writer.write(", ");
    265                 writeLiteral(writer);
    266                 break;
    267             case Format22c:
    268                 writeOpcode(writer);
    269                 writer.write(' ');
    270                 writeFirstRegister(writer);
    271                 writer.write(", ");
    272                 writeSecondRegister(writer);
    273                 writer.write(", ");
    274                 writer.write(referenceString);
    275                 break;
    276             case Format22cs:
    277                 writeOpcode(writer);
    278                 writer.write(' ');
    279                 writeFirstRegister(writer);
    280                 writer.write(", ");
    281                 writeSecondRegister(writer);
    282                 writer.write(", ");
    283                 writeFieldOffset(writer);
    284                 break;
    285             case Format22t:
    286                 writeOpcode(writer);
    287                 writer.write(' ');
    288                 writeFirstRegister(writer);
    289                 writer.write(", ");
    290                 writeSecondRegister(writer);
    291                 writer.write(", ");
    292                 writeTargetLabel(writer);
    293                 break;
    294             case Format22x:
    295             case Format32x:
    296                 writeOpcode(writer);
    297                 writer.write(' ');
    298                 writeFirstRegister(writer);
    299                 writer.write(", ");
    300                 writeSecondRegister(writer);
    301                 break;
    302             case Format23x:
    303                 writeOpcode(writer);
    304                 writer.write(' ');
    305                 writeFirstRegister(writer);
    306                 writer.write(", ");
    307                 writeSecondRegister(writer);
    308                 writer.write(", ");
    309                 writeThirdRegister(writer);
    310                 break;
    311              case Format25x:
    312                 writeOpcode(writer);
    313                 writer.write(' ');
    314                 writeInvoke25xRegisters(writer);  // vC, {vD, ...}
    315                 break;
    316             case Format35c:
    317                 writeOpcode(writer);
    318                 writer.write(' ');
    319                 writeInvokeRegisters(writer);
    320                 writer.write(", ");
    321                 writer.write(referenceString);
    322                 break;
    323             case Format35mi:
    324                 writeOpcode(writer);
    325                 writer.write(' ');
    326                 writeInvokeRegisters(writer);
    327                 writer.write(", ");
    328                 writeInlineIndex(writer);
    329                 break;
    330             case Format35ms:
    331                 writeOpcode(writer);
    332                 writer.write(' ');
    333                 writeInvokeRegisters(writer);
    334                 writer.write(", ");
    335                 writeVtableIndex(writer);
    336                 break;
    337             case Format3rc:
    338                 writeOpcode(writer);
    339                 writer.write(' ');
    340                 writeInvokeRangeRegisters(writer);
    341                 writer.write(", ");
    342                 writer.write(referenceString);
    343                 break;
    344             case Format3rmi:
    345                 writeOpcode(writer);
    346                 writer.write(' ');
    347                 writeInvokeRangeRegisters(writer);
    348                 writer.write(", ");
    349                 writeInlineIndex(writer);
    350                 break;
    351             case Format3rms:
    352                 writeOpcode(writer);
    353                 writer.write(' ');
    354                 writeInvokeRangeRegisters(writer);
    355                 writer.write(", ");
    356                 writeVtableIndex(writer);
    357                 break;
    358             default:
    359                 assert false;
    360                 return false;
    361         }
    362 
    363         if (commentOutInstruction) {
    364             writer.write("\nnop");
    365         }
    366 
    367         return true;
    368     }
    369 
    370     protected void writeOpcode(IndentingWriter writer) throws IOException {
    371         writer.write(instruction.getOpcode().name);
    372     }
    373 
    374     protected void writeTargetLabel(IndentingWriter writer) throws IOException {
    375         //this method is overridden by OffsetInstructionMethodItem, and should only be called for the formats that
    376         //have a target
    377         throw new RuntimeException();
    378     }
    379 
    380     protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
    381         methodDef.registerFormatter.writeTo(writer, registerNumber);
    382     }
    383 
    384     protected void writeFirstRegister(IndentingWriter writer) throws IOException {
    385         writeRegister(writer, ((OneRegisterInstruction)instruction).getRegisterA());
    386     }
    387 
    388     protected void writeSecondRegister(IndentingWriter writer) throws IOException {
    389         writeRegister(writer, ((TwoRegisterInstruction)instruction).getRegisterB());
    390     }
    391 
    392     protected void writeThirdRegister(IndentingWriter writer) throws IOException {
    393         writeRegister(writer, ((ThreeRegisterInstruction) instruction).getRegisterC());
    394     }
    395 
    396     protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
    397         FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
    398         final int regCount = instruction.getRegisterCount();
    399 
    400         writer.write('{');
    401         switch (regCount) {
    402             case 1:
    403                 writeRegister(writer, instruction.getRegisterC());
    404                 break;
    405             case 2:
    406                 writeRegister(writer, instruction.getRegisterC());
    407                 writer.write(", ");
    408                 writeRegister(writer, instruction.getRegisterD());
    409                 break;
    410             case 3:
    411                 writeRegister(writer, instruction.getRegisterC());
    412                 writer.write(", ");
    413                 writeRegister(writer, instruction.getRegisterD());
    414                 writer.write(", ");
    415                 writeRegister(writer, instruction.getRegisterE());
    416                 break;
    417             case 4:
    418                 writeRegister(writer, instruction.getRegisterC());
    419                 writer.write(", ");
    420                 writeRegister(writer, instruction.getRegisterD());
    421                 writer.write(", ");
    422                 writeRegister(writer, instruction.getRegisterE());
    423                 writer.write(", ");
    424                 writeRegister(writer, instruction.getRegisterF());
    425                 break;
    426             case 5:
    427                 writeRegister(writer, instruction.getRegisterC());
    428                 writer.write(", ");
    429                 writeRegister(writer, instruction.getRegisterD());
    430                 writer.write(", ");
    431                 writeRegister(writer, instruction.getRegisterE());
    432                 writer.write(", ");
    433                 writeRegister(writer, instruction.getRegisterF());
    434                 writer.write(", ");
    435                 writeRegister(writer, instruction.getRegisterG());
    436                 break;
    437         }
    438         writer.write('}');
    439     }
    440 
    441     protected void writeInvoke25xRegisters(IndentingWriter writer) throws IOException {
    442         OneFixedFourParameterRegisterInstruction instruction =
    443                 (OneFixedFourParameterRegisterInstruction)this.instruction;
    444         final int parameterRegCount = instruction.getParameterRegisterCount();
    445 
    446         writeRegister(writer, instruction.getRegisterFixedC());  // fixed register always present
    447 
    448         writer.write(", {");
    449         switch (parameterRegCount) {
    450             case 1:
    451                 writeRegister(writer, instruction.getRegisterParameterD());
    452                 break;
    453             case 2:
    454                 writeRegister(writer, instruction.getRegisterParameterD());
    455                 writer.write(", ");
    456                 writeRegister(writer, instruction.getRegisterParameterE());
    457                 break;
    458             case 3:
    459                 writeRegister(writer, instruction.getRegisterParameterD());
    460                 writer.write(", ");
    461                 writeRegister(writer, instruction.getRegisterParameterE());
    462                 writer.write(", ");
    463                 writeRegister(writer, instruction.getRegisterParameterF());
    464                 break;
    465             case 4:
    466                 writeRegister(writer, instruction.getRegisterParameterD());
    467                 writer.write(", ");
    468                 writeRegister(writer, instruction.getRegisterParameterE());
    469                 writer.write(", ");
    470                 writeRegister(writer, instruction.getRegisterParameterF());
    471                 writer.write(", ");
    472                 writeRegister(writer, instruction.getRegisterParameterG());
    473                 break;
    474         }
    475         writer.write('}');
    476     }
    477 
    478     protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
    479         RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
    480 
    481         int regCount = instruction.getRegisterCount();
    482         if (regCount == 0) {
    483             writer.write("{}");
    484         } else {
    485             int startRegister = instruction.getStartRegister();
    486             methodDef.registerFormatter.writeRegisterRange(writer, startRegister, startRegister+regCount-1);
    487         }
    488     }
    489 
    490     protected void writeLiteral(IndentingWriter writer) throws IOException {
    491         LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
    492     }
    493 
    494     protected void writeCommentIfLikelyFloat(IndentingWriter writer) throws IOException {
    495         writeCommentIfLikelyFloat(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
    496     }
    497 
    498     protected void writeCommentIfLikelyFloat(IndentingWriter writer, int val) throws IOException {
    499         if (NumberUtils.isLikelyFloat(val)) {
    500             writer.write("    # ");
    501             float fval = Float.intBitsToFloat(val);
    502             if (fval == Float.POSITIVE_INFINITY)
    503                 writer.write("Float.POSITIVE_INFINITY");
    504             else if (fval == Float.NEGATIVE_INFINITY)
    505                 writer.write("Float.NEGATIVE_INFINITY");
    506             else if (fval == Float.NaN)
    507                 writer.write("Float.NaN");
    508             else if (fval == Float.MAX_VALUE)
    509                 writer.write("Float.MAX_VALUE");
    510             else if (fval == (float)Math.PI)
    511                 writer.write("(float)Math.PI");
    512             else if (fval == (float)Math.E)
    513                 writer.write("(float)Math.E");
    514             else {
    515                 writer.write(Float.toString(fval));
    516                 writer.write('f');
    517             }
    518         }
    519     }
    520 
    521     protected void writeCommentIfLikelyDouble(IndentingWriter writer) throws IOException {
    522         writeCommentIfLikelyDouble(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
    523     }
    524 
    525     protected void writeCommentIfLikelyDouble(IndentingWriter writer, long val) throws IOException {
    526         if (NumberUtils.isLikelyDouble(val)) {
    527             writer.write("    # ");
    528             double dval = Double.longBitsToDouble(val);
    529             if (dval == Double.POSITIVE_INFINITY)
    530                 writer.write("Double.POSITIVE_INFINITY");
    531             else if (dval == Double.NEGATIVE_INFINITY)
    532                 writer.write("Double.NEGATIVE_INFINITY");
    533             else if (dval == Double.NaN)
    534                 writer.write("Double.NaN");
    535             else if (dval == Double.MAX_VALUE)
    536                 writer.write("Double.MAX_VALUE");
    537             else if (dval == Math.PI)
    538                 writer.write("Math.PI");
    539             else if (dval == Math.E)
    540                 writer.write("Math.E");
    541             else
    542                 writer.write(Double.toString(dval));
    543         }
    544     }
    545 
    546     protected boolean writeCommentIfResourceId(IndentingWriter writer) throws IOException {
    547         return writeCommentIfResourceId(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
    548     }
    549 
    550     protected boolean writeCommentIfResourceId(IndentingWriter writer, int val) throws IOException {
    551         Map<Integer,String> resourceIds = methodDef.classDef.options.resourceIds;
    552         String resource = resourceIds.get(Integer.valueOf(val));
    553         if (resource != null) {
    554             writer.write("    # ");
    555             writer.write(resource);
    556             return true;
    557         }
    558         return false;
    559     }
    560 
    561     protected void writeFieldOffset(IndentingWriter writer) throws IOException {
    562         writer.write("field@0x");
    563         writer.printUnsignedLongAsHex(((FieldOffsetInstruction)instruction).getFieldOffset());
    564     }
    565 
    566     protected void writeInlineIndex(IndentingWriter writer) throws IOException {
    567         writer.write("inline@");
    568         writer.printSignedIntAsDec(((InlineIndexInstruction)instruction).getInlineIndex());
    569     }
    570 
    571     protected void writeVtableIndex(IndentingWriter writer) throws IOException {
    572         writer.write("vtable@");
    573         writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
    574     }
    575 }
    576