1 /* 2 * Copyright 2012, 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; 33 34 import com.google.common.collect.ImmutableList; 35 import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction; 36 import org.jf.dexlib2.dexbacked.raw.CodeItem; 37 import org.jf.dexlib2.dexbacked.util.DebugInfo; 38 import org.jf.dexlib2.dexbacked.util.FixedSizeList; 39 import org.jf.dexlib2.dexbacked.util.VariableSizeListIterator; 40 import org.jf.dexlib2.dexbacked.util.VariableSizeLookaheadIterator; 41 import org.jf.dexlib2.iface.MethodImplementation; 42 import org.jf.dexlib2.iface.debug.DebugItem; 43 import org.jf.dexlib2.iface.instruction.Instruction; 44 import org.jf.util.AlignmentUtils; 45 import org.jf.util.ExceptionWithContext; 46 47 import javax.annotation.Nonnull; 48 import javax.annotation.Nullable; 49 import java.util.Iterator; 50 import java.util.List; 51 52 public class DexBackedMethodImplementation implements MethodImplementation { 53 @Nonnull public final DexBackedDexFile dexFile; 54 @Nonnull public final DexBackedMethod method; 55 private final int codeOffset; 56 57 public DexBackedMethodImplementation(@Nonnull DexBackedDexFile dexFile, 58 @Nonnull DexBackedMethod method, 59 int codeOffset) { 60 this.dexFile = dexFile; 61 this.method = method; 62 this.codeOffset = codeOffset; 63 } 64 65 @Override public int getRegisterCount() { return dexFile.readUshort(codeOffset); } 66 67 @Nonnull @Override public Iterable<? extends Instruction> getInstructions() { 68 // instructionsSize is the number of 16-bit code units in the instruction list, not the number of instructions 69 int instructionsSize = dexFile.readSmallUint(codeOffset + CodeItem.INSTRUCTION_COUNT_OFFSET); 70 71 final int instructionsStartOffset = codeOffset + CodeItem.INSTRUCTION_START_OFFSET; 72 final int endOffset = instructionsStartOffset + (instructionsSize*2); 73 return new Iterable<Instruction>() { 74 @Override 75 public Iterator<Instruction> iterator() { 76 return new VariableSizeLookaheadIterator<Instruction>(dexFile, instructionsStartOffset) { 77 @Override 78 protected Instruction readNextItem(@Nonnull DexReader reader) { 79 if (reader.getOffset() >= endOffset) { 80 return endOfData(); 81 } 82 83 Instruction instruction = DexBackedInstruction.readFrom(reader); 84 85 // Does the instruction extend past the end of the method? 86 int offset = reader.getOffset(); 87 if (offset > endOffset || offset < 0) { 88 throw new ExceptionWithContext("The last instruction in method %s is truncated", method); 89 } 90 return instruction; 91 } 92 }; 93 } 94 }; 95 } 96 97 @Nonnull 98 @Override 99 public List<? extends DexBackedTryBlock> getTryBlocks() { 100 final int triesSize = dexFile.readUshort(codeOffset + CodeItem.TRIES_SIZE_OFFSET); 101 if (triesSize > 0) { 102 int instructionsSize = dexFile.readSmallUint(codeOffset + CodeItem.INSTRUCTION_COUNT_OFFSET); 103 final int triesStartOffset = AlignmentUtils.alignOffset( 104 codeOffset + CodeItem.INSTRUCTION_START_OFFSET + (instructionsSize*2), 4); 105 final int handlersStartOffset = triesStartOffset + triesSize*CodeItem.TryItem.ITEM_SIZE; 106 107 return new FixedSizeList<DexBackedTryBlock>() { 108 @Nonnull 109 @Override 110 public DexBackedTryBlock readItem(int index) { 111 return new DexBackedTryBlock(dexFile, 112 triesStartOffset + index*CodeItem.TryItem.ITEM_SIZE, 113 handlersStartOffset); 114 } 115 116 @Override 117 public int size() { 118 return triesSize; 119 } 120 }; 121 } 122 return ImmutableList.of(); 123 } 124 125 @Nonnull 126 private DebugInfo getDebugInfo() { 127 int debugOffset = dexFile.readInt(codeOffset + CodeItem.DEBUG_INFO_OFFSET); 128 129 if (debugOffset == -1 || debugOffset == 0) { 130 return DebugInfo.newOrEmpty(dexFile, 0, this); 131 } 132 if (debugOffset < 0) { 133 System.err.println(String.format("%s: Invalid debug offset", method)); 134 return DebugInfo.newOrEmpty(dexFile, 0, this); 135 } 136 if (debugOffset >= dexFile.buf.length) { 137 System.err.println(String.format("%s: Invalid debug offset", method)); 138 return DebugInfo.newOrEmpty(dexFile, 0, this); 139 } 140 return DebugInfo.newOrEmpty(dexFile, debugOffset, this); 141 } 142 143 @Nonnull @Override 144 public Iterable<? extends DebugItem> getDebugItems() { 145 return getDebugInfo(); 146 } 147 148 @Nonnull 149 public Iterator<String> getParameterNames(@Nullable DexReader dexReader) { 150 return getDebugInfo().getParameterNames(dexReader); 151 } 152 153 /** 154 * Calculate and return the private size of a method implementation. 155 * 156 * Calculated as: debug info size + instructions size + try-catch size 157 * 158 * @return size in bytes 159 */ 160 public int getSize() { 161 int debugSize = getDebugInfo().getSize(); 162 163 //set last offset just before bytecode instructions (after insns_size) 164 int lastOffset = codeOffset + CodeItem.INSTRUCTION_START_OFFSET; 165 166 //set code_item ending offset to the end of instructions list (insns_size * ushort) 167 lastOffset += dexFile.readSmallUint(codeOffset + CodeItem.INSTRUCTION_COUNT_OFFSET) * 2; 168 169 //read any exception handlers and move code_item offset to the end 170 for (DexBackedTryBlock tryBlock: getTryBlocks()) { 171 Iterator<? extends DexBackedExceptionHandler> tryHandlerIter = 172 tryBlock.getExceptionHandlers().iterator(); 173 while (tryHandlerIter.hasNext()) { 174 tryHandlerIter.next(); 175 } 176 lastOffset = ((VariableSizeListIterator)tryHandlerIter).getReaderOffset(); 177 } 178 179 //method impl size = debug block size + code_item size 180 return debugSize + (lastOffset - codeOffset); 181 } 182 } 183