1 /* 2 * Copyright (c) 2022, 2023, 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.internal.foreign.SystemLookup; 28 import jdk.internal.foreign.Utils; 29 import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; 30 import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; 31 import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; 32 import jdk.internal.foreign.abi.fallback.FallbackLinker; 33 import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64Linker; 34 import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker; 35 import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker; 36 import jdk.internal.foreign.abi.s390.linux.LinuxS390Linker; 37 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; 38 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; 39 import jdk.internal.foreign.layout.AbstractLayout; 40 import jdk.internal.reflect.CallerSensitive; 41 import jdk.internal.reflect.Reflection; 42 43 import java.lang.foreign.AddressLayout; 44 import java.lang.foreign.GroupLayout; 45 import java.lang.foreign.MemoryLayout; 46 import java.lang.foreign.Arena; 47 import java.lang.foreign.FunctionDescriptor; 48 import java.lang.foreign.Linker; 49 import java.lang.foreign.MemorySegment; 50 import java.lang.foreign.PaddingLayout; 51 import java.lang.foreign.SequenceLayout; 52 import java.lang.foreign.StructLayout; 53 import java.lang.foreign.UnionLayout; 54 import java.lang.foreign.ValueLayout; 55 import java.lang.invoke.MethodHandle; 56 import java.lang.invoke.MethodType; 57 import java.util.List; 58 import java.nio.ByteOrder; 59 import java.util.Objects; 60 import java.util.Set; 61 62 public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker, 63 SysVx64Linker, WindowsAArch64Linker, 64 Windowsx64Linker, 65 LinuxPPC64Linker, LinuxPPC64leLinker, 66 LinuxRISCV64Linker, LinuxS390Linker, 67 FallbackLinker { 68 69 public interface UpcallStubFactory { 70 MemorySegment makeStub(MethodHandle target, Arena arena); 71 } 72 73 private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} 74 private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>(); 75 private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>(); 76 77 @Override 78 @CallerSensitive 79 public final MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) { 80 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle"); 81 SharedUtils.checkSymbol(symbol); 82 return downcallHandle0(function, options).bindTo(symbol); 83 } 84 85 @Override 86 @CallerSensitive 87 public final MethodHandle downcallHandle(FunctionDescriptor function, Option... options) { 88 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle"); 89 return downcallHandle0(function, options); 90 } 91 92 private final MethodHandle downcallHandle0(FunctionDescriptor function, Option... options) { 93 Objects.requireNonNull(function); 94 Objects.requireNonNull(options); 95 checkLayouts(function); 96 function = stripNames(function); 97 LinkerOptions optionSet = LinkerOptions.forDowncall(function, options); 98 validateVariadicLayouts(function, optionSet); 99 100 return DOWNCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> { 101 FunctionDescriptor fd = linkRequest.descriptor(); 102 MethodType type = fd.toMethodType(); 103 MethodHandle handle = arrangeDowncall(type, fd, linkRequest.options()); 104 handle = SharedUtils.maybeCheckCaptureSegment(handle, linkRequest.options()); 105 handle = SharedUtils.maybeInsertAllocator(fd, handle); 106 return handle; 107 }); 108 } 109 110 protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options); 111 112 @Override 113 @CallerSensitive 114 public final MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) { 115 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "upcallStub"); 116 Objects.requireNonNull(arena); 117 Objects.requireNonNull(target); 118 Objects.requireNonNull(function); 119 checkLayouts(function); 120 SharedUtils.checkExceptions(target); 121 function = stripNames(function); 122 LinkerOptions optionSet = LinkerOptions.forUpcall(function, options); 123 124 MethodType type = function.toMethodType(); 125 if (!type.equals(target.type())) { 126 throw new IllegalArgumentException("Wrong method handle type: " + target.type()); 127 } 128 129 UpcallStubFactory factory = UPCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> 130 arrangeUpcall(type, linkRequest.descriptor(), linkRequest.options())); 131 return factory.makeStub(target, arena); 132 } 133 134 protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options); 135 136 @Override 137 public SystemLookup defaultLookup() { 138 return SystemLookup.getInstance(); 139 } 140 141 /** {@return byte order used by this linker} */ 142 protected abstract ByteOrder linkerByteOrder(); 143 144 // C spec mandates that variadic arguments smaller than int are promoted to int, 145 // and float is promoted to double 146 // See: https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions 147 // We reject the corresponding layouts here, to avoid issues where unsigned values 148 // are sign extended when promoted. (as we don't have a way to unambiguously represent signed-ness atm). 149 private void validateVariadicLayouts(FunctionDescriptor function, LinkerOptions optionSet) { 150 if (optionSet.isVariadicFunction()) { 151 List<MemoryLayout> argumentLayouts = function.argumentLayouts(); 152 List<MemoryLayout> variadicLayouts = argumentLayouts.subList(optionSet.firstVariadicArgIndex(), argumentLayouts.size()); 153 154 for (MemoryLayout variadicLayout : variadicLayouts) { 155 if (variadicLayout.equals(ValueLayout.JAVA_BOOLEAN) 156 || variadicLayout.equals(ValueLayout.JAVA_BYTE) 157 || variadicLayout.equals(ValueLayout.JAVA_CHAR) 158 || variadicLayout.equals(ValueLayout.JAVA_SHORT) 159 || variadicLayout.equals(ValueLayout.JAVA_FLOAT)) { 160 throw new IllegalArgumentException("Invalid variadic argument layout: " + variadicLayout); 161 } 162 } 163 } 164 } 165 166 private void checkLayouts(FunctionDescriptor descriptor) { 167 descriptor.returnLayout().ifPresent(this::checkLayout); 168 descriptor.argumentLayouts().forEach(this::checkLayout); 169 } 170 171 private void checkLayout(MemoryLayout layout) { 172 // Note: we should not worry about padding layouts, as they cannot be present in a function descriptor 173 if (layout instanceof SequenceLayout) { 174 throw new IllegalArgumentException("Unsupported layout: " + layout); 175 } else { 176 checkLayoutRecursive(layout); 177 } 178 } 179 180 private void checkLayoutRecursive(MemoryLayout layout) { 181 if (layout instanceof ValueLayout vl) { 182 checkSupported(vl); 183 } else if (layout instanceof StructLayout sl) { 184 checkHasNaturalAlignment(layout); 185 long offset = 0; 186 long lastUnpaddedOffset = 0; 187 for (MemoryLayout member : sl.memberLayouts()) { 188 // check element offset before recursing so that an error points at the 189 // outermost layout first 190 checkMemberOffset(sl, member, lastUnpaddedOffset, offset); 191 checkLayoutRecursive(member); 192 193 offset += member.byteSize(); 194 if (!(member instanceof PaddingLayout)) { 195 lastUnpaddedOffset = offset; 196 } 197 } 198 checkGroupSize(sl, lastUnpaddedOffset); 199 } else if (layout instanceof UnionLayout ul) { 200 checkHasNaturalAlignment(layout); 201 long maxUnpaddedLayout = 0; 202 for (MemoryLayout member : ul.memberLayouts()) { 203 checkLayoutRecursive(member); 204 if (!(member instanceof PaddingLayout)) { 205 maxUnpaddedLayout = Long.max(maxUnpaddedLayout, member.byteSize()); 206 } 207 } 208 checkGroupSize(ul, maxUnpaddedLayout); 209 } else if (layout instanceof SequenceLayout sl) { 210 checkHasNaturalAlignment(layout); 211 checkLayoutRecursive(sl.elementLayout()); 212 } 213 } 214 215 // check for trailing padding 216 private static void checkGroupSize(GroupLayout gl, long maxUnpaddedOffset) { 217 long expectedSize = Utils.alignUp(maxUnpaddedOffset, gl.byteAlignment()); 218 if (gl.byteSize() != expectedSize) { 219 throw new IllegalArgumentException("Layout '" + gl + "' has unexpected size: " 220 + gl.byteSize() + " != " + expectedSize); 221 } 222 } 223 224 // checks both that there is no excess padding between 'memberLayout' and 225 // the previous layout 226 private static void checkMemberOffset(StructLayout parent, MemoryLayout memberLayout, 227 long lastUnpaddedOffset, long offset) { 228 long expectedOffset = Utils.alignUp(lastUnpaddedOffset, memberLayout.byteAlignment()); 229 if (expectedOffset != offset) { 230 throw new IllegalArgumentException("Member layout '" + memberLayout + "', of '" + parent + "'" + 231 " found at unexpected offset: " + offset + " != " + expectedOffset); 232 } 233 } 234 235 private static void checkSupported(ValueLayout valueLayout) { 236 valueLayout = valueLayout.withoutName(); 237 if (valueLayout instanceof AddressLayout addressLayout) { 238 valueLayout = addressLayout.withoutTargetLayout(); 239 } 240 if (!SUPPORTED_LAYOUTS.contains(valueLayout.withoutName())) { 241 throw new IllegalArgumentException("Unsupported layout: " + valueLayout); 242 } 243 } 244 245 private static void checkHasNaturalAlignment(MemoryLayout layout) { 246 if (!((AbstractLayout<?>) layout).hasNaturalAlignment()) { 247 throw new IllegalArgumentException("Layout alignment must be natural alignment: " + layout); 248 } 249 } 250 251 private static MemoryLayout stripNames(MemoryLayout ml) { 252 // we don't care about transferring alignment and byte order here 253 // since the linker already restricts those such that they will always be the same 254 return switch (ml) { 255 case StructLayout sl -> MemoryLayout.structLayout(stripNames(sl.memberLayouts())); 256 case UnionLayout ul -> MemoryLayout.unionLayout(stripNames(ul.memberLayouts())); 257 case SequenceLayout sl -> MemoryLayout.sequenceLayout(sl.elementCount(), stripNames(sl.elementLayout())); 258 case AddressLayout al -> al.targetLayout() 259 .map(tl -> al.withoutName().withTargetLayout(stripNames(tl))) 260 .orElseGet(al::withoutName); 261 default -> ml.withoutName(); // ValueLayout and PaddingLayout 262 }; 263 } 264 265 private static MemoryLayout[] stripNames(List<MemoryLayout> layouts) { 266 return layouts.stream() 267 .map(AbstractLinker::stripNames) 268 .toArray(MemoryLayout[]::new); 269 } 270 271 private static FunctionDescriptor stripNames(FunctionDescriptor function) { 272 return function.returnLayout() 273 .map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts()))) 274 .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts()))); 275 } 276 277 private static final Set<MemoryLayout> SUPPORTED_LAYOUTS = Set.of( 278 ValueLayout.JAVA_BOOLEAN, 279 ValueLayout.JAVA_BYTE, 280 ValueLayout.JAVA_CHAR, 281 ValueLayout.JAVA_SHORT, 282 ValueLayout.JAVA_INT, 283 ValueLayout.JAVA_FLOAT, 284 ValueLayout.JAVA_LONG, 285 ValueLayout.JAVA_DOUBLE, 286 ValueLayout.ADDRESS 287 ); 288 }