1 /* 2 * Copyright (c) 2019, 2020, 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.foreign.abi; 26 27 import jdk.incubator.foreign.CSupport; 28 import jdk.incubator.foreign.ForeignLinker; 29 import jdk.incubator.foreign.FunctionDescriptor; 30 import jdk.incubator.foreign.GroupLayout; 31 import jdk.incubator.foreign.MemoryAddress; 32 import jdk.incubator.foreign.MemoryHandles; 33 import jdk.incubator.foreign.MemoryLayout; 34 import jdk.incubator.foreign.MemorySegment; 35 import jdk.incubator.foreign.SequenceLayout; 36 import jdk.incubator.foreign.ValueLayout; 37 import jdk.internal.foreign.MemoryAddressImpl; 38 import jdk.internal.foreign.Utils; 39 import jdk.internal.foreign.abi.aarch64.AArch64Linker; 40 import jdk.internal.foreign.abi.x64.sysv.SysVVaList; 41 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; 42 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; 43 44 import java.lang.invoke.MethodHandle; 45 import java.lang.invoke.MethodHandles; 46 import java.lang.invoke.MethodType; 47 import java.lang.invoke.VarHandle; 48 import java.nio.ByteOrder; 49 import java.util.List; 50 import java.util.function.Consumer; 51 import java.util.stream.IntStream; 52 53 import static java.lang.invoke.MethodHandles.collectArguments; 54 import static java.lang.invoke.MethodHandles.identity; 55 import static java.lang.invoke.MethodHandles.insertArguments; 56 import static java.lang.invoke.MethodHandles.permuteArguments; 57 import static java.lang.invoke.MethodType.methodType; 58 import static jdk.incubator.foreign.CSupport.*; 59 60 public class SharedUtils { 61 62 private static final MethodHandle MH_ALLOC_BUFFER; 63 private static final MethodHandle MH_BASEADDRESS; 64 private static final MethodHandle MH_BUFFER_COPY; 65 66 private static final VarHandle VH_BYTE = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); 67 private static final VarHandle VH_CHAR = MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()); 68 private static final VarHandle VH_SHORT = MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()); 69 private static final VarHandle VH_INT = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); 70 private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()); 71 private static final VarHandle VH_FLOAT = MemoryHandles.varHandle(float.class, ByteOrder.nativeOrder()); 72 private static final VarHandle VH_DOUBLE = MemoryHandles.varHandle(double.class, ByteOrder.nativeOrder()); 73 74 static { 75 try { 76 var lookup = MethodHandles.lookup(); 77 MH_ALLOC_BUFFER = lookup.findStatic(SharedUtils.class, "allocateNative", 78 methodType(MemorySegment.class, MemoryLayout.class)); 79 MH_BASEADDRESS = lookup.findVirtual(MemorySegment.class, "baseAddress", 80 methodType(MemoryAddress.class)); 81 MH_BUFFER_COPY = lookup.findStatic(SharedUtils.class, "bufferCopy", 82 methodType(MemoryAddress.class, MemoryAddress.class, MemorySegment.class)); 83 } catch (ReflectiveOperationException e) { 84 throw new BootstrapMethodError(e); 85 } 86 } 87 88 // workaround for https://bugs.openjdk.java.net/browse/JDK-8239083 89 private static MemorySegment allocateNative(MemoryLayout layout) { 90 return MemorySegment.allocateNative(layout); 91 } 92 93 /** 94 * Align the specified type from a given address 95 * @return The address the data should be at based on alignment requirement 96 */ 97 public static long align(MemoryLayout t, boolean isVar, long addr) { 98 return alignUp(addr, alignment(t, isVar)); 99 } 100 101 public static long alignUp(long addr, long alignment) { 102 return ((addr - 1) | (alignment - 1)) + 1; 103 } 104 105 /** 106 * The alignment requirement for a given type 107 * @param isVar indicate if the type is a standalone variable. This change how 108 * array is aligned. for example. 109 */ 110 public static long alignment(MemoryLayout t, boolean isVar) { 111 if (t instanceof ValueLayout) { 112 return alignmentOfScalar((ValueLayout) t); 113 } else if (t instanceof SequenceLayout) { 114 // when array is used alone 115 return alignmentOfArray((SequenceLayout) t, isVar); 116 } else if (t instanceof GroupLayout) { 117 return alignmentOfContainer((GroupLayout) t); 118 } else if (t.isPadding()) { 119 return 1; 120 } else { 121 throw new IllegalArgumentException("Invalid type: " + t); 122 } 123 } 124 125 private static long alignmentOfScalar(ValueLayout st) { 126 return st.byteSize(); 127 } 128 129 private static long alignmentOfArray(SequenceLayout ar, boolean isVar) { 130 if (ar.elementCount().orElseThrow() == 0) { 131 // VLA or incomplete 132 return 16; 133 } else if ((ar.byteSize()) >= 16 && isVar) { 134 return 16; 135 } else { 136 // align as element type 137 MemoryLayout elementType = ar.elementLayout(); 138 return alignment(elementType, false); 139 } 140 } 141 142 private static long alignmentOfContainer(GroupLayout ct) { 143 // Most strict member 144 return ct.memberLayouts().stream().mapToLong(t -> alignment(t, false)).max().orElse(1); 145 } 146 147 /** 148 * Takes a MethodHandle that takes an input buffer as a first argument (a MemoryAddress), and returns nothing, 149 * and adapts it to return a MemorySegment, by allocating a MemorySegment for the input 150 * buffer, calling the target MethodHandle, and then returning the allocated MemorySegment. 151 * 152 * This allows viewing a MethodHandle that makes use of in memory return (IMR) as a MethodHandle that just returns 153 * a MemorySegment without requiring a pre-allocated buffer as an explicit input. 154 * 155 * @param handle the target handle to adapt 156 * @param cDesc the function descriptor of the native function (with actual return layout) 157 * @return the adapted handle 158 */ 159 public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc) { 160 if (handle.type().returnType() != void.class) 161 throw new IllegalArgumentException("return expected to be void for in memory returns"); 162 if (handle.type().parameterType(0) != MemoryAddress.class) 163 throw new IllegalArgumentException("MemoryAddress expected as first param"); 164 if (cDesc.returnLayout().isEmpty()) 165 throw new IllegalArgumentException("Return layout needed: " + cDesc); 166 167 MethodHandle ret = identity(MemorySegment.class); // (MemorySegment) MemorySegment 168 handle = collectArguments(ret, 1, handle); // (MemorySegment, MemoryAddress ...) MemorySegment 169 handle = collectArguments(handle, 1, MH_BASEADDRESS); // (MemorySegment, MemorySegment ...) MemorySegment 170 MethodType oldType = handle.type(); // (MemorySegment, MemorySegment, ...) MemorySegment 171 MethodType newType = oldType.dropParameterTypes(0, 1); // (MemorySegment, ...) MemorySegment 172 int[] reorder = IntStream.range(-1, newType.parameterCount()).toArray(); 173 reorder[0] = 0; // [0, 0, 1, 2, 3, ...] 174 handle = permuteArguments(handle, newType, reorder); // (MemorySegment, ...) MemoryAddress 175 handle = collectArguments(handle, 0, insertArguments(MH_ALLOC_BUFFER, 0, cDesc.returnLayout().get())); // (...) MemoryAddress 176 177 return handle; 178 } 179 180 /** 181 * Takes a MethodHandle that returns a MemorySegment, and adapts it to take an input buffer as a first argument 182 * (a MemoryAddress), and upon invocation, copies the contents of the returned MemorySegment into the input buffer 183 * passed as the first argument. 184 * 185 * @param target the target handle to adapt 186 * @return the adapted handle 187 */ 188 public static MethodHandle adaptUpcallForIMR(MethodHandle target) { 189 if (target.type().returnType() != MemorySegment.class) 190 throw new IllegalArgumentException("Must return MemorySegment for IMR"); 191 192 target = collectArguments(MH_BUFFER_COPY, 1, target); // (MemoryAddress, ...) MemoryAddress 193 194 return target; 195 } 196 197 private static MemoryAddress bufferCopy(MemoryAddress dest, MemorySegment buffer) { 198 MemoryAddressImpl.ofLongUnchecked(dest.toRawLongValue(), buffer.byteSize()) 199 .segment().copyFrom(buffer); 200 return dest; 201 } 202 203 public static void checkCompatibleType(Class<?> carrier, MemoryLayout layout, long addressSize) { 204 if (carrier.isPrimitive()) { 205 Utils.checkPrimitiveCarrierCompat(carrier, layout); 206 } else if (carrier == MemoryAddress.class) { 207 Utils.checkLayoutType(layout, ValueLayout.class); 208 if (layout.bitSize() != addressSize) 209 throw new IllegalArgumentException("Address size mismatch: " + addressSize + " != " + layout.bitSize()); 210 } else if (carrier == MemorySegment.class) { 211 Utils.checkLayoutType(layout, GroupLayout.class); 212 } else { 213 throw new IllegalArgumentException("Unsupported carrier: " + carrier); 214 } 215 } 216 217 public static void checkFunctionTypes(MethodType mt, FunctionDescriptor cDesc, long addressSize) { 218 if (mt.returnType() == void.class != cDesc.returnLayout().isEmpty()) 219 throw new IllegalArgumentException("Return type mismatch: " + mt + " != " + cDesc); 220 List<MemoryLayout> argLayouts = cDesc.argumentLayouts(); 221 if (mt.parameterCount() != argLayouts.size()) 222 throw new IllegalArgumentException("Arity mismatch: " + mt + " != " + cDesc); 223 224 int paramCount = mt.parameterCount(); 225 for (int i = 0; i < paramCount; i++) { 226 checkCompatibleType(mt.parameterType(i), argLayouts.get(i), addressSize); 227 } 228 cDesc.returnLayout().ifPresent(rl -> checkCompatibleType(mt.returnType(), rl, addressSize)); 229 } 230 231 public static Class<?> primitiveCarrierForSize(long size) { 232 if (size == 1) { 233 return byte.class; 234 } else if(size == 2) { 235 return short.class; 236 } else if (size <= 4) { 237 return int.class; 238 } else if (size <= 8) { 239 return long.class; 240 } 241 242 throw new IllegalArgumentException("Size too large: " + size); 243 } 244 245 public static ForeignLinker getSystemLinker() { 246 String arch = System.getProperty("os.arch"); 247 String os = System.getProperty("os.name"); 248 if (arch.equals("amd64") || arch.equals("x86_64")) { 249 if (os.startsWith("Windows")) { 250 return Windowsx64Linker.getInstance(); 251 } else { 252 return SysVx64Linker.getInstance(); 253 } 254 } else if (arch.equals("aarch64")) { 255 return AArch64Linker.getInstance(); 256 } 257 throw new UnsupportedOperationException("Unsupported os or arch: " + os + ", " + arch); 258 } 259 260 public static VaList newVaList(Consumer<VaList.Builder> actions) { 261 String name = CSupport.getSystemLinker().name(); 262 return switch(name) { 263 case Win64.NAME -> Windowsx64Linker.newVaList(actions); 264 case SysV.NAME -> SysVx64Linker.newVaList(actions); 265 case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform"); 266 default -> throw new IllegalStateException("Unknown linker name: " + name); 267 }; 268 } 269 270 public static VarHandle vhPrimitiveOrAddress(Class<?> carrier, MemoryLayout layout) { 271 return carrier == MemoryAddress.class 272 ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize()))) 273 : layout.varHandle(carrier); 274 } 275 276 public static VaList newVaListOfAddress(MemoryAddress ma) { 277 String name = CSupport.getSystemLinker().name(); 278 return switch(name) { 279 case Win64.NAME -> Windowsx64Linker.newVaListOfAddress(ma); 280 case SysV.NAME -> SysVx64Linker.newVaListOfAddress(ma); 281 case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform"); 282 default -> throw new IllegalStateException("Unknown linker name: " + name); 283 }; 284 } 285 286 public static VaList emptyVaList() { 287 String name = CSupport.getSystemLinker().name(); 288 return switch(name) { 289 case Win64.NAME -> Windowsx64Linker.emptyVaList(); 290 case SysV.NAME -> SysVx64Linker.emptyVaList(); 291 case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform"); 292 default -> throw new IllegalStateException("Unknown linker name: " + name); 293 }; 294 } 295 296 public static MethodType convertVaListCarriers(MethodType mt, Class<?> carrier) { 297 Class<?>[] params = new Class<?>[mt.parameterCount()]; 298 for (int i = 0; i < params.length; i++) { 299 Class<?> pType = mt.parameterType(i); 300 params[i] = ((pType == VaList.class) ? carrier : pType); 301 } 302 return methodType(mt.returnType(), params); 303 } 304 305 public static MethodHandle unboxVaLists(MethodType type, MethodHandle handle, MethodHandle unboxer) { 306 for (int i = 0; i < type.parameterCount(); i++) { 307 if (type.parameterType(i) == VaList.class) { 308 handle = MethodHandles.filterArguments(handle, i, unboxer); 309 } 310 } 311 return handle; 312 } 313 314 public static MethodHandle boxVaLists(MethodHandle handle, MethodHandle boxer) { 315 MethodType type = handle.type(); 316 for (int i = 0; i < type.parameterCount(); i++) { 317 if (type.parameterType(i) == VaList.class) { 318 handle = MethodHandles.filterArguments(handle, i, boxer); 319 } 320 } 321 return handle; 322 } 323 324 public static class SimpleVaArg { 325 public final Class<?> carrier; 326 public final MemoryLayout layout; 327 public final Object value; 328 329 public SimpleVaArg(Class<?> carrier, MemoryLayout layout, Object value) { 330 this.carrier = carrier; 331 this.layout = layout; 332 this.value = value; 333 } 334 335 public VarHandle varHandle() { 336 return carrier == MemoryAddress.class 337 ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize()))) 338 : layout.varHandle(carrier); 339 } 340 } 341 342 public static class EmptyVaList implements CSupport.VaList { 343 344 private final MemoryAddress address; 345 346 public EmptyVaList(MemoryAddress address) { 347 this.address = address; 348 } 349 350 private static UnsupportedOperationException uoe() { 351 return new UnsupportedOperationException("Empty VaList"); 352 } 353 354 @Override 355 public int vargAsInt(MemoryLayout layout) { 356 throw uoe(); 357 } 358 359 @Override 360 public long vargAsLong(MemoryLayout layout) { 361 throw uoe(); 362 } 363 364 @Override 365 public double vargAsDouble(MemoryLayout layout) { 366 throw uoe(); 367 } 368 369 @Override 370 public MemoryAddress vargAsAddress(MemoryLayout layout) { 371 throw uoe(); 372 } 373 374 @Override 375 public MemorySegment vargAsSegment(MemoryLayout layout) { 376 throw uoe(); 377 } 378 379 @Override 380 public void skip(MemoryLayout... layouts) { 381 throw uoe(); 382 } 383 384 @Override 385 public boolean isAlive() { 386 return true; 387 } 388 389 @Override 390 public void close() { 391 throw uoe(); 392 } 393 394 @Override 395 public VaList copy() { 396 return this; 397 } 398 399 @Override 400 public MemoryAddress address() { 401 return address; 402 } 403 } 404 405 static void writeOverSized(MemoryAddress ptr, Class<?> type, Object o) { 406 // use VH_LONG for integers to zero out the whole register in the process 407 if (type == long.class) { 408 VH_LONG.set(ptr, (long) o); 409 } else if (type == int.class) { 410 VH_LONG.set(ptr, (long) (int) o); 411 } else if (type == short.class) { 412 VH_LONG.set(ptr, (long) (short) o); 413 } else if (type == char.class) { 414 VH_LONG.set(ptr, (long) (char) o); 415 } else if (type == byte.class) { 416 VH_LONG.set(ptr, (long) (byte) o); 417 } else if (type == float.class) { 418 VH_FLOAT.set(ptr, (float) o); 419 } else if (type == double.class) { 420 VH_DOUBLE.set(ptr, (double) o); 421 } else { 422 throw new IllegalArgumentException("Unsupported carrier: " + type); 423 } 424 } 425 426 static void write(MemoryAddress ptr, Class<?> type, Object o) { 427 if (type == long.class) { 428 VH_LONG.set(ptr, (long) o); 429 } else if (type == int.class) { 430 VH_INT.set(ptr, (int) o); 431 } else if (type == short.class) { 432 VH_SHORT.set(ptr, (short) o); 433 } else if (type == char.class) { 434 VH_CHAR.set(ptr, (char) o); 435 } else if (type == byte.class) { 436 VH_BYTE.set(ptr, (byte) o); 437 } else if (type == float.class) { 438 VH_FLOAT.set(ptr, (float) o); 439 } else if (type == double.class) { 440 VH_DOUBLE.set(ptr, (double) o); 441 } else { 442 throw new IllegalArgumentException("Unsupported carrier: " + type); 443 } 444 } 445 446 static Object read(MemoryAddress ptr, Class<?> type) { 447 if (type == long.class) { 448 return (long) VH_LONG.get(ptr); 449 } else if (type == int.class) { 450 return (int) VH_INT.get(ptr); 451 } else if (type == short.class) { 452 return (short) VH_SHORT.get(ptr); 453 } else if (type == char.class) { 454 return (char) VH_CHAR.get(ptr); 455 } else if (type == byte.class) { 456 return (byte) VH_BYTE.get(ptr); 457 } else if (type == float.class) { 458 return (float) VH_FLOAT.get(ptr); 459 } else if (type == double.class) { 460 return (double) VH_DOUBLE.get(ptr); 461 } else { 462 throw new IllegalArgumentException("Unsupported carrier: " + type); 463 } 464 } 465 }