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