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 }