/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.runtime;
import org.jikesrvm.ArchitectureSpecific.VM_StackframeLayoutConstants;
import org.jikesrvm.VM;
import org.jikesrvm.scheduler.VM_Scheduler;
import org.jikesrvm.scheduler.VM_Thread;
import org.jikesrvm.scheduler.greenthreads.VM_FileSystem;
import org.jikesrvm.util.VM_HashMap;
import org.jikesrvm.util.VM_StringUtilities;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* Interface to the dynamic libraries of our underlying operating system.
*/
public final class VM_DynamicLibrary {
/**
* Currently loaded dynamic libraries.
*/
private static final VM_HashMap<String, VM_DynamicLibrary> dynamicLibraries =
new VM_HashMap<String, VM_DynamicLibrary>();
/**
* Add symbol for the boot image runner to find symbols within it.
*/
public static void boot() {
System.loadLibrary("rvmdynlib");
}
/**
* The name of the library
*/
private final String libName;
/**
* Value returned from dlopen
*/
private final Address libHandler;
/**
* Load a dynamic library and maintain it in this object.
* @param libraryName library name
*/
private VM_DynamicLibrary(String libraryName) {
// Convert file name from unicode to filesystem character set.
// (Assume file name is ASCII, for now).
//
byte[] asciiName = VM_StringUtilities.stringToBytesNullTerminated(libraryName);
// make sure we have enough stack to load the library.
// This operation has been known to require more than 20K of stack.
VM_Thread myThread = VM_Scheduler.getCurrentThread();
Offset remaining = VM_Magic.getFramePointer().diff(myThread.stackLimit);
int stackNeededInBytes = VM_StackframeLayoutConstants.STACK_SIZE_DLOPEN - remaining.toInt();
if (stackNeededInBytes > 0) {
if (myThread.hasNativeStackFrame()) {
throw new java.lang.StackOverflowError("dlopen");
} else {
VM_Thread.resizeCurrentStack(myThread.getStackLength() + stackNeededInBytes, null);
}
}
libHandler = VM_SysCall.sysCall.sysDlopen(asciiName);
if (libHandler.isZero()) {
VM.sysWriteln("error loading library: " + libraryName);
throw new UnsatisfiedLinkError();
}
libName = libraryName;
try {
callOnLoad();
} catch (UnsatisfiedLinkError e) {
unload();
throw e;
}
if (VM.verboseJNI) {
VM.sysWriteln("[Loaded native library: " + libName + "]");
}
}
/**
* Called after we've successfully loaded the shared library
*/
private void callOnLoad() {
// Run any JNI_OnLoad functions defined within the library
Address JNI_OnLoadAddress = getSymbol("JNI_OnLoad");
if (!JNI_OnLoadAddress.isZero()) {
int version = runJNI_OnLoad(JNI_OnLoadAddress);
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 VM_Processor of the JNIEnv to be correctly populated (this
* wouldn't happen with a VM_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.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
* (actually an address to an AixLinkage 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 = VM_StringUtilities.stringToBytesNullTerminated(symbolName);
return VM_SysCall.sysCall.sysDlsym(libHandler, asciiName);
}
/**
* unload a dynamic library
*/
public void unload() {
VM.sysWrite("VM_DynamicLibrary.unload: not implemented yet \n");
}
/**
* Tell the operating system to remove the dynamic library from the
* system space.
*/
public void clean() {
VM.sysWrite("VM_DynamicLibrary.clean: not implemented yet \n");
}
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) {
VM_DynamicLibrary dl = dynamicLibraries.get(libname);
if (dl != null) return 1; // success: already loaded
if (VM_FileSystem.stat(libname, VM_FileSystem.STAT_EXISTS) == 1) {
dynamicLibraries.put(libname, new VM_DynamicLibrary(libname));
return 1;
} else {
return 0; // fail; file does not exist
}
}
/**
* Resolve a symbol to an address in a currently loaded dynamic library.
* @return the address of the symbol of Address.zero() if it cannot be resolved
*/
public static synchronized Address resolveSymbol(String symbol) {
for (VM_DynamicLibrary lib : dynamicLibraries.values()) {
Address symbolAddress = lib.getSymbol(symbol);
if (!symbolAddress.isZero()) {
return symbolAddress;
}
}
return Address.zero();
}
}