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 */ 013 package org.jikesrvm.runtime; 014 015 import java.util.Iterator; 016 import org.jikesrvm.ArchitectureSpecific.StackframeLayoutConstants; 017 import org.jikesrvm.VM; 018 import org.jikesrvm.scheduler.RVMThread; 019 import org.jikesrvm.util.ImmutableEntryHashMapRVM; 020 import org.jikesrvm.util.StringUtilities; 021 import org.vmmagic.unboxed.Address; 022 import org.vmmagic.unboxed.Offset; 023 024 /** 025 * Interface to the dynamic libraries of our underlying operating system. 026 */ 027 public 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 boot image runner to find symbols within it. 037 */ 038 public static void boot() { 039 System.loadLibrary("rvmdynlib"); 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 * Load a dynamic library and maintain it in this object. 064 * @param libraryName library name 065 */ 066 private DynamicLibrary(String libraryName) { 067 // Convert file name from unicode to filesystem character set. 068 // (Assume file name is ASCII, for now). 069 // 070 byte[] asciiName = StringUtilities.stringToBytesNullTerminated(libraryName); 071 072 // make sure we have enough stack to load the library. 073 // This operation has been known to require more than 20K of stack. 074 RVMThread myThread = RVMThread.getCurrentThread(); 075 Offset remaining = Magic.getFramePointer().diff(myThread.stackLimit); 076 int stackNeededInBytes = StackframeLayoutConstants.STACK_SIZE_DLOPEN - remaining.toInt(); 077 if (stackNeededInBytes > 0) { 078 if (myThread.hasNativeStackFrame()) { 079 throw new java.lang.StackOverflowError("dlopen"); 080 } else { 081 RVMThread.resizeCurrentStack(myThread.getStackLength() + stackNeededInBytes, null); 082 } 083 } 084 085 libHandler = SysCall.sysCall.sysDlopen(asciiName); 086 087 if (libHandler.isZero()) { 088 VM.sysWriteln("error loading library: " + libraryName); 089 throw new UnsatisfiedLinkError(); 090 } 091 092 libName = libraryName; 093 jniOnLoad = getJNI_OnLoad(); 094 jniOnUnload = getJNI_OnUnload(); 095 try { 096 callOnLoad(); 097 } catch (UnsatisfiedLinkError e) { 098 unload(); 099 throw e; 100 } 101 102 if (VM.verboseJNI) { 103 VM.sysWriteln("[Loaded native library: " + libName + "]"); 104 } 105 } 106 107 /** 108 * Get the unique JNI_OnLoad symbol associated with this library 109 * @return JNI_OnLoad address or zero if not present 110 */ 111 private Address getJNI_OnLoad() { 112 Address candidate = getSymbol("JNI_OnLoad"); 113 Iterator<DynamicLibrary> libs = dynamicLibraries.valueIterator(); 114 while(libs.hasNext()) { 115 DynamicLibrary lib = libs.next(); 116 if (lib.jniOnLoad == candidate) { 117 return Address.zero(); 118 } 119 } 120 return candidate; 121 } 122 123 /** 124 * Get the unique JNI_OnUnload symbol associated with this library 125 * @return JNI_OnUnload address or zero if not present 126 */ 127 private Address getJNI_OnUnload() { 128 Address candidate = getSymbol("JNI_OnUnload"); 129 Iterator<DynamicLibrary> libs = dynamicLibraries.valueIterator(); 130 while(libs.hasNext()) { 131 DynamicLibrary lib = libs.next(); 132 if (lib.jniOnUnload == candidate) { 133 return Address.zero(); 134 } 135 } 136 return candidate; 137 } 138 139 /** 140 * Called after we've successfully loaded the shared library 141 */ 142 private void callOnLoad() { 143 // Run any JNI_OnLoad functions defined within the library 144 if (!jniOnLoad.isZero()) { 145 int version = runJNI_OnLoad(jniOnLoad); 146 checkJNIVersion(version); 147 } 148 } 149 150 /** 151 * Method call to run the onload method. Performed as a native 152 * method as the JNI_OnLoad method may contain JNI calls and we need 153 * the RVMThread of the JNIEnv to be correctly populated (this 154 * wouldn't happen with a SysCall) 155 * 156 * @param JNI_OnLoadAddress address of JNI_OnLoad function 157 * @return the JNI version returned by the JNI_OnLoad function 158 */ 159 private static native int runJNI_OnLoad(Address JNI_OnLoadAddress); 160 161 /** 162 * Check JNI version is ≤ 1.4 and if not throw an 163 * UnsatisfiedLinkError 164 * @param version to check 165 */ 166 private static void checkJNIVersion(int version) { 167 int major = version >>> 16; 168 int minor = version & 0xFFFF; 169 if (major > 1 || minor > 4) { 170 throw new UnsatisfiedLinkError("Unsupported JNI version: " + major + "." + minor); 171 } 172 } 173 174 /** 175 * @return the true name of the dynamic library 176 */ 177 public String getLibName() { return libName; } 178 179 /** 180 * look up this dynamic library for a symbol 181 * @param symbolName symbol name 182 * @return The <code>Address</code> of the symbol system handler 183 * (actually an address to an AixLinkage triplet). 184 * (-1: not found or couldn't be created) 185 */ 186 public Address getSymbol(String symbolName) { 187 // Convert file name from unicode to filesystem character set 188 // (assume file name is ascii, for now). 189 // 190 byte[] asciiName = StringUtilities.stringToBytesNullTerminated(symbolName); 191 return SysCall.sysCall.sysDlsym(libHandler, asciiName); 192 } 193 194 /** 195 * unload a dynamic library 196 */ 197 public void unload() { 198 VM.sysWrite("DynamicLibrary.unload: not implemented yet \n"); 199 } 200 201 /** 202 * Tell the operating system to remove the dynamic library from the 203 * system space. 204 */ 205 public void clean() { 206 VM.sysWrite("DynamicLibrary.clean: not implemented yet \n"); 207 } 208 209 @Override 210 public String toString() { 211 return "dynamic library " + libName + ", handler=0x" + Long.toHexString(libHandler.toWord().toLong()); 212 } 213 214 /** 215 * Load a dynamic library 216 * @param libname the name of the library to load. 217 * @return 0 on failure, 1 on success 218 */ 219 public static synchronized int load(String libname) { 220 DynamicLibrary dl = dynamicLibraries.get(libname); 221 if (dl != null) return 1; // success: already loaded 222 223 if (FileSystem.stat(libname, FileSystem.STAT_EXISTS) == 1) { 224 dynamicLibraries.put(libname, new DynamicLibrary(libname)); 225 return 1; 226 } else { 227 return 0; // fail; file does not exist 228 } 229 } 230 231 /** 232 * Resolve a symbol to an address in a currently loaded dynamic library. 233 * @return the address of the symbol of Address.zero() if it cannot be resolved 234 */ 235 public static synchronized Address resolveSymbol(String symbol) { 236 for (DynamicLibrary lib : dynamicLibraries.values()) { 237 Address symbolAddress = lib.getSymbol(symbol); 238 if (!symbolAddress.isZero()) { 239 return symbolAddress; 240 } 241 } 242 return Address.zero(); 243 } 244 }