• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 package org.jf.baksmali.Adaptors.Format;
30 
31 import org.jf.baksmali.Adaptors.MethodDefinition;
32 import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
33 import org.jf.baksmali.Adaptors.MethodItem;
34 import org.jf.baksmali.Renderers.LongRenderer;
35 import org.jf.baksmali.BaksmaliOptions;
36 import org.jf.dexlib2.Opcode;
37 import org.jf.dexlib2.ReferenceType;
38 import org.jf.dexlib2.VerificationError;
39 import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
40 import org.jf.dexlib2.iface.instruction.*;
41 import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
42 import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
43 import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
44 import org.jf.dexlib2.iface.reference.Reference;
45 import org.jf.dexlib2.util.ReferenceUtil;
46 import org.jf.util.ExceptionWithContext;
47 import org.jf.util.IndentingWriter;
48 import org.jf.util.NumberUtils;
49 
50 import javax.annotation.Nonnull;
51 import java.io.IOException;
52 import java.util.Map;
53 
54 public class InstructionMethodItem<T extends Instruction> extends MethodItem {
55     @Nonnull protected final MethodDefinition methodDef;
56     @Nonnull protected final T instruction;
57 
InstructionMethodItem(@onnull MethodDefinition methodDef, int codeAddress, @Nonnull T instruction)58     public InstructionMethodItem(@Nonnull MethodDefinition methodDef, int codeAddress, @Nonnull T instruction) {
59         super(codeAddress);
60         this.methodDef = methodDef;
61         this.instruction = instruction;
62     }
63 
getSortOrder()64     public double getSortOrder() {
65         //instructions should appear after everything except an "end try" label and .catch directive
66         return 100;
67     }
68 
isAllowedOdex(@onnull Opcode opcode)69     private boolean isAllowedOdex(@Nonnull Opcode opcode) {
70         BaksmaliOptions options = methodDef.classDef.options;
71         if (options.allowOdex) {
72             return true;
73         }
74 
75         if (methodDef.classDef.options.apiLevel >= 14) {
76             return false;
77         }
78 
79         return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR;
80     }
81 
writeInvalidItemIndex(InvalidItemIndex ex, int type, IndentingWriter writer)82     private String writeInvalidItemIndex(InvalidItemIndex ex, int type, IndentingWriter writer)
83             throws IOException {
84         writer.write("#");
85         writer.write(ex.getMessage());
86         writer.write("\n");
87         return String.format("%s@%d", ReferenceType.toString(type), ex.getInvalidIndex());
88     }
89 
90     @Override
writeTo(IndentingWriter writer)91     public boolean writeTo(IndentingWriter writer) throws IOException {
92         Opcode opcode = instruction.getOpcode();
93         String verificationErrorName = null;
94         String referenceString = null;
95         String referenceString2 = null;
96 
97         boolean commentOutInstruction = false;
98 
99         if (instruction instanceof Instruction20bc) {
100             int verificationError = ((Instruction20bc)instruction).getVerificationError();
101             verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
102             if (verificationErrorName == null) {
103                 writer.write("#was invalid verification error type: ");
104                 writer.printSignedIntAsDec(verificationError);
105                 writer.write("\n");
106                 verificationErrorName = "generic-error";
107             }
108         }
109 
110         if (instruction instanceof ReferenceInstruction) {
111             ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
112             String classContext = null;
113             if (methodDef.classDef.options.implicitReferences) {
114                 classContext = methodDef.method.getDefiningClass();
115             }
116 
117             try {
118                 Reference reference = referenceInstruction.getReference();
119                 referenceString = ReferenceUtil.getReferenceString(reference, classContext);
120                 assert referenceString != null;
121             } catch (InvalidItemIndex ex) {
122                 commentOutInstruction = true;
123                 referenceString = writeInvalidItemIndex(ex, referenceInstruction.getReferenceType(),
124                         writer);
125             } catch (ReferenceType.InvalidReferenceTypeException ex) {
126                 writer.write("#invalid reference type: ");
127                 writer.printSignedIntAsDec(ex.getReferenceType());
128                 commentOutInstruction = true;
129 
130                 referenceString = "invalid_reference";
131             }
132 
133             if (instruction instanceof DualReferenceInstruction) {
134                 DualReferenceInstruction dualReferenceInstruction =
135                         (DualReferenceInstruction) instruction;
136                 try {
137                     Reference reference2 = dualReferenceInstruction.getReference2();
138                     referenceString2 = ReferenceUtil.getReferenceString(reference2, classContext);
139                 } catch (InvalidItemIndex ex) {
140                     commentOutInstruction = true;
141                     referenceString2 = writeInvalidItemIndex(ex,
142                             dualReferenceInstruction.getReferenceType2(), writer);
143                 } catch (ReferenceType.InvalidReferenceTypeException ex) {
144                     writer.write("#invalid reference type: ");
145                     writer.printSignedIntAsDec(ex.getReferenceType());
146                     commentOutInstruction = true;
147 
148                     referenceString2 = "invalid_reference";
149                 }
150             }
151         }
152 
153         if (instruction instanceof Instruction31t) {
154             boolean validPayload = true;
155 
156             switch (instruction.getOpcode()) {
157                 case PACKED_SWITCH:
158                     int baseAddress = methodDef.getPackedSwitchBaseAddress(
159                             this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
160                     if (baseAddress == -1) {
161                         validPayload = false;
162                     }
163                     break;
164                 case SPARSE_SWITCH:
165                     baseAddress = methodDef.getSparseSwitchBaseAddress(
166                             this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
167                     if (baseAddress == -1) {
168                         validPayload = false;
169                     }
170                     break;
171                 case FILL_ARRAY_DATA:
172                     try {
173                         methodDef.findPayloadOffset(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
174                                 Opcode.ARRAY_PAYLOAD);
175                     } catch (InvalidSwitchPayload ex) {
176                         validPayload = false;
177                     }
178                     break;
179                 default:
180                     throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
181             }
182 
183             if (!validPayload) {
184                 writer.write("#invalid payload reference\n");
185                 commentOutInstruction = true;
186             }
187         }
188 
189         if (opcode.odexOnly()) {
190             if (!isAllowedOdex(opcode)) {
191                 writer.write("#disallowed odex opcode\n");
192                 commentOutInstruction = true;
193             }
194         }
195 
196         if (commentOutInstruction) {
197             writer.write("#");
198         }
199 
200         switch (instruction.getOpcode().format) {
201             case Format10t:
202                 writeOpcode(writer);
203                 writer.write(' ');
204                 writeTargetLabel(writer);
205                 break;
206             case Format10x:
207                 if (instruction instanceof UnknownInstruction) {
208                     writer.write("#unknown opcode: 0x");
209                     writer.printUnsignedLongAsHex(((UnknownInstruction)instruction).getOriginalOpcode());
210                     writer.write('\n');
211                 }
212                 writeOpcode(writer);
213                 break;
214             case Format11n:
215                 writeOpcode(writer);
216                 writer.write(' ');
217                 writeFirstRegister(writer);
218                 writer.write(", ");
219                 writeLiteral(writer);
220                 break;
221             case Format11x:
222                 writeOpcode(writer);
223                 writer.write(' ');
224                 writeFirstRegister(writer);
225                 break;
226             case Format12x:
227                 writeOpcode(writer);
228                 writer.write(' ');
229                 writeFirstRegister(writer);
230                 writer.write(", ");
231                 writeSecondRegister(writer);
232                 break;
233             case Format20bc:
234                 writeOpcode(writer);
235                 writer.write(' ');
236                 writer.write(verificationErrorName);
237                 writer.write(", ");
238                 writer.write(referenceString);
239                 break;
240             case Format20t:
241             case Format30t:
242                 writeOpcode(writer);
243                 writer.write(' ');
244                 writeTargetLabel(writer);
245                 break;
246             case Format21c:
247             case Format31c:
248                 writeOpcode(writer);
249                 writer.write(' ');
250                 writeFirstRegister(writer);
251                 writer.write(", ");
252                 writer.write(referenceString);
253                 break;
254             case Format21ih:
255             case Format21lh:
256             case Format21s:
257             case Format31i:
258             case Format51l:
259                 writeOpcode(writer);
260                 writer.write(' ');
261                 writeFirstRegister(writer);
262                 writer.write(", ");
263                 writeLiteral(writer);
264                 if (instruction.getOpcode().setsWideRegister()) {
265                     writeCommentIfLikelyDouble(writer);
266                 } else {
267                     boolean isResourceId = writeCommentIfResourceId(writer);
268                     if (!isResourceId) writeCommentIfLikelyFloat(writer);
269                 }
270                 break;
271             case Format21t:
272             case Format31t:
273                 writeOpcode(writer);
274                 writer.write(' ');
275                 writeFirstRegister(writer);
276                 writer.write(", ");
277                 writeTargetLabel(writer);
278                 break;
279             case Format22b:
280             case Format22s:
281                 writeOpcode(writer);
282                 writer.write(' ');
283                 writeFirstRegister(writer);
284                 writer.write(", ");
285                 writeSecondRegister(writer);
286                 writer.write(", ");
287                 writeLiteral(writer);
288                 break;
289             case Format22c:
290                 writeOpcode(writer);
291                 writer.write(' ');
292                 writeFirstRegister(writer);
293                 writer.write(", ");
294                 writeSecondRegister(writer);
295                 writer.write(", ");
296                 writer.write(referenceString);
297                 break;
298             case Format22cs:
299                 writeOpcode(writer);
300                 writer.write(' ');
301                 writeFirstRegister(writer);
302                 writer.write(", ");
303                 writeSecondRegister(writer);
304                 writer.write(", ");
305                 writeFieldOffset(writer);
306                 break;
307             case Format22t:
308                 writeOpcode(writer);
309                 writer.write(' ');
310                 writeFirstRegister(writer);
311                 writer.write(", ");
312                 writeSecondRegister(writer);
313                 writer.write(", ");
314                 writeTargetLabel(writer);
315                 break;
316             case Format22x:
317             case Format32x:
318                 writeOpcode(writer);
319                 writer.write(' ');
320                 writeFirstRegister(writer);
321                 writer.write(", ");
322                 writeSecondRegister(writer);
323                 break;
324             case Format23x:
325                 writeOpcode(writer);
326                 writer.write(' ');
327                 writeFirstRegister(writer);
328                 writer.write(", ");
329                 writeSecondRegister(writer);
330                 writer.write(", ");
331                 writeThirdRegister(writer);
332                 break;
333             case Format35c:
334                 writeOpcode(writer);
335                 writer.write(' ');
336                 writeInvokeRegisters(writer);
337                 writer.write(", ");
338                 writer.write(referenceString);
339                 break;
340             case Format35mi:
341                 writeOpcode(writer);
342                 writer.write(' ');
343                 writeInvokeRegisters(writer);
344                 writer.write(", ");
345                 writeInlineIndex(writer);
346                 break;
347             case Format35ms:
348                 writeOpcode(writer);
349                 writer.write(' ');
350                 writeInvokeRegisters(writer);
351                 writer.write(", ");
352                 writeVtableIndex(writer);
353                 break;
354             case Format3rc:
355                 writeOpcode(writer);
356                 writer.write(' ');
357                 writeInvokeRangeRegisters(writer);
358                 writer.write(", ");
359                 writer.write(referenceString);
360                 break;
361             case Format3rmi:
362                 writeOpcode(writer);
363                 writer.write(' ');
364                 writeInvokeRangeRegisters(writer);
365                 writer.write(", ");
366                 writeInlineIndex(writer);
367                 break;
368             case Format3rms:
369                 writeOpcode(writer);
370                 writer.write(' ');
371                 writeInvokeRangeRegisters(writer);
372                 writer.write(", ");
373                 writeVtableIndex(writer);
374                 break;
375             case Format45cc:
376                 writeOpcode(writer);
377                 writer.write(' ');
378                 writeInvokeRegisters(writer);
379                 writer.write(", ");
380                 writer.write(referenceString);
381                 writer.write(", ");
382                 writer.write(referenceString2);
383                 break;
384             case Format4rcc:
385                 writeOpcode(writer);
386                 writer.write(' ');
387                 writeInvokeRangeRegisters(writer);
388                 writer.write(", ");
389                 writer.write(referenceString);
390                 writer.write(", ");
391                 writer.write(referenceString2);
392                 break;
393             default:
394                 assert false;
395                 return false;
396         }
397 
398         if (commentOutInstruction) {
399             writer.write("\nnop");
400         }
401 
402         return true;
403     }
404 
writeOpcode(IndentingWriter writer)405     protected void writeOpcode(IndentingWriter writer) throws IOException {
406         writer.write(instruction.getOpcode().name);
407     }
408 
writeTargetLabel(IndentingWriter writer)409     protected void writeTargetLabel(IndentingWriter writer) throws IOException {
410         //this method is overridden by OffsetInstructionMethodItem, and should only be called for the formats that
411         //have a target
412         throw new RuntimeException();
413     }
414 
writeRegister(IndentingWriter writer, int registerNumber)415     protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
416         methodDef.registerFormatter.writeTo(writer, registerNumber);
417     }
418 
writeFirstRegister(IndentingWriter writer)419     protected void writeFirstRegister(IndentingWriter writer) throws IOException {
420         writeRegister(writer, ((OneRegisterInstruction)instruction).getRegisterA());
421     }
422 
writeSecondRegister(IndentingWriter writer)423     protected void writeSecondRegister(IndentingWriter writer) throws IOException {
424         writeRegister(writer, ((TwoRegisterInstruction)instruction).getRegisterB());
425     }
426 
writeThirdRegister(IndentingWriter writer)427     protected void writeThirdRegister(IndentingWriter writer) throws IOException {
428         writeRegister(writer, ((ThreeRegisterInstruction) instruction).getRegisterC());
429     }
430 
writeInvokeRegisters(IndentingWriter writer)431     protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
432         FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
433         final int regCount = instruction.getRegisterCount();
434 
435         writer.write('{');
436         switch (regCount) {
437             case 1:
438                 writeRegister(writer, instruction.getRegisterC());
439                 break;
440             case 2:
441                 writeRegister(writer, instruction.getRegisterC());
442                 writer.write(", ");
443                 writeRegister(writer, instruction.getRegisterD());
444                 break;
445             case 3:
446                 writeRegister(writer, instruction.getRegisterC());
447                 writer.write(", ");
448                 writeRegister(writer, instruction.getRegisterD());
449                 writer.write(", ");
450                 writeRegister(writer, instruction.getRegisterE());
451                 break;
452             case 4:
453                 writeRegister(writer, instruction.getRegisterC());
454                 writer.write(", ");
455                 writeRegister(writer, instruction.getRegisterD());
456                 writer.write(", ");
457                 writeRegister(writer, instruction.getRegisterE());
458                 writer.write(", ");
459                 writeRegister(writer, instruction.getRegisterF());
460                 break;
461             case 5:
462                 writeRegister(writer, instruction.getRegisterC());
463                 writer.write(", ");
464                 writeRegister(writer, instruction.getRegisterD());
465                 writer.write(", ");
466                 writeRegister(writer, instruction.getRegisterE());
467                 writer.write(", ");
468                 writeRegister(writer, instruction.getRegisterF());
469                 writer.write(", ");
470                 writeRegister(writer, instruction.getRegisterG());
471                 break;
472         }
473         writer.write('}');
474     }
475 
writeInvokeRangeRegisters(IndentingWriter writer)476     protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
477         RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
478 
479         int regCount = instruction.getRegisterCount();
480         if (regCount == 0) {
481             writer.write("{}");
482         } else {
483             int startRegister = instruction.getStartRegister();
484             methodDef.registerFormatter.writeRegisterRange(writer, startRegister, startRegister+regCount-1);
485         }
486     }
487 
writeLiteral(IndentingWriter writer)488     protected void writeLiteral(IndentingWriter writer) throws IOException {
489         LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
490     }
491 
writeCommentIfLikelyFloat(IndentingWriter writer)492     protected void writeCommentIfLikelyFloat(IndentingWriter writer) throws IOException {
493         writeCommentIfLikelyFloat(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
494     }
495 
writeCommentIfLikelyFloat(IndentingWriter writer, int val)496     protected void writeCommentIfLikelyFloat(IndentingWriter writer, int val) throws IOException {
497         if (NumberUtils.isLikelyFloat(val)) {
498             writer.write("    # ");
499             float fval = Float.intBitsToFloat(val);
500             if (fval == Float.POSITIVE_INFINITY)
501                 writer.write("Float.POSITIVE_INFINITY");
502             else if (fval == Float.NEGATIVE_INFINITY)
503                 writer.write("Float.NEGATIVE_INFINITY");
504             else if (fval == Float.NaN)
505                 writer.write("Float.NaN");
506             else if (fval == Float.MAX_VALUE)
507                 writer.write("Float.MAX_VALUE");
508             else if (fval == (float)Math.PI)
509                 writer.write("(float)Math.PI");
510             else if (fval == (float)Math.E)
511                 writer.write("(float)Math.E");
512             else {
513                 writer.write(Float.toString(fval));
514                 writer.write('f');
515             }
516         }
517     }
518 
writeCommentIfLikelyDouble(IndentingWriter writer)519     protected void writeCommentIfLikelyDouble(IndentingWriter writer) throws IOException {
520         writeCommentIfLikelyDouble(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
521     }
522 
writeCommentIfLikelyDouble(IndentingWriter writer, long val)523     protected void writeCommentIfLikelyDouble(IndentingWriter writer, long val) throws IOException {
524         if (NumberUtils.isLikelyDouble(val)) {
525             writer.write("    # ");
526             double dval = Double.longBitsToDouble(val);
527             if (dval == Double.POSITIVE_INFINITY)
528                 writer.write("Double.POSITIVE_INFINITY");
529             else if (dval == Double.NEGATIVE_INFINITY)
530                 writer.write("Double.NEGATIVE_INFINITY");
531             else if (dval == Double.NaN)
532                 writer.write("Double.NaN");
533             else if (dval == Double.MAX_VALUE)
534                 writer.write("Double.MAX_VALUE");
535             else if (dval == Math.PI)
536                 writer.write("Math.PI");
537             else if (dval == Math.E)
538                 writer.write("Math.E");
539             else
540                 writer.write(Double.toString(dval));
541         }
542     }
543 
writeCommentIfResourceId(IndentingWriter writer)544     protected boolean writeCommentIfResourceId(IndentingWriter writer) throws IOException {
545         return writeCommentIfResourceId(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
546     }
547 
writeCommentIfResourceId(IndentingWriter writer, int val)548     protected boolean writeCommentIfResourceId(IndentingWriter writer, int val) throws IOException {
549         Map<Integer,String> resourceIds = methodDef.classDef.options.resourceIds;
550         String resource = resourceIds.get(Integer.valueOf(val));
551         if (resource != null) {
552             writer.write("    # ");
553             writer.write(resource);
554             return true;
555         }
556         return false;
557     }
558 
writeFieldOffset(IndentingWriter writer)559     protected void writeFieldOffset(IndentingWriter writer) throws IOException {
560         writer.write("field@0x");
561         writer.printUnsignedLongAsHex(((FieldOffsetInstruction)instruction).getFieldOffset());
562     }
563 
writeInlineIndex(IndentingWriter writer)564     protected void writeInlineIndex(IndentingWriter writer) throws IOException {
565         writer.write("inline@");
566         writer.printSignedIntAsDec(((InlineIndexInstruction)instruction).getInlineIndex());
567     }
568 
writeVtableIndex(IndentingWriter writer)569     protected void writeVtableIndex(IndentingWriter writer) throws IOException {
570         writer.write("vtable@");
571         writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
572     }
573 }
574