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     private String writeInvalidItemIndex(InvalidItemIndex ex, int type, IndentingWriter writer)
     83             throws IOException {
     84         writer.write("#");
     85         writer.write(ex.getMessage());
     86         writer.write("\n");
     87         return String.format("%s@%d", ReferenceType.toString(type), ex.getInvalidIndex());
     88     }
     89 
     90     @Override
     91     public boolean writeTo(IndentingWriter writer) throws IOException {
     92         Opcode opcode = instruction.getOpcode();
     93         String verificationErrorName = null;
     94         String referenceString = null;
     95         String referenceString2 = null;
     96 
     97         boolean commentOutInstruction = false;
     98 
     99         if (instruction instanceof Instruction20bc) {
    100             int verificationError = ((Instruction20bc)instruction).getVerificationError();
    101             verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
    102             if (verificationErrorName == null) {
    103                 writer.write("#was invalid verification error type: ");
    104                 writer.printSignedIntAsDec(verificationError);
    105                 writer.write("\n");
    106                 verificationErrorName = "generic-error";
    107             }
    108         }
    109 
    110         if (instruction instanceof ReferenceInstruction) {
    111             ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
    112             String classContext = null;
    113             if (methodDef.classDef.options.implicitReferences) {
    114                 classContext = methodDef.method.getDefiningClass();
    115             }
    116 
    117             try {
    118                 Reference reference = referenceInstruction.getReference();
    119                 referenceString = ReferenceUtil.getReferenceString(reference, classContext);
    120                 assert referenceString != null;
    121             } catch (InvalidItemIndex ex) {
    122                 commentOutInstruction = true;
    123                 referenceString = writeInvalidItemIndex(ex, referenceInstruction.getReferenceType(),
    124                         writer);
    125             } catch (ReferenceType.InvalidReferenceTypeException ex) {
    126                 writer.write("#invalid reference type: ");
    127                 writer.printSignedIntAsDec(ex.getReferenceType());
    128                 commentOutInstruction = true;
    129 
    130                 referenceString = "invalid_reference";
    131             }
    132 
    133             if (instruction instanceof DualReferenceInstruction) {
    134                 DualReferenceInstruction dualReferenceInstruction =
    135                         (DualReferenceInstruction) instruction;
    136                 try {
    137                     Reference reference2 = dualReferenceInstruction.getReference2();
    138                     referenceString2 = ReferenceUtil.getReferenceString(reference2, classContext);
    139                 } catch (InvalidItemIndex ex) {
    140                     commentOutInstruction = true;
    141                     referenceString2 = writeInvalidItemIndex(ex,
    142                             dualReferenceInstruction.getReferenceType2(), writer);
    143                 } catch (ReferenceType.InvalidReferenceTypeException ex) {
    144                     writer.write("#invalid reference type: ");
    145                     writer.printSignedIntAsDec(ex.getReferenceType());
    146                     commentOutInstruction = true;
    147 
    148                     referenceString2 = "invalid_reference";
    149                 }
    150             }
    151         }
    152 
    153         if (instruction instanceof Instruction31t) {
    154             boolean validPayload = true;
    155 
    156             switch (instruction.getOpcode()) {
    157                 case PACKED_SWITCH:
    158                     int baseAddress = methodDef.getPackedSwitchBaseAddress(
    159                             this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
    160                     if (baseAddress == -1) {
    161                         validPayload = false;
    162                     }
    163                     break;
    164                 case SPARSE_SWITCH:
    165                     baseAddress = methodDef.getSparseSwitchBaseAddress(
    166                             this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
    167                     if (baseAddress == -1) {
    168                         validPayload = false;
    169                     }
    170                     break;
    171                 case FILL_ARRAY_DATA:
    172                     try {
    173                         methodDef.findPayloadOffset(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
    174                                 Opcode.ARRAY_PAYLOAD);
    175                     } catch (InvalidSwitchPayload ex) {
    176                         validPayload = false;
    177                     }
    178                     break;
    179                 default:
    180                     throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
    181             }
    182 
    183             if (!validPayload) {
    184                 writer.write("#invalid payload reference\n");
    185                 commentOutInstruction = true;
    186             }
    187         }
    188 
    189         if (opcode.odexOnly()) {
    190             if (!isAllowedOdex(opcode)) {
    191                 writer.write("#disallowed odex opcode\n");
    192                 commentOutInstruction = true;
    193             }
    194         }
    195 
    196         if (commentOutInstruction) {
    197             writer.write("#");
    198         }
    199 
    200         switch (instruction.getOpcode().format) {
    201             case Format10t:
    202                 writeOpcode(writer);
    203                 writer.write(' ');
    204                 writeTargetLabel(writer);
    205                 break;
    206             case Format10x:
    207                 if (instruction instanceof UnknownInstruction) {
    208                     writer.write("#unknown opcode: 0x");
    209                     writer.printUnsignedLongAsHex(((UnknownInstruction)instruction).getOriginalOpcode());
    210                     writer.write('\n');
    211                 }
    212                 writeOpcode(writer);
    213                 break;
    214             case Format11n:
    215                 writeOpcode(writer);
    216                 writer.write(' ');
    217                 writeFirstRegister(writer);
    218                 writer.write(", ");
    219                 writeLiteral(writer);
    220                 break;
    221             case Format11x:
    222                 writeOpcode(writer);
    223                 writer.write(' ');
    224                 writeFirstRegister(writer);
    225                 break;
    226             case Format12x:
    227                 writeOpcode(writer);
    228                 writer.write(' ');
    229                 writeFirstRegister(writer);
    230                 writer.write(", ");
    231                 writeSecondRegister(writer);
    232                 break;
    233             case Format20bc:
    234                 writeOpcode(writer);
    235                 writer.write(' ');
    236                 writer.write(verificationErrorName);
    237                 writer.write(", ");
    238                 writer.write(referenceString);
    239                 break;
    240             case Format20t:
    241             case Format30t:
    242                 writeOpcode(writer);
    243                 writer.write(' ');
    244                 writeTargetLabel(writer);
    245                 break;
    246             case Format21c:
    247             case Format31c:
    248                 writeOpcode(writer);
    249                 writer.write(' ');
    250                 writeFirstRegister(writer);
    251                 writer.write(", ");
    252                 writer.write(referenceString);
    253                 break;
    254             case Format21ih:
    255             case Format21lh:
    256             case Format21s:
    257             case Format31i:
    258             case Format51l:
    259                 writeOpcode(writer);
    260                 writer.write(' ');
    261                 writeFirstRegister(writer);
    262                 writer.write(", ");
    263                 writeLiteral(writer);
    264                 if (instruction.getOpcode().setsWideRegister()) {
    265                     writeCommentIfLikelyDouble(writer);
    266                 } else {
    267                     boolean isResourceId = writeCommentIfResourceId(writer);
    268                     if (!isResourceId) writeCommentIfLikelyFloat(writer);
    269                 }
    270                 break;
    271             case Format21t:
    272             case Format31t:
    273                 writeOpcode(writer);
    274                 writer.write(' ');
    275                 writeFirstRegister(writer);
    276                 writer.write(", ");
    277                 writeTargetLabel(writer);
    278                 break;
    279             case Format22b:
    280             case Format22s:
    281                 writeOpcode(writer);
    282                 writer.write(' ');
    283                 writeFirstRegister(writer);
    284                 writer.write(", ");
    285                 writeSecondRegister(writer);
    286                 writer.write(", ");
    287                 writeLiteral(writer);
    288                 break;
    289             case Format22c:
    290                 writeOpcode(writer);
    291                 writer.write(' ');
    292                 writeFirstRegister(writer);
    293                 writer.write(", ");
    294                 writeSecondRegister(writer);
    295                 writer.write(", ");
    296                 writer.write(referenceString);
    297                 break;
    298             case Format22cs:
    299                 writeOpcode(writer);
    300                 writer.write(' ');
    301                 writeFirstRegister(writer);
    302                 writer.write(", ");
    303                 writeSecondRegister(writer);
    304                 writer.write(", ");
    305                 writeFieldOffset(writer);
    306                 break;
    307             case Format22t:
    308                 writeOpcode(writer);
    309                 writer.write(' ');
    310                 writeFirstRegister(writer);
    311                 writer.write(", ");
    312                 writeSecondRegister(writer);
    313                 writer.write(", ");
    314                 writeTargetLabel(writer);
    315                 break;
    316             case Format22x:
    317             case Format32x:
    318                 writeOpcode(writer);
    319                 writer.write(' ');
    320                 writeFirstRegister(writer);
    321                 writer.write(", ");
    322                 writeSecondRegister(writer);
    323                 break;
    324             case Format23x:
    325                 writeOpcode(writer);
    326                 writer.write(' ');
    327                 writeFirstRegister(writer);
    328                 writer.write(", ");
    329                 writeSecondRegister(writer);
    330                 writer.write(", ");
    331                 writeThirdRegister(writer);
    332                 break;
    333             case Format35c:
    334                 writeOpcode(writer);
    335                 writer.write(' ');
    336                 writeInvokeRegisters(writer);
    337                 writer.write(", ");
    338                 writer.write(referenceString);
    339                 break;
    340             case Format35mi:
    341                 writeOpcode(writer);
    342                 writer.write(' ');
    343                 writeInvokeRegisters(writer);
    344                 writer.write(", ");
    345                 writeInlineIndex(writer);
    346                 break;
    347             case Format35ms:
    348                 writeOpcode(writer);
    349                 writer.write(' ');
    350                 writeInvokeRegisters(writer);
    351                 writer.write(", ");
    352                 writeVtableIndex(writer);
    353                 break;
    354             case Format3rc:
    355                 writeOpcode(writer);
    356                 writer.write(' ');
    357                 writeInvokeRangeRegisters(writer);
    358                 writer.write(", ");
    359                 writer.write(referenceString);
    360                 break;
    361             case Format3rmi:
    362                 writeOpcode(writer);
    363                 writer.write(' ');
    364                 writeInvokeRangeRegisters(writer);
    365                 writer.write(", ");
    366                 writeInlineIndex(writer);
    367                 break;
    368             case Format3rms:
    369                 writeOpcode(writer);
    370                 writer.write(' ');
    371                 writeInvokeRangeRegisters(writer);
    372                 writer.write(", ");
    373                 writeVtableIndex(writer);
    374                 break;
    375             case Format45cc:
    376                 writeOpcode(writer);
    377                 writer.write(' ');
    378                 writeInvokeRegisters(writer);
    379                 writer.write(", ");
    380                 writer.write(referenceString);
    381                 writer.write(", ");
    382                 writer.write(referenceString2);
    383                 break;
    384             case Format4rcc:
    385                 writeOpcode(writer);
    386                 writer.write(' ');
    387                 writeInvokeRangeRegisters(writer);
    388                 writer.write(", ");
    389                 writer.write(referenceString);
    390                 writer.write(", ");
    391                 writer.write(referenceString2);
    392                 break;
    393             default:
    394                 assert false;
    395                 return false;
    396         }
    397 
    398         if (commentOutInstruction) {
    399             writer.write("\nnop");
    400         }
    401 
    402         return true;
    403     }
    404 
    405     protected void writeOpcode(IndentingWriter writer) throws IOException {
    406         writer.write(instruction.getOpcode().name);
    407     }
    408 
    409     protected void writeTargetLabel(IndentingWriter writer) throws IOException {
    410         //this method is overridden by OffsetInstructionMethodItem, and should only be called for the formats that
    411         //have a target
    412         throw new RuntimeException();
    413     }
    414 
    415     protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
    416         methodDef.registerFormatter.writeTo(writer, registerNumber);
    417     }
    418 
    419     protected void writeFirstRegister(IndentingWriter writer) throws IOException {
    420         writeRegister(writer, ((OneRegisterInstruction)instruction).getRegisterA());
    421     }
    422 
    423     protected void writeSecondRegister(IndentingWriter writer) throws IOException {
    424         writeRegister(writer, ((TwoRegisterInstruction)instruction).getRegisterB());
    425     }
    426 
    427     protected void writeThirdRegister(IndentingWriter writer) throws IOException {
    428         writeRegister(writer, ((ThreeRegisterInstruction) instruction).getRegisterC());
    429     }
    430 
    431     protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
    432         FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
    433         final int regCount = instruction.getRegisterCount();
    434 
    435         writer.write('{');
    436         switch (regCount) {
    437             case 1:
    438                 writeRegister(writer, instruction.getRegisterC());
    439                 break;
    440             case 2:
    441                 writeRegister(writer, instruction.getRegisterC());
    442                 writer.write(", ");
    443                 writeRegister(writer, instruction.getRegisterD());
    444                 break;
    445             case 3:
    446                 writeRegister(writer, instruction.getRegisterC());
    447                 writer.write(", ");
    448                 writeRegister(writer, instruction.getRegisterD());
    449                 writer.write(", ");
    450                 writeRegister(writer, instruction.getRegisterE());
    451                 break;
    452             case 4:
    453                 writeRegister(writer, instruction.getRegisterC());
    454                 writer.write(", ");
    455                 writeRegister(writer, instruction.getRegisterD());
    456                 writer.write(", ");
    457                 writeRegister(writer, instruction.getRegisterE());
    458                 writer.write(", ");
    459                 writeRegister(writer, instruction.getRegisterF());
    460                 break;
    461             case 5:
    462                 writeRegister(writer, instruction.getRegisterC());
    463                 writer.write(", ");
    464                 writeRegister(writer, instruction.getRegisterD());
    465                 writer.write(", ");
    466                 writeRegister(writer, instruction.getRegisterE());
    467                 writer.write(", ");
    468                 writeRegister(writer, instruction.getRegisterF());
    469                 writer.write(", ");
    470                 writeRegister(writer, instruction.getRegisterG());
    471                 break;
    472         }
    473         writer.write('}');
    474     }
    475 
    476     protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
    477         RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
    478 
    479         int regCount = instruction.getRegisterCount();
    480         if (regCount == 0) {
    481             writer.write("{}");
    482         } else {
    483             int startRegister = instruction.getStartRegister();
    484             methodDef.registerFormatter.writeRegisterRange(writer, startRegister, startRegister+regCount-1);
    485         }
    486     }
    487 
    488     protected void writeLiteral(IndentingWriter writer) throws IOException {
    489         LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
    490     }
    491 
    492     protected void writeCommentIfLikelyFloat(IndentingWriter writer) throws IOException {
    493         writeCommentIfLikelyFloat(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
    494     }
    495 
    496     protected void writeCommentIfLikelyFloat(IndentingWriter writer, int val) throws IOException {
    497         if (NumberUtils.isLikelyFloat(val)) {
    498             writer.write("    # ");
    499             float fval = Float.intBitsToFloat(val);
    500             if (fval == Float.POSITIVE_INFINITY)
    501                 writer.write("Float.POSITIVE_INFINITY");
    502             else if (fval == Float.NEGATIVE_INFINITY)
    503                 writer.write("Float.NEGATIVE_INFINITY");
    504             else if (Float.isNaN(fval))
    505                 writer.write("Float.NaN");
    506             else if (fval == Float.MAX_VALUE)
    507                 writer.write("Float.MAX_VALUE");
    508             else if (fval == (float)Math.PI)
    509                 writer.write("(float)Math.PI");
    510             else if (fval == (float)Math.E)
    511                 writer.write("(float)Math.E");
    512             else {
    513                 writer.write(Float.toString(fval));
    514                 writer.write('f');
    515             }
    516         }
    517     }
    518 
    519     protected void writeCommentIfLikelyDouble(IndentingWriter writer) throws IOException {
    520         writeCommentIfLikelyDouble(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
    521     }
    522 
    523     protected void writeCommentIfLikelyDouble(IndentingWriter writer, long val) throws IOException {
    524         if (NumberUtils.isLikelyDouble(val)) {
    525             writer.write("    # ");
    526             double dval = Double.longBitsToDouble(val);
    527             if (dval == Double.POSITIVE_INFINITY)
    528                 writer.write("Double.POSITIVE_INFINITY");
    529             else if (dval == Double.NEGATIVE_INFINITY)
    530                 writer.write("Double.NEGATIVE_INFINITY");
    531             else if (Double.isNaN(dval))
    532                 writer.write("Double.NaN");
    533             else if (dval == Double.MAX_VALUE)
    534                 writer.write("Double.MAX_VALUE");
    535             else if (dval == Math.PI)
    536                 writer.write("Math.PI");
    537             else if (dval == Math.E)
    538                 writer.write("Math.E");
    539             else
    540                 writer.write(Double.toString(dval));
    541         }
    542     }
    543 
    544     protected boolean writeCommentIfResourceId(IndentingWriter writer) throws IOException {
    545         return writeCommentIfResourceId(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
    546     }
    547 
    548     protected boolean writeCommentIfResourceId(IndentingWriter writer, int val) throws IOException {
    549         Map<Integer,String> resourceIds = methodDef.classDef.options.resourceIds;
    550         String resource = resourceIds.get(Integer.valueOf(val));
    551         if (resource != null) {
    552             writer.write("    # ");
    553             writer.write(resource);
    554             return true;
    555         }
    556         return false;
    557     }
    558 
    559     protected void writeFieldOffset(IndentingWriter writer) throws IOException {
    560         writer.write("field@0x");
    561         writer.printUnsignedLongAsHex(((FieldOffsetInstruction)instruction).getFieldOffset());
    562     }
    563 
    564     protected void writeInlineIndex(IndentingWriter writer) throws IOException {
    565         writer.write("inline@");
    566         writer.printSignedIntAsDec(((InlineIndexInstruction)instruction).getInlineIndex());
    567     }
    568 
    569     protected void writeVtableIndex(IndentingWriter writer) throws IOException {
    570         writer.write("vtable@");
    571         writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
    572     }
    573 }
    574