/* Copyright (c) 2007 Wayne Meissner, All Rights Reserved * Copyright (c) 2007-2013 Timothy Wall, All Rights Reserved * * The contents of this file is dual-licensed under 2 * alternative Open Source/Free licenses: LGPL 2.1 or later and * Apache License 2.0. (starting with JNA version 4.0.0). * * You can freely decide which license you want to apply to * the project. * * You may obtain a copy of the LGPL License at: * * http://www.gnu.org/licenses/licenses.html * * A copy is also included in the downloadable source code package * containing JNA, in file "LGPL2.1". * * You may obtain a copy of the Apache License at: * * http://www.apache.org/licenses/ * * A copy is also included in the downloadable source code package * containing JNA, in file "AL2.0". */ package com.sun.jna; import java.io.BufferedReader; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; /** * Provides management of native library resources. One instance of this * class corresponds to a single loaded native library. May also be used * to map to the current process (see {@link NativeLibrary#getProcess()}). * <p> * <a name=library_search_paths></a> * <b>Library Search Paths</b> * A search for a given library will scan the following locations: * <ol> * <li><code>jna.library.path</code> User-customizable path * <li><code>jna.platform.library.path</code> Platform-specific paths * <li>On OSX, <code>~/Library/Frameworks</code>, * <code>/Library/Frameworks</code>, and * <code>/System/Library/Frameworks</code> will be searched for a framework * with a name corresponding to that requested. Absolute paths to frameworks * are also accepted, either ending at the framework name (sans ".framework") * or the full path to the framework shared library * (e.g. CoreServices.framework/CoreServices). * <li>Context class loader classpath. Deployed native libraries may be * installed on the classpath under * <code>${os-prefix}/LIBRARY_FILENAME</code>, where <code>${os-prefix}</code> * is the OS/Arch prefix returned by {@link * Platform#getNativeLibraryResourcePrefix()}. If bundled in a jar file, the * resource will be extracted to <code>jna.tmpdir</code> for loading, and * later removed (but only if <code>jna.nounpack</code> is false or not set). * </ol> * You may set the system property <code>jna.debug_load=true</code> to make * JNA print the steps of its library search to the console. * @author Wayne Meissner, split library loading from Function.java * @author twall */ public class NativeLibrary { private long handle; private final String libraryName; private final String libraryPath; private final Map<String, Function> functions = new HashMap<String, Function>(); final int callFlags; private String encoding; final Map<String, ?> options; private static final Map<String, Reference<NativeLibrary>> libraries = new HashMap<String, Reference<NativeLibrary>>(); private static final Map<String, List<String>> searchPaths = Collections.synchronizedMap(new HashMap<String, List<String>>()); private static final List<String> librarySearchPath = new ArrayList<String>(); static { // Force initialization of native library if (Native.POINTER_SIZE == 0) throw new Error("Native library not initialized"); } private static String functionKey(String name, int flags, String encoding) { return name + "|" + flags + "|" + encoding; } private NativeLibrary(String libraryName, String libraryPath, long handle, Map<String, ?> options) { this.libraryName = getLibraryName(libraryName); this.libraryPath = libraryPath; this.handle = handle; Object option = options.get(Library.OPTION_CALLING_CONVENTION); int callingConvention = option instanceof Number ? ((Number)option).intValue() : Function.C_CONVENTION; this.callFlags = callingConvention; this.options = options; this.encoding = (String)options.get(Library.OPTION_STRING_ENCODING); if (this.encoding == null) { this.encoding = Native.getDefaultStringEncoding(); } // Special workaround for w32 kernel32.GetLastError // Short-circuit the function to use built-in GetLastError access if (Platform.isWindows() && "kernel32".equals(this.libraryName.toLowerCase())) { synchronized(functions) { Function f = new Function(this, "GetLastError", Function.ALT_CONVENTION, encoding) { @Override Object invoke(Object[] args, Class<?> returnType, boolean b, int fixedArgs) { return Integer.valueOf(Native.getLastError()); } @Override Object invoke(Method invokingMethod, Class<?>[] paramTypes, Class<?> returnType, Object[] inArgs, Map<String, ?> options) { return Integer.valueOf(Native.getLastError()); } }; functions.put(functionKey("GetLastError", callFlags, encoding), f); } } } private static final int DEFAULT_OPEN_OPTIONS = -1; private static int openFlags(Map<String, ?> options) { Object opt = options.get(Library.OPTION_OPEN_FLAGS); if (opt instanceof Number) { return ((Number)opt).intValue(); } return DEFAULT_OPEN_OPTIONS; } private static NativeLibrary loadLibrary(String libraryName, Map<String, ?> options) { if (Native.DEBUG_LOAD) { System.out.println("Looking for library '" + libraryName + "'"); } boolean isAbsolutePath = new File(libraryName).isAbsolute(); List<String> searchPath = new ArrayList<String>(); int openFlags = openFlags(options); // Append web start path, if available. Note that this does not // attempt any library name variations String webstartPath = Native.getWebStartLibraryPath(libraryName); if (webstartPath != null) { if (Native.DEBUG_LOAD) { System.out.println("Adding web start path " + webstartPath); } searchPath.add(webstartPath); } // // Prepend any custom search paths specifically for this library // List<String> customPaths = searchPaths.get(libraryName); if (customPaths != null) { synchronized (customPaths) { searchPath.addAll(0, customPaths); } } if (Native.DEBUG_LOAD) { System.out.println("Adding paths from jna.library.path: " + System.getProperty("jna.library.path")); } searchPath.addAll(initPaths("jna.library.path")); String libraryPath = findLibraryPath(libraryName, searchPath); long handle = 0; // // Only search user specified paths first. This will also fall back // to dlopen/LoadLibrary() since findLibraryPath returns the mapped // name if it cannot find the library. // try { if (Native.DEBUG_LOAD) { System.out.println("Trying " + libraryPath); } handle = Native.open(libraryPath, openFlags); } catch(UnsatisfiedLinkError e) { // Add the system paths back for all fallback searching if (Native.DEBUG_LOAD) { System.out.println("Adding system paths: " + librarySearchPath); } searchPath.addAll(librarySearchPath); } try { if (handle == 0) { libraryPath = findLibraryPath(libraryName, searchPath); if (Native.DEBUG_LOAD) { System.out.println("Trying " + libraryPath); } handle = Native.open(libraryPath, openFlags); if (handle == 0) { throw new UnsatisfiedLinkError("Failed to load library '" + libraryName + "'"); } } } catch(UnsatisfiedLinkError e) { // For android, try to "preload" the library using // System.loadLibrary(), which looks into the private /data/data // path, not found in any properties if (Platform.isAndroid()) { try { if (Native.DEBUG_LOAD) { System.out.println("Preload (via System.loadLibrary) " + libraryName); } System.loadLibrary(libraryName); handle = Native.open(libraryPath, openFlags); } catch(UnsatisfiedLinkError e2) { e = e2; } } else if (Platform.isLinux() || Platform.isFreeBSD()) { // // Failed to load the library normally - try to match libfoo.so.* // if (Native.DEBUG_LOAD) { System.out.println("Looking for version variants"); } libraryPath = matchLibrary(libraryName, searchPath); if (libraryPath != null) { if (Native.DEBUG_LOAD) { System.out.println("Trying " + libraryPath); } try { handle = Native.open(libraryPath, openFlags); } catch(UnsatisfiedLinkError e2) { e = e2; } } } // Search framework libraries on OS X else if (Platform.isMac() && !libraryName.endsWith(".dylib")) { if (Native.DEBUG_LOAD) { System.out.println("Looking for matching frameworks"); } libraryPath = matchFramework(libraryName); if (libraryPath != null) { try { if (Native.DEBUG_LOAD) { System.out.println("Trying " + libraryPath); } handle = Native.open(libraryPath, openFlags); } catch(UnsatisfiedLinkError e2) { e = e2; } } } // Try the same library with a "lib" prefix else if (Platform.isWindows() && !isAbsolutePath) { if (Native.DEBUG_LOAD) { System.out.println("Looking for lib- prefix"); } libraryPath = findLibraryPath("lib" + libraryName, searchPath); if (libraryPath != null) { if (Native.DEBUG_LOAD) { System.out.println("Trying " + libraryPath); } try { handle = Native.open(libraryPath, openFlags); } catch(UnsatisfiedLinkError e2) { e = e2; } } } // As a last resort, try to extract the library from the class // path, using the current context class loader. if (handle == 0) { try { File embedded = Native.extractFromResourcePath(libraryName, (ClassLoader)options.get(Library.OPTION_CLASSLOADER)); try { handle = Native.open(embedded.getAbsolutePath(), openFlags); libraryPath = embedded.getAbsolutePath(); } finally { // Don't leave temporary files around if (Native.isUnpacked(embedded)) { Native.deleteLibrary(embedded); } } } catch(IOException e2) { e = new UnsatisfiedLinkError(e2.getMessage()); } } if (handle == 0) { throw new UnsatisfiedLinkError("Unable to load library '" + libraryName + "': " + e.getMessage()); } } if (Native.DEBUG_LOAD) { System.out.println("Found library '" + libraryName + "' at " + libraryPath); } return new NativeLibrary(libraryName, libraryPath, handle, options); } /** Look for a matching framework (OSX) */ static String matchFramework(String libraryName) { File framework = new File(libraryName); if (framework.isAbsolute()) { if (libraryName.indexOf(".framework") != -1 && framework.exists()) { return framework.getAbsolutePath(); } framework = new File(new File(framework.getParentFile(), framework.getName() + ".framework"), framework.getName()); if (framework.exists()) { return framework.getAbsolutePath(); } } else { final String[] PREFIXES = { System.getProperty("user.home"), "", "/System" }; String suffix = libraryName.indexOf(".framework") == -1 ? libraryName + ".framework/" + libraryName : libraryName; for (int i=0;i < PREFIXES.length;i++) { String libraryPath = PREFIXES[i] + "/Library/Frameworks/" + suffix; if (new File(libraryPath).exists()) { return libraryPath; } } } return null; } private String getLibraryName(String libraryName) { String simplified = libraryName; final String BASE = "---"; String template = mapSharedLibraryName(BASE); int prefixEnd = template.indexOf(BASE); if (prefixEnd > 0 && simplified.startsWith(template.substring(0, prefixEnd))) { simplified = simplified.substring(prefixEnd); } String suffix = template.substring(prefixEnd + BASE.length()); int suffixStart = simplified.indexOf(suffix); if (suffixStart != -1) { simplified = simplified.substring(0, suffixStart); } return simplified; } /** * Returns an instance of NativeLibrary for the specified name. * The library is loaded if not already loaded. If already loaded, the * existing instance is returned.<p> * More than one name may map to the same NativeLibrary instance; only * a single instance will be provided for any given unique file path. * * @param libraryName The library name to load. * This can be short form (e.g. "c"), * an explicit version (e.g. "libc.so.6"), or * the full path to the library (e.g. "/lib/libc.so.6"). */ public static final NativeLibrary getInstance(String libraryName) { return getInstance(libraryName, Collections.<String, Object>emptyMap()); } /** * Returns an instance of NativeLibrary for the specified name. * The library is loaded if not already loaded. If already loaded, the * existing instance is returned.<p> * More than one name may map to the same NativeLibrary instance; only * a single instance will be provided for any given unique file path. * * @param libraryName The library name to load. * This can be short form (e.g. "c"), * an explicit version (e.g. "libc.so.6"), or * the full path to the library (e.g. "/lib/libc.so.6"). * @param classLoader The class loader to use to load the native library. * This only affects library loading when the native library is * included somewhere in the classpath, either bundled in a jar file * or as a plain file within the classpath. */ public static final NativeLibrary getInstance(String libraryName, ClassLoader classLoader) { return getInstance(libraryName, Collections.singletonMap(Library.OPTION_CLASSLOADER, classLoader)); } /** * Returns an instance of NativeLibrary for the specified name. * The library is loaded if not already loaded. If already loaded, the * existing instance is returned.<p> * More than one name may map to the same NativeLibrary instance; only * a single instance will be provided for any given unique file path. * * @param libraryName The library name to load. * This can be short form (e.g. "c"), * an explicit version (e.g. "libc.so.6" or * "QuickTime.framework/Versions/Current/QuickTime"), or * the full (absolute) path to the library (e.g. "/lib/libc.so.6"). * @param libraryOptions Native library options for the given library (see {@link Library}). */ public static final NativeLibrary getInstance(String libraryName, Map<String, ?> libraryOptions) { Map<String, Object> options = new HashMap<String, Object>(libraryOptions); if (options.get(Library.OPTION_CALLING_CONVENTION) == null) { options.put(Library.OPTION_CALLING_CONVENTION, Integer.valueOf(Function.C_CONVENTION)); } // Use current process to load libraries we know are already // loaded by the VM to ensure we get the correct version if ((Platform.isLinux() || Platform.isFreeBSD() || Platform.isAIX()) && Platform.C_LIBRARY_NAME.equals(libraryName)) { libraryName = null; } synchronized (libraries) { Reference<NativeLibrary> ref = libraries.get(libraryName + options); NativeLibrary library = (ref != null) ? ref.get() : null; if (library == null) { if (libraryName == null) { library = new NativeLibrary("<process>", null, Native.open(null, openFlags(options)), options); } else { library = loadLibrary(libraryName, options); } ref = new WeakReference<NativeLibrary>(library); libraries.put(library.getName() + options, ref); File file = library.getFile(); if (file != null) { libraries.put(file.getAbsolutePath() + options, ref); libraries.put(file.getName() + options, ref); } } return library; } } /** * Returns an instance of NativeLibrary which refers to the current * process. This is useful for accessing functions which were already * mapped by some other mechanism, without having to reference or even * know the exact name of the native library. */ public static synchronized final NativeLibrary getProcess() { return getInstance(null); } /** * Returns an instance of NativeLibrary which refers to the current * process. This is useful for accessing functions which were already * mapped by some other mechanism, without having to reference or even * know the exact name of the native library. */ public static synchronized final NativeLibrary getProcess(Map<String, ?> options) { return getInstance(null, options); } /** * Add a path to search for the specified library, ahead of any system * paths. This is similar to setting <code>jna.library.path</code>, but * only extends the search path for a single library. * * @param libraryName The name of the library to use the path for * @param path The path to use when trying to load the library */ public static final void addSearchPath(String libraryName, String path) { synchronized (searchPaths) { List<String> customPaths = searchPaths.get(libraryName); if (customPaths == null) { customPaths = Collections.synchronizedList(new ArrayList<String>()); searchPaths.put(libraryName, customPaths); } customPaths.add(path); } } /** * Create a new {@link Function} that is linked with a native * function that follows the NativeLibrary's calling convention. * * <p>The allocated instance represents a pointer to the named native * function from the library. * * @param functionName * Name of the native function to be linked with * @throws UnsatisfiedLinkError if the function is not found */ public Function getFunction(String functionName) { return getFunction(functionName, callFlags); } /** * Create a new {@link Function} that is linked with a native * function that follows the NativeLibrary's calling convention. * * <p>The allocated instance represents a pointer to the named native * function from the library. * * @param name * Name of the native function to be linked with. Uses a * function mapper option if one was provided to * transform the name. * @param method * Method to which the native function is to be mapped * @throws UnsatisfiedLinkError if the function is not found */ Function getFunction(String name, Method method) { FunctionMapper mapper = (FunctionMapper) options.get(Library.OPTION_FUNCTION_MAPPER); if (mapper != null) { name = mapper.getFunctionName(this, method); } // If there's native method profiler prefix, strip it String prefix = System.getProperty("jna.profiler.prefix", "$$YJP$$"); if (name.startsWith(prefix)) { name = name.substring(prefix.length()); } int flags = this.callFlags; Class<?>[] etypes = method.getExceptionTypes(); for (int i=0;i < etypes.length;i++) { if (LastErrorException.class.isAssignableFrom(etypes[i])) { flags |= Function.THROW_LAST_ERROR; } } return getFunction(name, flags); } /** * Create a new {@link Function} that is linked with a native * function that follows a given calling flags. * * @param functionName * Name of the native function to be linked with * @param callFlags * Flags affecting the function invocation * @throws UnsatisfiedLinkError if the function is not found */ public Function getFunction(String functionName, int callFlags) { return getFunction(functionName, callFlags, encoding); } /** * Create a new {@link Function} that is linked with a native * function that follows a given calling flags. * * @param functionName * Name of the native function to be linked with * @param callFlags * Flags affecting the function invocation * @param encoding * Encoding to use to convert between Java and native * strings. * @throws UnsatisfiedLinkError if the function is not found */ public Function getFunction(String functionName, int callFlags, String encoding) { if (functionName == null) { throw new NullPointerException("Function name may not be null"); } synchronized (functions) { String key = functionKey(functionName, callFlags, encoding); Function function = functions.get(key); if (function == null) { function = new Function(this, functionName, callFlags, encoding); functions.put(key, function); } return function; } } /** @return this native library instance's options. */ public Map<String, ?> getOptions() { return options; } /** Look up the given global variable within this library. * @param symbolName * @return Pointer representing the global variable address * @throws UnsatisfiedLinkError if the symbol is not found */ public Pointer getGlobalVariableAddress(String symbolName) { try { return new Pointer(getSymbolAddress(symbolName)); } catch(UnsatisfiedLinkError e) { throw new UnsatisfiedLinkError("Error looking up '" + symbolName + "': " + e.getMessage()); } } /** * Used by the Function class to locate a symbol * @throws UnsatisfiedLinkError if the symbol can't be found */ long getSymbolAddress(String name) { if (handle == 0) { throw new UnsatisfiedLinkError("Library has been unloaded"); } return Native.findSymbol(handle, name); } @Override public String toString() { return "Native Library <" + libraryPath + "@" + handle + ">"; } /** Returns the simple name of this library. */ public String getName() { return libraryName; } /** * Returns the file on disk corresponding to this NativeLibrary instance. * If this NativeLibrary represents the current process, this function will return null. */ public File getFile() { if (libraryPath == null) return null; return new File(libraryPath); } /** Close the library when it is no longer referenced. */ @Override protected void finalize() { dispose(); } /** Close all open native libraries. */ static void disposeAll() { Set<Reference<NativeLibrary>> values; synchronized(libraries) { values = new LinkedHashSet<Reference<NativeLibrary>>(libraries.values()); } for (Reference<NativeLibrary> ref : values) { NativeLibrary lib = ref.get(); if (lib != null) { lib.dispose(); } } } /** Close the native library we're mapped to. */ public void dispose() { Set<String> keys = new HashSet<String>(); synchronized(libraries) { for (Map.Entry<String, Reference<NativeLibrary>> e : libraries.entrySet()) { Reference<NativeLibrary> ref = e.getValue(); if (ref.get() == this) { keys.add(e.getKey()); } } for (String k : keys) { libraries.remove(k); } } synchronized(this) { if (handle != 0) { Native.close(handle); handle = 0; } } } private static List<String> initPaths(String key) { String value = System.getProperty(key, ""); if ("".equals(value)) { return Collections.emptyList(); } StringTokenizer st = new StringTokenizer(value, File.pathSeparator); List<String> list = new ArrayList<String>(); while (st.hasMoreTokens()) { String path = st.nextToken(); if (!"".equals(path)) { list.add(path); } } return list; } /** Use standard library search paths to find the library. */ private static String findLibraryPath(String libName, List<String> searchPath) { // // If a full path to the library was specified, don't search for it // if (new File(libName).isAbsolute()) { return libName; } // // Get the system name for the library (e.g. libfoo.so) // String name = mapSharedLibraryName(libName); // Search in the JNA paths for it for (String path : searchPath) { File file = new File(path, name); if (file.exists()) { return file.getAbsolutePath(); } if (Platform.isMac()) { // Native libraries delivered via JNLP class loader // may require a .jnilib extension to be found if (name.endsWith(".dylib")) { file = new File(path, name.substring(0, name.lastIndexOf(".dylib")) + ".jnilib"); if (file.exists()) { return file.getAbsolutePath(); } } } } // // Default to returning the mapped library name and letting the system // search for it // return name; } /** Similar to {@link System#mapLibraryName}, except that it maps to standard shared library formats rather than specifically JNI formats. @param libName base (undecorated) name of library */ static String mapSharedLibraryName(String libName) { if (Platform.isMac()) { if (libName.startsWith("lib") && (libName.endsWith(".dylib") || libName.endsWith(".jnilib"))) { return libName; } String name = System.mapLibraryName(libName); // On MacOSX, System.mapLibraryName() returns the .jnilib extension // (the suffix for JNI libraries); ordinarily shared libraries have // a .dylib suffix if (name.endsWith(".jnilib")) { return name.substring(0, name.lastIndexOf(".jnilib")) + ".dylib"; } return name; } else if (Platform.isLinux() || Platform.isFreeBSD()) { if (isVersionedName(libName) || libName.endsWith(".so")) { // A specific version was requested - use as is for search return libName; } } else if (Platform.isAIX()) { // can be libx.a, libx.a(shr.o), libx.so if (libName.startsWith("lib")) { return libName; } } else if (Platform.isWindows()) { if (libName.endsWith(".drv") || libName.endsWith(".dll")) { return libName; } } return System.mapLibraryName(libName); } private static boolean isVersionedName(String name) { if (name.startsWith("lib")) { int so = name.lastIndexOf(".so."); if (so != -1 && so + 4 < name.length()) { for (int i=so+4;i < name.length();i++) { char ch = name.charAt(i); if (!Character.isDigit(ch) && ch != '.') { return false; } } return true; } } return false; } /** * matchLibrary() is very Linux specific. It is here to deal with the case * where /usr/lib/libc.so does not exist, or it is not a valid symlink to * a versioned file (e.g. /lib/libc.so.6). */ static String matchLibrary(final String libName, List<String> searchPath) { File lib = new File(libName); if (lib.isAbsolute()) { searchPath = Arrays.asList(lib.getParent()); } FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String filename) { return (filename.startsWith("lib" + libName + ".so") || (filename.startsWith(libName + ".so") && libName.startsWith("lib"))) && isVersionedName(filename); } }; Collection<File> matches = new LinkedList<File>(); for (String path : searchPath) { File[] files = new File(path).listFiles(filter); if (files != null && files.length > 0) { matches.addAll(Arrays.asList(files)); } } // // Search through the results and return the highest numbered version // i.e. libc.so.6 is preferred over libc.so.5 double bestVersion = -1; String bestMatch = null; for (File f : matches) { String path = f.getAbsolutePath(); String ver = path.substring(path.lastIndexOf(".so.") + 4); double version = parseVersion(ver); if (version > bestVersion) { bestVersion = version; bestMatch = path; } } return bestMatch; } static double parseVersion(String ver) { double v = 0; double divisor = 1; int dot = ver.indexOf("."); while (ver != null) { String num; if (dot != -1) { num = ver.substring(0, dot); ver = ver.substring(dot + 1); dot = ver.indexOf("."); } else { num = ver; ver = null; } try { v += Integer.parseInt(num) / divisor; } catch(NumberFormatException e) { return 0; } divisor *= 100; } return v; } static { String webstartPath = Native.getWebStartLibraryPath("jnidispatch"); if (webstartPath != null) { librarySearchPath.add(webstartPath); } if (System.getProperty("jna.platform.library.path") == null && !Platform.isWindows()) { // Add default path lookups for unix-like systems String platformPath = ""; String sep = ""; String archPath = ""; // // Search first for an arch specific path if one exists, but always // include the generic paths if they exist. // NOTES (wmeissner): // Some older linux amd64 distros did not have /usr/lib64, and // 32bit distros only have /usr/lib. FreeBSD also only has // /usr/lib by default, with /usr/lib32 for 32bit compat. // Solaris seems to have both, but defaults to 32bit userland even // on 64bit machines, so we have to explicitly search the 64bit // one when running a 64bit JVM. // if (Platform.isLinux() || Platform.isSolaris() || Platform.isFreeBSD() || Platform.iskFreeBSD()) { // Linux & FreeBSD use /usr/lib32, solaris uses /usr/lib/32 archPath = (Platform.isSolaris() ? "/" : "") + Pointer.SIZE * 8; } String[] paths = { "/usr/lib" + archPath, "/lib" + archPath, "/usr/lib", "/lib", }; // Multi-arch support on Ubuntu (and other // multi-arch distributions) // paths is scanned against real directory // so for platforms which are not multi-arch // this should continue to work. if (Platform.isLinux() || Platform.iskFreeBSD() || Platform.isGNU()) { String multiArchPath = getMultiArchPath(); // Assemble path with all possible options paths = new String[] { "/usr/lib/" + multiArchPath, "/lib/" + multiArchPath, "/usr/lib" + archPath, "/lib" + archPath, "/usr/lib", "/lib", }; } // We might be wrong with the multiArchPath above. Raspbian, // the Raspberry Pi flavor of Debian, for example, uses // uses arm-linux-gnuabihf since it's using the hard-float // ABI for armv6. Other distributions might use a different // tuple for the same thing. Query ldconfig to get the additional // library paths it knows about. if (Platform.isLinux()) { ArrayList<String> ldPaths = getLinuxLdPaths(); // prepend the paths we already have for (int i=paths.length-1; 0 <= i; i--) { int found = ldPaths.indexOf(paths[i]); if (found != -1) { ldPaths.remove(found); } ldPaths.add(0, paths[i]); } paths = ldPaths.toArray(new String[ldPaths.size()]); } for (int i=0;i < paths.length;i++) { File dir = new File(paths[i]); if (dir.exists() && dir.isDirectory()) { platformPath += sep + paths[i]; sep = File.pathSeparator; } } if (!"".equals(platformPath)) { System.setProperty("jna.platform.library.path", platformPath); } } librarySearchPath.addAll(initPaths("jna.platform.library.path")); } private static String getMultiArchPath() { String cpu = Platform.ARCH; String kernel = Platform.iskFreeBSD() ? "-kfreebsd" : (Platform.isGNU() ? "" : "-linux"); String libc = "-gnu"; if (Platform.isIntel()) { cpu = (Platform.is64Bit() ? "x86_64" : "i386"); } else if (Platform.isPPC()) { cpu = (Platform.is64Bit() ? "powerpc64" : "powerpc"); } else if (Platform.isARM()) { cpu = "arm"; libc = "-gnueabi"; } return cpu + kernel + libc; } /** * Get the library paths from ldconfig cache. Tested against ldconfig 2.13. */ private static ArrayList<String> getLinuxLdPaths() { ArrayList<String> ldPaths = new ArrayList<String>(); try { Process process = Runtime.getRuntime().exec("/sbin/ldconfig -p"); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String buffer = ""; while ((buffer = reader.readLine()) != null) { int startPath = buffer.indexOf(" => "); int endPath = buffer.lastIndexOf('/'); if (startPath != -1 && endPath != -1 && startPath < endPath) { String path = buffer.substring(startPath+4, endPath); if (ldPaths.contains(path) == false) { ldPaths.add(path); } } } reader.close(); } catch (Exception e) { } return ldPaths; } }