Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java
41159 views
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
package jdk.internal.jimage;
26
27
import java.io.ByteArrayInputStream;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.lang.reflect.InvocationTargetException;
31
import java.lang.reflect.Method;
32
import java.nio.ByteBuffer;
33
import java.nio.ByteOrder;
34
import java.nio.IntBuffer;
35
import java.nio.channels.FileChannel;
36
import java.nio.file.Path;
37
import java.nio.file.StandardOpenOption;
38
import java.security.AccessController;
39
import java.security.PrivilegedAction;
40
import java.util.Objects;
41
import java.util.stream.IntStream;
42
import jdk.internal.jimage.decompressor.Decompressor;
43
44
/**
45
* @implNote This class needs to maintain JDK 8 source compatibility.
46
*
47
* It is used internally in the JDK to implement jimage/jrtfs access,
48
* but also compiled and delivered as part of the jrtfs.jar to support access
49
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
50
*/
51
public class BasicImageReader implements AutoCloseable {
52
@SuppressWarnings("removal")
53
private static boolean isSystemProperty(String key, String value, String def) {
54
// No lambdas during bootstrap
55
return AccessController.doPrivileged(
56
new PrivilegedAction<Boolean>() {
57
@Override
58
public Boolean run() {
59
return value.equals(System.getProperty(key, def));
60
}
61
});
62
}
63
64
static private final boolean IS_64_BIT =
65
isSystemProperty("sun.arch.data.model", "64", "32");
66
static private final boolean USE_JVM_MAP =
67
isSystemProperty("jdk.image.use.jvm.map", "true", "true");
68
static private final boolean MAP_ALL =
69
isSystemProperty("jdk.image.map.all", "true", IS_64_BIT ? "true" : "false");
70
71
private final Path imagePath;
72
private final ByteOrder byteOrder;
73
private final String name;
74
private final ByteBuffer memoryMap;
75
private final FileChannel channel;
76
private final ImageHeader header;
77
private final long indexSize;
78
private final IntBuffer redirect;
79
private final IntBuffer offsets;
80
private final ByteBuffer locations;
81
private final ByteBuffer strings;
82
private final ImageStringsReader stringsReader;
83
private final Decompressor decompressor;
84
85
@SuppressWarnings("removal")
86
protected BasicImageReader(Path path, ByteOrder byteOrder)
87
throws IOException {
88
this.imagePath = Objects.requireNonNull(path);
89
this.byteOrder = Objects.requireNonNull(byteOrder);
90
this.name = this.imagePath.toString();
91
92
ByteBuffer map;
93
94
if (USE_JVM_MAP && BasicImageReader.class.getClassLoader() == null) {
95
// Check to see if the jvm has opened the file using libjimage
96
// native entry when loading the image for this runtime
97
map = NativeImageBuffer.getNativeMap(name);
98
} else {
99
map = null;
100
}
101
102
// Open the file only if no memory map yet or is 32 bit jvm
103
if (map != null && MAP_ALL) {
104
channel = null;
105
} else {
106
channel = FileChannel.open(imagePath, StandardOpenOption.READ);
107
// No lambdas during bootstrap
108
AccessController.doPrivileged(new PrivilegedAction<Void>() {
109
@Override
110
public Void run() {
111
if (BasicImageReader.class.getClassLoader() == null) {
112
try {
113
Class<?> fileChannelImpl =
114
Class.forName("sun.nio.ch.FileChannelImpl");
115
Method setUninterruptible =
116
fileChannelImpl.getMethod("setUninterruptible");
117
setUninterruptible.invoke(channel);
118
} catch (ClassNotFoundException |
119
NoSuchMethodException |
120
IllegalAccessException |
121
InvocationTargetException ex) {
122
// fall thru - will only happen on JDK-8 systems where this code
123
// is only used by tools using jrt-fs (non-critical.)
124
}
125
}
126
127
return null;
128
}
129
});
130
}
131
132
// If no memory map yet and 64 bit jvm then memory map entire file
133
if (MAP_ALL && map == null) {
134
map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
135
}
136
137
// Assume we have a memory map to read image file header
138
ByteBuffer headerBuffer = map;
139
int headerSize = ImageHeader.getHeaderSize();
140
141
// If no memory map then read header from image file
142
if (headerBuffer == null) {
143
headerBuffer = ByteBuffer.allocateDirect(headerSize);
144
if (channel.read(headerBuffer, 0L) == headerSize) {
145
headerBuffer.rewind();
146
} else {
147
throw new IOException("\"" + name + "\" is not an image file");
148
}
149
} else if (headerBuffer.capacity() < headerSize) {
150
throw new IOException("\"" + name + "\" is not an image file");
151
}
152
153
// Interpret the image file header
154
header = readHeader(intBuffer(headerBuffer, 0, headerSize));
155
indexSize = header.getIndexSize();
156
157
// If no memory map yet then must be 32 bit jvm not previously mapped
158
if (map == null) {
159
// Just map the image index
160
map = channel.map(FileChannel.MapMode.READ_ONLY, 0, indexSize);
161
}
162
163
memoryMap = map.asReadOnlyBuffer();
164
165
// Interpret the image index
166
if (memoryMap.capacity() < indexSize) {
167
throw new IOException("The image file \"" + name + "\" is corrupted");
168
}
169
redirect = intBuffer(memoryMap, header.getRedirectOffset(), header.getRedirectSize());
170
offsets = intBuffer(memoryMap, header.getOffsetsOffset(), header.getOffsetsSize());
171
locations = slice(memoryMap, header.getLocationsOffset(), header.getLocationsSize());
172
strings = slice(memoryMap, header.getStringsOffset(), header.getStringsSize());
173
174
stringsReader = new ImageStringsReader(this);
175
decompressor = new Decompressor();
176
}
177
178
protected BasicImageReader(Path imagePath) throws IOException {
179
this(imagePath, ByteOrder.nativeOrder());
180
}
181
182
public static BasicImageReader open(Path imagePath) throws IOException {
183
return new BasicImageReader(imagePath, ByteOrder.nativeOrder());
184
}
185
186
public ImageHeader getHeader() {
187
return header;
188
}
189
190
private ImageHeader readHeader(IntBuffer buffer) throws IOException {
191
ImageHeader result = ImageHeader.readFrom(buffer);
192
193
if (result.getMagic() != ImageHeader.MAGIC) {
194
throw new IOException("\"" + name + "\" is not an image file");
195
}
196
197
if (result.getMajorVersion() != ImageHeader.MAJOR_VERSION ||
198
result.getMinorVersion() != ImageHeader.MINOR_VERSION) {
199
throw new IOException("The image file \"" + name + "\" is not " +
200
"the correct version. Major: " + result.getMajorVersion() +
201
". Minor: " + result.getMinorVersion());
202
}
203
204
return result;
205
}
206
207
private static ByteBuffer slice(ByteBuffer buffer, int position, int capacity) {
208
// Note that this is the only limit and position manipulation of
209
// BasicImageReader private ByteBuffers. The synchronize could be avoided
210
// by cloning the buffer to make a local copy, but at the cost of creating
211
// a new object.
212
synchronized(buffer) {
213
buffer.limit(position + capacity);
214
buffer.position(position);
215
return buffer.slice();
216
}
217
}
218
219
private IntBuffer intBuffer(ByteBuffer buffer, int offset, int size) {
220
return slice(buffer, offset, size).order(byteOrder).asIntBuffer();
221
}
222
223
public static void releaseByteBuffer(ByteBuffer buffer) {
224
Objects.requireNonNull(buffer);
225
226
if (!MAP_ALL) {
227
ImageBufferCache.releaseBuffer(buffer);
228
}
229
}
230
231
public String getName() {
232
return name;
233
}
234
235
public ByteOrder getByteOrder() {
236
return byteOrder;
237
}
238
239
public Path getImagePath() {
240
return imagePath;
241
}
242
243
@Override
244
public void close() throws IOException {
245
if (channel != null) {
246
channel.close();
247
}
248
}
249
250
public ImageStringsReader getStrings() {
251
return stringsReader;
252
}
253
254
public ImageLocation findLocation(String module, String name) {
255
int index = getLocationIndex(module, name);
256
if (index < 0) {
257
return null;
258
}
259
long[] attributes = getAttributes(offsets.get(index));
260
if (!ImageLocation.verify(module, name, attributes, stringsReader)) {
261
return null;
262
}
263
return new ImageLocation(attributes, stringsReader);
264
}
265
266
public ImageLocation findLocation(String name) {
267
int index = getLocationIndex(name);
268
if (index < 0) {
269
return null;
270
}
271
long[] attributes = getAttributes(offsets.get(index));
272
if (!ImageLocation.verify(name, attributes, stringsReader)) {
273
return null;
274
}
275
return new ImageLocation(attributes, stringsReader);
276
}
277
278
public boolean verifyLocation(String module, String name) {
279
int index = getLocationIndex(module, name);
280
if (index < 0) {
281
return false;
282
}
283
int locationOffset = offsets.get(index);
284
return ImageLocation.verify(module, name, locations, locationOffset, stringsReader);
285
}
286
287
// Details of the algorithm used here can be found in
288
// jdk.tools.jlink.internal.PerfectHashBuilder.
289
public int getLocationIndex(String name) {
290
int count = header.getTableLength();
291
int index = redirect.get(ImageStringsReader.hashCode(name) % count);
292
if (index < 0) {
293
// index is twos complement of location attributes index.
294
return -index - 1;
295
} else if (index > 0) {
296
// index is hash seed needed to compute location attributes index.
297
return ImageStringsReader.hashCode(name, index) % count;
298
} else {
299
// No entry.
300
return -1;
301
}
302
}
303
304
private int getLocationIndex(String module, String name) {
305
int count = header.getTableLength();
306
int index = redirect.get(ImageStringsReader.hashCode(module, name) % count);
307
if (index < 0) {
308
// index is twos complement of location attributes index.
309
return -index - 1;
310
} else if (index > 0) {
311
// index is hash seed needed to compute location attributes index.
312
return ImageStringsReader.hashCode(module, name, index) % count;
313
} else {
314
// No entry.
315
return -1;
316
}
317
}
318
319
public String[] getEntryNames() {
320
int[] attributeOffsets = new int[offsets.capacity()];
321
offsets.get(attributeOffsets);
322
return IntStream.of(attributeOffsets)
323
.filter(o -> o != 0)
324
.mapToObj(o -> ImageLocation.readFrom(this, o).getFullName())
325
.sorted()
326
.toArray(String[]::new);
327
}
328
329
ImageLocation getLocation(int offset) {
330
return ImageLocation.readFrom(this, offset);
331
}
332
333
public long[] getAttributes(int offset) {
334
if (offset < 0 || offset >= locations.limit()) {
335
throw new IndexOutOfBoundsException("offset");
336
}
337
return ImageLocation.decompress(locations, offset);
338
}
339
340
public String getString(int offset) {
341
if (offset < 0 || offset >= strings.limit()) {
342
throw new IndexOutOfBoundsException("offset");
343
}
344
return ImageStringsReader.stringFromByteBuffer(strings, offset);
345
}
346
347
public int match(int offset, String string, int stringOffset) {
348
if (offset < 0 || offset >= strings.limit()) {
349
throw new IndexOutOfBoundsException("offset");
350
}
351
return ImageStringsReader.stringFromByteBufferMatches(strings, offset, string, stringOffset);
352
}
353
354
private byte[] getBufferBytes(ByteBuffer buffer) {
355
Objects.requireNonNull(buffer);
356
byte[] bytes = new byte[buffer.limit()];
357
buffer.get(bytes);
358
359
return bytes;
360
}
361
362
private ByteBuffer readBuffer(long offset, long size) {
363
if (offset < 0 || Integer.MAX_VALUE <= offset) {
364
throw new IndexOutOfBoundsException("Bad offset: " + offset);
365
}
366
367
if (size < 0 || Integer.MAX_VALUE <= size) {
368
throw new IndexOutOfBoundsException("Bad size: " + size);
369
}
370
371
if (MAP_ALL) {
372
ByteBuffer buffer = slice(memoryMap, (int)offset, (int)size);
373
buffer.order(ByteOrder.BIG_ENDIAN);
374
375
return buffer;
376
} else {
377
if (channel == null) {
378
throw new InternalError("Image file channel not open");
379
}
380
381
ByteBuffer buffer = ImageBufferCache.getBuffer(size);
382
int read;
383
try {
384
read = channel.read(buffer, offset);
385
buffer.rewind();
386
} catch (IOException ex) {
387
ImageBufferCache.releaseBuffer(buffer);
388
throw new RuntimeException(ex);
389
}
390
391
if (read != size) {
392
ImageBufferCache.releaseBuffer(buffer);
393
throw new RuntimeException("Short read: " + read +
394
" instead of " + size + " bytes");
395
}
396
397
return buffer;
398
}
399
}
400
401
public byte[] getResource(String name) {
402
Objects.requireNonNull(name);
403
ImageLocation location = findLocation(name);
404
405
return location != null ? getResource(location) : null;
406
}
407
408
public byte[] getResource(ImageLocation loc) {
409
ByteBuffer buffer = getResourceBuffer(loc);
410
411
if (buffer != null) {
412
byte[] bytes = getBufferBytes(buffer);
413
ImageBufferCache.releaseBuffer(buffer);
414
415
return bytes;
416
}
417
418
return null;
419
}
420
421
public ByteBuffer getResourceBuffer(ImageLocation loc) {
422
Objects.requireNonNull(loc);
423
long offset = loc.getContentOffset() + indexSize;
424
long compressedSize = loc.getCompressedSize();
425
long uncompressedSize = loc.getUncompressedSize();
426
427
if (compressedSize < 0 || Integer.MAX_VALUE < compressedSize) {
428
throw new IndexOutOfBoundsException(
429
"Bad compressed size: " + compressedSize);
430
}
431
432
if (uncompressedSize < 0 || Integer.MAX_VALUE < uncompressedSize) {
433
throw new IndexOutOfBoundsException(
434
"Bad uncompressed size: " + uncompressedSize);
435
}
436
437
if (compressedSize == 0) {
438
return readBuffer(offset, uncompressedSize);
439
} else {
440
ByteBuffer buffer = readBuffer(offset, compressedSize);
441
442
if (buffer != null) {
443
byte[] bytesIn = getBufferBytes(buffer);
444
ImageBufferCache.releaseBuffer(buffer);
445
byte[] bytesOut;
446
447
try {
448
bytesOut = decompressor.decompressResource(byteOrder,
449
(int strOffset) -> getString(strOffset), bytesIn);
450
} catch (IOException ex) {
451
throw new RuntimeException(ex);
452
}
453
454
return ByteBuffer.wrap(bytesOut);
455
}
456
}
457
458
return null;
459
}
460
461
public InputStream getResourceStream(ImageLocation loc) {
462
Objects.requireNonNull(loc);
463
byte[] bytes = getResource(loc);
464
465
return new ByteArrayInputStream(bytes);
466
}
467
}
468
469