< prev index next >

src/java.base/share/classes/java/lang/foreign/SymbolLookup.java

Print this page

 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.lang.foreign;
 27 
 28 import jdk.internal.access.JavaLangAccess;
 29 import jdk.internal.access.SharedSecrets;
 30 import jdk.internal.foreign.MemorySessionImpl;
 31 import jdk.internal.foreign.Utils;
 32 import jdk.internal.javac.PreviewFeature;
 33 import jdk.internal.loader.BuiltinClassLoader;
 34 import jdk.internal.loader.NativeLibrary;
 35 import jdk.internal.loader.RawNativeLibraries;
 36 import jdk.internal.reflect.CallerSensitive;
 37 import jdk.internal.reflect.Reflection;
 38 
 39 import java.lang.invoke.MethodHandles;
 40 import java.nio.file.Path;
 41 import java.util.Objects;
 42 import java.util.Optional;
 43 import java.util.function.BiFunction;
 44 
 45 /**
 46  * A <em>symbol lookup</em> retrieves the address of a symbol in one or more libraries.
 47  * A symbol is a named entity, such as a function or a global variable.
 48  * <p>
 49  * A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#find(String)}
 50  * method takes the name of a symbol and returns the address of the symbol in that library.
 51  * <p>
 52  * The address of a symbol is modelled as a zero-length {@linkplain MemorySegment memory segment}. The segment can be used in different ways:

102  * Note also that a library lookup for library {@code L} exposes symbols in {@code L} even if {@code L} was previously loaded
103  * through JNI (the association with a class loader is immaterial to the library lookup):
104  *
105  * {@snippet lang = java:
106  * System.loadLibrary("GL"); // libGL.so loaded here
107  * libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
108  *}
109  *
110  * <p>
111  * Finally, each {@link Linker} provides a symbol lookup for libraries that are commonly used on the OS and processor
112  * combination supported by that {@link Linker}. This symbol lookup, which is known as a <em>default lookup</em>,
113  * helps clients to quickly find addresses of well-known symbols. For example, a {@link Linker} for Linux/x64 might choose to
114  * expose symbols in {@code libc} through the default lookup:
115  *
116  * {@snippet lang = java:
117  * Linker nativeLinker = Linker.nativeLinker();
118  * SymbolLookup stdlib = nativeLinker.defaultLookup();
119  * MemorySegment malloc = stdlib.find("malloc").orElseThrow();
120  *}
121  *
122  * @since 19
123  */
124 @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
125 @FunctionalInterface
126 public interface SymbolLookup {
127 
128     /**
129      * Returns the address of the symbol with the given name.
130      * @param name the symbol name.
131      * @return a zero-length memory segment whose address indicates the address of the symbol, if found.
132      */
133     Optional<MemorySegment> find(String name);
134 
135     /**
136      * {@return a composed symbol lookup that returns result of finding the symbol with this lookup if found,
137      * otherwise returns the result of finding the symbol with the other lookup}
138      *
139      * @apiNote This method could be used to chain multiple symbol lookups together, e.g. so that symbols could
140      * be retrieved, in order, from multiple libraries:
141      * {@snippet lang = java:
142      * var lookup = SymbolLookup.libraryLookup("foo", arena)
143      *         .or(SymbolLookup.libraryLookup("bar", arena))
144      *         .or(SymbolLookup.loaderLookup());

149      *
150      * @param other the symbol lookup that should be used to look for symbols not found in this lookup.
151      */
152     default SymbolLookup or(SymbolLookup other) {
153         Objects.requireNonNull(other);
154         return name -> find(name).or(() -> other.find(name));
155     }
156 
157     /**
158      * Returns a symbol lookup for symbols in the libraries associated with the caller's class loader.
159      * <p>
160      * A library is associated with a class loader {@code CL} when the library is loaded via an invocation of
161      * {@link System#load(String)} or {@link System#loadLibrary(String)} from code in a class defined by {@code CL}.
162      * If that code makes further invocations of {@link System#load(String)} or {@link System#loadLibrary(String)},
163      * then more libraries are loaded and associated with {@code CL}. The symbol lookup returned by this method is always
164      * current: it reflects all the libraries associated with the relevant class loader, even if they were loaded after
165      * this method returned.
166      * <p>
167      * Libraries associated with a class loader are unloaded when the class loader becomes
168      * <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
169      * returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} which keeps the caller's
170      * class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded
171      * (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
172      * obtained by it, is reachable.
173      * <p>
174      * In cases where this method is called from a context where there is no caller frame on the stack
175      * (e.g. when called directly from a JNI attached thread), the caller's class loader defaults to the
176      * {@linkplain ClassLoader#getSystemClassLoader system class loader}.
177      *
178      * @return a symbol lookup for symbols in the libraries associated with the caller's class loader.
179      * @see System#load(String)
180      * @see System#loadLibrary(String)
181      */
182     @CallerSensitive
183     static SymbolLookup loaderLookup() {
184         Class<?> caller = Reflection.getCallerClass();
185         // If there's no caller class, fallback to system loader
186         ClassLoader loader = caller != null ?
187                 caller.getClassLoader() :
188                 ClassLoader.getSystemClassLoader();
189         Arena loaderArena;// builtin loaders never go away
190         if ((loader == null || loader instanceof BuiltinClassLoader)) {
191             loaderArena = Arena.global();
192         } else {
193             MemorySessionImpl session = MemorySessionImpl.heapSession(loader);
194             loaderArena = session.asArena();
195         }
196         return name -> {
197             Objects.requireNonNull(name);
198             if (Utils.containsNullChars(name)) return Optional.empty();
199             JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
200             // note: ClassLoader::findNative supports a null loader
201             long addr = javaLangAccess.findNative(loader, name);
202             return addr == 0L ?
203                     Optional.empty() :
204                     Optional.of(MemorySegment.ofAddress(addr)
205                                     .reinterpret(loaderArena, null));
206         };
207     }
208 
209     /**
210      * Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library.
211      * The lifetime of the returned library lookup is controlled by the provided arena.
212      * For instance, if the provided arena is a confined arena, the library
213      * associated with the returned lookup will be unloaded when the provided confined arena is
214      * {@linkplain Arena#close() closed}.
215      * <p>
216      * This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
217      * Restricted methods are unsafe, and, if used incorrectly, their use might crash
218      * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
219      * restricted methods, and use safe and supported functionalities, where possible.
220      *
221      * @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS,
222      * the library name is resolved according to the specification of the {@code dlopen} function for that OS.
223      * In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function.
224      *
225      * @param name the name of the library in which symbols should be looked up.
226      * @param arena the arena associated with symbols obtained from the returned lookup.
227      * @return a new symbol lookup suitable to find symbols in a library with the given name.
228      * @throws IllegalStateException if {@code arena.scope().isAlive() == false}
229      * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
230      * thread {@code T}, other than the arena's owner thread.
231      * @throws IllegalArgumentException if {@code name} does not identify a valid library.
232      * @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
233      */
234     @CallerSensitive
235     static SymbolLookup libraryLookup(String name, Arena arena) {
236         Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
237         if (Utils.containsNullChars(name)) {
238             throw new IllegalArgumentException("Cannot open library: " + name);
239         }
240         return libraryLookup(name, RawNativeLibraries::load, arena);
241     }
242 
243     /**
244      * Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols
245      * in that library. The lifetime of the returned library lookup is controlled by the provided arena.
246      * For instance, if the provided arena is a confined arena, the library
247      * associated with the returned lookup will be unloaded when the provided confined arena is
248      * {@linkplain Arena#close() closed}.
249      * <p>
250      * This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
251      * Restricted methods are unsafe, and, if used incorrectly, their use might crash
252      * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
253      * restricted methods, and use safe and supported functionalities, where possible.
254      *
255      * @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are
256      * implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions.
257      * @param path the path of the library in which symbols should be looked up.
258      * @param arena the arena associated with symbols obtained from the returned lookup.
259      * @return a new symbol lookup suitable to find symbols in a library with the given path.
260      * @throws IllegalStateException if {@code arena.scope().isAlive() == false}
261      * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
262      * thread {@code T}, other than the arena's owner thread.
263      * @throws IllegalArgumentException if {@code path} does not point to a valid library.
264      * @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
265      */
266     @CallerSensitive
267     static SymbolLookup libraryLookup(Path path, Arena arena) {
268         Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
269         return libraryLookup(path, RawNativeLibraries::load, arena);
270     }
271 
272     private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, Arena libArena) {
273         Objects.requireNonNull(libDesc);

 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.lang.foreign;
 27 
 28 import jdk.internal.access.JavaLangAccess;
 29 import jdk.internal.access.SharedSecrets;
 30 import jdk.internal.foreign.MemorySessionImpl;
 31 import jdk.internal.foreign.Utils;

 32 import jdk.internal.loader.BuiltinClassLoader;
 33 import jdk.internal.loader.NativeLibrary;
 34 import jdk.internal.loader.RawNativeLibraries;
 35 import jdk.internal.reflect.CallerSensitive;
 36 import jdk.internal.reflect.Reflection;
 37 
 38 import java.lang.invoke.MethodHandles;
 39 import java.nio.file.Path;
 40 import java.util.Objects;
 41 import java.util.Optional;
 42 import java.util.function.BiFunction;
 43 
 44 /**
 45  * A <em>symbol lookup</em> retrieves the address of a symbol in one or more libraries.
 46  * A symbol is a named entity, such as a function or a global variable.
 47  * <p>
 48  * A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#find(String)}
 49  * method takes the name of a symbol and returns the address of the symbol in that library.
 50  * <p>
 51  * The address of a symbol is modelled as a zero-length {@linkplain MemorySegment memory segment}. The segment can be used in different ways:

101  * Note also that a library lookup for library {@code L} exposes symbols in {@code L} even if {@code L} was previously loaded
102  * through JNI (the association with a class loader is immaterial to the library lookup):
103  *
104  * {@snippet lang = java:
105  * System.loadLibrary("GL"); // libGL.so loaded here
106  * libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
107  *}
108  *
109  * <p>
110  * Finally, each {@link Linker} provides a symbol lookup for libraries that are commonly used on the OS and processor
111  * combination supported by that {@link Linker}. This symbol lookup, which is known as a <em>default lookup</em>,
112  * helps clients to quickly find addresses of well-known symbols. For example, a {@link Linker} for Linux/x64 might choose to
113  * expose symbols in {@code libc} through the default lookup:
114  *
115  * {@snippet lang = java:
116  * Linker nativeLinker = Linker.nativeLinker();
117  * SymbolLookup stdlib = nativeLinker.defaultLookup();
118  * MemorySegment malloc = stdlib.find("malloc").orElseThrow();
119  *}
120  *
121  * @since 22
122  */

123 @FunctionalInterface
124 public interface SymbolLookup {
125 
126     /**
127      * Returns the address of the symbol with the given name.
128      * @param name the symbol name.
129      * @return a zero-length memory segment whose address indicates the address of the symbol, if found.
130      */
131     Optional<MemorySegment> find(String name);
132 
133     /**
134      * {@return a composed symbol lookup that returns result of finding the symbol with this lookup if found,
135      * otherwise returns the result of finding the symbol with the other lookup}
136      *
137      * @apiNote This method could be used to chain multiple symbol lookups together, e.g. so that symbols could
138      * be retrieved, in order, from multiple libraries:
139      * {@snippet lang = java:
140      * var lookup = SymbolLookup.libraryLookup("foo", arena)
141      *         .or(SymbolLookup.libraryLookup("bar", arena))
142      *         .or(SymbolLookup.loaderLookup());

147      *
148      * @param other the symbol lookup that should be used to look for symbols not found in this lookup.
149      */
150     default SymbolLookup or(SymbolLookup other) {
151         Objects.requireNonNull(other);
152         return name -> find(name).or(() -> other.find(name));
153     }
154 
155     /**
156      * Returns a symbol lookup for symbols in the libraries associated with the caller's class loader.
157      * <p>
158      * A library is associated with a class loader {@code CL} when the library is loaded via an invocation of
159      * {@link System#load(String)} or {@link System#loadLibrary(String)} from code in a class defined by {@code CL}.
160      * If that code makes further invocations of {@link System#load(String)} or {@link System#loadLibrary(String)},
161      * then more libraries are loaded and associated with {@code CL}. The symbol lookup returned by this method is always
162      * current: it reflects all the libraries associated with the relevant class loader, even if they were loaded after
163      * this method returned.
164      * <p>
165      * Libraries associated with a class loader are unloaded when the class loader becomes
166      * <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
167      * returned by this method is associated with a {@linkplain MemorySegment.Scope scope} which keeps the caller's
168      * class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded
169      * (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
170      * obtained by it, is reachable.
171      * <p>
172      * In cases where this method is called from a context where there is no caller frame on the stack
173      * (e.g. when called directly from a JNI attached thread), the caller's class loader defaults to the
174      * {@linkplain ClassLoader#getSystemClassLoader system class loader}.
175      *
176      * @return a symbol lookup for symbols in the libraries associated with the caller's class loader.
177      * @see System#load(String)
178      * @see System#loadLibrary(String)
179      */
180     @CallerSensitive
181     static SymbolLookup loaderLookup() {
182         Class<?> caller = Reflection.getCallerClass();
183         // If there's no caller class, fallback to system loader
184         ClassLoader loader = caller != null ?
185                 caller.getClassLoader() :
186                 ClassLoader.getSystemClassLoader();
187         Arena loaderArena;// builtin loaders never go away
188         if ((loader == null || loader instanceof BuiltinClassLoader)) {
189             loaderArena = Arena.global();
190         } else {
191             MemorySessionImpl session = MemorySessionImpl.createHeap(loader);
192             loaderArena = session.asArena();
193         }
194         return name -> {
195             Objects.requireNonNull(name);
196             if (Utils.containsNullChars(name)) return Optional.empty();
197             JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
198             // note: ClassLoader::findNative supports a null loader
199             long addr = javaLangAccess.findNative(loader, name);
200             return addr == 0L ?
201                     Optional.empty() :
202                     Optional.of(MemorySegment.ofAddress(addr)
203                                     .reinterpret(loaderArena, null));
204         };
205     }
206 
207     /**
208      * Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library.
209      * The lifetime of the returned library lookup is controlled by the provided arena.
210      * For instance, if the provided arena is a confined arena, the library
211      * associated with the returned lookup will be unloaded when the provided confined arena is
212      * {@linkplain Arena#close() closed}.
213      * <p>
214      * This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
215      * Restricted methods are unsafe, and, if used incorrectly, their use might crash
216      * the JVM or, worse, silently result in memory corruption.

217      *
218      * @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS,
219      * the library name is resolved according to the specification of the {@code dlopen} function for that OS.
220      * In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function.
221      *
222      * @param name the name of the library in which symbols should be looked up.
223      * @param arena the arena associated with symbols obtained from the returned lookup.
224      * @return a new symbol lookup suitable to find symbols in a library with the given name.
225      * @throws IllegalStateException if {@code arena.scope().isAlive() == false}
226      * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
227      * thread {@code T}, other than the arena's owner thread.
228      * @throws IllegalArgumentException if {@code name} does not identify a valid library.
229      * @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
230      */
231     @CallerSensitive
232     static SymbolLookup libraryLookup(String name, Arena arena) {
233         Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
234         if (Utils.containsNullChars(name)) {
235             throw new IllegalArgumentException("Cannot open library: " + name);
236         }
237         return libraryLookup(name, RawNativeLibraries::load, arena);
238     }
239 
240     /**
241      * Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols
242      * in that library. The lifetime of the returned library lookup is controlled by the provided arena.
243      * For instance, if the provided arena is a confined arena, the library
244      * associated with the returned lookup will be unloaded when the provided confined arena is
245      * {@linkplain Arena#close() closed}.
246      * <p>
247      * This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
248      * Restricted methods are unsafe, and, if used incorrectly, their use might crash
249      * the JVM or, worse, silently result in memory corruption.

250      *
251      * @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are
252      * implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions.
253      * @param path the path of the library in which symbols should be looked up.
254      * @param arena the arena associated with symbols obtained from the returned lookup.
255      * @return a new symbol lookup suitable to find symbols in a library with the given path.
256      * @throws IllegalStateException if {@code arena.scope().isAlive() == false}
257      * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
258      * thread {@code T}, other than the arena's owner thread.
259      * @throws IllegalArgumentException if {@code path} does not point to a valid library.
260      * @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
261      */
262     @CallerSensitive
263     static SymbolLookup libraryLookup(Path path, Arena arena) {
264         Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
265         return libraryLookup(path, RawNativeLibraries::load, arena);
266     }
267 
268     private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, Arena libArena) {
269         Objects.requireNonNull(libDesc);
< prev index next >