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 (Float.isNaN(fval)) 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 (Double.isNaN(dval)) 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