InstructionMethodItem.java revision 4f2620415d505a35d2d14b866cde10a54b1b7c8c
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.baksmali.Adaptors.Format;
30
31import org.jf.baksmali.Adaptors.MethodDefinition;
32import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
33import org.jf.baksmali.Adaptors.MethodItem;
34import org.jf.baksmali.Renderers.LongRenderer;
35import org.jf.baksmali.baksmaliOptions;
36import org.jf.dexlib2.Opcode;
37import org.jf.dexlib2.ReferenceType;
38import org.jf.dexlib2.VerificationError;
39import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
40import org.jf.dexlib2.iface.instruction.*;
41import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
42import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
43import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
44import org.jf.dexlib2.iface.reference.Reference;
45import org.jf.dexlib2.util.ReferenceUtil;
46import org.jf.util.ExceptionWithContext;
47import org.jf.util.IndentingWriter;
48
49import javax.annotation.Nonnull;
50import java.io.IOException;
51import java.util.Map;
52
53public class InstructionMethodItem<T extends Instruction> extends MethodItem {
54    @Nonnull protected final MethodDefinition methodDef;
55    @Nonnull protected final T instruction;
56
57    public InstructionMethodItem(@Nonnull MethodDefinition methodDef, int codeAddress, @Nonnull T instruction) {
58        super(codeAddress);
59        this.methodDef = methodDef;
60        this.instruction = instruction;
61    }
62
63    public double getSortOrder() {
64        //instructions should appear after everything except an "end try" label and .catch directive
65        return 100;
66    }
67
68    private boolean isAllowedOdex(@Nonnull Opcode opcode) {
69        baksmaliOptions options = methodDef.classDef.options;
70        if (options.allowOdex) {
71            return true;
72        }
73
74        if (methodDef.classDef.options.apiLevel >= 14) {
75            return false;
76        }
77
78        return opcode.isOdexedInstanceVolatile() || opcode.isOdexedStaticVolatile() ||
79            opcode == Opcode.THROW_VERIFICATION_ERROR;
80    }
81
82    @Override
83    public boolean writeTo(IndentingWriter writer) throws IOException {
84        Opcode opcode = instruction.getOpcode();
85        String verificationErrorName = null;
86        String referenceString = null;
87
88        boolean commentOutInstruction = false;
89
90        if (instruction instanceof Instruction20bc) {
91            int verificationError = ((Instruction20bc)instruction).getVerificationError();
92            verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
93            if (verificationErrorName == null) {
94                writer.write("#was invalid verification error type: ");
95                writer.printSignedIntAsDec(verificationError);
96                writer.write("\n");
97                verificationErrorName = "generic-error";
98            }
99        }
100
101        if (instruction instanceof ReferenceInstruction) {
102            ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
103            try {
104                Reference reference = referenceInstruction.getReference();
105                referenceString = ReferenceUtil.getReferenceString(reference);
106                assert referenceString != null;
107            } catch (InvalidItemIndex ex) {
108                writer.write("#");
109                writer.write(ex.getMessage());
110                writer.write("\n");
111                commentOutInstruction = true;
112
113                referenceString = String.format("%s@%d",
114                    ReferenceType.toString(referenceInstruction.getReferenceType()),
115                    ex.getInvalidIndex());
116            } catch (ReferenceType.InvalidReferenceTypeException ex) {
117                writer.write("#invalid reference type: ");
118                writer.printSignedIntAsDec(ex.getReferenceType());
119                commentOutInstruction = true;
120
121                referenceString = "invalid_reference";
122            }
123        }
124
125        if (instruction instanceof Instruction31t) {
126            Opcode payloadOpcode;
127            switch (instruction.getOpcode()) {
128                case PACKED_SWITCH:
129                    payloadOpcode = Opcode.PACKED_SWITCH_PAYLOAD;
130                    break;
131                case SPARSE_SWITCH:
132                    payloadOpcode = Opcode.SPARSE_SWITCH_PAYLOAD;
133                    break;
134                case FILL_ARRAY_DATA:
135                    payloadOpcode = Opcode.ARRAY_PAYLOAD;
136                    break;
137                default:
138                    throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
139            }
140
141            try {
142                methodDef.findSwitchPayload(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
143                        payloadOpcode);
144            } catch (InvalidSwitchPayload ex) {
145                writer.write("#invalid payload reference");
146                commentOutInstruction = true;
147            }
148        }
149
150        if (opcode.odexOnly()) {
151            if (!isAllowedOdex(opcode)) {
152                writer.write("#disallowed odex opcode\n");
153                commentOutInstruction = true;
154            }
155        }
156
157        if (commentOutInstruction) {
158            writer.write("#");
159        }
160
161        switch (instruction.getOpcode().format) {
162            case Format10t:
163                writeOpcode(writer);
164                writer.write(' ');
165                writeTargetLabel(writer);
166                break;
167            case Format10x:
168                if (instruction instanceof UnknownInstruction) {
169                    writer.write("#unknown opcode: 0x");
170                    writer.printUnsignedLongAsHex(((UnknownInstruction)instruction).getOriginalOpcode());
171                    writer.write('\n');
172                }
173                writeOpcode(writer);
174                break;
175            case Format11n:
176                writeOpcode(writer);
177                writer.write(' ');
178                writeFirstRegister(writer);
179                writer.write(", ");
180                writeLiteral(writer);
181                break;
182            case Format11x:
183                writeOpcode(writer);
184                writer.write(' ');
185                writeFirstRegister(writer);
186                break;
187            case Format12x:
188                writeOpcode(writer);
189                writer.write(' ');
190                writeFirstRegister(writer);
191                writer.write(", ");
192                writeSecondRegister(writer);
193                break;
194            case Format20bc:
195                writeOpcode(writer);
196                writer.write(' ');
197                writer.write(verificationErrorName);
198                writer.write(", ");
199                writer.write(referenceString);
200                break;
201            case Format20t:
202            case Format30t:
203                writeOpcode(writer);
204                writer.write(' ');
205                writeTargetLabel(writer);
206                break;
207            case Format21c:
208            case Format31c:
209                writeOpcode(writer);
210                writer.write(' ');
211                writeFirstRegister(writer);
212                writer.write(", ");
213                writer.write(referenceString);
214                break;
215            case Format21ih:
216            case Format21lh:
217            case Format21s:
218            case Format31i:
219            case Format51l:
220                writeOpcode(writer);
221                writer.write(' ');
222                writeFirstRegister(writer);
223                writer.write(", ");
224                writeLiteral(writer);
225                if (instruction.getOpcode().setsWideRegister() == false)
226                    writeResourceId(writer);
227                break;
228            case Format21t:
229            case Format31t:
230                writeOpcode(writer);
231                writer.write(' ');
232                writeFirstRegister(writer);
233                writer.write(", ");
234                writeTargetLabel(writer);
235                break;
236            case Format22b:
237            case Format22s:
238                writeOpcode(writer);
239                writer.write(' ');
240                writeFirstRegister(writer);
241                writer.write(", ");
242                writeSecondRegister(writer);
243                writer.write(", ");
244                writeLiteral(writer);
245                break;
246            case Format22c:
247                writeOpcode(writer);
248                writer.write(' ');
249                writeFirstRegister(writer);
250                writer.write(", ");
251                writeSecondRegister(writer);
252                writer.write(", ");
253                writer.write(referenceString);
254                break;
255            case Format22cs:
256                writeOpcode(writer);
257                writer.write(' ');
258                writeFirstRegister(writer);
259                writer.write(", ");
260                writeSecondRegister(writer);
261                writer.write(", ");
262                writeFieldOffset(writer);
263                break;
264            case Format22t:
265                writeOpcode(writer);
266                writer.write(' ');
267                writeFirstRegister(writer);
268                writer.write(", ");
269                writeSecondRegister(writer);
270                writer.write(", ");
271                writeTargetLabel(writer);
272                break;
273            case Format22x:
274            case Format32x:
275                writeOpcode(writer);
276                writer.write(' ');
277                writeFirstRegister(writer);
278                writer.write(", ");
279                writeSecondRegister(writer);
280                break;
281            case Format23x:
282                writeOpcode(writer);
283                writer.write(' ');
284                writeFirstRegister(writer);
285                writer.write(", ");
286                writeSecondRegister(writer);
287                writer.write(", ");
288                writeThirdRegister(writer);
289                break;
290            case Format35c:
291                writeOpcode(writer);
292                writer.write(' ');
293                writeInvokeRegisters(writer);
294                writer.write(", ");
295                writer.write(referenceString);
296                break;
297            case Format35mi:
298                writeOpcode(writer);
299                writer.write(' ');
300                writeInvokeRegisters(writer);
301                writer.write(", ");
302                writeInlineIndex(writer);
303                break;
304            case Format35ms:
305                writeOpcode(writer);
306                writer.write(' ');
307                writeInvokeRegisters(writer);
308                writer.write(", ");
309                writeVtableIndex(writer);
310                break;
311            case Format3rc:
312                writeOpcode(writer);
313                writer.write(' ');
314                writeInvokeRangeRegisters(writer);
315                writer.write(", ");
316                writer.write(referenceString);
317                break;
318            case Format3rmi:
319                writeOpcode(writer);
320                writer.write(' ');
321                writeInvokeRangeRegisters(writer);
322                writer.write(", ");
323                writeInlineIndex(writer);
324                break;
325            case Format3rms:
326                writeOpcode(writer);
327                writer.write(' ');
328                writeInvokeRangeRegisters(writer);
329                writer.write(", ");
330                writeVtableIndex(writer);
331                break;
332            default:
333                assert false;
334                return false;
335        }
336
337        if (commentOutInstruction) {
338            writer.write("\nnop");
339        }
340
341        return true;
342    }
343
344    protected void writeOpcode(IndentingWriter writer) throws IOException {
345        writer.write(instruction.getOpcode().name);
346    }
347
348    protected void writeTargetLabel(IndentingWriter writer) throws IOException {
349        //this method is overridden by OffsetInstructionMethodItem, and should only be called for the formats that
350        //have a target
351        throw new RuntimeException();
352    }
353
354    protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
355        methodDef.registerFormatter.writeTo(writer, registerNumber);
356    }
357
358    protected void writeFirstRegister(IndentingWriter writer) throws IOException {
359        writeRegister(writer, ((OneRegisterInstruction)instruction).getRegisterA());
360    }
361
362    protected void writeSecondRegister(IndentingWriter writer) throws IOException {
363        writeRegister(writer, ((TwoRegisterInstruction)instruction).getRegisterB());
364    }
365
366    protected void writeThirdRegister(IndentingWriter writer) throws IOException {
367        writeRegister(writer, ((ThreeRegisterInstruction) instruction).getRegisterC());
368    }
369
370    protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
371        FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
372        final int regCount = instruction.getRegisterCount();
373
374        writer.write('{');
375        switch (regCount) {
376            case 1:
377                writeRegister(writer, instruction.getRegisterC());
378                break;
379            case 2:
380                writeRegister(writer, instruction.getRegisterC());
381                writer.write(", ");
382                writeRegister(writer, instruction.getRegisterD());
383                break;
384            case 3:
385                writeRegister(writer, instruction.getRegisterC());
386                writer.write(", ");
387                writeRegister(writer, instruction.getRegisterD());
388                writer.write(", ");
389                writeRegister(writer, instruction.getRegisterE());
390                break;
391            case 4:
392                writeRegister(writer, instruction.getRegisterC());
393                writer.write(", ");
394                writeRegister(writer, instruction.getRegisterD());
395                writer.write(", ");
396                writeRegister(writer, instruction.getRegisterE());
397                writer.write(", ");
398                writeRegister(writer, instruction.getRegisterF());
399                break;
400            case 5:
401                writeRegister(writer, instruction.getRegisterC());
402                writer.write(", ");
403                writeRegister(writer, instruction.getRegisterD());
404                writer.write(", ");
405                writeRegister(writer, instruction.getRegisterE());
406                writer.write(", ");
407                writeRegister(writer, instruction.getRegisterF());
408                writer.write(", ");
409                writeRegister(writer, instruction.getRegisterG());
410                break;
411        }
412        writer.write('}');
413    }
414
415    protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
416        RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
417
418        int regCount = instruction.getRegisterCount();
419        if (regCount == 0) {
420            writer.write("{}");
421        } else {
422            int startRegister = instruction.getStartRegister();
423            methodDef.registerFormatter.writeRegisterRange(writer, startRegister, startRegister+regCount-1);
424        }
425    }
426
427    protected void writeLiteral(IndentingWriter writer) throws IOException {
428        LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
429    }
430
431    protected void writeResourceId(IndentingWriter writer) throws IOException {
432        writeResourceId(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
433    }
434
435    protected void writeResourceId(IndentingWriter writer, int val) throws IOException {
436        Map<Integer,String> resourceIds = methodDef.classDef.options.resourceIds;
437        String resource = resourceIds.get(Integer.valueOf(val));
438        if (resource != null) {
439            writer.write("    # ");
440            writer.write(resource);
441        }
442    }
443
444    protected void writeFieldOffset(IndentingWriter writer) throws IOException {
445        writer.write("field@0x");
446        writer.printUnsignedLongAsHex(((FieldOffsetInstruction)instruction).getFieldOffset());
447    }
448
449    protected void writeInlineIndex(IndentingWriter writer) throws IOException {
450        writer.write("inline@");
451        writer.printSignedIntAsDec(((InlineIndexInstruction)instruction).getInlineIndex());
452    }
453
454    protected void writeVtableIndex(IndentingWriter writer) throws IOException {
455        writer.write("vtable@");
456        writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
457    }
458}
459