/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dexfuzz.rawdex; import dexfuzz.Log; import dexfuzz.program.MutatableCode; import java.io.IOException; import java.util.LinkedList; import java.util.List; public class CodeItem implements RawDexObject { public short registersSize; public short insSize; public short outsSize; public short triesSize; public int debugInfoOff; // NB: this is a special case public int insnsSize; public List insns; public TryItem[] tries; public EncodedCatchHandlerList handlers; private MutatableCode mutatableCode; public static class MethodMetaInfo { public String methodName; public boolean isStatic; public String shorty; } public MethodMetaInfo meta = new MethodMetaInfo(); @Override public void read(DexRandomAccessFile file) throws IOException { file.alignForwards(4); file.getOffsetTracker().getNewOffsettable(file, this); registersSize = file.readUShort(); insSize = file.readUShort(); outsSize = file.readUShort(); triesSize = file.readUShort(); debugInfoOff = file.readUInt(); insnsSize = file.readUInt(); populateInstructionList(file); if (triesSize > 0) { if ((insnsSize % 2) != 0) { // Consume padding. file.readUShort(); } tries = new TryItem[triesSize]; for (int i = 0; i < triesSize; i++) { (tries[i] = new TryItem()).read(file); } (handlers = new EncodedCatchHandlerList()).read(file); } } private void populateInstructionList(DexRandomAccessFile file) throws IOException { insns = new LinkedList(); long insnsOffset = file.getFilePointer(); if (insnsOffset != 0) { long finger = insnsOffset; long insnsEnd = insnsOffset + (2 * insnsSize); while (finger < insnsEnd) { file.seek(finger); Instruction newInsn = new Instruction(); newInsn.read(file); insns.add(newInsn); finger += (2 * newInsn.getSize()); } file.seek(finger); } } @Override public void write(DexRandomAccessFile file) throws IOException { file.alignForwards(4); file.getOffsetTracker().updatePositionOfNextOffsettable(file); file.writeUShort(registersSize); file.writeUShort(insSize); file.writeUShort(outsSize); file.writeUShort(triesSize); // We do not support retaining debug info currently. file.writeUInt(0 /*debug_info_off*/); file.writeUInt(insnsSize); for (Instruction insn : insns) { insn.write(file); } if (triesSize > 0) { if ((insnsSize % 2) != 0) { // produce padding file.writeUShort((short) 0); } for (TryItem tryItem : tries) { tryItem.write(file); } handlers.write(file); } } /** * CodeTranslator should call this to notify a CodeItem about its * mutatable code, so it can always get the "latest" view of its * instructions. */ public void registerMutatableCode(MutatableCode mutatableCode) { this.mutatableCode = mutatableCode; } @Override public void incrementIndex(IndexUpdateKind kind, int insertedIdx) { if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) { // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns) // are only interested in TYPE_IDs. handlers.incrementIndex(kind, insertedIdx); } if (kind == IndexUpdateKind.PROTO_ID) { // The only kind we can't encounter in an instruction. return; } List insnsToIncrement = insns; // If we have an associated MutatableCode, then it may have created some new insns // that we won't know about yet, during the mutation phase. // // Ask for the latest view of the insns. if (mutatableCode != null) { insnsToIncrement = mutatableCode.requestLatestInstructions(); } for (Instruction insn : insnsToIncrement) { Opcode opcode = insn.info.opcode; switch (kind) { case STRING_ID: if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) { // STRING@BBBB if (insn.vregB >= insertedIdx) { insn.vregB++; } } break; case TYPE_ID: if (opcode == Opcode.CONST_CLASS || opcode == Opcode.CHECK_CAST || opcode == Opcode.NEW_INSTANCE || opcode == Opcode.FILLED_NEW_ARRAY || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) { // TYPE@BBBB if (insn.vregB >= insertedIdx) { insn.vregB++; } } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) { // TYPE@CCCC if (insn.vregC >= insertedIdx) { insn.vregC++; } } break; case FIELD_ID: if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) { // FIELD@BBBB if (insn.vregB >= insertedIdx) { insn.vregB++; } } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) { // FIELD@CCCC if (insn.vregC >= insertedIdx) { insn.vregC++; } } break; case METHOD_ID: if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE) || Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) { // METHOD@BBBB if (insn.vregB >= insertedIdx) { insn.vregB++; } } break; default: Log.errorAndQuit("Unexpected IndexUpdateKind requested " + "in Instruction.incrementIndex()"); } } } }