1 /* 2 * Copyright (c) 2007, 2019, 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 package nsk.share.jdi.sde; 24 25 import java.io.File; 26 import java.io.FileInputStream; 27 import java.io.FileOutputStream; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.UnsupportedEncodingException; 31 32 import nsk.share.Consts; 33 34 /* 35 * Inserts in class file 'SourceDebugExtension' attribute based on input .SMAP file. 36 */ 37 public class InstallSDE { 38 static final String nameSDE = "SourceDebugExtension"; 39 40 private byte[] orig; 41 42 private byte[] sdeAttr; 43 44 private byte[] gen; 45 46 private int origPos = 0; 47 48 private int genPos = 0; 49 50 private int sdeIndex; 51 install(File inClassFile, File smapFile, File outClassFile, boolean verbose)52 public static void install(File inClassFile, File smapFile, File outClassFile, boolean verbose) throws IOException { 53 new InstallSDE(inClassFile, smapFile, outClassFile, verbose); 54 } 55 install(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose)56 public static void install(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose) throws IOException { 57 new InstallSDE(aOrig, aSdeAttr, outClassFile, verbose); 58 } 59 install(File inOutClassFile, File attrFile, boolean verbose)60 public static void install(File inOutClassFile, File attrFile, boolean verbose) throws IOException { 61 File tmpFile = new File(inOutClassFile.getPath() + "tmp-out"); 62 File tmpInOutClassFile = new File(inOutClassFile.getPath() + "tmp-in"); 63 64 // Workaround delayed file deletes on Windows using a tmp file name 65 if (!inOutClassFile.renameTo(tmpInOutClassFile)) { 66 throw new IOException("inOutClassFile.renameTo(tmpInOutClassFile) failed"); 67 } 68 69 new InstallSDE(tmpInOutClassFile, attrFile, tmpFile, verbose); 70 71 if (!tmpInOutClassFile.delete()) { 72 throw new IOException("tmpInOutClassFile.delete() failed"); 73 } 74 if (!tmpFile.renameTo(inOutClassFile)) { 75 throw new IOException("tmpFile.renameTo(inOutClassFile) failed"); 76 } 77 } 78 abort(String msg)79 private static void abort(String msg) { 80 System.err.println(msg); 81 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); 82 } 83 InstallSDE(File inClassFile, File attrFile, File outClassFile, boolean verbose)84 private InstallSDE(File inClassFile, File attrFile, File outClassFile, boolean verbose) throws IOException { 85 if (!inClassFile.exists()) { 86 abort("no such file: " + inClassFile); 87 } 88 if (!attrFile.exists()) { 89 abort("no such file: " + attrFile); 90 } 91 92 // get the bytes 93 orig = readWhole(inClassFile); 94 sdeAttr = readWhole(attrFile); 95 gen = new byte[orig.length + sdeAttr.length + 100]; 96 97 // do it 98 addSDE(verbose); 99 100 // write result 101 FileOutputStream outStream = new FileOutputStream(outClassFile); 102 outStream.write(gen, 0, genPos); 103 outStream.close(); 104 } 105 InstallSDE(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose)106 private InstallSDE(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose) throws IOException { 107 orig = aOrig; 108 sdeAttr = aSdeAttr; 109 gen = new byte[orig.length + sdeAttr.length + 100]; 110 111 // do it 112 addSDE(verbose); 113 114 // write result 115 FileOutputStream outStream = new FileOutputStream(outClassFile); 116 outStream.write(gen, 0, genPos); 117 outStream.close(); 118 } 119 readWhole(File input)120 private byte[] readWhole(File input) throws IOException { 121 FileInputStream inStream = new FileInputStream(input); 122 try { 123 return readWhole(inStream, (int) input.length()); 124 } finally { 125 inStream.close(); 126 } 127 } 128 readWhole(InputStream inStream, int len)129 private byte[] readWhole(InputStream inStream, int len) throws IOException { 130 byte[] bytes = new byte[len]; 131 132 if (inStream.read(bytes, 0, len) != len) { 133 abort("expected size: " + len); 134 } 135 136 return bytes; 137 } 138 addSDE(boolean verbose)139 private void addSDE(boolean verbose) throws UnsupportedEncodingException { 140 copy(4 + 2 + 2); // magic min/maj version 141 int constantPoolCountPos = genPos; 142 int constantPoolCount = readU2(); 143 writeU2(constantPoolCount); 144 // copy old constant pool return index of SDE symbol, if found 145 sdeIndex = copyConstantPool(constantPoolCount, verbose); 146 if (sdeIndex < 0) { 147 // if "SourceDebugExtension" symbol not there add it 148 writeUtf8ForSDE(); 149 150 // increment the countantPoolCount 151 sdeIndex = constantPoolCount; 152 ++constantPoolCount; 153 randomAccessWriteU2(constantPoolCountPos, constantPoolCount); 154 155 if (verbose) { 156 System.out.println("SourceDebugExtension not found, installed at: " + sdeIndex); 157 } 158 } else { 159 if (verbose) { 160 System.out.println("SourceDebugExtension found at: " + sdeIndex); 161 } 162 } 163 copy(2 + 2 + 2); // access, this, super 164 int interfaceCount = readU2(); 165 writeU2(interfaceCount); 166 if (verbose) { 167 System.out.println("interfaceCount: " + interfaceCount); 168 } 169 copy(interfaceCount * 2); 170 copyMembers(verbose); // fields 171 copyMembers(verbose); // methods 172 int attrCountPos = genPos; 173 int attrCount = readU2(); 174 writeU2(attrCount); 175 if (verbose) { 176 System.out.println("class attrCount: " + attrCount); 177 } 178 // copy the class attributes, return true if SDE attr found (not copied) 179 if (!copyAttrs(attrCount, verbose)) { 180 // we will be adding SDE and it isn't already counted 181 ++attrCount; 182 randomAccessWriteU2(attrCountPos, attrCount); 183 if (verbose) { 184 System.out.println("class attrCount incremented"); 185 } 186 } 187 writeAttrForSDE(sdeIndex); 188 } 189 copyMembers(boolean verbose)190 private void copyMembers(boolean verbose) { 191 int count = readU2(); 192 writeU2(count); 193 if (verbose) { 194 System.out.println("members count: " + count); 195 } 196 for (int i = 0; i < count; ++i) { 197 copy(6); // access, name, descriptor 198 int attrCount = readU2(); 199 writeU2(attrCount); 200 if (verbose) { 201 System.out.println("member attr count: " + attrCount); 202 } 203 copyAttrs(attrCount, verbose); 204 } 205 } 206 copyAttrs(int attrCount, boolean verbose)207 private boolean copyAttrs(int attrCount, boolean verbose) { 208 boolean sdeFound = false; 209 for (int i = 0; i < attrCount; ++i) { 210 int nameIndex = readU2(); 211 // don't write old SDE 212 if (nameIndex == sdeIndex) { 213 sdeFound = true; 214 if (verbose) { 215 System.out.println("SDE attr found"); 216 } 217 } else { 218 writeU2(nameIndex); // name 219 int len = readU4(); 220 writeU4(len); 221 copy(len); 222 if (verbose) { 223 System.out.println("attr len: " + len); 224 } 225 } 226 } 227 return sdeFound; 228 } 229 writeAttrForSDE(int index)230 private void writeAttrForSDE(int index) { 231 writeU2(index); 232 writeU4(sdeAttr.length); 233 for (int i = 0; i < sdeAttr.length; ++i) { 234 writeU1(sdeAttr[i]); 235 } 236 } 237 randomAccessWriteU2(int pos, int val)238 private void randomAccessWriteU2(int pos, int val) { 239 int savePos = genPos; 240 genPos = pos; 241 writeU2(val); 242 genPos = savePos; 243 } 244 readU1()245 private int readU1() { 246 return ((int) orig[origPos++]) & 0xFF; 247 } 248 readU2()249 private int readU2() { 250 int res = readU1(); 251 return (res << 8) + readU1(); 252 } 253 readU4()254 private int readU4() { 255 int res = readU2(); 256 return (res << 16) + readU2(); 257 } 258 writeU1(int val)259 private void writeU1(int val) { 260 gen[genPos++] = (byte) val; 261 } 262 writeU2(int val)263 private void writeU2(int val) { 264 writeU1(val >> 8); 265 writeU1(val & 0xFF); 266 } 267 writeU4(int val)268 private void writeU4(int val) { 269 writeU2(val >> 16); 270 writeU2(val & 0xFFFF); 271 } 272 copy(int count)273 private void copy(int count) { 274 for (int i = 0; i < count; ++i) { 275 gen[genPos++] = orig[origPos++]; 276 } 277 } 278 readBytes(int count)279 private byte[] readBytes(int count) { 280 byte[] bytes = new byte[count]; 281 for (int i = 0; i < count; ++i) { 282 bytes[i] = orig[origPos++]; 283 } 284 return bytes; 285 } 286 writeBytes(byte[] bytes)287 private void writeBytes(byte[] bytes) { 288 for (int i = 0; i < bytes.length; ++i) { 289 gen[genPos++] = bytes[i]; 290 } 291 } 292 copyConstantPool(int constantPoolCount, boolean verbose)293 private int copyConstantPool(int constantPoolCount, boolean verbose) throws UnsupportedEncodingException { 294 int sdeIndex = -1; 295 // copy const pool index zero not in class file 296 if ( verbose ) 297 System.out.println("cp count=" + constantPoolCount); 298 for (int i = 1; i < constantPoolCount; ++i) { 299 int tag = readU1(); 300 writeU1(tag); 301 if ( verbose ) 302 System.out.println(i + ": tag=" + tag); 303 switch (tag) { 304 case 7: // Class 305 case 8: // String 306 case 16: // MethodType 307 copy(2); 308 break; 309 case 15: // MethodHandle 310 copy(3); 311 break; 312 case 9: // Field 313 case 10: // Method 314 case 11: // InterfaceMethod 315 case 3: // Integer 316 case 4: // Float 317 case 12: // NameAndType 318 case 17: // InvokeDynamic_17 (will go away) 319 case 18: // InokeDynamic 320 copy(4); 321 break; 322 case 5: // Long 323 case 6: // Double 324 copy(8); 325 ++i; 326 break; 327 case 1: // Utf8 328 int len = readU2(); 329 writeU2(len); 330 byte[] utf8 = readBytes(len); 331 String str = new String(utf8, "UTF-8"); 332 if (verbose) { 333 System.out.println(i + " read class attr -- '" + str + "'"); 334 } 335 if (str.equals(nameSDE)) { 336 sdeIndex = i; 337 } 338 writeBytes(utf8); 339 break; 340 default: 341 abort("unexpected tag: " + tag); 342 break; 343 } 344 } 345 return sdeIndex; 346 } 347 writeUtf8ForSDE()348 private void writeUtf8ForSDE() { 349 int len = nameSDE.length(); 350 writeU1(1); // Utf8 tag 351 writeU2(len); 352 for (int i = 0; i < len; ++i) { 353 writeU1(nameSDE.charAt(i)); 354 } 355 } 356 } 357