1 /* 2 * Copyright (c) 2014, 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.IOException; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.nio.IntBuffer; 31 import java.nio.channels.FileChannel; 32 import java.nio.file.Paths; 33 import static java.nio.file.StandardOpenOption.READ; 34 import jdk.internal.jimage.decompressor.Decompressor; 35 36 final class ImageJavaSubstrate implements ImageSubstrate { 37 38 private final String imagePath; 39 private final ByteOrder byteOrder; 40 private final FileChannel channel; 41 private final ImageHeader header; 42 private final long indexSize; 43 private final int[] redirect; 44 private final int[] offsets; 45 private final byte[] locations; 46 private final byte[] strings; 47 48 private final Decompressor decompressor = new Decompressor(); 49 50 private ImageJavaSubstrate(String imagePath, ByteOrder byteOrder) 51 throws IOException { 52 this.imagePath = imagePath; 53 this.byteOrder = byteOrder; 54 channel = FileChannel.open(Paths.get(imagePath), READ); 55 56 int headerSize = ImageHeader.getHeaderSize(); 57 ByteBuffer buffer = getIndexBuffer(0, headerSize); 58 header = ImageHeader.readFrom(buffer.asIntBuffer()); 59 60 if (header.getMagic() != ImageHeader.MAGIC || 61 header.getMajorVersion() != ImageHeader.MAJOR_VERSION || 62 header.getMinorVersion() != ImageHeader.MINOR_VERSION) { 63 throw new IOException("Image not found \"" + imagePath + "\""); 64 } 65 66 indexSize = header.getIndexSize(); 67 68 redirect = readIntegers(header.getRedirectOffset(), 69 header.getRedirectSize()); 70 offsets = readIntegers(header.getOffsetsOffset(), 71 header.getOffsetsSize()); 72 locations = readBytes(header.getLocationsOffset(), 73 header.getLocationsSize()); 74 strings = readBytes(header.getStringsOffset(), 75 header.getStringsSize()); 76 } 77 78 static ImageSubstrate openImage(String imagePath, ByteOrder byteOrder) 79 throws IOException { 80 return new ImageJavaSubstrate(imagePath, byteOrder); 81 } 82 83 @Override 84 public void close() { 85 try { 86 channel.close(); 87 } catch (IOException ex) { 88 // Mostly harmless 89 } 90 } 91 92 @Override 93 public boolean supportsDataBuffer() { 94 return false; 95 } 96 97 private int[] readIntegers(long offset, long size) { 98 assert size < Integer.MAX_VALUE; 99 IntBuffer buffer = readBuffer(offset, size).asIntBuffer(); 100 int[] integers = new int[(int)size / 4]; 101 buffer.get(integers); 102 103 return integers; 104 } 105 106 private byte[] readBytes(long offset, long size) { 107 assert size < Integer.MAX_VALUE; 108 ByteBuffer buffer = readBuffer(offset, size); 109 byte[] bytes = new byte[(int)size]; 110 buffer.get(bytes); 111 112 return bytes; 113 } 114 115 private ByteBuffer readBuffer(long offset, long size) { 116 assert size < Integer.MAX_VALUE; 117 ByteBuffer buffer = ByteBuffer.allocate((int)size); 118 buffer.order(byteOrder); 119 120 if (!readBuffer(buffer, offset, size)) { 121 return null; 122 } 123 124 return buffer; 125 } 126 127 private boolean readBuffer(ByteBuffer buffer, long offset, long size) { 128 assert size < Integer.MAX_VALUE; 129 assert buffer.limit() == size; 130 int read = 0; 131 132 try { 133 read = channel.read(buffer, offset); 134 buffer.rewind(); 135 } catch (IOException ex) { 136 // fall thru 137 } 138 139 return read == size; 140 } 141 142 @Override 143 public ByteBuffer getIndexBuffer(long offset, long size) { 144 assert size < Integer.MAX_VALUE; 145 return readBuffer(offset, size); 146 } 147 148 @Override 149 public ByteBuffer getDataBuffer(long offset, long size) { 150 assert size < Integer.MAX_VALUE; 151 return getIndexBuffer(indexSize + offset, size); 152 } 153 154 @Override 155 public boolean read(long offset, 156 ByteBuffer compressedBuffer, long compressedSize, 157 ByteBuffer uncompressedBuffer, long uncompressedSize) { 158 assert compressedSize < Integer.MAX_VALUE; 159 assert uncompressedSize < Integer.MAX_VALUE; 160 boolean isRead = readBuffer(compressedBuffer, 161 indexSize + offset, compressedSize); 162 if (isRead) { 163 byte[] bytesIn = new byte[(int)compressedSize]; 164 compressedBuffer.get(bytesIn); 165 byte[] bytesOut; 166 try { 167 bytesOut = decompressor.decompressResource(byteOrder, (int strOffset) -> { 168 return new UTF8String(getStringBytes(strOffset)).toString(); 169 }, bytesIn); 170 } catch (IOException ex) { 171 throw new RuntimeException(ex); 172 } 173 uncompressedBuffer.put(bytesOut); 174 uncompressedBuffer.rewind(); 175 } 176 177 return isRead; 178 } 179 180 @Override 181 public boolean read(long offset, 182 ByteBuffer uncompressedBuffer, long uncompressedSize) { 183 assert uncompressedSize < Integer.MAX_VALUE; 184 boolean isRead = readBuffer(uncompressedBuffer, 185 indexSize + offset, uncompressedSize); 186 187 return isRead; 188 } 189 190 @Override 191 public byte[] getStringBytes(int offset) { 192 if (offset == 0) { 193 return new byte[0]; 194 } 195 196 int length = strings.length - offset; 197 198 for (int i = offset; i < strings.length; i++) { 199 if (strings[i] == 0) { 200 length = i - offset; 201 break; 202 } 203 } 204 205 byte[] bytes = new byte[length]; 206 System.arraycopy(strings, offset, bytes, 0, length); 207 208 return bytes; 209 } 210 211 @Override 212 public long[] getAttributes(int offset) { 213 return ImageLocationBase.decompress(locations, offset); 214 } 215 216 @Override 217 public ImageLocation findLocation(UTF8String name, ImageStringsReader strings) { 218 int count = header.getTableLength(); 219 int index = redirect[name.hashCode() % count]; 220 221 if (index < 0) { 222 index = -index - 1; 223 } else { 224 index = name.hashCode(index) % count; 225 } 226 227 long[] attributes = getAttributes(offsets[index]); 228 229 ImageLocation imageLocation = new ImageLocation(attributes, strings); 230 231 if (!imageLocation.verify(name)) { 232 return null; 233 } 234 235 return imageLocation; 236 } 237 238 @Override 239 public int[] attributeOffsets() { 240 return offsets; 241 } 242 }