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.SysVx64Linker; 41 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; 42 43 import java.lang.invoke.MethodHandle; 44 import java.lang.invoke.MethodHandles; 45 import java.lang.invoke.MethodType; 46 import java.lang.invoke.VarHandle; 47 import java.util.List; 48 import java.util.function.Consumer; 49 import java.util.stream.IntStream; 50 51 import static java.lang.invoke.MethodHandles.collectArguments; 52 import static java.lang.invoke.MethodHandles.identity; 53 import static java.lang.invoke.MethodHandles.insertArguments; 54 import static java.lang.invoke.MethodHandles.permuteArguments; 55 import static java.lang.invoke.MethodType.methodType; 56 import static jdk.incubator.foreign.CSupport.*; 57 58 public class SharedUtils { 59 60 private static final MethodHandle MH_ALLOC_BUFFER; 61 private static final MethodHandle MH_BASEADDRESS; 62 private static final MethodHandle MH_BUFFER_COPY; 63 64 static { 65 try { 66 var lookup = MethodHandles.lookup(); 67 MH_ALLOC_BUFFER = lookup.findStatic(SharedUtils.class, "allocateNative", 68 methodType(MemorySegment.class, MemoryLayout.class)); 69 MH_BASEADDRESS = lookup.findVirtual(MemorySegment.class, "baseAddress", 70 methodType(MemoryAddress.class)); 71 MH_BUFFER_COPY = lookup.findStatic(SharedUtils.class, "bufferCopy", 72 methodType(MemoryAddress.class, MemoryAddress.class, MemorySegment.class)); 73 } catch (ReflectiveOperationException e) { 74 throw new BootstrapMethodError(e); 75 } 76 } 77 78 // workaround for https://bugs.openjdk.java.net/browse/JDK-8239083 79 private static MemorySegment allocateNative(MemoryLayout layout) { 80 return MemorySegment.allocateNative(layout); 81 } 82 83 /** 84 * Align the specified type from a given address 85 * @return The address the data should be at based on alignment requirement 86 */ 87 public static long align(MemoryLayout t, boolean isVar, long addr) { 88 return alignUp(addr, alignment(t, isVar)); 89 } 90 91 public static long alignUp(long addr, long alignment) { 92 return ((addr - 1) | (alignment - 1)) + 1; 93 } 94 95 /** 96 * The alignment requirement for a given type 97 * @param isVar indicate if the type is a standalone variable. This change how 98 * array is aligned. for example. 99 */ 100 public static long alignment(MemoryLayout t, boolean isVar) { 101 if (t instanceof ValueLayout) { 102 return alignmentOfScalar((ValueLayout) t); 103 } else if (t instanceof SequenceLayout) { 104 // when array is used alone 105 return alignmentOfArray((SequenceLayout) t, isVar); 106 } else if (t instanceof GroupLayout) { 107 return alignmentOfContainer((GroupLayout) t); 108 } else if (t.isPadding()) { 109 return 1; 110 } else { 111 throw new IllegalArgumentException("Invalid type: " + t); 112 } 113 } 114 115 private static long alignmentOfScalar(ValueLayout st) { 116 return st.byteSize(); 117 } 118 119 private static long alignmentOfArray(SequenceLayout ar, boolean isVar) { 120 if (ar.elementCount().orElseThrow() == 0) { 121 // VLA or incomplete 122 return 16; 123 } else if ((ar.byteSize()) >= 16 && isVar) { 124 return 16; 125 } else { 126 // align as element type 127 MemoryLayout elementType = ar.elementLayout(); 128 return alignment(elementType, false); 129 } 130 } 131 132 private static long alignmentOfContainer(GroupLayout ct) { 133 // Most strict member 134 return ct.memberLayouts().stream().mapToLong(t -> alignment(t, false)).max().orElse(1); 135 } 136 137 /** 138 * Takes a MethodHandle that takes an input buffer as a first argument (a MemoryAddress), and returns nothing, 139 * and adapts it to return a MemorySegment, by allocating a MemorySegment for the input 140 * buffer, calling the target MethodHandle, and then returning the allocated MemorySegment. 141 * 142 * This allows viewing a MethodHandle that makes use of in memory return (IMR) as a MethodHandle that just returns 143 * a MemorySegment without requiring a pre-allocated buffer as an explicit input. 144 * 145 * @param handle the target handle to adapt 146 * @param cDesc the function descriptor of the native function (with actual return layout) 147 * @return the adapted handle 148 */ 149 public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc) { 150 if (handle.type().returnType() != void.class) 151 throw new IllegalArgumentException("return expected to be void for in memory returns"); 152 if (handle.type().parameterType(0) != MemoryAddress.class) 153 throw new IllegalArgumentException("MemoryAddress expected as first param"); 154 if (cDesc.returnLayout().isEmpty()) 155 throw new IllegalArgumentException("Return layout needed: " + cDesc); 156 157 MethodHandle ret = identity(MemorySegment.class); // (MemorySegment) MemorySegment 158 handle = collectArguments(ret, 1, handle); // (MemorySegment, MemoryAddress ...) MemorySegment 159 handle = collectArguments(handle, 1, MH_BASEADDRESS); // (MemorySegment, MemorySegment ...) MemorySegment 160 MethodType oldType = handle.type(); // (MemorySegment, MemorySegment, ...) MemorySegment 161 MethodType newType = oldType.dropParameterTypes(0, 1); // (MemorySegment, ...) MemorySegment 162 int[] reorder = IntStream.range(-1, newType.parameterCount()).toArray(); 163 reorder[0] = 0; // [0, 0, 1, 2, 3, ...] 164 handle = permuteArguments(handle, newType, reorder); // (MemorySegment, ...) MemoryAddress 165 handle = collectArguments(handle, 0, insertArguments(MH_ALLOC_BUFFER, 0, cDesc.returnLayout().get())); // (...) MemoryAddress 166 167 return handle; 168 } 169 170 /** 171 * Takes a MethodHandle that returns a MemorySegment, and adapts it to take an input buffer as a first argument 172 * (a MemoryAddress), and upon invocation, copies the contents of the returned MemorySegment into the input buffer 173 * passed as the first argument. 174 * 175 * @param target the target handle to adapt 176 * @return the adapted handle 177 */ 178 public static MethodHandle adaptUpcallForIMR(MethodHandle target) { 179 if (target.type().returnType() != MemorySegment.class) 180 throw new IllegalArgumentException("Must return MemorySegment for IMR"); 181 182 target = collectArguments(MH_BUFFER_COPY, 1, target); // (MemoryAddress, ...) MemoryAddress 183 184 return target; 185 } 186 187 private static MemoryAddress bufferCopy(MemoryAddress dest, MemorySegment buffer) { 188 MemoryAddressImpl.ofLongUnchecked(dest.toRawLongValue(), buffer.byteSize()) 189 .segment().copyFrom(buffer); 190 return dest; 191 } 192 193 public static void checkCompatibleType(Class<?> carrier, MemoryLayout layout, long addressSize) { 194 if (carrier.isPrimitive()) { 195 Utils.checkPrimitiveCarrierCompat(carrier, layout); 196 } else if (carrier == MemoryAddress.class) { 197 Utils.checkLayoutType(layout, ValueLayout.class); 198 if (layout.bitSize() != addressSize) 199 throw new IllegalArgumentException("Address size mismatch: " + addressSize + " != " + layout.bitSize()); 200 } else if (carrier == MemorySegment.class) { 201 Utils.checkLayoutType(layout, GroupLayout.class); 202 } else { 203 throw new IllegalArgumentException("Unsupported carrier: " + carrier); 204 } 205 } 206 207 public static void checkFunctionTypes(MethodType mt, FunctionDescriptor cDesc, long addressSize) { 208 if (mt.returnType() == void.class != cDesc.returnLayout().isEmpty()) 209 throw new IllegalArgumentException("Return type mismatch: " + mt + " != " + cDesc); 210 List<MemoryLayout> argLayouts = cDesc.argumentLayouts(); 211 if (mt.parameterCount() != argLayouts.size()) 212 throw new IllegalArgumentException("Arity mismatch: " + mt + " != " + cDesc); 213 214 int paramCount = mt.parameterCount(); 215 for (int i = 0; i < paramCount; i++) { 216 checkCompatibleType(mt.parameterType(i), argLayouts.get(i), addressSize); 217 } 218 cDesc.returnLayout().ifPresent(rl -> checkCompatibleType(mt.returnType(), rl, addressSize)); 219 } 220 221 public static Class<?> primitiveCarrierForSize(long size) { 222 if (size == 1) { 223 return byte.class; 224 } else if(size == 2) { 225 return short.class; 226 } else if (size <= 4) { 227 return int.class; 228 } else if (size <= 8) { 229 return long.class; 230 } 231 232 throw new IllegalArgumentException("Size too large: " + size); 233 } 234 235 public static ForeignLinker getSystemLinker() { 236 String arch = System.getProperty("os.arch"); 237 String os = System.getProperty("os.name"); 238 if (arch.equals("amd64") || arch.equals("x86_64")) { 239 if (os.startsWith("Windows")) { 240 return Windowsx64Linker.getInstance(); 241 } else { 242 return SysVx64Linker.getInstance(); 243 } 244 } else if (arch.equals("aarch64")) { 245 return AArch64Linker.getInstance(); 246 } 247 throw new UnsupportedOperationException("Unsupported os or arch: " + os + ", " + arch); 248 } 249 250 public static VaList newVaList(Consumer<VaList.Builder> actions) { 251 String name = CSupport.getSystemLinker().name(); 252 return switch(name) { 253 case Win64.NAME -> Windowsx64Linker.newVaList(actions); 254 case SysV.NAME -> SysVx64Linker.newVaList(actions); 255 case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform"); 256 default -> throw new IllegalStateException("Unknown linker name: " + name); 257 }; 258 } 259 260 public static VarHandle vhPrimitiveOrAddress(Class<?> carrier, MemoryLayout layout) { 261 return carrier == MemoryAddress.class 262 ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize()))) 263 : layout.varHandle(carrier); 264 } 265 266 public static VaList newVaListOfAddress(MemoryAddress ma) { 267 String name = CSupport.getSystemLinker().name(); 268 return switch(name) { 269 case Win64.NAME -> Windowsx64Linker.newVaListOfAddress(ma); 270 case SysV.NAME -> SysVx64Linker.newVaListOfAddress(ma); 271 case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform"); 272 default -> throw new IllegalStateException("Unknown linker name: " + name); 273 }; 274 } 275 276 public static class SimpleVaArg { 277 public final Class<?> carrier; 278 public final MemoryLayout layout; 279 public final Object value; 280 281 public SimpleVaArg(Class<?> carrier, MemoryLayout layout, Object value) { 282 this.carrier = carrier; 283 this.layout = layout; 284 this.value = value; 285 } 286 287 public VarHandle varHandle() { 288 return carrier == MemoryAddress.class 289 ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize()))) 290 : layout.varHandle(carrier); 291 } 292 } 293 }