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 }