1 /* 2 * Copyright (c) 2014, 2016, 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) { 63 Objects.requireNonNull(bytes); 64 long[] attributes = new long[ATTRIBUTE_COUNT]; 65 66 if (bytes != null) { 67 while (bytes.hasRemaining()) { 68 int data = bytes.get() & 0xFF; 69 int kind = data >>> 3; 70 71 if (kind == ATTRIBUTE_END) { 72 break; 73 } 74 75 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 76 throw new InternalError( 77 "Invalid jimage attribute kind: " + kind); 78 } 79 80 int length = (data & 0x7) + 1; 81 long value = 0; 82 83 for (int j = 0; j < length; j++) { 84 value <<= 8; 85 86 if (!bytes.hasRemaining()) { 87 throw new InternalError("Missing jimage attribute data"); 88 } 89 90 value |= bytes.get() & 0xFF; 91 } 92 93 attributes[kind] = value; 94 } 95 } 96 97 return attributes; 98 } 99 100 public static byte[] compress(long[] attributes) { 101 Objects.requireNonNull(attributes); 102 ImageStream stream = new ImageStream(16); 103 104 for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) { 105 long value = attributes[kind]; 106 107 if (value != 0) { 108 int n = (63 - Long.numberOfLeadingZeros(value)) >> 3; 109 stream.put((kind << 3) | n); 110 111 for (int i = n; i >= 0; i--) { 112 stream.put((int)(value >> (i << 3))); 113 } 114 } 115 } 116 117 stream.put(ATTRIBUTE_END << 3); 118 119 return stream.toArray(); 120 } 121 122 public boolean verify(String name) { 123 return verify(name, attributes, strings); 124 } 125 126 /** 127 * A simpler verification would be {@code name.equals(getFullName())}, but 128 * by not creating the full name and enabling early returns we allocate 129 * fewer objects. Could possibly be made allocation free by extending 130 * ImageStrings to test if strings at an offset match the name region. 131 */ 132 static boolean verify(String name, final long[] attributes, 133 final ImageStrings strings) { 134 Objects.requireNonNull(name); 135 final int length = name.length(); 136 int index = 0; 137 int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; 138 if (moduleOffset != 0) { 139 String module = strings.get(moduleOffset); 140 final int moduleLen = module.length(); 141 index = moduleLen + 1; 142 if (length <= index 143 || name.charAt(0) != '/' 144 || !name.regionMatches(1, module, 0, moduleLen) 145 || name.charAt(index++) != '/') { 146 return false; 147 } 148 } 149 150 return verifyName(name, index, length, attributes, strings); 151 } 152 153 static boolean verify(String module, String name, 154 final long[] attributes, final ImageStrings strings) { 155 Objects.requireNonNull(module); 156 Objects.requireNonNull(name); 157 int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; 158 if (moduleOffset != 0) { 159 if (!module.equals(strings.get(moduleOffset))) { 160 return false; 161 } 162 } 163 164 return verifyName(name, 0, name.length(), attributes, strings); 165 } 166 167 private static boolean verifyName(String name, int index, final int length, 168 final long[] attributes, final ImageStrings strings) { 169 170 int parentOffset = (int) attributes[ATTRIBUTE_PARENT]; 171 if (parentOffset != 0) { 172 String parent = strings.get(parentOffset); 173 final int parentLen = parent.length(); 174 if (!name.regionMatches(index, parent, 0, parentLen)) { 175 return false; 176 } 177 index += parentLen; 178 if (length <= index || name.charAt(index++) != '/') { 179 return false; 180 } 181 } 182 String base = strings.get((int) attributes[ATTRIBUTE_BASE]); 183 final int baseLen = base.length(); 184 if (!name.regionMatches(index, base, 0, baseLen)) { 185 return false; 186 } 187 index += baseLen; 188 int extOffset = (int) attributes[ATTRIBUTE_EXTENSION]; 189 if (extOffset != 0) { 190 String extension = strings.get(extOffset); 191 int extLen = extension.length(); 192 if (length <= index 193 || name.charAt(index++) != '.' 194 || !name.regionMatches(index, extension, 0, extLen)) { 195 return false; 196 } 197 index += extLen; 198 } 199 return length == index; 200 } 201 202 long getAttribute(int kind) { 203 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 204 throw new InternalError( 205 "Invalid jimage attribute kind: " + kind); 206 } 207 208 return attributes[kind]; 209 } 210 211 String getAttributeString(int kind) { 212 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 213 throw new InternalError( 214 "Invalid jimage attribute kind: " + kind); 215 } 216 217 return getStrings().get((int)attributes[kind]); 218 } 219 220 public String getModule() { 221 return getAttributeString(ATTRIBUTE_MODULE); 222 } 223 224 public int getModuleOffset() { 225 return (int)getAttribute(ATTRIBUTE_MODULE); 226 } 227 228 public String getBase() { 229 return getAttributeString(ATTRIBUTE_BASE); 230 } 231 232 public int getBaseOffset() { 233 return (int)getAttribute(ATTRIBUTE_BASE); 234 } 235 236 public String getParent() { 237 return getAttributeString(ATTRIBUTE_PARENT); 238 } 239 240 public int getParentOffset() { 241 return (int)getAttribute(ATTRIBUTE_PARENT); 242 } 243 244 public String getExtension() { 245 return getAttributeString(ATTRIBUTE_EXTENSION); 246 } 247 248 public int getExtensionOffset() { 249 return (int)getAttribute(ATTRIBUTE_EXTENSION); 250 } 251 252 public String getFullName() { 253 return getFullName(false); 254 } 255 256 public String getFullName(boolean modulesPrefix) { 257 StringBuilder builder = new StringBuilder(); 258 259 if (getModuleOffset() != 0) { 260 if (modulesPrefix) { 261 builder.append("/modules"); 262 } 263 264 builder.append('/'); 265 builder.append(getModule()); 266 builder.append('/'); 267 } 268 269 if (getParentOffset() != 0) { 270 builder.append(getParent()); 271 builder.append('/'); 272 } 273 274 builder.append(getBase()); 275 276 if (getExtensionOffset() != 0) { 277 builder.append('.'); 278 builder.append(getExtension()); 279 } 280 281 return builder.toString(); 282 } 283 284 String buildName(boolean includeModule, boolean includeParent, 285 boolean includeName) { 286 StringBuilder builder = new StringBuilder(); 287 288 if (includeModule && getModuleOffset() != 0) { 289 builder.append("/modules/"); 290 builder.append(getModule()); 291 } 292 293 if (includeParent && getParentOffset() != 0) { 294 builder.append('/'); 295 builder.append(getParent()); 296 } 297 298 if (includeName) { 299 if (includeModule || includeParent) { 300 builder.append('/'); 301 } 302 303 builder.append(getBase()); 304 305 if (getExtensionOffset() != 0) { 306 builder.append('.'); 307 builder.append(getExtension()); 308 } 309 } 310 311 return builder.toString(); 312 } 313 314 public long getContentOffset() { 315 return getAttribute(ATTRIBUTE_OFFSET); 316 } 317 318 public long getCompressedSize() { 319 return getAttribute(ATTRIBUTE_COMPRESSED); 320 } 321 322 public long getUncompressedSize() { 323 return getAttribute(ATTRIBUTE_UNCOMPRESSED); 324 } 325 326 static ImageLocation readFrom(BasicImageReader reader, int offset) { 327 Objects.requireNonNull(reader); 328 long[] attributes = reader.getAttributes(offset); 329 ImageStringsReader strings = reader.getStrings(); 330 331 return new ImageLocation(attributes, strings); 332 } 333 }