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 &ge; 1 and &le; 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}