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