001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.runtime; 014 015import java.util.Iterator; 016import org.jikesrvm.architecture.StackFrameLayout; 017import org.jikesrvm.VM; 018import org.jikesrvm.scheduler.RVMThread; 019import org.jikesrvm.util.ImmutableEntryHashMapRVM; 020import org.jikesrvm.util.StringUtilities; 021import org.vmmagic.unboxed.Address; 022import org.vmmagic.unboxed.Offset; 023 024/** 025 * Interface to the dynamic libraries of our underlying operating system. 026 */ 027public final class DynamicLibrary { 028 029 /** 030 * Currently loaded dynamic libraries. 031 */ 032 private static final ImmutableEntryHashMapRVM<String, DynamicLibrary> dynamicLibraries = 033 new ImmutableEntryHashMapRVM<String, DynamicLibrary>(); 034 035 /** 036 * Add symbol for the bootloader to find symbols within it. 037 */ 038 public static void boot() { 039 System.loadLibrary("jvm_jni"); 040 } 041 042 /** 043 * The name of the library 044 */ 045 private final String libName; 046 047 /** 048 * Value returned from dlopen 049 */ 050 private final Address libHandler; 051 052 /** 053 * Address of JNI_OnLoad method 054 */ 055 private final Address jniOnLoad; 056 057 /** 058 * Address of JNI_OnUnLoad 059 */ 060 private final Address jniOnUnload; 061 062 /** 063 * Maintain a loaded library, call it's JNI_OnLoad function if present 064 * @param libName library name 065 * @param libHandler handle of loaded library 066 */ 067 private DynamicLibrary(String libName, Address libHandler) { 068 this.libName = libName; 069 this.libHandler = libHandler; 070 jniOnLoad = getJNI_OnLoad(); 071 jniOnUnload = getJNI_OnUnload(); 072 try { 073 callOnLoad(); 074 } catch (UnsatisfiedLinkError e) { 075 unload(); 076 throw e; 077 } 078 079 if (VM.verboseJNI) { 080 VM.sysWriteln("[Loaded native library: " + libName + "]"); 081 } 082 } 083 084 /** 085 * Get the unique JNI_OnLoad symbol associated with this library 086 * @return JNI_OnLoad address or zero if not present 087 */ 088 private Address getJNI_OnLoad() { 089 Address candidate = getSymbol("JNI_OnLoad"); 090 Iterator<DynamicLibrary> libs = dynamicLibraries.valueIterator(); 091 while (libs.hasNext()) { 092 DynamicLibrary lib = libs.next(); 093 if (lib.jniOnLoad.EQ(candidate)) { 094 return Address.zero(); 095 } 096 } 097 return candidate; 098 } 099 100 /** 101 * Get the unique JNI_OnUnload symbol associated with this library 102 * @return JNI_OnUnload address or zero if not present 103 */ 104 private Address getJNI_OnUnload() { 105 Address candidate = getSymbol("JNI_OnUnload"); 106 Iterator<DynamicLibrary> libs = dynamicLibraries.valueIterator(); 107 while (libs.hasNext()) { 108 DynamicLibrary lib = libs.next(); 109 if (lib.jniOnUnload.EQ(candidate)) { 110 return Address.zero(); 111 } 112 } 113 return candidate; 114 } 115 116 /** 117 * Called after we've successfully loaded the shared library 118 */ 119 private void callOnLoad() { 120 // Run any JNI_OnLoad functions defined within the library 121 if (!jniOnLoad.isZero()) { 122 int version = runJNI_OnLoad(jniOnLoad); 123 checkJNIVersion(version); 124 } 125 } 126 127 /** 128 * Method call to run the onload method. Performed as a native 129 * method as the JNI_OnLoad method may contain JNI calls and we need 130 * the RVMThread of the JNIEnv to be correctly populated (this 131 * wouldn't happen with a SysCall) 132 * 133 * @param JNI_OnLoadAddress address of JNI_OnLoad function 134 * @return the JNI version returned by the JNI_OnLoad function 135 */ 136 private static native int runJNI_OnLoad(Address JNI_OnLoadAddress); 137 138 /** 139 * Check JNI version is ≥ 1 and ≤ 1.4 and if not throw an 140 * UnsatisfiedLinkError 141 * @param version to check 142 */ 143 private static void checkJNIVersion(int version) { 144 int major = version >>> 16; 145 int minor = version & 0xFFFF; 146 if (major != 1 || minor > 4) { 147 throw new UnsatisfiedLinkError("Unsupported JNI version: " + major + "." + minor); 148 } 149 } 150 151 /** 152 * @return the true name of the dynamic library 153 */ 154 public String getLibName() { 155 return libName; 156 } 157 158 /** 159 * look up this dynamic library for a symbol 160 * @param symbolName symbol name 161 * @return The <code>Address</code> of the symbol system handler 162 * (or an address of a PowerPC Linkage triplet). 163 * (-1: not found or couldn't be created) 164 */ 165 public Address getSymbol(String symbolName) { 166 // Convert file name from unicode to filesystem character set 167 // (assume file name is ascii, for now). 168 // 169 byte[] asciiName = StringUtilities.stringToBytesNullTerminated(symbolName); 170 return SysCall.sysCall.sysDlsym(libHandler, asciiName); 171 } 172 173 /** 174 * unload a dynamic library 175 */ 176 public void unload() { 177 VM.sysWrite("DynamicLibrary.unload: not implemented yet \n"); 178 } 179 180 /** 181 * Tell the operating system to remove the dynamic library from the 182 * system space. 183 */ 184 public void clean() { 185 VM.sysWrite("DynamicLibrary.clean: not implemented yet \n"); 186 } 187 188 @Override 189 public String toString() { 190 return "dynamic library " + libName + ", handler=0x" + Long.toHexString(libHandler.toWord().toLong()); 191 } 192 193 /** 194 * Load a dynamic library 195 * @param libName the name of the library to load. 196 * @return 0 on failure, 1 on success 197 */ 198 public static synchronized int load(String libName) { 199 DynamicLibrary dl = dynamicLibraries.get(libName); 200 if (dl != null) { 201 return 1; // success: already loaded 202 } else { 203 // Convert file name from unicode to filesystem character set. 204 // (Assume file name is ASCII, for now). 205 // 206 byte[] asciiName = StringUtilities.stringToBytesNullTerminated(libName); 207 208 // make sure we have enough stack to load the library. 209 // This operation has been known to require more than 20K of stack. 210 RVMThread myThread = RVMThread.getCurrentThread(); 211 Offset remaining = Magic.getFramePointer().diff(myThread.stackLimit); 212 int stackNeededInBytes = StackFrameLayout.getStackSizeDLOpen() - remaining.toInt(); 213 if (stackNeededInBytes > 0) { 214 if (myThread.hasNativeStackFrame()) { 215 throw new java.lang.StackOverflowError("Not enough space to open shared library"); 216 } else { 217 RVMThread.resizeCurrentStack(myThread.getStackLength() + stackNeededInBytes, null); 218 } 219 } 220 221 Address libHandler = SysCall.sysCall.sysDlopen(asciiName); 222 223 if (!libHandler.isZero()) { 224 dynamicLibraries.put(libName, new DynamicLibrary(libName, libHandler)); 225 return 1; 226 } else { 227 return 0; // fail; file does not exist 228 } 229 } 230 } 231 232 /** 233 * Resolves a symbol to an address in a currently loaded dynamic library. 234 * @param symbol the symbol to resolves 235 * @return the address of the symbol of Address.zero() if it cannot be resolved 236 */ 237 public static synchronized Address resolveSymbol(String symbol) { 238 for (DynamicLibrary lib : dynamicLibraries.values()) { 239 Address symbolAddress = lib.getSymbol(symbol); 240 if (!symbolAddress.isZero()) { 241 return symbolAddress; 242 } 243 } 244 return Address.zero(); 245 } 246}