1 /* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.writer; 33 34 import com.google.common.collect.Ordering; 35 import com.google.common.primitives.Ints; 36 import org.jf.dexlib2.Opcode; 37 import org.jf.dexlib2.Opcodes; 38 import org.jf.dexlib2.ReferenceType; 39 import org.jf.dexlib2.iface.instruction.DualReferenceInstruction; 40 import org.jf.dexlib2.iface.instruction.ReferenceInstruction; 41 import org.jf.dexlib2.iface.instruction.SwitchElement; 42 import org.jf.dexlib2.iface.instruction.formats.*; 43 import org.jf.dexlib2.iface.reference.FieldReference; 44 import org.jf.dexlib2.iface.reference.MethodProtoReference; 45 import org.jf.dexlib2.iface.reference.MethodReference; 46 import org.jf.dexlib2.iface.reference.Reference; 47 import org.jf.dexlib2.iface.reference.StringReference; 48 import org.jf.dexlib2.iface.reference.TypeReference; 49 import org.jf.util.ExceptionWithContext; 50 51 import javax.annotation.Nonnull; 52 import java.io.IOException; 53 import java.util.Comparator; 54 import java.util.List; 55 56 public class InstructionWriter<StringRef extends StringReference, TypeRef extends TypeReference, 57 FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, 58 ProtoRefKey extends MethodProtoReference> { 59 @Nonnull private final Opcodes opcodes; 60 @Nonnull private final DexDataWriter writer; 61 @Nonnull private final StringSection<?, StringRef> stringSection; 62 @Nonnull private final TypeSection<?, ?, TypeRef> typeSection; 63 @Nonnull private final FieldSection<?, ?, FieldRefKey, ?> fieldSection; 64 @Nonnull private final MethodSection<?, ?, ?, MethodRefKey, ?> methodSection; 65 @Nonnull private final ProtoSection<?, ?, ProtoRefKey, ?> protoSection; 66 67 @Nonnull static <StringRef extends StringReference, TypeRef extends TypeReference, FieldRefKey extends FieldReference, 68 MethodRefKey extends MethodReference, ProtoRefKey extends MethodProtoReference> 69 InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey, ProtoRefKey> 70 makeInstructionWriter( 71 @Nonnull Opcodes opcodes, 72 @Nonnull DexDataWriter writer, 73 @Nonnull StringSection<?, StringRef> stringSection, 74 @Nonnull TypeSection<?, ?, TypeRef> typeSection, 75 @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection, 76 @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection, 77 @Nonnull ProtoSection<?, ?, ProtoRefKey, ?> protoSection) { 78 return new InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey, ProtoRefKey>( 79 opcodes, writer, stringSection, typeSection, fieldSection, methodSection, protoSection); 80 } 81 82 InstructionWriter(@Nonnull Opcodes opcodes, 83 @Nonnull DexDataWriter writer, 84 @Nonnull StringSection<?, StringRef> stringSection, 85 @Nonnull TypeSection<?, ?, TypeRef> typeSection, 86 @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection, 87 @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection, 88 @Nonnull ProtoSection<?, ?, ProtoRefKey, ?> protoSection) { 89 this.opcodes = opcodes; 90 this.writer = writer; 91 this.stringSection = stringSection; 92 this.typeSection = typeSection; 93 this.fieldSection = fieldSection; 94 this.methodSection = methodSection; 95 this.protoSection = protoSection; 96 } 97 98 private short getOpcodeValue(Opcode opcode) { 99 Short value = opcodes.getOpcodeValue(opcode); 100 if (value == null) { 101 throw new ExceptionWithContext("Instruction %s is invalid for api %d", opcode.name, opcodes.api); 102 } 103 return value; 104 } 105 106 public void write(@Nonnull Instruction10t instruction) { 107 try { 108 writer.write(getOpcodeValue(instruction.getOpcode())); 109 writer.write(instruction.getCodeOffset()); 110 } catch (IOException ex) { 111 throw new RuntimeException(ex); 112 } 113 } 114 115 public void write(@Nonnull Instruction10x instruction) { 116 try { 117 writer.write(getOpcodeValue(instruction.getOpcode())); 118 writer.write(0); 119 } catch (IOException ex) { 120 throw new RuntimeException(ex); 121 } 122 } 123 124 public void write(@Nonnull Instruction11n instruction) { 125 try { 126 writer.write(getOpcodeValue(instruction.getOpcode())); 127 writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral())); 128 } catch (IOException ex) { 129 throw new RuntimeException(ex); 130 } 131 } 132 133 public void write(@Nonnull Instruction11x instruction) { 134 try { 135 writer.write(getOpcodeValue(instruction.getOpcode())); 136 writer.write(instruction.getRegisterA()); 137 } catch (IOException ex) { 138 throw new RuntimeException(ex); 139 } 140 } 141 142 public void write(@Nonnull Instruction12x instruction) { 143 try { 144 writer.write(getOpcodeValue(instruction.getOpcode())); 145 writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); 146 } catch (IOException ex) { 147 throw new RuntimeException(ex); 148 } 149 } 150 151 public void write(@Nonnull Instruction20bc instruction) { 152 try { 153 writer.write(getOpcodeValue(instruction.getOpcode())); 154 writer.write(instruction.getVerificationError()); 155 writer.writeUshort(getReferenceIndex(instruction)); 156 } catch (IOException ex) { 157 throw new RuntimeException(ex); 158 } 159 } 160 161 public void write(@Nonnull Instruction20t instruction) { 162 try { 163 writer.write(getOpcodeValue(instruction.getOpcode())); 164 writer.write(0); 165 writer.writeShort(instruction.getCodeOffset()); 166 } catch (IOException ex) { 167 throw new RuntimeException(ex); 168 } 169 } 170 171 public void write(@Nonnull Instruction21c instruction) { 172 try { 173 writer.write(getOpcodeValue(instruction.getOpcode())); 174 writer.write(instruction.getRegisterA()); 175 writer.writeUshort(getReferenceIndex(instruction)); 176 } catch (IOException ex) { 177 throw new RuntimeException(ex); 178 } 179 } 180 181 public void write(@Nonnull Instruction21ih instruction) { 182 try { 183 writer.write(getOpcodeValue(instruction.getOpcode())); 184 writer.write(instruction.getRegisterA()); 185 writer.writeShort(instruction.getHatLiteral()); 186 } catch (IOException ex) { 187 throw new RuntimeException(ex); 188 } 189 } 190 191 public void write(@Nonnull Instruction21lh instruction) { 192 try { 193 writer.write(getOpcodeValue(instruction.getOpcode())); 194 writer.write(instruction.getRegisterA()); 195 writer.writeShort(instruction.getHatLiteral()); 196 } catch (IOException ex) { 197 throw new RuntimeException(ex); 198 } 199 } 200 201 public void write(@Nonnull Instruction21s instruction) { 202 try { 203 writer.write(getOpcodeValue(instruction.getOpcode())); 204 writer.write(instruction.getRegisterA()); 205 writer.writeShort(instruction.getNarrowLiteral()); 206 } catch (IOException ex) { 207 throw new RuntimeException(ex); 208 } 209 } 210 211 public void write(@Nonnull Instruction21t instruction) { 212 try { 213 writer.write(getOpcodeValue(instruction.getOpcode())); 214 writer.write(instruction.getRegisterA()); 215 writer.writeShort(instruction.getCodeOffset()); 216 } catch (IOException ex) { 217 throw new RuntimeException(ex); 218 } 219 } 220 221 public void write(@Nonnull Instruction22b instruction) { 222 try { 223 writer.write(getOpcodeValue(instruction.getOpcode())); 224 writer.write(instruction.getRegisterA()); 225 writer.write(instruction.getRegisterB()); 226 writer.write(instruction.getNarrowLiteral()); 227 } catch (IOException ex) { 228 throw new RuntimeException(ex); 229 } 230 } 231 232 public void write(@Nonnull Instruction22c instruction) { 233 try { 234 writer.write(getOpcodeValue(instruction.getOpcode())); 235 writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); 236 writer.writeUshort(getReferenceIndex(instruction)); 237 } catch (IOException ex) { 238 throw new RuntimeException(ex); 239 } 240 } 241 242 public void write(@Nonnull Instruction22cs instruction) { 243 try { 244 writer.write(getOpcodeValue(instruction.getOpcode())); 245 writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); 246 writer.writeUshort(instruction.getFieldOffset()); 247 } catch (IOException ex) { 248 throw new RuntimeException(ex); 249 } 250 } 251 252 public void write(@Nonnull Instruction22s instruction) { 253 try { 254 writer.write(getOpcodeValue(instruction.getOpcode())); 255 writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); 256 writer.writeShort(instruction.getNarrowLiteral()); 257 } catch (IOException ex) { 258 throw new RuntimeException(ex); 259 } 260 } 261 262 public void write(@Nonnull Instruction22t instruction) { 263 try { 264 writer.write(getOpcodeValue(instruction.getOpcode())); 265 writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); 266 writer.writeShort(instruction.getCodeOffset()); 267 } catch (IOException ex) { 268 throw new RuntimeException(ex); 269 } 270 } 271 272 public void write(@Nonnull Instruction22x instruction) { 273 try { 274 writer.write(getOpcodeValue(instruction.getOpcode())); 275 writer.write(instruction.getRegisterA()); 276 writer.writeUshort(instruction.getRegisterB()); 277 } catch (IOException ex) { 278 throw new RuntimeException(ex); 279 } 280 } 281 282 public void write(@Nonnull Instruction23x instruction) { 283 try { 284 writer.write(getOpcodeValue(instruction.getOpcode())); 285 writer.write(instruction.getRegisterA()); 286 writer.write(instruction.getRegisterB()); 287 writer.write(instruction.getRegisterC()); 288 } catch (IOException ex) { 289 throw new RuntimeException(ex); 290 } 291 } 292 293 public void write(@Nonnull Instruction30t instruction) { 294 try { 295 writer.write(getOpcodeValue(instruction.getOpcode())); 296 writer.write(0); 297 writer.writeInt(instruction.getCodeOffset()); 298 } catch (IOException ex) { 299 throw new RuntimeException(ex); 300 } 301 } 302 303 public void write(@Nonnull Instruction31c instruction) { 304 try { 305 writer.write(getOpcodeValue(instruction.getOpcode())); 306 writer.write(instruction.getRegisterA()); 307 writer.writeInt(getReferenceIndex(instruction)); 308 } catch (IOException ex) { 309 throw new RuntimeException(ex); 310 } 311 } 312 313 public void write(@Nonnull Instruction31i instruction) { 314 try { 315 writer.write(getOpcodeValue(instruction.getOpcode())); 316 writer.write(instruction.getRegisterA()); 317 writer.writeInt(instruction.getNarrowLiteral()); 318 } catch (IOException ex) { 319 throw new RuntimeException(ex); 320 } 321 } 322 323 public void write(@Nonnull Instruction31t instruction) { 324 try { 325 writer.write(getOpcodeValue(instruction.getOpcode())); 326 writer.write(instruction.getRegisterA()); 327 writer.writeInt(instruction.getCodeOffset()); 328 } catch (IOException ex) { 329 throw new RuntimeException(ex); 330 } 331 } 332 333 public void write(@Nonnull Instruction32x instruction) { 334 try { 335 writer.write(getOpcodeValue(instruction.getOpcode())); 336 writer.write(0); 337 writer.writeUshort(instruction.getRegisterA()); 338 writer.writeUshort(instruction.getRegisterB()); 339 } catch (IOException ex) { 340 throw new RuntimeException(ex); 341 } 342 } 343 344 public void write(@Nonnull Instruction35c instruction) { 345 try { 346 writer.write(getOpcodeValue(instruction.getOpcode())); 347 writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); 348 writer.writeUshort(getReferenceIndex(instruction)); 349 writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); 350 writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); 351 } catch (IOException ex) { 352 throw new RuntimeException(ex); 353 } 354 } 355 356 public void write(@Nonnull Instruction35mi instruction) { 357 try { 358 writer.write(getOpcodeValue(instruction.getOpcode())); 359 writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); 360 writer.writeUshort(instruction.getInlineIndex()); 361 writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); 362 writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); 363 } catch (IOException ex) { 364 throw new RuntimeException(ex); 365 } 366 } 367 368 public void write(@Nonnull Instruction35ms instruction) { 369 try { 370 writer.write(getOpcodeValue(instruction.getOpcode())); 371 writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); 372 writer.writeUshort(instruction.getVtableIndex()); 373 writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); 374 writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); 375 } catch (IOException ex) { 376 throw new RuntimeException(ex); 377 } 378 } 379 380 public void write(@Nonnull Instruction3rc instruction) { 381 try { 382 writer.write(getOpcodeValue(instruction.getOpcode())); 383 writer.write(instruction.getRegisterCount()); 384 writer.writeUshort(getReferenceIndex(instruction)); 385 writer.writeUshort(instruction.getStartRegister()); 386 } catch (IOException ex) { 387 throw new RuntimeException(ex); 388 } 389 } 390 391 public void write(@Nonnull Instruction3rmi instruction) { 392 try { 393 writer.write(getOpcodeValue(instruction.getOpcode())); 394 writer.write(instruction.getRegisterCount()); 395 writer.writeUshort(instruction.getInlineIndex()); 396 writer.writeUshort(instruction.getStartRegister()); 397 } catch (IOException ex) { 398 throw new RuntimeException(ex); 399 } 400 } 401 402 403 public void write(@Nonnull Instruction3rms instruction) { 404 try { 405 writer.write(getOpcodeValue(instruction.getOpcode())); 406 writer.write(instruction.getRegisterCount()); 407 writer.writeUshort(instruction.getVtableIndex()); 408 writer.writeUshort(instruction.getStartRegister()); 409 } catch (IOException ex) { 410 throw new RuntimeException(ex); 411 } 412 } 413 414 public void write(@Nonnull Instruction45cc instruction) { 415 try { 416 writer.write(getOpcodeValue(instruction.getOpcode())); 417 writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); 418 writer.writeUshort(getReferenceIndex(instruction)); 419 writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); 420 writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); 421 writer.writeUshort(getReference2Index(instruction)); 422 } catch (IOException ex) { 423 throw new RuntimeException(ex); 424 } 425 } 426 427 public void write(@Nonnull Instruction4rcc instruction) { 428 try { 429 writer.write(getOpcodeValue(instruction.getOpcode())); 430 writer.write(instruction.getRegisterCount()); 431 writer.writeUshort(getReferenceIndex(instruction)); 432 writer.writeUshort(instruction.getStartRegister()); 433 writer.writeUshort(getReference2Index(instruction)); 434 } catch (IOException ex) { 435 throw new RuntimeException(ex); 436 } 437 } 438 439 public void write(@Nonnull Instruction51l instruction) { 440 try { 441 writer.write(getOpcodeValue(instruction.getOpcode())); 442 writer.write(instruction.getRegisterA()); 443 writer.writeLong(instruction.getWideLiteral()); 444 } catch (IOException ex) { 445 throw new RuntimeException(ex); 446 } 447 } 448 449 public void write(@Nonnull ArrayPayload instruction) { 450 try { 451 writer.writeUshort(getOpcodeValue(instruction.getOpcode())); 452 writer.writeUshort(instruction.getElementWidth()); 453 List<Number> elements = instruction.getArrayElements(); 454 writer.writeInt(elements.size()); 455 switch (instruction.getElementWidth()) { 456 case 1: 457 for (Number element: elements) { 458 writer.write(element.byteValue()); 459 } 460 break; 461 case 2: 462 for (Number element: elements) { 463 writer.writeShort(element.shortValue()); 464 } 465 break; 466 case 4: 467 for (Number element: elements) { 468 writer.writeInt(element.intValue()); 469 } 470 break; 471 case 8: 472 for (Number element: elements) { 473 writer.writeLong(element.longValue()); 474 } 475 break; 476 } 477 if ((writer.getPosition() & 1) != 0) { 478 writer.write(0); 479 } 480 } catch (IOException ex) { 481 throw new RuntimeException(ex); 482 } 483 } 484 485 public void write(@Nonnull SparseSwitchPayload instruction) { 486 try { 487 writer.writeUbyte(0); 488 writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8); 489 List<? extends SwitchElement> elements = Ordering.from(switchElementComparator).immutableSortedCopy( 490 instruction.getSwitchElements()); 491 writer.writeUshort(elements.size()); 492 for (SwitchElement element: elements) { 493 writer.writeInt(element.getKey()); 494 } 495 for (SwitchElement element: elements) { 496 writer.writeInt(element.getOffset()); 497 } 498 } catch (IOException ex) { 499 throw new RuntimeException(ex); 500 } 501 } 502 503 private final Comparator<SwitchElement> switchElementComparator = new Comparator<SwitchElement>() { 504 @Override public int compare(SwitchElement element1, SwitchElement element2) { 505 return Ints.compare(element1.getKey(), element2.getKey()); 506 } 507 }; 508 509 public void write(@Nonnull PackedSwitchPayload instruction) { 510 try { 511 writer.writeUbyte(0); 512 writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8); 513 List<? extends SwitchElement> elements = instruction.getSwitchElements(); 514 writer.writeUshort(elements.size()); 515 if (elements.size() == 0) { 516 writer.writeInt(0); 517 } else { 518 writer.writeInt(elements.get(0).getKey()); 519 for (SwitchElement element: elements) { 520 writer.writeInt(element.getOffset()); 521 } 522 } 523 } catch (IOException ex) { 524 throw new RuntimeException(ex); 525 } 526 } 527 528 private static int packNibbles(int a, int b) { 529 return (b << 4) | a; 530 } 531 532 private int getReferenceIndex(ReferenceInstruction referenceInstruction) { 533 return getReferenceIndex(referenceInstruction.getReferenceType(), 534 referenceInstruction.getReference()); 535 } 536 537 private int getReference2Index(DualReferenceInstruction referenceInstruction) { 538 return getReferenceIndex(referenceInstruction.getReferenceType2(), 539 referenceInstruction.getReference2()); 540 } 541 542 private int getReferenceIndex(int referenceType, Reference reference) { 543 switch (referenceType) { 544 case ReferenceType.FIELD: 545 return fieldSection.getItemIndex((FieldRefKey) reference); 546 case ReferenceType.METHOD: 547 return methodSection.getItemIndex((MethodRefKey) reference); 548 case ReferenceType.STRING: 549 return stringSection.getItemIndex((StringRef) reference); 550 case ReferenceType.TYPE: 551 return typeSection.getItemIndex((TypeRef) reference); 552 case ReferenceType.METHOD_PROTO: 553 return protoSection.getItemIndex((ProtoRefKey) reference); 554 default: 555 throw new ExceptionWithContext("Unknown reference type: %d", referenceType); 556 } 557 } 558 } 559