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 public String toString() {
210 return "dynamic library " + libName + ", handler=0x" + Long.toHexString(libHandler.toWord().toLong());
211 }
212
213 /**
214 * Load a dynamic library
215 * @param libname the name of the library to load.
216 * @return 0 on failure, 1 on success
217 */
218 public static synchronized int load(String libname) {
219 DynamicLibrary dl = dynamicLibraries.get(libname);
220 if (dl != null) return 1; // success: already loaded
221
222 if (FileSystem.stat(libname, FileSystem.STAT_EXISTS) == 1) {
223 dynamicLibraries.put(libname, new DynamicLibrary(libname));
224 return 1;
225 } else {
226 return 0; // fail; file does not exist
227 }
228 }
229
230 /**
231 * Resolve a symbol to an address in a currently loaded dynamic library.
232 * @return the address of the symbol of Address.zero() if it cannot be resolved
233 */
234 public static synchronized Address resolveSymbol(String symbol) {
235 for (DynamicLibrary lib : dynamicLibraries.values()) {
236 Address symbolAddress = lib.getSymbol(symbol);
237 if (!symbolAddress.isZero()) {
238 return symbolAddress;
239 }
240 }
241 return Address.zero();
242 }
243 }