Home | History | Annotate | Download | only in raw
      1 /*
      2  * Copyright 2013, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.dexbacked.raw;
     33 
     34 import com.google.common.base.Joiner;
     35 import com.google.common.collect.Lists;
     36 import org.jf.dexlib2.VerificationError;
     37 import org.jf.dexlib2.dexbacked.DexReader;
     38 import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction;
     39 import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
     40 import org.jf.dexlib2.iface.instruction.*;
     41 import org.jf.dexlib2.iface.instruction.formats.*;
     42 import org.jf.dexlib2.util.AnnotatedBytes;
     43 import org.jf.dexlib2.util.ReferenceUtil;
     44 import org.jf.util.ExceptionWithContext;
     45 import org.jf.util.NumberUtils;
     46 
     47 import javax.annotation.Nonnull;
     48 import javax.annotation.Nullable;
     49 import java.util.List;
     50 
     51 public class CodeItem {
     52     public static final int REGISTERS_OFFSET = 0;
     53     public static final int INS_OFFSET = 2;
     54     public static final int OUTS_OFFSET = 4;
     55     public static final int TRIES_SIZE_OFFSET = 6;
     56     public static final int DEBUG_INFO_OFFSET = 8;
     57     public static final int INSTRUCTION_COUNT_OFFSET = 12;
     58     public static final int INSTRUCTION_START_OFFSET = 16;
     59 
     60     public static class TryItem {
     61         public static final int ITEM_SIZE = 8;
     62 
     63         public static final int START_ADDRESS_OFFSET = 0;
     64         public static final int CODE_UNIT_COUNT_OFFSET = 4;
     65         public static final int HANDLER_OFFSET = 6;
     66     }
     67 
     68     @Nonnull
     69     public static SectionAnnotator makeAnnotator(@Nonnull DexAnnotator annotator, @Nonnull MapItem mapItem) {
     70         return new SectionAnnotator(annotator, mapItem) {
     71             private SectionAnnotator debugInfoAnnotator = null;
     72 
     73             @Override public void annotateSection(@Nonnull AnnotatedBytes out) {
     74                 debugInfoAnnotator = annotator.getAnnotator(ItemType.DEBUG_INFO_ITEM);
     75                 super.annotateSection(out);
     76             }
     77 
     78             @Nonnull @Override public String getItemName() {
     79                 return "code_item";
     80             }
     81 
     82             @Override public int getItemAlignment() {
     83                 return 4;
     84             }
     85 
     86             @Override
     87             public void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) {
     88                 try {
     89                     DexReader reader = dexFile.readerAt(out.getCursor());
     90 
     91                     int registers = reader.readUshort();
     92                     out.annotate(2, "registers_size = %d", registers);
     93 
     94                     int inSize = reader.readUshort();
     95                     out.annotate(2, "ins_size = %d", inSize);
     96 
     97                     int outSize = reader.readUshort();
     98                     out.annotate(2, "outs_size = %d", outSize);
     99 
    100                     int triesCount = reader.readUshort();
    101                     out.annotate(2, "tries_size = %d", triesCount);
    102 
    103                     int debugInfoOffset = reader.readInt();
    104                     out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
    105 
    106                     if (debugInfoOffset > 0) {
    107                         addDebugInfoIdentity(debugInfoOffset, itemIdentity);
    108                     }
    109 
    110                     int instructionSize = reader.readSmallUint();
    111                     out.annotate(4, "insns_size = 0x%x", instructionSize);
    112 
    113                     out.annotate(0, "instructions:");
    114                     out.indent();
    115 
    116                     out.setLimit(out.getCursor(), out.getCursor() + instructionSize * 2);
    117 
    118                     int end = reader.getOffset() + instructionSize*2;
    119                     try {
    120                         while (reader.getOffset() < end) {
    121                             Instruction instruction = DexBackedInstruction.readFrom(reader);
    122 
    123                             // if we read past the end of the instruction list
    124                             if (reader.getOffset() > end) {
    125                                 out.annotateTo(end, "truncated instruction");
    126                                 reader.setOffset(end);
    127                             } else {
    128                                 switch (instruction.getOpcode().format) {
    129                                     case Format10x:
    130                                         annotateInstruction10x(out, instruction);
    131                                         break;
    132                                     case Format35c:
    133                                         annotateInstruction35c(out, (Instruction35c)instruction);
    134                                         break;
    135                                     case Format3rc:
    136                                         annotateInstruction3rc(out, (Instruction3rc)instruction);
    137                                         break;
    138                                     case ArrayPayload:
    139                                         annotateArrayPayload(out, (ArrayPayload)instruction);
    140                                         break;
    141                                     case PackedSwitchPayload:
    142                                         annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction);
    143                                         break;
    144                                     case SparseSwitchPayload:
    145                                         annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction);
    146                                         break;
    147                                     default:
    148                                         annotateDefaultInstruction(out, instruction);
    149                                         break;
    150                                 }
    151                             }
    152 
    153                             assert reader.getOffset() == out.getCursor();
    154                         }
    155                     } catch (ExceptionWithContext ex) {
    156                         ex.printStackTrace(System.err);
    157                         out.annotate(0, "annotation error: %s", ex.getMessage());
    158                         out.moveTo(end);
    159                         reader.setOffset(end);
    160                     } finally {
    161                         out.clearLimit();
    162                         out.deindent();
    163                     }
    164 
    165                     if (triesCount > 0) {
    166                         if ((reader.getOffset() % 4) != 0) {
    167                             reader.readUshort();
    168                             out.annotate(2, "padding");
    169                         }
    170 
    171                         out.annotate(0, "try_items:");
    172                         out.indent();
    173                         try {
    174                             for (int i=0; i<triesCount; i++) {
    175                                 out.annotate(0, "try_item[%d]:", i);
    176                                 out.indent();
    177                                 try {
    178                                     int startAddr = reader.readSmallUint();
    179                                     out.annotate(4, "start_addr = 0x%x", startAddr);
    180 
    181                                     int instructionCount = reader.readUshort();
    182                                     out.annotate(2, "insn_count = 0x%x", instructionCount);
    183 
    184                                     int handlerOffset = reader.readUshort();
    185                                     out.annotate(2, "handler_off = 0x%x", handlerOffset);
    186                                 } finally {
    187                                     out.deindent();
    188                                 }
    189                             }
    190                         } finally {
    191                             out.deindent();
    192                         }
    193 
    194                         int handlerListCount = reader.readSmallUleb128();
    195                         out.annotate(0, "encoded_catch_handler_list:");
    196                         out.annotateTo(reader.getOffset(), "size = %d", handlerListCount);
    197                         out.indent();
    198                         try {
    199                             for (int i=0; i<handlerListCount; i++) {
    200                                 out.annotate(0, "encoded_catch_handler[%d]", i);
    201                                 out.indent();
    202                                 try {
    203                                     int handlerCount = reader.readSleb128();
    204                                     out.annotateTo(reader.getOffset(), "size = %d", handlerCount);
    205                                     boolean hasCatchAll = handlerCount <= 0;
    206                                     handlerCount = Math.abs(handlerCount);
    207                                     if (handlerCount != 0) {
    208                                         out.annotate(0, "handlers:");
    209                                         out.indent();
    210                                         try {
    211                                             for (int j=0; j<handlerCount; j++) {
    212                                                 out.annotate(0, "encoded_type_addr_pair[%d]", i);
    213                                                 out.indent();
    214                                                 try {
    215                                                     int typeIndex = reader.readSmallUleb128();
    216                                                     out.annotateTo(reader.getOffset(), TypeIdItem.getReferenceAnnotation(dexFile, typeIndex));
    217 
    218                                                     int handlerAddress = reader.readSmallUleb128();
    219                                                     out.annotateTo(reader.getOffset(), "addr = 0x%x", handlerAddress);
    220                                                 } finally {
    221                                                     out.deindent();
    222                                                 }
    223                                             }
    224                                         } finally {
    225                                             out.deindent();
    226                                         }
    227                                     }
    228                                     if (hasCatchAll) {
    229                                         int catchAllAddress = reader.readSmallUleb128();
    230                                         out.annotateTo(reader.getOffset(), "catch_all_addr = 0x%x", catchAllAddress);
    231                                     }
    232                                 } finally {
    233                                     out.deindent();
    234                                 }
    235                             }
    236                         } finally {
    237                             out.deindent();
    238                         }
    239                     }
    240                 } catch (ExceptionWithContext ex) {
    241                     out.annotate(0, "annotation error: %s", ex.getMessage());
    242                 }
    243             }
    244 
    245             private String formatRegister(int registerNum) {
    246                 return String.format("v%d", registerNum);
    247             }
    248 
    249             private void annotateInstruction10x(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
    250                 out.annotate(2, instruction.getOpcode().name);
    251             }
    252 
    253             private void annotateInstruction35c(@Nonnull AnnotatedBytes out, @Nonnull Instruction35c instruction) {
    254                 List<String> args = Lists.newArrayList();
    255 
    256                 int registerCount = instruction.getRegisterCount();
    257                 if (registerCount == 1) {
    258                     args.add(formatRegister(instruction.getRegisterC()));
    259                 } else if (registerCount == 2) {
    260                     args.add(formatRegister(instruction.getRegisterC()));
    261                     args.add(formatRegister(instruction.getRegisterD()));
    262                 } else if (registerCount == 3) {
    263                     args.add(formatRegister(instruction.getRegisterC()));
    264                     args.add(formatRegister(instruction.getRegisterD()));
    265                     args.add(formatRegister(instruction.getRegisterE()));
    266                 } else if (registerCount == 4) {
    267                     args.add(formatRegister(instruction.getRegisterC()));
    268                     args.add(formatRegister(instruction.getRegisterD()));
    269                     args.add(formatRegister(instruction.getRegisterE()));
    270                     args.add(formatRegister(instruction.getRegisterF()));
    271                 } else if (registerCount == 5) {
    272                     args.add(formatRegister(instruction.getRegisterC()));
    273                     args.add(formatRegister(instruction.getRegisterD()));
    274                     args.add(formatRegister(instruction.getRegisterE()));
    275                     args.add(formatRegister(instruction.getRegisterF()));
    276                     args.add(formatRegister(instruction.getRegisterG()));
    277                 }
    278 
    279                 String reference = ReferenceUtil.getReferenceString(instruction.getReference());
    280 
    281                 out.annotate(6, String.format("%s {%s}, %s",
    282                         instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
    283             }
    284 
    285             private void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
    286                 int startRegister = instruction.getStartRegister();
    287                 int endRegister = startRegister + instruction.getRegisterCount() - 1;
    288                 String reference = ReferenceUtil.getReferenceString(instruction.getReference());
    289                 out.annotate(6, String.format("%s {%s .. %s}, %s",
    290                         instruction.getOpcode().name, formatRegister(startRegister), formatRegister(endRegister),
    291                         reference));
    292             }
    293 
    294             private void annotateDefaultInstruction(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) {
    295                 List<String> args = Lists.newArrayList();
    296 
    297                 if (instruction instanceof OneRegisterInstruction) {
    298                     args.add(formatRegister(((OneRegisterInstruction)instruction).getRegisterA()));
    299                     if (instruction instanceof TwoRegisterInstruction) {
    300                         args.add(formatRegister(((TwoRegisterInstruction)instruction).getRegisterB()));
    301                         if (instruction instanceof ThreeRegisterInstruction) {
    302                             args.add(formatRegister(((ThreeRegisterInstruction)instruction).getRegisterC()));
    303                         }
    304                     }
    305                 }  else if (instruction instanceof VerificationErrorInstruction) {
    306                     String verificationError = VerificationError.getVerificationErrorName(
    307                         ((VerificationErrorInstruction) instruction).getVerificationError());
    308                     if (verificationError != null) {
    309                         args.add(verificationError);
    310                     } else {
    311                         args.add("invalid verification error type");
    312                     }
    313                 }
    314 
    315                 if (instruction instanceof ReferenceInstruction) {
    316                     args.add(ReferenceUtil.getReferenceString(((ReferenceInstruction)instruction).getReference()));
    317                 } else if (instruction instanceof OffsetInstruction) {
    318                     int offset = ((OffsetInstruction)instruction).getCodeOffset();
    319                     String sign = offset>=0?"+":"-";
    320                     args.add(String.format("%s0x%x", sign, Math.abs(offset)));
    321                 } else if (instruction instanceof NarrowLiteralInstruction) {
    322                     int value = ((NarrowLiteralInstruction)instruction).getNarrowLiteral();
    323                     if (NumberUtils.isLikelyFloat(value)) {
    324                         args.add(String.format("%d # %f", value, Float.intBitsToFloat(value)));
    325                     } else {
    326                         args.add(String.format("%d", value));
    327                     }
    328                 } else if (instruction instanceof WideLiteralInstruction) {
    329                     long value = ((WideLiteralInstruction)instruction).getWideLiteral();
    330                     if (NumberUtils.isLikelyDouble(value)) {
    331                         args.add(String.format("%d # %f", value, Double.longBitsToDouble(value)));
    332                     } else {
    333                         args.add(String.format("%d", value));
    334                     }
    335                 } else if (instruction instanceof FieldOffsetInstruction) {
    336                     int fieldOffset = ((FieldOffsetInstruction)instruction).getFieldOffset();
    337                     args.add(String.format("field@0x%x", fieldOffset));
    338                 } else if (instruction instanceof VtableIndexInstruction) {
    339                     int vtableIndex = ((VtableIndexInstruction)instruction).getVtableIndex();
    340                     args.add(String.format("vtable@%d", vtableIndex));
    341                 } else if (instruction instanceof InlineIndexInstruction) {
    342                     int inlineIndex = ((InlineIndexInstruction)instruction).getInlineIndex();
    343                     args.add(String.format("inline@%d", inlineIndex));
    344                 }
    345 
    346                 out.annotate(instruction.getCodeUnits()*2, "%s %s",
    347                         instruction.getOpcode().name, Joiner.on(", ").join(args));
    348             }
    349 
    350             private void annotateArrayPayload(@Nonnull AnnotatedBytes out, @Nonnull ArrayPayload instruction) {
    351                 List<Number> elements = instruction.getArrayElements();
    352                 int elementWidth = instruction.getElementWidth();
    353 
    354                 out.annotate(2, instruction.getOpcode().name);
    355                 out.indent();
    356                 out.annotate(2, "element_width = %d", elementWidth);
    357                 out.annotate(4, "size = %d", elements.size());
    358                 out.annotate(0, "elements:");
    359                 out.indent();
    360                 for (int i=0; i<elements.size(); i++) {
    361                     if (elementWidth == 8) {
    362                         long value = elements.get(i).longValue();
    363                         if (NumberUtils.isLikelyDouble(value)) {
    364                             out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Double.longBitsToDouble(value));
    365                         } else {
    366                             out.annotate(elementWidth, "element[%d] = %d", i, value);
    367                         }
    368                     } else {
    369                         int value = elements.get(i).intValue();
    370                         if (NumberUtils.isLikelyFloat(value)) {
    371                             out.annotate(elementWidth, "element[%d] = %d # %f", i, value, Float.intBitsToFloat(value));
    372                         } else {
    373                             out.annotate(elementWidth, "element[%d] = %d", i, value);
    374                         }
    375                     }
    376                 }
    377                 if (out.getCursor() % 2 != 0) {
    378                     out.annotate(1, "padding");
    379                 }
    380                 out.deindent();
    381                 out.deindent();
    382             }
    383 
    384             private void annotatePackedSwitchPayload(@Nonnull AnnotatedBytes out,
    385                                                      @Nonnull PackedSwitchPayload instruction) {
    386                 List<? extends SwitchElement> elements = instruction.getSwitchElements();
    387 
    388                 out.annotate(2, instruction.getOpcode().name);
    389                 out.indent();
    390 
    391                 out.annotate(2, "size = %d", elements.size());
    392                 if (elements.size() == 0) {
    393                     out.annotate(4, "first_key");
    394                 } else {
    395                     out.annotate(4, "first_key = %d", elements.get(0).getKey());
    396                     out.annotate(0, "targets:");
    397                     out.indent();
    398                     for (int i=0; i<elements.size(); i++) {
    399                         out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
    400                     }
    401                     out.deindent();
    402                 }
    403                 out.deindent();
    404             }
    405 
    406             private void annotateSparseSwitchPayload(@Nonnull AnnotatedBytes out,
    407                                                      @Nonnull SparseSwitchPayload instruction) {
    408                 List<? extends SwitchElement> elements = instruction.getSwitchElements();
    409 
    410                 out.annotate(2, instruction.getOpcode().name);
    411                 out.indent();
    412                 out.annotate(2, "size = %d", elements.size());
    413                 if (elements.size() > 0) {
    414                     out.annotate(0, "keys:");
    415                     out.indent();
    416                     for (int i=0; i<elements.size(); i++) {
    417                         out.annotate(4, "key[%d] = %d", i, elements.get(i).getKey());
    418                     }
    419                     out.deindent();
    420                     out.annotate(0, "targets:");
    421                     out.indent();
    422                     for (int i=0; i<elements.size(); i++) {
    423                         out.annotate(4, "target[%d] = %d", i, elements.get(i).getOffset());
    424                     }
    425                     out.deindent();
    426                 }
    427                 out.deindent();
    428             }
    429 
    430             private void addDebugInfoIdentity(int debugInfoOffset, String methodString) {
    431                 if (debugInfoAnnotator != null) {
    432                     debugInfoAnnotator.setItemIdentity(debugInfoOffset, methodString);
    433                 }
    434             }
    435         };
    436     }
    437 }
    438