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