1 /* 2 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * An extension of BinaryCode that allows code to be printed. 26 * Includes printing of disassembled byte codes, exception info, 27 * local variable and line number info. 28 * 29 */ 30 31 package ilib; 32 33 import java.io.ByteArrayInputStream; 34 import java.io.CharArrayWriter; 35 import java.io.DataInputStream; 36 import java.io.IOException; 37 import java.io.PrintWriter; 38 import java.io.PrintStream; 39 import java.util.Map; 40 import java.util.HashMap; 41 42 class InjectBytecodes implements RuntimeConstants { 43 44 private final ClassReaderWriter c; 45 private final PrintStream output; 46 private final int length; 47 private final int[] map; 48 private final byte[] widening; 49 private final Injector[] before = new Injector[256]; 50 private final Injector[] after = new Injector[256]; 51 private final String className; 52 private final String methodName; 53 private final Map<Integer,byte[]> snippets = new HashMap<Integer,byte[]>(); 54 55 private int pos; 56 private int newPos; 57 58 private class Span { 59 final int delta; 60 final int target; 61 final int newDelta; 62 final int newTarget; 63 64 Span(int delta) { 65 this.delta = delta; 66 this.target = pos + delta; 67 this.newTarget = map[target]; 68 this.newDelta = newTarget - newPos; 69 } 70 } 71 72 /** 73 * Constructor 74 */ 75 InjectBytecodes(ClassReaderWriter c, int length, 76 String className, String methodName) { 77 this.c = c; 78 this.output = System.out; 79 this.length = length; 80 this.map = new int[length + 1]; 81 this.widening = new byte[length + 1]; 82 this.className = className; 83 this.methodName = methodName; 84 c.markLocalPositionStart(); 85 for (int i = 0; i <= length; ++i) { 86 map[i] = i; 87 } 88 } 89 90 public void inject(int at, byte[] newCode) { 91 snippets.put(new Integer(at), newCode); 92 trace("external "); 93 inject(at, newCode.length); 94 } 95 96 private void inject(int at, int len) { 97 if (Inject.verbose) { 98 traceln("Injecting " + len + " at " + at); 99 } 100 for (int i = at; i <= length; ++i) { 101 map[i] += len; 102 } 103 } 104 105 private void widen(int at, int len) { 106 int delta = len - widening[at]; 107 if (Inject.verbose) { 108 traceln(); 109 traceln("Widening to " + len + " at " + at); 110 } 111 inject(c.localPosition(), delta); // inject at end of instruction 112 widening[at] = (byte)len; // mark at beginning of instruction 113 } 114 115 public void injectBefore(int code, Injector inj) { 116 before[code] = inj; 117 } 118 119 public void injectAfter(int code, Injector inj) { 120 after[code] = inj; 121 } 122 123 private void trace(String str) { 124 if (Inject.verbose) { 125 output.print(str); 126 } 127 } 128 129 private void traceln(String str) { 130 if (Inject.verbose) { 131 output.println(str); 132 } 133 } 134 135 private void traceln() { 136 if (Inject.verbose) { 137 output.println(); 138 } 139 } 140 141 private void trace(int i) { 142 if (Inject.verbose) { 143 output.print(i); 144 } 145 } 146 147 /** 148 * Print an integer so that it takes 'length' characters in 149 * the output. Temporary until formatting code is stable. 150 */ 151 private void traceFixedWidthInt(int x, int length) { 152 if (Inject.verbose) { 153 CharArrayWriter baStream = new CharArrayWriter(); 154 PrintWriter pStream = new PrintWriter(baStream); 155 pStream.print(x); 156 String str = baStream.toString(); 157 for (int cnt = length - str.length(); cnt > 0; --cnt) 158 trace(" "); 159 trace(str); 160 } 161 } 162 163 void adjustOffsets() throws IOException { 164 if (Inject.verbose) { 165 traceln(); 166 traceln("Method " + methodName); 167 traceln(); 168 } 169 c.rewind(); 170 while (c.localPosition() < length) { 171 insertAtInstruction(); 172 } 173 trace("Searching for adjustments..."); 174 c.rewind(); 175 while (c.localPosition() < length) { 176 if (!adjustInstruction()) { 177 c.rewind(); 178 traceln(); 179 traceln("Restarting adjustments after change..."); 180 } 181 } 182 // write the new bytecodes 183 traceln(); 184 traceln(); 185 trace("Writing new code..."); 186 c.rewind(); 187 while (c.localPosition() < length) { 188 writeInstruction(); 189 } 190 if (!snippets.isEmpty()) { 191 throw new Error("not all snippets written"); 192 } 193 } 194 195 /** 196 * Walk one instruction inserting instrumentation at specified instructions 197 */ 198 private void insertAtInstruction() throws IOException { 199 pos = c.localPosition(); 200 int opcode = c.readU1(); 201 if (opcode == opc_wide) { 202 // no support for instrumenting wide instructions 203 int wopcode = c.readU1(); 204 int lvIndex = c.readU2(); 205 switch (wopcode) { 206 case opc_aload: case opc_astore: 207 case opc_fload: case opc_fstore: 208 case opc_iload: case opc_istore: 209 case opc_lload: case opc_lstore: 210 case opc_dload: case opc_dstore: 211 case opc_ret: 212 break; 213 214 case opc_iinc: 215 c.readS2(); 216 break; 217 default: 218 throw new Error("Invalid wide opcode: " + wopcode); 219 } 220 } else { 221 Injector inj; 222 223 inj = before[opcode]; 224 if (inj != null) { 225 inject(pos, inj.bytecodes(className, methodName, pos)); 226 } 227 228 switch (opcode) { 229 case opc_tableswitch:{ 230 int header = (pos+1+3) & (~3); // 4byte boundry 231 c.skip(header - (pos+1)); // skip old padding 232 233 c.readU4(); 234 int low = c.readU4(); 235 int high = c.readU4(); 236 c.skip((high+1-low) * 4); 237 break; 238 } 239 240 case opc_lookupswitch:{ 241 int header = (pos+1+3) & (~3); // 4byte boundry 242 c.skip(header - (pos+1)); // skip padding 243 244 c.readU4(); 245 int npairs = c.readU4(); 246 c.skip(npairs * 8); 247 break; 248 } 249 250 default: { 251 int instrLen = opcLengths[opcode]; 252 c.skip(instrLen-1); 253 } 254 } 255 inj = after[opcode]; 256 if (inj != null) { 257 pos = c.localPosition(); 258 inject(pos, inj.bytecodes(className, methodName, pos)); 259 } 260 } 261 } 262 263 /** 264 * Walk one instruction adjusting for insertions 265 */ 266 private boolean adjustInstruction() throws IOException { 267 pos = c.localPosition(); 268 newPos = map[pos]; 269 int opcode = c.readU1(); 270 if (Inject.verbose) { 271 traceln(); 272 traceFixedWidthInt(pos, 4); 273 traceFixedWidthInt(newPos, 4); 274 trace(" "); 275 } 276 if (opcode == opc_wide) { 277 int wopcode = c.readU1(); 278 int lvIndex = c.readU2(); 279 if (Inject.verbose) { 280 trace(opcNames[wopcode] + "_w "); 281 } 282 switch (wopcode) { 283 case opc_aload: case opc_astore: 284 case opc_fload: case opc_fstore: 285 case opc_iload: case opc_istore: 286 case opc_lload: case opc_lstore: 287 case opc_dload: case opc_dstore: 288 case opc_ret: 289 trace(lvIndex); 290 break; 291 292 case opc_iinc: 293 int constVal = c.readS2(); 294 if (Inject.verbose) { 295 trace(lvIndex + " " + constVal); 296 } 297 break; 298 default: 299 throw new Error("Invalid wide opcode: " + wopcode); 300 } 301 } else { 302 if (Inject.verbose) { 303 trace(opcNames[opcode]); 304 } 305 switch (opcode) { 306 307 case opc_tableswitch:{ 308 int widened = widening[pos]; 309 int header = (pos+1+3) & (~3); // 4byte boundry 310 int newHeader = (newPos+1+3) & (~3); // 4byte boundry 311 312 c.skip(header - (pos+1)); // skip old padding 313 314 Span defaultSkip = new Span(c.readU4()); 315 int low = c.readU4(); 316 int high = c.readU4(); 317 if (Inject.verbose) { 318 trace(" " + low + " to " + high); 319 trace(": default= [was] " + defaultSkip.target); 320 trace(" [now] " + defaultSkip.newTarget); 321 for (int i = low; i <= high; ++i) { 322 Span jump = new Span(c.readU4()); 323 traceln(""); 324 trace('\t'); 325 traceFixedWidthInt(i, 5); 326 trace(": " + jump.newTarget); 327 } 328 } else { 329 c.skip((high+1-low) * 4); 330 } 331 int newPadding = newHeader - newPos; 332 int oldPadding = header - pos; 333 int deltaPadding = newPadding - oldPadding; 334 if (widened != deltaPadding) { 335 widen(pos, deltaPadding); 336 return false; // cause restart 337 } 338 break; 339 } 340 341 case opc_lookupswitch:{ 342 int widened = widening[pos]; 343 int header = (pos+1+3) & (~3); // 4byte boundry 344 int newHeader = (newPos+1+3) & (~3); // 4byte boundry 345 346 c.skip(header - (pos+1)); // skip old padding 347 348 Span defaultSkip = new Span(c.readU4()); 349 int npairs = c.readU4(); 350 if (Inject.verbose) { 351 trace(" npairs: " + npairs); 352 trace(": default= [was] " + defaultSkip.target); 353 trace(" [now] " + defaultSkip.newTarget); 354 for (int i = 0; i< npairs; ++i) { 355 int match = c.readU4(); 356 Span jump = new Span(c.readU4()); 357 traceln(""); 358 trace('\t'); 359 traceFixedWidthInt(match, 5); 360 trace(": " + jump.newTarget); 361 } 362 } else { 363 c.skip(npairs * 8); 364 } 365 int newPadding = newHeader - newPos; 366 int oldPadding = header - pos; 367 int deltaPadding = newPadding - oldPadding; 368 if (widened != deltaPadding) { 369 widen(pos, deltaPadding); 370 return false; // cause restart 371 } 372 break; 373 } 374 375 case opc_jsr: case opc_goto: 376 case opc_ifeq: case opc_ifge: case opc_ifgt: 377 case opc_ifle: case opc_iflt: case opc_ifne: 378 case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: 379 case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: 380 case opc_if_acmpeq: case opc_if_acmpne: 381 case opc_ifnull: case opc_ifnonnull: { 382 int widened = widening[pos]; 383 Span jump = new Span(c.readS2()); 384 if (widened == 0) { // not yet widened 385 int newDelta = jump.newDelta; 386 if ((newDelta < -32768) || (newDelta > 32767)) { 387 switch (opcode) { 388 case opc_jsr: case opc_goto: 389 widen(pos, 2); // will convert to wide 390 break; 391 default: 392 widen(pos, 5); // will inject goto_w 393 break; 394 } 395 return false; // cause restart 396 } 397 } 398 if (Inject.verbose) { 399 trace(" [was] " + jump.target + " ==> " + 400 " [now] " + jump.newTarget); 401 } 402 break; 403 } 404 405 case opc_jsr_w: 406 case opc_goto_w: { 407 Span jump = new Span(c.readU4()); 408 if (Inject.verbose) { 409 trace(" [was] " + jump.target + 410 " [now] " + jump.newTarget); 411 } 412 break; 413 } 414 415 default: { 416 int instrLen = opcLengths[opcode]; 417 c.skip(instrLen-1); 418 break; 419 } 420 } 421 } 422 return true; // successful return 423 } 424 425 426 /** 427 * Walk one instruction writing the transformed instruction. 428 */ 429 private void writeInstruction() throws IOException { 430 pos = c.localPosition(); 431 newPos = map[pos]; 432 byte[] newCode = snippets.remove(new Integer(pos)); 433 if (newCode != null) { 434 traceln(); 435 traceFixedWidthInt(pos, 4); 436 trace(" ... -- Inserting new code"); 437 c.writeBytes(newCode); 438 } 439 int opcode = c.readU1(); 440 if (Inject.verbose) { 441 traceln(); 442 traceFixedWidthInt(pos, 4); 443 traceFixedWidthInt(newPos, 4); 444 trace(" "); 445 } 446 if (opcode == opc_wide) { 447 int wopcode = c.readU1(); 448 int lvIndex = c.readU2(); 449 if (Inject.verbose) { 450 trace(opcNames[wopcode] + "_w "); 451 } 452 c.writeU1(opcode); 453 c.writeU1(wopcode); 454 c.writeU2(lvIndex); 455 switch (wopcode) { 456 case opc_aload: case opc_astore: 457 case opc_fload: case opc_fstore: 458 case opc_iload: case opc_istore: 459 case opc_lload: case opc_lstore: 460 case opc_dload: case opc_dstore: 461 case opc_ret: 462 trace(lvIndex); 463 break; 464 465 case opc_iinc: 466 int constVal = c.readS2(); 467 c.writeU2(constVal); // ??? U vs S 468 if (Inject.verbose) { 469 trace(lvIndex + " " + constVal); 470 } 471 break; 472 default: 473 throw new Error("Invalid wide opcode: " + wopcode); 474 } 475 } else { 476 if (Inject.verbose) { 477 trace(opcNames[opcode]); 478 } 479 switch (opcode) { 480 481 case opc_tableswitch:{ 482 int header = (pos+1+3) & (~3); // 4byte boundry 483 int newHeader = (newPos+1+3) & (~3); // 4byte boundry 484 485 c.skip(header - (pos+1)); // skip old padding 486 487 Span defaultSkip = new Span(c.readU4()); 488 int low = c.readU4(); 489 int high = c.readU4(); 490 491 c.writeU1(opcode); // copy instruction 492 for (int i = newPos+1; i < newHeader; ++i) { 493 c.writeU1(0); // write new padding 494 } 495 c.writeU4(defaultSkip.newDelta); 496 c.writeU4(low); 497 c.writeU4(high); 498 499 if (Inject.verbose) { 500 trace(" " + low + " to " + high); 501 trace(": default= [was] " + defaultSkip.target); 502 trace(" [now] " + defaultSkip.newTarget); 503 } 504 for (int i = low; i <= high; ++i) { 505 Span jump = new Span(c.readU4()); 506 c.writeU4(jump.newDelta); 507 if (Inject.verbose) { 508 traceln(""); 509 trace('\t'); 510 traceFixedWidthInt(i, 5); 511 trace(": " + jump.newTarget); 512 } 513 } 514 break; 515 } 516 517 case opc_lookupswitch:{ 518 int header = (pos+1+3) & (~3); // 4byte boundry 519 int newHeader = (newPos+1+3) & (~3); // 4byte boundry 520 521 c.skip(header - (pos+1)); // skip old padding 522 523 Span defaultSkip = new Span(c.readU4()); 524 int npairs = c.readU4(); 525 if (Inject.verbose) { 526 trace(" npairs: " + npairs); 527 trace(": default= [was] " + defaultSkip.target); 528 trace(" [now] " + defaultSkip.newTarget); 529 } 530 c.writeU1(opcode); // copy instruction 531 for (int i = newPos+1; i < newHeader; ++i) { 532 c.writeU1(0); // write new padding 533 } 534 c.writeU4(defaultSkip.newDelta); 535 c.writeU4(npairs); 536 for (int i = 0; i< npairs; ++i) { 537 int match = c.readU4(); 538 Span jump = new Span(c.readU4()); 539 c.writeU4(match); 540 c.writeU4(jump.newDelta); 541 if (Inject.verbose) { 542 traceln(""); 543 trace('\t'); 544 traceFixedWidthInt(match, 5); 545 trace(": " + jump.newTarget); 546 } 547 } 548 break; 549 } 550 551 case opc_jsr: case opc_goto: 552 case opc_ifeq: case opc_ifge: case opc_ifgt: 553 case opc_ifle: case opc_iflt: case opc_ifne: 554 case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: 555 case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: 556 case opc_if_acmpeq: case opc_if_acmpne: 557 case opc_ifnull: case opc_ifnonnull: { 558 int widened = widening[pos]; 559 Span jump = new Span(c.readS2()); 560 int newOpcode = opcode; // default to unchanged 561 if (widened == 0) { // not widened 562 c.writeU1(opcode); // rewrite instruction 563 c.writeU2(jump.newDelta); 564 } else if (widened == 2) { // wide form 565 switch (opcode) { 566 case opc_jsr: 567 newOpcode = opc_jsr_w; 568 break; 569 case opc_goto: 570 newOpcode = opc_jsr_w; 571 break; 572 default: 573 throw new Error("unexpected opcode: " + 574 opcode); 575 } 576 c.writeU1(newOpcode); // write wide instruction 577 c.writeU4(jump.newDelta); // write new and wide delta 578 } else if (widened == 5) { // insert goto_w 579 switch (opcode) { 580 case opc_ifeq: 581 newOpcode = opc_ifne; 582 break; 583 case opc_ifge: 584 newOpcode = opc_iflt; 585 break; 586 case opc_ifgt: 587 newOpcode = opc_ifle; 588 break; 589 case opc_ifle: 590 newOpcode = opc_ifgt; 591 break; 592 case opc_iflt: 593 newOpcode = opc_ifge; 594 break; 595 case opc_ifne: 596 newOpcode = opc_ifeq; 597 break; 598 case opc_if_icmpeq: 599 newOpcode = opc_if_icmpne; 600 break; 601 case opc_if_icmpne: 602 newOpcode = opc_if_icmpeq; 603 break; 604 case opc_if_icmpge: 605 newOpcode = opc_if_icmplt; 606 break; 607 case opc_if_icmpgt: 608 newOpcode = opc_if_icmple; 609 break; 610 case opc_if_icmple: 611 newOpcode = opc_if_icmpgt; 612 break; 613 case opc_if_icmplt: 614 newOpcode = opc_if_icmpge; 615 break; 616 case opc_if_acmpeq: 617 newOpcode = opc_if_acmpne; 618 break; 619 case opc_if_acmpne: 620 newOpcode = opc_if_acmpeq; 621 break; 622 case opc_ifnull: 623 newOpcode = opc_ifnonnull; 624 break; 625 case opc_ifnonnull: 626 newOpcode = opc_ifnull; 627 break; 628 default: 629 throw new Error("unexpected opcode: " + 630 opcode); 631 } 632 c.writeU1(newOpcode); // write inverse branch 633 c.writeU2(3 + 5); // beyond if and goto_w 634 c.writeU1(opc_goto_w);// add a goto_w 635 c.writeU4(jump.newDelta); // write new and wide delta 636 } else { 637 throw new Error("unexpected widening"); 638 } 639 640 if (Inject.verbose) { 641 trace(" [was] " + jump.target + " ==> " + 642 opcNames[newOpcode] + 643 " [now] " + jump.newTarget); 644 } 645 break; 646 } 647 648 case opc_jsr_w: 649 case opc_goto_w: { 650 Span jump = new Span(c.readU4()); 651 c.writeU1(opcode); // instruction itself 652 c.writeU4(jump.newDelta); 653 if (Inject.verbose) { 654 trace(" [was] " + jump.target + 655 " [now] " + jump.newTarget); 656 } 657 break; 658 } 659 660 default: { 661 int instrLen = opcLengths[opcode]; 662 c.writeU1(opcode); // instruction itself 663 c.copy(instrLen-1); 664 } 665 } 666 } 667 } 668 669 /** 670 * Copy the exception table for this method code 671 */ 672 void copyExceptionTable() throws IOException { 673 int tableLength = c.copyU2(); // exception table len 674 if (tableLength > 0) { 675 traceln(); 676 traceln("Exception table:"); 677 traceln(" from:old/new to:old/new target:old/new type"); 678 for (int tcnt = tableLength; tcnt > 0; --tcnt) { 679 int startPC = c.readU2(); 680 int newStartPC = map[startPC]; 681 c.writeU2(newStartPC); 682 int endPC = c.readU2(); 683 int newEndPC = map[endPC]; 684 c.writeU2(newEndPC); 685 int handlerPC = c.readU2(); 686 int newHandlerPC = map[handlerPC]; 687 c.writeU2(newHandlerPC); 688 int catchType = c.copyU2(); 689 if (Inject.verbose) { 690 traceFixedWidthInt(startPC, 6); 691 traceFixedWidthInt(newStartPC, 6); 692 traceFixedWidthInt(endPC, 6); 693 traceFixedWidthInt(newEndPC, 6); 694 traceFixedWidthInt(handlerPC, 6); 695 traceFixedWidthInt(newHandlerPC, 6); 696 trace(" "); 697 if (catchType == 0) 698 traceln("any"); 699 else { 700 traceln("" + catchType); 701 } 702 } 703 } 704 } 705 } 706 707 /** 708 * Copy the line number table for this method code 709 */ 710 void copyLineNumberAttr() throws IOException { 711 // name index already read 712 c.copy(4); // attr len 713 int tableLength = c.copyU2(); // line table len 714 if (tableLength > 0) { 715 if (Inject.verbose) { 716 traceln(); 717 traceln("Line numbers for method " + methodName); 718 } 719 for (int tcnt = tableLength; tcnt > 0; --tcnt) { 720 int startPC = c.readU2(); 721 int newStartPC = map[startPC]; 722 c.writeU2(newStartPC); 723 int lineNumber = c.copyU2(); 724 if (Inject.verbose) { 725 traceln(" line " + lineNumber + 726 ": [was] " + startPC + 727 " [now] " + newStartPC); 728 } 729 } 730 } 731 } 732 733 /** 734 * Copy the local variable table for this method code 735 */ 736 void copyLocalVarAttr() throws IOException { 737 // name index already read 738 c.copy(4); // attr len 739 int tableLength = c.copyU2(); // local var table len 740 if (tableLength > 0) { 741 if (Inject.verbose) { 742 traceln(); 743 traceln("Local variables for method " + methodName); 744 } 745 for (int tcnt = tableLength; tcnt > 0; --tcnt) { 746 int startPC = c.readU2(); 747 int newStartPC = map[startPC]; 748 c.writeU2(newStartPC); 749 int length = c.readU2(); 750 int endPC = startPC + length; 751 int newEndPC = map[endPC]; 752 int newLength = newEndPC - newStartPC; 753 c.writeU2(newLength); 754 int nameIndex = c.copyU2(); 755 int descriptorIndex = c.copyU2(); 756 int index = c.copyU2(); 757 if (Inject.verbose) { 758 trace(" "); 759 trace(descriptorIndex); 760 trace(" "); 761 trace(nameIndex); 762 traceln(" pc= [was] " + startPC + " [now] " + newStartPC + 763 ", length= [was] " + length + " [now] " + newLength + 764 ", slot=" + index); 765 } 766 } 767 } 768 } 769 }