AnalyzedInstruction.java revision 00fc68adf2e39aeb9fed35293f2576bbe729ec4b
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2010 Ben Gruver (JesusFreke)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib.Code.Analysis;
30
31import org.jf.dexlib.Code.*;
32import org.jf.dexlib.Item;
33import org.jf.dexlib.ItemType;
34import org.jf.dexlib.MethodIdItem;
35import org.jf.dexlib.Util.ExceptionWithContext;
36
37import java.util.*;
38
39public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
40    /**
41     * The actual instruction
42     */
43    protected Instruction instruction;
44
45    /**
46     * The index of the instruction, where the first instruction in the method is at index 0, and so on
47     */
48    protected final int instructionIndex;
49
50    /**
51     * Instructions that can pass on execution to this one during normal execution
52     */
53    protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>();
54
55    /**
56     * Instructions that can execution could pass on to next during normal execution
57     */
58    protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
59
60    /**
61     * This contains the register types *before* the instruction has executed
62     */
63    protected final RegisterType[] preRegisterMap;
64
65    /**
66     * This contains the register types *after* the instruction has executed
67     */
68    protected final RegisterType[] postRegisterMap;
69
70    /**
71     * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
72     * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
73     */
74    protected final Instruction originalInstruction;
75
76
77    /**
78     * A dead instruction is one that is unreachable because it follows an odexed instruction that can't be deodexed
79     * because it's object register is always null. In the non-odexed code that the odex was generated from, we would
80     * have technically considered this code reachable and could verify it, even though the instruction that ended up
81     * being odexed was always null, because we would assume both "paths" out of the instruction are valid - the one
82     * where execution proceeds normally to the next instruction, and the one where an exception occurs and execution
83     * either goes to a catch block, or out of the method.
84     *
85     * However, in the odexed case, we can't verify the code following an undeodexable instruction because we lack
86     * the register information from the undeodexable instruction - because we don't know the actual method or field
87     * that is being accessed.
88     *
89     * The undeodexable instruction is guaranteed to throw an NPE, so the following code is effectivetly unreachable.
90     * Once we detect an undeodexeable instruction, the following code is marked as dead up until a non-dead execution
91     * path merges in. Additionally, we remove the predecessors/successors of any dead instruction. For example, if
92     * there is a dead goto instruction, then we would remove the target instruction as a successor, and we would
93     * also remove the dead goto instruction as a predecessor to the target.
94     */
95    protected boolean dead = false;
96
97    public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
98        this.instruction = instruction;
99        this.originalInstruction = instruction;
100        this.instructionIndex = instructionIndex;
101        this.postRegisterMap = new RegisterType[registerCount];
102        this.preRegisterMap = new RegisterType[registerCount];
103        RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null);
104        for (int i=0; i<registerCount; i++) {
105            preRegisterMap[i] = unknown;
106            postRegisterMap[i] = unknown;
107        }
108    }
109
110    public int getInstructionIndex() {
111        return instructionIndex;
112    }
113
114    public int getPredecessorCount() {
115        return predecessors.size();
116    }
117
118    public SortedSet<AnalyzedInstruction> getPredecessors() {
119        return Collections.unmodifiableSortedSet(predecessors);
120    }
121
122    protected boolean addPredecessor(AnalyzedInstruction predecessor) {
123        return predecessors.add(predecessor);
124    }
125
126    protected void addSuccessor(AnalyzedInstruction successor) {
127        successors.add(successor);
128    }
129
130    protected void setDeodexedInstruction(Instruction instruction) {
131        assert originalInstruction.opcode.odexOnly();
132        this.instruction = instruction;
133    }
134
135    protected void restoreOdexedInstruction() {
136        assert originalInstruction.opcode.odexOnly();
137        instruction = originalInstruction;
138    }
139
140    public int getSuccessorCount() {
141        return successors.size();
142    }
143
144    public List<AnalyzedInstruction> getSuccesors() {
145        return Collections.unmodifiableList(successors);
146    }
147
148    public Instruction getInstruction() {
149        return instruction;
150    }
151
152    public Instruction getOriginalInstruction() {
153        return originalInstruction;
154    }
155
156    public boolean isDead() {
157        return dead;
158    }
159
160    /**
161     * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction
162     * that can be the first successfully executed instruction in the method. The first instruction is always a
163     * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then
164     * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise,
165     * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the
166     * corresponding exception handler is a beginning instruction, etc.
167     *
168     * To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has
169     * an instruction index of -1.
170     * @return a boolean value indicating whether this instruction is a beginning instruction
171     */
172    public boolean isBeginningInstruction() {
173        if (predecessors.size() == 0) {
174            return false;
175        }
176
177        if (predecessors.first().instructionIndex == -1) {
178            return true;
179        }
180        return false;
181    }
182
183    /*
184     * Merges the given register type into the specified pre-instruction register, and also sets the post-instruction
185     * register type accordingly if it isn't a destination register for this instruction
186     * @param registerNumber Which register to set
187     * @param registerType The register type
188     * @returns true If the post-instruction register type was changed. This might be false if either the specified
189     * register is a destination register for this instruction, or if the pre-instruction register type didn't change
190     * after merging in the given register type
191     */
192    protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) {
193        assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
194        assert registerType != null;
195
196        RegisterType oldRegisterType = preRegisterMap[registerNumber];
197        RegisterType mergedRegisterType = oldRegisterType.merge(registerType);
198
199        if (mergedRegisterType == oldRegisterType) {
200            return false;
201        }
202
203        preRegisterMap[registerNumber] = mergedRegisterType;
204        verifiedInstructions.clear(instructionIndex);
205
206        if (!setsRegister(registerNumber)) {
207            postRegisterMap[registerNumber] = mergedRegisterType;
208            return true;
209        }
210
211        return false;
212    }
213
214    /**
215     * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
216     * given register. Any dead, unreachable, or odexed predecessor is ignored
217     * @param registerNumber the register number
218     * @return The register type resulting from merging the post-instruction register types from all predecessors
219     */
220    protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) {
221        RegisterType mergedRegisterType = null;
222        for (AnalyzedInstruction predecessor: predecessors) {
223            RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
224            assert predecessorRegisterType != null;
225            mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
226        }
227        return mergedRegisterType;
228    }
229
230    /*
231      * Sets the "post-instruction" register type as indicated.
232      * @param registerNumber Which register to set
233      * @param registerType The "post-instruction" register type
234      * @returns true if the given register type is different than the existing post-instruction register type
235      */
236     protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
237         assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
238         assert registerType != null;
239
240         RegisterType oldRegisterType = postRegisterMap[registerNumber];
241         if (oldRegisterType == registerType) {
242             return false;
243         }
244
245         postRegisterMap[registerNumber] = registerType;
246         return true;
247     }
248
249
250    protected boolean isInvokeInit() {
251        if (instruction == null ||
252                (instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE &&
253                instruction.opcode != Opcode.INVOKE_DIRECT_EMPTY)) {
254            return false;
255        }
256
257        //TODO: check access flags instead of name?
258
259        InstructionWithReference instruction = (InstructionWithReference)this.instruction;
260        Item item = instruction.getReferencedItem();
261        assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
262        MethodIdItem method = (MethodIdItem)item;
263
264        if (!method.getMethodName().getStringValue().equals("<init>")) {
265            return false;
266        }
267
268        return true;
269    }
270
271    public boolean setsRegister() {
272        return instruction.opcode.setsRegister();
273    }
274
275    public boolean setsWideRegister() {
276        return instruction.opcode.setsWideRegister();
277    }
278
279    public boolean setsRegister(int registerNumber) {
280        //When constructing a new object, the register type will be an uninitialized reference after the new-instance
281        //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
282        //instructions don't normally change any registers, calling an <init> method will change the type of its
283        //object register. If the uninitialized reference has been copied to other registers, they will be initialized
284        //as well, so we need to check for that too
285        if (isInvokeInit()) {
286            int destinationRegister;
287            if (instruction instanceof FiveRegisterInstruction) {
288                destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
289            } else {
290                assert instruction instanceof RegisterRangeInstruction;
291                RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
292                assert rangeInstruction.getRegCount() > 0;
293                destinationRegister = rangeInstruction.getStartRegister();
294            }
295
296            if (registerNumber == destinationRegister) {
297                return true;
298            }
299            RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber);
300            if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
301                preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
302
303                return false;
304            }
305            //check if the uninit ref has been copied to another register
306            if (getPreInstructionRegisterType(registerNumber) == preInstructionDestRegisterType) {
307                return true;
308            }
309            return false;
310        }
311
312        if (!setsRegister()) {
313            return false;
314        }
315        int destinationRegister = getDestinationRegister();
316
317        if (registerNumber == destinationRegister) {
318            return true;
319        }
320        if (setsWideRegister() && registerNumber == (destinationRegister + 1)) {
321            return true;
322        }
323        return false;
324    }
325
326    public int getDestinationRegister() {
327        if (!this.instruction.opcode.setsRegister()) {
328            throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
329                    "store a value");
330        }
331        return ((SingleRegisterInstruction)instruction).getRegisterA();
332    }
333
334    public int getRegisterCount() {
335        return postRegisterMap.length;
336    }
337
338    public RegisterType getPostInstructionRegisterType(int registerNumber) {
339        return postRegisterMap[registerNumber];
340    }
341
342    public RegisterType getPreInstructionRegisterType(int registerNumber) {
343        return preRegisterMap[registerNumber];
344    }
345
346    public int compareTo(AnalyzedInstruction analyzedInstruction) {
347        //TODO: out of curiosity, check the disassembly of this to see if it retrieves the value of analyzedInstruction.instructionIndex for every access. It should, because the field is final. What about if we set the field to non-final?
348        if (instructionIndex < analyzedInstruction.instructionIndex) {
349            return -1;
350        } else if (instructionIndex == analyzedInstruction.instructionIndex) {
351            return 0;
352        } else {
353            return 1;
354        }
355    }
356}
357
358