/**
* Copyright 2013, Landz and its contributors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package z.znr.invoke.linux.x64;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NativeLibrary {
private static final List<String> libraryNames = new ArrayList<String>();
private static final List<String> searchPaths = Collections.unmodifiableList(getPropertyPaths("java.library.path"));
private static final List<com.kenai.jffi.Library> nativeLibraries = new ArrayList<com.kenai.jffi.Library>();
private NativeLibrary() {}
public static void loadLibrary(String libraryName) {
com.kenai.jffi.Library lib;
lib = openLibrary(libraryName);
if (lib == null) {
String path;
if (libraryName != null && (path = locateLibrary(libraryName)) != null && !libraryName.equals(path)) {
lib = openLibrary(path);
}
}
if (lib == null) {
throw new UnsatisfiedLinkError(com.kenai.jffi.Library.getLastError());
}
nativeLibraries.add(lib);
libraryNames.add(libraryName);
}
private static final List<String> getPropertyPaths(String propName) {
String value = System.getProperty(propName);
if (value != null) {
String[] paths = value.split(File.pathSeparator);
return new ArrayList<String>(Arrays.asList(paths));
}
return Collections.emptyList();
}
private static final String locateLibrary(String libraryName) {
if (new File(libraryName).isAbsolute()) {
return libraryName;
}
return locateLibrary(libraryName, searchPaths);
}
private static final String locateLibrary(final String libName, List<String> libraryPath) {
FilenameFilter filter = new FilenameFilter() {
Pattern p = Pattern.compile("lib" + libName + "\\.so\\.[0-9]+$");
String exact = "lib" + libName + ".so";
public boolean accept(File dir, String name) {
return p.matcher(name).matches() || exact.equals(name);
}
};
List<File> matches = new LinkedList<File>();
for (String path : libraryPath) {
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
//
int version = 0;
String bestMatch = null;
for (File file : matches) {
String path = file.getAbsolutePath();
if (bestMatch == null && path.endsWith(".so")) {
bestMatch = path;
version = 0;
} else {
String num = path.substring(path.lastIndexOf(".so.") + 4);
try {
if (Integer.parseInt(num) >= version) {
bestMatch = path;
}
} catch (NumberFormatException e) {
} // Just skip if not a number
}
}
return bestMatch != null ? bestMatch : mapLibraryName(libName);
}
private static final String mapLibraryName(String libName) {
// Older JDK on linux map 'c' to 'libc.so' which doesn't work
return "c".equals(libName) || "libc.so".equals(libName)
? "libc.so.6" : System.mapLibraryName(libName);
}
public static final long getSymbolAddress(String name) {
for (com.kenai.jffi.Library l : nativeLibraries) {
long address = l.getSymbolAddress(name);
if (address != 0) {
return address;
}
}
return 0;
}
public static final long findSymbolAddress(String name) {
long address = getSymbolAddress(name);
if (address == 0) {
throw new UnsatisfiedLinkError(com.kenai.jffi.Library.getLastError());
}
return address;
}
private static final Pattern BAD_ELF = Pattern.compile("(.*): invalid ELF header");
private static final Pattern ELF_GROUP = Pattern.compile("GROUP\\s*\\(\\s*(\\S*).*\\)");
private static final com.kenai.jffi.Library openLibrary(String path) {
com.kenai.jffi.Library lib;
lib = com.kenai.jffi.Library.getCachedInstance(path, com.kenai.jffi.Library.LAZY | com.kenai.jffi.Library.GLOBAL);
if (lib != null) {
return lib;
}
// If dlopen() fails with 'invalid ELF header', then it is likely to be a ld script - parse it for the real library path
Matcher badElf = BAD_ELF.matcher(com.kenai.jffi.Library.getLastError());
if (badElf.lookingAt()) {
File f = new File(badElf.group(1));
if (f.isFile() && f.length() < (4 * 1024)) {
Matcher sharedObject = ELF_GROUP.matcher(readAll(f));
if (sharedObject.lookingAt()) {
return com.kenai.jffi.Library.getCachedInstance(sharedObject.group(1), com.kenai.jffi.Library.LAZY | com.kenai.jffi.Library.GLOBAL);
}
}
}
return null;
}
private static final String readAll(File f) {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
if (br != null) try { br.close(); } catch (IOException e) { throw new RuntimeException(e); }
}
}
}