假设我加载了一个具有函数get_gg()的本机库gg3,并且想要加载另一个也具有get_gg()函数的本机库gg4--我如何以"库感知"的方式查找一个库(例如,在某些情况下使用gg4中的get_gg(),否则使用gg3中的get_gg())?

我try 了CLinker.systemCLinker().lookup(),但似乎只获取了加载的本机库中找到的第一个符号.

在内部类中搜索后,我发现了SystemLookup.libLookup()个看起来很有希望但不是公共的,所以我实现了一个类似的方法来查找加载的本地库:

private static SymbolLookup libLookup(Function<NativeLibraries, NativeLibrary> loader) {
        NativeLibrary lib = loader.apply(NativeLibraries.rawNativeLibraries(SystemLookup.class, false));
        return name -> {
            Objects.requireNonNull(name);
            try {
                long addr = lib.lookup(name);
                return addr == 0 ?
                        Optional.empty() :
                        Optional.of(NativeSymbol.ofAddress(name, MemoryAddress.ofLong(addr), ResourceScope.globalScope()));
            } catch (NoSuchMethodException e) {
                return Optional.empty();
            }
        };
    }

然而,这显然是被禁止的,因为我不能从另一个类加载器加载库(并且我不知道最初使用的是哪个类加载器):

java.lang.UnsatisfiedLinkError: Native Library already loaded in another classloader

那么,我如何才能使用适当的类加载器来查找,或者在不重新加载的情况下设法找到库,这样我就可以专门从它中查找符号了?

推荐答案

FWIW,执行此操作所需的API设置为public in 19

在此之前,建议通过绑定dlopendlclosedlsym(或Windows等效项)来手动实现这一点,以加载库和查找符号.

例如,在我的Windows计算机上:

public class Main {
    
    static final MethodHandle MH_LoadLibraryA;
    static final MethodHandle MH_FreeLibrary;
    static final MethodHandle MH_GetProcAddress;

    static {
        System.loadLibrary("Kernel32");
        SymbolLookup lookup = SymbolLookup.loaderLookup();
        CLinker linker = CLinker.systemCLinker();
        MH_LoadLibraryA = linker.downcallHandle(
            lookup.lookup("LoadLibraryA").orElseThrow(),
            FunctionDescriptor.of(ADDRESS, ADDRESS));
        MH_FreeLibrary = linker.downcallHandle(
            lookup.lookup("FreeLibrary").orElseThrow(),
            FunctionDescriptor.of(JAVA_INT, ADDRESS));
        MH_GetProcAddress = linker.downcallHandle(
            lookup.lookup("GetProcAddress").orElseThrow(),
            FunctionDescriptor.of(ADDRESS, ADDRESS, ADDRESS));
    }

    static MemoryAddress LoadLibraryA(String name) {
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator allocator = SegmentAllocator.nativeAllocator(scope);
            return (MemoryAddress) MH_LoadLibraryA.invokeExact((Addressable) allocator.allocateUtf8String(name));
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    static boolean FreeLibrary(MemoryAddress handle) {
        try {
            return ((int) MH_FreeLibrary.invokeExact((Addressable) handle)) != 0;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    static MemoryAddress GetProcAddress(MemoryAddress handle, String name) {
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator allocator = SegmentAllocator.nativeAllocator(scope);
            return (MemoryAddress) MH_GetProcAddress.invokeExact((Addressable) handle, (Addressable) allocator.allocateUtf8String(name));
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
    
    static SymbolLookup libraryLookup(String libraryName, ResourceScope scope) {
        MemoryAddress handle = LoadLibraryA(libraryName);
        if (handle == MemoryAddress.NULL) {
            throw new IllegalArgumentException("Cannot find library: " + libraryName);
        }
        scope.addCloseAction(() -> FreeLibrary(handle));
        return name -> {
            var addr = GetProcAddress(handle, name);
            return addr == MemoryAddress.NULL ?
                Optional.empty() : Optional.of(NativeSymbol.ofAddress(name, addr, scope));
        };
    }

    public static void main(String[] args) throws Throwable {
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SymbolLookup lookup = libraryLookup("gg", scope);
            MethodHandle handle = CLinker.systemCLinker().downcallHandle(
                lookup.lookup("get_gg").orElseThrow(),
                FunctionDescriptor.of(JAVA_INT));
            int x = (int) handle.invoke();
            System.out.println(x);
        }
    }
}

Java相关问答推荐

Android -如何修复Java.time.zone. ZoneRulesExcept:未知时区ID:Europe/Kyiv

使用Java Streams API比较两个不同的Java集合对象和一个公共属性

Apache POI:使用反射获取zoom 级别

无法处理批处理侦听器中的反序列化异常

Jakarta CDI强制bean构造/注册遗留事件侦听器

测试期间未执行开放重写方法

Java流传输一个列表并创建单个对象

在Java中如何从Executors.newFixedThreadPool(MAX_THREAD_COUNT())迁移到虚拟线程

使用Spring和ActiveMQ的侦听器方法引发属性名称不能重复为空警告

与Spring Boot相关的实体未正确保存

JOOQ中的子查询使用的是默认方言,而不是配置的方言

如何从日期中截取时间并将其传递给组件?

如何从日志(log)行中删除包名称?

在macOS上读取文件会导致FileNotFound,即使文件存在(并且具有权限)

是否在settings.xml中使用条件Maven镜像?

通过Java列表中的某些字段搜索值

在JDK Flight Recorder中只记录单个线程

为什么mvn编译生命周期阶段不只是编译已更改的java文件?

为什么项目名称出现在我的GET请求中?

Xml Reader 将 BMP 外部的字符解析为代理项对,这会导致无效的 xml