1 /*
  2  * Copyright (c) 2014, 2021, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.internal.jimage;
 27 
 28 import java.nio.ByteBuffer;
 29 import java.util.Objects;
 30 
 31 /**
 32  * @implNote This class needs to maintain JDK 8 source compatibility.
 33  *
 34  * It is used internally in the JDK to implement jimage/jrtfs access,
 35  * but also compiled and delivered as part of the jrtfs.jar to support access
 36  * to the jimage file provided by the shipped JDK by tools running on JDK 8.
 37  */
 38 public class ImageLocation {
 39     public static final int ATTRIBUTE_END = 0;
 40     public static final int ATTRIBUTE_MODULE = 1;
 41     public static final int ATTRIBUTE_PARENT = 2;
 42     public static final int ATTRIBUTE_BASE = 3;
 43     public static final int ATTRIBUTE_EXTENSION = 4;
 44     public static final int ATTRIBUTE_OFFSET = 5;
 45     public static final int ATTRIBUTE_COMPRESSED = 6;
 46     public static final int ATTRIBUTE_UNCOMPRESSED = 7;
 47     public static final int ATTRIBUTE_COUNT = 8;
 48 
 49     protected final long[] attributes;
 50 
 51     protected final ImageStrings strings;
 52 
 53     public ImageLocation(long[] attributes, ImageStrings strings) {
 54         this.attributes = Objects.requireNonNull(attributes);
 55         this.strings = Objects.requireNonNull(strings);
 56     }
 57 
 58     ImageStrings getStrings() {
 59         return strings;
 60     }
 61 
 62     static long[] decompress(ByteBuffer bytes, int offset) {
 63         Objects.requireNonNull(bytes);
 64         long[] attributes = new long[ATTRIBUTE_COUNT];
 65 
 66         int limit = bytes.limit();
 67         while (offset < limit) {
 68             int data = bytes.get(offset++) & 0xFF;
 69             if (data <= 0x7) { // ATTRIBUTE_END
 70                 break;
 71             }
 72             int kind = data >>> 3;
 73             if (ATTRIBUTE_COUNT <= kind) {
 74                 throw new InternalError(
 75                     "Invalid jimage attribute kind: " + kind);
 76             }
 77 
 78             int length = (data & 0x7) + 1;
 79             attributes[kind] = readValue(length, bytes, offset, limit);
 80             offset += length;
 81         }
 82         return attributes;
 83     }
 84 
 85     public static byte[] compress(long[] attributes) {
 86         Objects.requireNonNull(attributes);
 87         ImageStream stream = new ImageStream(16);
 88 
 89         for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) {
 90             long value = attributes[kind];
 91 
 92             if (value != 0) {
 93                 int n = (63 - Long.numberOfLeadingZeros(value)) >> 3;
 94                 stream.put((kind << 3) | n);
 95 
 96                 for (int i = n; i >= 0; i--) {
 97                     stream.put((int)(value >> (i << 3)));
 98                 }
 99             }
100         }
101 
102         stream.put(ATTRIBUTE_END << 3);
103 
104         return stream.toArray();
105      }
106 
107     public boolean verify(String name) {
108         return verify(name, attributes, strings);
109     }
110 
111     /**
112      * A simpler verification would be {@code name.equals(getFullName())}, but
113      * by not creating the full name and enabling early returns we allocate
114      * fewer objects.
115      */
116     static boolean verify(String name, long[] attributes, ImageStrings strings) {
117         Objects.requireNonNull(name);
118         final int length = name.length();
119         int index = 0;
120         int moduleOffset = (int)attributes[ATTRIBUTE_MODULE];
121         if (moduleOffset != 0 && length >= 1) {
122             int moduleLen = strings.match(moduleOffset, name, 1);
123             index = moduleLen + 1;
124             if (moduleLen < 0
125                     || length <= index
126                     || name.charAt(0) != '/'
127                     || name.charAt(index++) != '/') {
128                 return false;
129             }
130         }
131         return verifyName(null, name, index, length, 0,
132                 (int) attributes[ATTRIBUTE_PARENT],
133                 (int) attributes[ATTRIBUTE_BASE],
134                 (int) attributes[ATTRIBUTE_EXTENSION],
135                 strings);
136     }
137 
138     static boolean verify(String module, String name, ByteBuffer locations,
139                           int locationOffset, ImageStrings strings) {
140         int moduleOffset = 0;
141         int parentOffset = 0;
142         int baseOffset = 0;
143         int extOffset = 0;
144 
145         int limit = locations.limit();
146         while (locationOffset < limit) {
147             int data = locations.get(locationOffset++) & 0xFF;
148             if (data <= 0x7) { // ATTRIBUTE_END
149                 break;
150             }
151             int kind = data >>> 3;
152             if (ATTRIBUTE_COUNT <= kind) {
153                 throw new InternalError(
154                         "Invalid jimage attribute kind: " + kind);
155             }
156 
157             int length = (data & 0x7) + 1;
158             switch (kind) {
159                 case ATTRIBUTE_MODULE:
160                     moduleOffset = (int) readValue(length, locations, locationOffset, limit);
161                     break;
162                 case ATTRIBUTE_BASE:
163                     baseOffset = (int) readValue(length, locations, locationOffset, limit);
164                     break;
165                 case ATTRIBUTE_PARENT:
166                     parentOffset = (int) readValue(length, locations, locationOffset, limit);
167                     break;
168                 case ATTRIBUTE_EXTENSION:
169                     extOffset = (int) readValue(length, locations, locationOffset, limit);
170                     break;
171             }
172             locationOffset += length;
173         }
174         return verifyName(module, name, 0, name.length(),
175                 moduleOffset, parentOffset, baseOffset, extOffset, strings);
176     }
177 
178     private static long readValue(int length, ByteBuffer buffer, int offset, int limit) {
179         long value = 0;
180         for (int j = 0; j < length; j++) {
181             value <<= 8;
182             if (offset >= limit) {
183                 throw new InternalError("Missing jimage attribute data");
184             }
185             value |= buffer.get(offset++) & 0xFF;
186         }
187         return value;
188     }
189 
190     static boolean verify(String module, String name, long[] attributes,
191             ImageStrings strings) {
192         Objects.requireNonNull(module);
193         Objects.requireNonNull(name);
194         return verifyName(module, name, 0, name.length(),
195                 (int) attributes[ATTRIBUTE_MODULE],
196                 (int) attributes[ATTRIBUTE_PARENT],
197                 (int) attributes[ATTRIBUTE_BASE],
198                 (int) attributes[ATTRIBUTE_EXTENSION],
199                 strings);
200     }
201 
202     private static boolean verifyName(String module, String name, int index, int length,
203             int moduleOffset, int parentOffset, int baseOffset, int extOffset, ImageStrings strings) {
204 
205         if (moduleOffset != 0) {
206             if (strings.match(moduleOffset, module, 0) != module.length()) {
207                 return false;
208             }
209         }
210         if (parentOffset != 0) {
211             int parentLen = strings.match(parentOffset, name, index);
212             if (parentLen < 0) {
213                 return false;
214             }
215             index += parentLen;
216             if (length <= index || name.charAt(index++) != '/') {
217                 return false;
218             }
219         }
220         int baseLen = strings.match(baseOffset, name, index);
221         if (baseLen < 0) {
222             return false;
223         }
224         index += baseLen;
225         if (extOffset != 0) {
226             if (length <= index
227                     || name.charAt(index++) != '.') {
228                 return false;
229             }
230 
231             int extLen = strings.match(extOffset, name, index);
232             if (extLen < 0) {
233                 return false;
234             }
235             index += extLen;
236         }
237         return length == index;
238     }
239 
240     long getAttribute(int kind) {
241         if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) {
242             throw new InternalError(
243                 "Invalid jimage attribute kind: " + kind);
244         }
245         return attributes[kind];
246     }
247 
248     String getAttributeString(int kind) {
249         if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) {
250             throw new InternalError(
251                 "Invalid jimage attribute kind: " + kind);
252         }
253         return getStrings().get((int)attributes[kind]);
254     }
255 
256     public String getModule() {
257         return getAttributeString(ATTRIBUTE_MODULE);
258     }
259 
260     public int getModuleOffset() {
261         return (int)getAttribute(ATTRIBUTE_MODULE);
262     }
263 
264     public String getBase() {
265         return getAttributeString(ATTRIBUTE_BASE);
266     }
267 
268     public int getBaseOffset() {
269         return (int)getAttribute(ATTRIBUTE_BASE);
270     }
271 
272     public String getParent() {
273         return getAttributeString(ATTRIBUTE_PARENT);
274     }
275 
276     public int getParentOffset() {
277         return (int)getAttribute(ATTRIBUTE_PARENT);
278     }
279 
280     public String getExtension() {
281         return getAttributeString(ATTRIBUTE_EXTENSION);
282     }
283 
284     public int getExtensionOffset() {
285         return (int)getAttribute(ATTRIBUTE_EXTENSION);
286     }
287 
288     public String getFullName() {
289         return getFullName(false);
290     }
291 
292     public String getFullName(boolean modulesPrefix) {
293         StringBuilder builder = new StringBuilder();
294 
295         if (getModuleOffset() != 0) {
296             if (modulesPrefix) {
297                 builder.append("/modules");
298             }
299 
300             builder.append('/');
301             builder.append(getModule());
302             builder.append('/');
303         }
304 
305         if (getParentOffset() != 0) {
306             builder.append(getParent());
307             builder.append('/');
308         }
309 
310         builder.append(getBase());
311 
312         if (getExtensionOffset() != 0) {
313             builder.append('.');
314             builder.append(getExtension());
315         }
316 
317         return builder.toString();
318     }
319 
320     String buildName(boolean includeModule, boolean includeParent,
321             boolean includeName) {
322         StringBuilder builder = new StringBuilder();
323 
324         if (includeModule && getModuleOffset() != 0) {
325             builder.append("/modules/");
326             builder.append(getModule());
327          }
328 
329         if (includeParent && getParentOffset() != 0) {
330             builder.append('/');
331             builder.append(getParent());
332         }
333 
334         if (includeName) {
335             if (includeModule || includeParent) {
336                 builder.append('/');
337             }
338 
339             builder.append(getBase());
340 
341             if (getExtensionOffset() != 0) {
342                 builder.append('.');
343                 builder.append(getExtension());
344             }
345         }
346 
347         return builder.toString();
348    }
349 
350     public long getContentOffset() {
351         return getAttribute(ATTRIBUTE_OFFSET);
352     }
353 
354     public long getCompressedSize() {
355         return getAttribute(ATTRIBUTE_COMPRESSED);
356     }
357 
358     public long getUncompressedSize() {
359         return getAttribute(ATTRIBUTE_UNCOMPRESSED);
360     }
361 
362     static ImageLocation readFrom(BasicImageReader reader, int offset) {
363         Objects.requireNonNull(reader);
364         long[] attributes = reader.getAttributes(offset);
365         ImageStringsReader strings = reader.getStrings();
366 
367         return new ImageLocation(attributes, strings);
368     }
369 }