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);
|