blob: af3c3776c78bdff7ea9c7624ad1ca96e4458356b [file] [log] [blame]
/*
* 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<Instruction> 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<Instruction>();
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<Instruction> 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()");
}
}
}
}