/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.runtime; import java.util.Iterator; import org.jikesrvm.architecture.StackFrameLayout; import org.jikesrvm.VM; import org.jikesrvm.scheduler.RVMThread; import org.jikesrvm.util.ImmutableEntryHashMapRVM; import org.jikesrvm.util.StringUtilities; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Offset; /** * Interface to the dynamic libraries of our underlying operating system. */ public final class DynamicLibrary { /** * Currently loaded dynamic libraries. */ private static final ImmutableEntryHashMapRVM<String, DynamicLibrary> dynamicLibraries = new ImmutableEntryHashMapRVM<String, DynamicLibrary>(); /** * Add symbol for the bootloader to find symbols within it. */ public static void boot() { System.loadLibrary("jvm_jni"); } /** * The name of the library */ private final String libName; /** * Value returned from dlopen */ private final Address libHandler; /** * Address of JNI_OnLoad method */ private final Address jniOnLoad; /** * Address of JNI_OnUnLoad */ private final Address jniOnUnload; /** * Maintain a loaded library, call it's JNI_OnLoad function if present * @param libName library name * @param libHandler handle of loaded library */ private DynamicLibrary(String libName, Address libHandler) { this.libName = libName; this.libHandler = libHandler; jniOnLoad = getJNI_OnLoad(); jniOnUnload = getJNI_OnUnload(); try { callOnLoad(); } catch (UnsatisfiedLinkError e) { unload(); throw e; } if (VM.verboseJNI) { VM.sysWriteln("[Loaded native library: " + libName + "]"); } } /** * Get the unique JNI_OnLoad symbol associated with this library * @return JNI_OnLoad address or zero if not present */ private Address getJNI_OnLoad() { Address candidate = getSymbol("JNI_OnLoad"); Iterator<DynamicLibrary> libs = dynamicLibraries.valueIterator(); while (libs.hasNext()) { DynamicLibrary lib = libs.next(); if (lib.jniOnLoad.EQ(candidate)) { return Address.zero(); } } return candidate; } /** * Get the unique JNI_OnUnload symbol associated with this library * @return JNI_OnUnload address or zero if not present */ private Address getJNI_OnUnload() { Address candidate = getSymbol("JNI_OnUnload"); Iterator<DynamicLibrary> libs = dynamicLibraries.valueIterator(); while (libs.hasNext()) { DynamicLibrary lib = libs.next(); if (lib.jniOnUnload.EQ(candidate)) { return Address.zero(); } } return candidate; } /** * Called after we've successfully loaded the shared library */ private void callOnLoad() { // Run any JNI_OnLoad functions defined within the library if (!jniOnLoad.isZero()) { int version = runJNI_OnLoad(jniOnLoad); checkJNIVersion(version); } } /** * Method call to run the onload method. Performed as a native * method as the JNI_OnLoad method may contain JNI calls and we need * the RVMThread of the JNIEnv to be correctly populated (this * wouldn't happen with a SysCall) * * @param JNI_OnLoadAddress address of JNI_OnLoad function * @return the JNI version returned by the JNI_OnLoad function */ private static native int runJNI_OnLoad(Address JNI_OnLoadAddress); /** * Check JNI version is ≥ 1 and ≤ 1.4 and if not throw an * UnsatisfiedLinkError * @param version to check */ private static void checkJNIVersion(int version) { int major = version >>> 16; int minor = version & 0xFFFF; if (major != 1 || minor > 4) { throw new UnsatisfiedLinkError("Unsupported JNI version: " + major + "." + minor); } } /** * @return the true name of the dynamic library */ public String getLibName() { return libName; } /** * look up this dynamic library for a symbol * @param symbolName symbol name * @return The <code>Address</code> of the symbol system handler * (or an address of a PowerPC Linkage triplet). * (-1: not found or couldn't be created) */ public Address getSymbol(String symbolName) { // Convert file name from unicode to filesystem character set // (assume file name is ascii, for now). // byte[] asciiName = StringUtilities.stringToBytesNullTerminated(symbolName); return SysCall.sysCall.sysDlsym(libHandler, asciiName); } /** * unload a dynamic library */ public void unload() { VM.sysWriteln("DynamicLibrary.unload: not implemented yet"); } /** * Tell the operating system to remove the dynamic library from the * system space. */ public void clean() { VM.sysWriteln("DynamicLibrary.clean: not implemented yet"); } @Override public String toString() { return "dynamic library " + libName + ", handler=0x" + Long.toHexString(libHandler.toWord().toLong()); } /** * Load a dynamic library * @param libName the name of the library to load. * @return 0 on failure, 1 on success */ public static synchronized int load(String libName) { DynamicLibrary dl = dynamicLibraries.get(libName); if (dl != null) { return 1; // success: already loaded } else { // Convert file name from unicode to filesystem character set. // (Assume file name is ASCII, for now). // byte[] asciiName = StringUtilities.stringToBytesNullTerminated(libName); // make sure we have enough stack to load the library. // This operation has been known to require more than 20K of stack. RVMThread myThread = RVMThread.getCurrentThread(); Offset remaining = Magic.getFramePointer().diff(myThread.stackLimit); int stackNeededInBytes = StackFrameLayout.getStackSizeDLOpen() - remaining.toInt(); if (stackNeededInBytes > 0) { if (myThread.hasNativeStackFrame()) { throw new java.lang.StackOverflowError("Not enough space to open shared library"); } else { RVMThread.resizeCurrentStack(myThread.getStackLength() + stackNeededInBytes, null); } } Address libHandler = SysCall.sysCall.sysDlopen(asciiName); if (!libHandler.isZero()) { dynamicLibraries.put(libName, new DynamicLibrary(libName, libHandler)); return 1; } else { return 0; // fail; file does not exist } } } /** * Resolves a symbol to an address in a currently loaded dynamic library. * @param symbol the symbol to resolves * @return the address of the symbol of Address.zero() if it cannot be resolved */ public static synchronized Address resolveSymbol(String symbol) { for (DynamicLibrary lib : dynamicLibraries.values()) { Address symbolAddress = lib.getSymbol(symbol); if (!symbolAddress.isZero()) { return symbolAddress; } } return Address.zero(); } }