1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dexfuzz.rawdex;
18 
19 import dexfuzz.Log;
20 import dexfuzz.program.MutatableCode;
21 
22 import java.io.IOException;
23 import java.util.LinkedList;
24 import java.util.List;
25 
26 public class CodeItem implements RawDexObject {
27   public short registersSize;
28   public short insSize;
29   public short outsSize;
30   public short triesSize;
31   public int debugInfoOff; // NB: this is a special case
32   public int insnsSize;
33   public List<Instruction> insns;
34   public TryItem[] tries;
35   public EncodedCatchHandlerList handlers;
36 
37   private MutatableCode mutatableCode;
38 
39   public static class MethodMetaInfo {
40     public String methodName;
41     public boolean isStatic;
42     public String shorty;
43   }
44 
45   public MethodMetaInfo meta = new MethodMetaInfo();
46 
47   @Override
read(DexRandomAccessFile file)48   public void read(DexRandomAccessFile file) throws IOException {
49     file.alignForwards(4);
50     file.getOffsetTracker().getNewOffsettable(file, this);
51     registersSize = file.readUShort();
52     insSize = file.readUShort();
53     outsSize = file.readUShort();
54     triesSize = file.readUShort();
55     debugInfoOff = file.readUInt();
56     insnsSize = file.readUInt();
57     populateInstructionList(file);
58     if (triesSize > 0) {
59       if ((insnsSize % 2) != 0) {
60         // Consume padding.
61         file.readUShort();
62       }
63       tries = new TryItem[triesSize];
64       for (int i = 0; i < triesSize; i++) {
65         (tries[i] = new TryItem()).read(file);
66       }
67       (handlers = new EncodedCatchHandlerList()).read(file);
68     }
69   }
70 
populateInstructionList(DexRandomAccessFile file)71   private void populateInstructionList(DexRandomAccessFile file) throws IOException {
72     insns = new LinkedList<Instruction>();
73     long insnsOffset = file.getFilePointer();
74     if (insnsOffset != 0) {
75       long finger = insnsOffset;
76       long insnsEnd = insnsOffset + (2 * insnsSize);
77 
78       while (finger < insnsEnd) {
79         file.seek(finger);
80         Instruction newInsn = new Instruction();
81         newInsn.read(file);
82         insns.add(newInsn);
83         finger += (2 * newInsn.getSize());
84       }
85 
86       file.seek(finger);
87     }
88   }
89 
90   @Override
write(DexRandomAccessFile file)91   public void write(DexRandomAccessFile file) throws IOException {
92     file.alignForwards(4);
93     file.getOffsetTracker().updatePositionOfNextOffsettable(file);
94     file.writeUShort(registersSize);
95     file.writeUShort(insSize);
96     file.writeUShort(outsSize);
97     file.writeUShort(triesSize);
98     // We do not support retaining debug info currently.
99     file.writeUInt(0 /*debug_info_off*/);
100     file.writeUInt(insnsSize);
101     for (Instruction insn : insns) {
102       insn.write(file);
103     }
104     if (triesSize > 0) {
105       if ((insnsSize % 2) != 0) {
106         // produce padding
107         file.writeUShort((short) 0);
108       }
109       for (TryItem tryItem : tries) {
110         tryItem.write(file);
111       }
112       handlers.write(file);
113     }
114   }
115 
116   /**
117    * CodeTranslator should call this to notify a CodeItem about its
118    * mutatable code, so it can always get the "latest" view of its
119    * instructions.
120    */
registerMutatableCode(MutatableCode mutatableCode)121   public void registerMutatableCode(MutatableCode mutatableCode) {
122     this.mutatableCode = mutatableCode;
123   }
124 
125   @Override
incrementIndex(IndexUpdateKind kind, int insertedIdx)126   public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
127     if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) {
128       // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns)
129       // are only interested in TYPE_IDs.
130       handlers.incrementIndex(kind, insertedIdx);
131     }
132 
133     if (kind == IndexUpdateKind.PROTO_ID) {
134       // The only kind we can't encounter in an instruction.
135       return;
136     }
137 
138     List<Instruction> insnsToIncrement = insns;
139 
140     // If we have an associated MutatableCode, then it may have created some new insns
141     // that we won't know about yet, during the mutation phase.
142     //
143     // Ask for the latest view of the insns.
144     if (mutatableCode != null) {
145       insnsToIncrement = mutatableCode.requestLatestInstructions();
146     }
147 
148     for (Instruction insn : insnsToIncrement) {
149       Opcode opcode = insn.info.opcode;
150       switch (kind) {
151         case STRING_ID:
152           if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
153             // STRING@BBBB
154             if (insn.vregB >= insertedIdx) {
155               insn.vregB++;
156             }
157           }
158           break;
159         case TYPE_ID:
160           if (opcode == Opcode.CONST_CLASS
161               || opcode == Opcode.CHECK_CAST
162               || opcode == Opcode.NEW_INSTANCE
163               || opcode == Opcode.FILLED_NEW_ARRAY
164               || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
165             // TYPE@BBBB
166             if (insn.vregB >= insertedIdx) {
167               insn.vregB++;
168             }
169           } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) {
170             // TYPE@CCCC
171             if (insn.vregC >= insertedIdx) {
172               insn.vregC++;
173             }
174           }
175           break;
176         case FIELD_ID:
177           if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) {
178             // FIELD@BBBB
179             if (insn.vregB >= insertedIdx) {
180               insn.vregB++;
181             }
182           } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) {
183             // FIELD@CCCC
184             if (insn.vregC >= insertedIdx) {
185               insn.vregC++;
186             }
187           }
188           break;
189         case METHOD_ID:
190           if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
191               || Opcode.isBetween(opcode,
192                   Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) {
193             // METHOD@BBBB
194             if (insn.vregB >= insertedIdx) {
195               insn.vregB++;
196             }
197           }
198           break;
199         default:
200           Log.errorAndQuit("Unexpected IndexUpdateKind requested "
201               + "in Instruction.incrementIndex()");
202       }
203     }
204   }
205 }
206