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