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