library loading - continued

Ty Young youngty1997 at gmail.com
Tue May 11 01:08:35 UTC 2021


Someone off-list pointed out that dl* functions are provided by a 
different library than libc: dl.


Also, does System.load()/System.loadLibrary() affect 
CLinker.systemLookup()? Just noticed that CLinker.systemLookup()'s name 
is rather problematic if it doesn't.



On 5/10/21 5:25 PM, Ty Young wrote:
> It's not possible to create bindings for dlfcn, the header that 
> provides the function declarations dlopen, dlclose, dlsym, etc, it 
> seems. CLinker.systemLookup() fails to provide the symbol, anyway, and 
> attempting to load libc fails. The header says that it's part of the 
> GNU C library but maybe I'm barking up the wrong tree or something.
>
>
> Is System.load()/System.loadLibrary() really the best approach? Not 
> only does it have issues loading libc(and others?) but it isn't 
> exactly all that primitive of a API either. Both dlopen(Linux) and 
> LoadLibrary(Windows) both return pointers(which can be user managed) 
> when loading libraries and dlopen has modes that can be set, but 
> System.load()/System.loadLibrary() both register the loaded libraries 
> with a ClassLoader, a JVM construct. How does one get a pointer of a 
> loaded library from a ClassLoader and then use that pointer to unload 
> a library?
>
>
> In addition to all that, it's no longer clear(compared to the old way 
> Panama did things) where a particular symbol is coming from. Yes, we 
> know it's from a ClassLoader(if you bother reading javadoc, which 
> doesn't work on Netbeans) but there isn't a visual representation of 
> this in code.
>
>
> On 5/10/21 7:49 AM, Maurizio Cimadamore wrote:
>> Hi,
>> in my email [1] I described a plan to simplify the library loading 
>> mechanism based on the `LibraryLookup` abstraction. The motivations 
>> behind the restructuring were three-fold:
>>
>> * Semantics of `LibraryLookup::ofDefault` is unstable; on some 
>> platforms, default lookup leaks other loaded library symbols; on 
>> other platforms (e.g. Windows) implementing it required use of 
>> debugging APIs.
>> * `LibraryLookup` has its own concept of lifecycle which is 
>> incompatible with `ResourceScope`, and also with that typically 
>> associated with JNI loaded libraries (whose lifecycle is associated 
>> with that of the owning classloader)
>> * `LibraryLookup` does not offer a migration path for JNI-based 
>> frameworks. That is, many frameworks are written with the assumption 
>> that `System::loadLibrary` will affect the set of available symbols 
>> _everywhere_ else (at least within the same classloader context)
>>
>> For these reasons, we decided to remove `LibraryLookup` and to 
>> provide a more basic lookup capability instead, as a static method 
>> (`CLinker::findNative`). This method would lookup symbols in 
>> libraries loaded by the classloader used by the caller; in doing so, 
>> the new method addresses the migration issues which affected 
>> `LibraryLookup`.
>>
>> When writing this patch [2, 3] we have realized that this new scheme 
>> was not without its own issues:
>>
>> * Loading symbols in the standard C library is now incredibly 
>> difficult, sometimes requiring surprising workarounds [4, 5]
>> * There is no way to e.g. implement a lookup which filters the set of 
>> symbols available to users
>>
>> After staring at these issues, we realized that having a lookup 
>> abstraction was not, in itself, the root cause of the problems we 
>> were seeing in [1]. These problems were, rather, caused by the fact 
>> that the lookup abstraction we had was introducing its own concept of 
>> library loading which had nothing to do with JNI library loading. In 
>> other words, `LibraryLookup`, despite its name, wasn't a *pure* 
>> lookup abstraction, it was attempting to do a little more (e.g. 
>> keeping libraries loaded using GC reachability), without too much 
>> success.
>>
>> Having realized that, we now believe we can safely reintroduce some 
>> kind of lookup abstraction, in the form of `SymbolLookup`: a simple 
>> functional interface which, given a symbol name, gives us the address 
>> of that symbol (if available). The use or a more neutral name here 
>> (`SymbolLookup` instead of `LibraryLookup`) is deliberate: not always 
>> a lookup is a result of library loading; lookup objects might in fact 
>> be created (or composed) by users, using existing resources.
>>
>> How do developers obtain a `SymbolLookup` ? Two ways:
>>
>> * They can obtain a `SymbolLookup` for a given class loader - which 
>> allows to search symbols in all the libraries loaded by that class 
>> loader
>> * They can ask `CLinker` a so called *system lookup* - a lookup which 
>> allows to search for basic C symbols (such as `strlen` and `qsort`)
>>
>> Note that the first mechanism allows us to solve the migration 
>> problem, by exposing the lookup abstraction associated with a 
>> specific class loader. This means frameworks will still be able to 
>> call `System::loadLibrary` to *side-effect* the results of a class 
>> loader-based symbol lookup. Note that, since `SymbolLookup` is a 
>> simple functional interface, it would be possible for developers to 
>> set up symbol lookup chains e.g. where lookup for parent class loader 
>> is consulted first (e.g. following classloader delegation). This 
>> flexibility will certainly come in handy in real world use cases.
>>
>> The second mechanism gives us something similar to the previous 
>> concept of *default lookup* - but without the messy bits: the system 
>> lookup is simply a system-dependent symbol lookup object, which might 
>> help in retrieving common C symbols. The API makes no promises as to 
>> *which* libraries will be consulted by this lookup (this is an 
>> implementation detail). That said, the implementation (unlike before) 
>> will not make use of `RTLD_DEFAULT` whose semantics is brittle and 
>> not implementable across all OSs.
>>
>> It is possible that, in the future, we might add more ways to obtain 
>> a symbol lookup - for instance:
>>
>> ```
>> SymbolLookup.ofLibrary(String libName, ResourceScope scope)
>>
>> ```
>>
>> This is not too different from what we had in the original 
>> `LibraryLookup` abstraction, and would allow developers to load a 
>> library and associate its lifecycle with a `ResourceScope` (rather 
>> than a class loader). That is, when the scope is closed, the library 
>> will be unloaded. However, adding these new mode will require some 
>> additional foundational work on the `CLinker` support - as we need to 
>> make sure that the memory address used by a downcall method handle 
>> cannot be unloaded while the downcall method handle is being invoked. 
>> This means that, at the very minimum, the linker will need to 
>> acquire/release the scope associated with the address of the native 
>> function, to prevent premature closing of said scope.
>>
>> Summing up, we believe that while investigating for a more minimal 
>> library lookup mechanism we have found a way to provide most (all?) 
>> of the functionalities available before, in a more disciplined and 
>> compositional manner.
>>
>> Cheers
>> Maurizio
>>
>> [1] - 
>> https://mail.openjdk.java.net/pipermail/panama-dev/2021-April/013577.html
>> [2] - https://git.openjdk.java.net/panama-foreign/pull/526
>> [3] - https://git.openjdk.java.net/panama-foreign/pull/529
>> [4] - 
>> https://github.com/sundararajana/panama-foreign/blob/197db28d5097b1689b4befcd7129eb7af13f41c2/test/jdk/java/foreign/libStdLibTest.c
>> [5] - https://github.com/openjdk/panama-foreign/pull/527
>>
>>
>>


More information about the panama-dev mailing list