/* * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.truffle.hotspot.nfi; import java.lang.reflect.Field; import org.graalvm.compiler.core.target.Backend; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.hotspot.meta.HotSpotProviders; import org.graalvm.compiler.options.OptionValues; import com.oracle.nfi.api.NativeFunctionInterface; import com.oracle.nfi.api.NativeFunctionPointer; import com.oracle.nfi.api.NativeLibraryHandle; import sun.misc.Unsafe; public class HotSpotNativeFunctionInterface implements NativeFunctionInterface { private final HotSpotNativeLibraryHandle rtldDefault; private final HotSpotNativeFunctionPointer libraryLoadFunctionPointer; private final HotSpotNativeFunctionPointer functionLookupFunctionPointer; private final NativeCallStubGraphBuilder graphBuilder; private HotSpotNativeFunctionHandle libraryLookupFunctionHandle; private HotSpotNativeFunctionHandle dllLookupFunctionHandle; public HotSpotNativeFunctionInterface(OptionValues options, HotSpotProviders providers, RawNativeCallNodeFactory factory, Backend backend, long dlopen, long dlsym, long rtldDefault) { this.rtldDefault = rtldDefault == GraalHotSpotVMConfig.INVALID_RTLD_DEFAULT_HANDLE ? null : new HotSpotNativeLibraryHandle("RTLD_DEFAULT", rtldDefault); this.libraryLoadFunctionPointer = new HotSpotNativeFunctionPointer(dlopen, "os::dll_load"); this.functionLookupFunctionPointer = new HotSpotNativeFunctionPointer(dlsym, "os::dll_lookup"); this.graphBuilder = new NativeCallStubGraphBuilder(options, providers, backend, factory); } @Override public HotSpotNativeLibraryHandle getLibraryHandle(String libPath) { if (libraryLookupFunctionHandle == null) { libraryLookupFunctionHandle = createHandle(libraryLoadFunctionPointer, long.class, long.class, long.class, int.class); } int ebufLen = 1024; // Allocating a single chunk for both the error message buffer and the // file name simplifies deallocation below. long buffer = UNSAFE.allocateMemory(ebufLen + libPath.length() + 1); long ebuf = buffer; long libPathCString = writeCString(UNSAFE, libPath, buffer + ebufLen); try { long handle = (long) libraryLookupFunctionHandle.call(libPathCString, ebuf, ebufLen); if (handle == 0) { throw new UnsatisfiedLinkError(readCString(UNSAFE, ebuf, ebufLen)); } return new HotSpotNativeLibraryHandle(libPath, handle); } finally { UNSAFE.freeMemory(buffer); } } @Override public HotSpotNativeFunctionHandle getFunctionHandle(NativeLibraryHandle library, String name, Class<?> returnType, Class<?>... argumentTypes) { HotSpotNativeFunctionPointer functionPointer = lookupFunctionPointer(name, library, false); return createHandle(functionPointer, returnType, argumentTypes); } @Override public HotSpotNativeFunctionHandle getFunctionHandle(NativeLibraryHandle[] libraries, String name, Class<?> returnType, Class<?>... argumentTypes) { HotSpotNativeFunctionPointer functionPointer = null; for (NativeLibraryHandle libraryHandle : libraries) { functionPointer = lookupFunctionPointer(name, libraryHandle, false); if (functionPointer != null) { return createHandle(functionPointer, returnType, argumentTypes); } } // Fall back to default library path return getFunctionHandle(name, returnType, argumentTypes); } @Override public HotSpotNativeFunctionHandle getFunctionHandle(String name, Class<?> returnType, Class<?>... argumentTypes) { if (rtldDefault == null) { throw new UnsatisfiedLinkError(name); } return getFunctionHandle(rtldDefault, name, returnType, argumentTypes); } private HotSpotNativeFunctionPointer lookupFunctionPointer(String name, NativeLibraryHandle library, boolean linkageErrorIfMissing) { if (name == null || library == null) { throw new NullPointerException(); } if (dllLookupFunctionHandle == null) { dllLookupFunctionHandle = createHandle(functionLookupFunctionPointer, long.class, long.class, long.class); } long nameCString = createCString(UNSAFE, name); try { long functionPointer = (long) dllLookupFunctionHandle.call(((HotSpotNativeLibraryHandle) library).value, nameCString); if (functionPointer == 0L) { if (!linkageErrorIfMissing) { return null; } throw new UnsatisfiedLinkError(name); } return new HotSpotNativeFunctionPointer(functionPointer, name); } finally { UNSAFE.freeMemory(nameCString); } } @Override public HotSpotNativeFunctionHandle getFunctionHandle(NativeFunctionPointer functionPointer, Class<?> returnType, Class<?>... argumentTypes) { if (!(functionPointer instanceof HotSpotNativeFunctionPointer)) { throw new UnsatisfiedLinkError(functionPointer.getName()); } return createHandle(functionPointer, returnType, argumentTypes); } private HotSpotNativeFunctionHandle createHandle(NativeFunctionPointer functionPointer, Class<?> returnType, Class<?>... argumentTypes) { HotSpotNativeFunctionPointer hs = (HotSpotNativeFunctionPointer) functionPointer; if (hs != null) { HotSpotNativeFunctionHandle handle = new HotSpotNativeFunctionHandle(graphBuilder, hs, returnType, argumentTypes); graphBuilder.installNativeFunctionStub(handle); return handle; } else { return null; } } @Override public HotSpotNativeFunctionPointer getFunctionPointer(NativeLibraryHandle[] libraries, String name) { for (NativeLibraryHandle libraryHandle : libraries) { HotSpotNativeFunctionPointer functionPointer = lookupFunctionPointer(name, libraryHandle, false); if (functionPointer != null) { return functionPointer; } } // Fall back to default library path if (rtldDefault == null) { throw new UnsatisfiedLinkError(name); } return lookupFunctionPointer(name, rtldDefault, false); } @Override public boolean isDefaultLibrarySearchSupported() { return rtldDefault != null; } @Override public NativeFunctionPointer getNativeFunctionPointerFromRawValue(long rawValue) { return new HotSpotNativeFunctionPointer(rawValue, null); } private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { try { return Unsafe.getUnsafe(); } catch (SecurityException se) { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (Exception e) { throw new RuntimeException("exception while trying to get Unsafe", e); } } } /** * Copies the contents of a {@link String} to a native memory buffer as a {@code '\0'} * terminated C string. The native memory buffer is allocated via * {@link Unsafe#allocateMemory(long)}. The caller is responsible for releasing the buffer when * it is no longer needed via {@link Unsafe#freeMemory(long)}. * * @return the native memory pointer of the C string created from {@code s} */ private static long createCString(Unsafe unsafe, String s) { return writeCString(unsafe, s, unsafe.allocateMemory(s.length() + 1)); } /** * Writes the contents of a {@link String} to a native memory buffer as a {@code '\0'} * terminated C string. The caller is responsible for ensuring the buffer is at least * {@code s.length() + 1} bytes long. The caller is also responsible for releasing the buffer * when it is no longer. * * @return the value of {@code buf} */ private static long writeCString(Unsafe unsafe, String s, long buf) { int size = s.length(); for (int i = 0; i < size; i++) { unsafe.putByte(buf + i, (byte) s.charAt(i)); } unsafe.putByte(buf + size, (byte) '\0'); return buf; } /** * Reads a {@code '\0'} terminated C string. */ private static String readCString(Unsafe unsafe, long buf, long bufLen) { final StringBuilder builder = new StringBuilder(); for (long n = 0; n < bufLen; n++) { final char c = (char) unsafe.getByte(buf + n); if (c == '\0') { break; } builder.append(c); } return builder.toString(); } }