Linker 是 Java 平台的预览 API。
外部函数通常驻留在可以按需加载的库中。每个库都符合特定的 ABI(应用程序二进制接口)。 ABI 是一组与构建库的编译器、操作系统和处理器相关联的调用约定和数据类型。例如,Linux/x64 上的 C 编译器通常构建符合 SystemV ABI 的库。
链接器详细了解特定 ABI 使用的调用约定和数据类型。对于符合该 ABI 的任何库,链接器可以在 JVM 中运行的 Java 代码和库中的外部函数之间进行调解。尤其:
此外,链接器提供了一种在符合 ABI 的库中查找外部函数的方法。每个链接器选择一组常用于与 ABI 关联的操作系统和处理器组合的库。例如,Linux/x64 的链接器可能会选择两个库:libc 和 libm。这些库中的函数通过 符号查找 公开。
nativeLinker() 方法为与 Java 运行时当前正在执行的操作系统和处理器关联的 ABI 提供链接器。此链接器还通过其 默认查找 提供对使用 Java 运行时加载的本机库的访问。
Downcall 方法句柄
链接外部函数 是一个进程,它需要一个函数描述符,一组内存布局,它们一起指定要链接的外部函数的签名,并在完成时返回一个向下调用方法句柄,即可以被调用的方法句柄用于调用目标外部函数。与返回的方法句柄关联的 Java 方法类型 是 衍生的PREVIEW 来自函数描述符中的参数和返回布局。 downcall 方法句柄类型,然后可能会被额外的前导参数修饰,如果两者都存在,则按给定的顺序:
- 如果创建了 不指定目标地址 downcall 方法句柄,则 downcall 方法句柄类型具有类型为
MemorySegment的前导参数PREVIEW 从中可以导出目标外部函数的地址。 - 如果函数描述符的返回布局是组布局,则生成的向下调用方法句柄接受类型为
SegmentAllocator的附加前导参数PREVIEW ,链接器运行时使用它来分配与 downcall 方法句柄返回的结构关联的内存区域。
上行存根
创建上行存根 需要方法句柄和函数描述符;在这种情况下,函数描述符中的内存布局集指定了与向上调用存根关联的函数指针的签名。提供的方法句柄的类型必须匹配与调用存根关联的方法类型,即 衍生的PREVIEW 从提供的函数描述符。
Upcall 存根由 MemorySegment 类型的实例建模PREVIEW upcall 存根可以通过引用传递给其他 downcall 方法句柄,并且它们通过关联的 scope 释放PREVIEW .
安全考虑
创建向下调用方法句柄本质上是不安全的。外部库中的符号通常不包含足够的签名信息(例如外部函数参数的数量和类型)。因此,链接器运行时无法验证链接请求。当客户端与通过无效链接请求(例如,通过指定具有过多参数布局的函数描述符)获得的向下调用方法句柄交互时,此类交互的结果是不确定的,并可能导致 JVM 崩溃。在 downcall 句柄调用中,链接器运行时为MemorySegment 类型的任何参数 A 保证以下内容PREVIEW 其对应的布局是ValueLayout.ADDRESS PREVIEW :
A的范围是 活PREVIEW .否则,调用抛出IllegalStateException;- 调用发生在线程
T中,这样A.scope().isAccessibleBy(T) == true。否则,调用抛出WrongThreadException;和 A的范围是 保持活力PREVIEW 在调用期间。
0 。但是,如果返回布局是 无界PREVIEW 地址布局,则返回段的大小为 Long.MAX_VALUE 。
创建向上调用存根时,链接器运行时根据提供的函数描述符验证目标方法句柄的类型,如果检测到任何不匹配则报告错误。至于向下调用,如果外部代码将与向上调用存根关联的函数指针转换为与提供的函数描述符不兼容的类型,则可能会发生 JVM 崩溃。此外,如果与上调用存根关联的目标方法句柄返回一个内存段PREVIEW clients必须保证这个地址在upcall完成后不会失效。这可能会导致未指定的行为,甚至 JVM 崩溃,因为向上调用通常是在向下调用方法句柄调用的上下文中执行的。
对应布局为 地址布局 的向上调用存根参数PREVIEW 是与 全球范围 关联的本机段PREVIEW .在正常情况下,此段参数的大小为 0 。但是,如果与 upcall 存根参数关联的布局是 无界PREVIEW 地址布局,则段参数的大小为 Long.MAX_VALUE 。
- 实现要求:
- 此接口的实现是不可变的、线程安全的和 value-based 。
- 自从:
- 19
-
内部类总结
内部类 -
方法总结
修饰符和类型方法描述返回一组常用库中符号的符号查找。downcallHandle(FunctionDescriptorPREVIEW function, Linker.OptionPREVIEW... options) 创建一个方法句柄,可用于调用具有给定签名的外部函数。default MethodHandledowncallHandle(MemorySegmentPREVIEW symbol, FunctionDescriptorPREVIEW function, Linker.OptionPREVIEW... options) 创建一个方法句柄,可用于调用具有给定签名和地址的外部函数。返回与底层本机平台关联的 ABI 的链接器。upcallStub(MethodHandle target, FunctionDescriptorPREVIEW function, SegmentScopePREVIEW scope) 创建一个存根,它可以作为函数指针传递给其他外部函数,与给定范围相关联。
-
方法详情
-
nativeLinker
返回与底层本机平台关联的 ABI 的链接器。底层本地平台是 Java 运行时当前正在执行的操作系统和处理器的组合。与返回的链接器交互时,客户端必须使用
function descriptor描述外部函数的签名PREVIEW 其参数和返回布局指定如下:- 标量类型由 值布局 建模PREVIEW 合适载体的实例。 C 中标量类型的示例有
int、long、size_t等。标量类型与其对应布局之间的映射取决于返回的链接器的 ABI; - 复合类型由 组布局 建模PREVIEW .根据返回链接器的 ABI,额外的 paddingPREVIEW 成员布局可能需要符合 C 中复合类型定义的大小和对齐约束(例如使用
struct或union);和 - 指针类型由 值布局 建模PREVIEW 载体实例
MemorySegmentPREVIEW . C 中指针类型的例子是int**和int(*)(size_t*, size_t*);
上面未列出的任何布局都是 unsupported ;当用于创建
downcall method handle或 上行存根 时,包含不受支持的布局的函数描述符将导致抛出IllegalArgumentException。不直接支持可变参数函数(例如,在形式参数列表末尾使用尾部省略号
...声明的 C 函数或使用空的形式参数列表)。但是,可以使用 链接器选项 链接可变参数函数PREVIEW 指示可变参数列表的开始,以及描述给定变量 arity callsite 的专用函数描述符。或者,在外部库允许的情况下,客户端可以通过传递类型为VaList的尾随参数来与可变参数函数进行交互PREVIEW (例如在vsprintf中)。这个方法是restricted 。受限方法是不安全的,如果使用不当,它们的使用可能会使 JVM 崩溃,或者更糟的是,无声地导致内存损坏。因此,客户应避免依赖受限的方法,并尽可能使用安全和受支持的功能。
- API 注意:
- 目前不可能获得用于操作系统和处理器的不同组合的链接器。
- 实现注意事项:
-
与返回的链接器关联的 默认查找 公开的库是在 Java 运行时当前正在执行的进程中加载的本机库。例如,在 Linux 上,这些库通常包括
libc、libm和libdl。 - 返回:
- 与 Java 运行时当前正在执行的操作系统和处理器关联的 ABI 的链接器。
- 抛出:
UnsupportedOperationException- 如果不支持底层原生平台。IllegalCallerException- 如果调用者所在的模块未启用本机访问。
- 标量类型由 值布局 建模PREVIEW 合适载体的实例。 C 中标量类型的示例有
-
downcallHandle
default MethodHandle downcallHandle(MemorySegment PREVIEW symbol, FunctionDescriptor PREVIEW function, Linker.Option PREVIEW ... options) 创建一个方法句柄,可用于调用具有给定签名和地址的外部函数。如果提供的方法类型的返回类型是
MemorySegment,那么生成的方法句柄具有一个额外的前缀参数,类型为SegmentAllocatorPREVIEW ,链接器将使用它来分配按值返回的结构。调用这个方法相当于下面的代码:
linker.downcallHandle(function).bindTo(symbol);- 参数:
symbol- 目标函数的地址。function- 目标函数的函数描述符。options- 任何链接器选项。- 返回:
- 向下调用方法句柄。方法句柄类型是inferred
- 抛出:
IllegalArgumentException- 如果此链接器不支持提供的函数描述符。或者如果符号是MemorySegment.NULLPREVIEWIllegalArgumentException- 如果给出了无效的链接器选项组合。
-
downcallHandle
创建一个方法句柄,可用于调用具有给定签名的外部函数。生成的方法句柄具有一个前缀参数(作为第一个参数),对应于外部函数入口点,类型为MemorySegmentPREVIEW , 用于指定要调用的目标函数的地址。如果提供的函数描述符的返回布局是
GroupLayoutPREVIEW ,那么生成的方法句柄具有一个额外的前缀参数(紧接在地址参数之后插入),类型为SegmentAllocatorPREVIEW ),链接器将使用它来分配按值返回的结构。返回的方法句柄将抛出一个
IllegalArgumentException如果MemorySegmentPREVIEW 传递给它的参数与MemorySegment.NULL相关联PREVIEW 地址,或者NullPointerException如果该参数是null。- 参数:
function- 目标函数的函数描述符。options- 任何链接器选项。- 返回:
- 向下调用方法句柄。方法句柄类型是来自提供的函数描述符的inferred 。
- 抛出:
IllegalArgumentException- 如果此链接器不支持提供的函数描述符。IllegalArgumentException- 如果给出了无效的链接器选项组合。
-
upcallStub
MemorySegment PREVIEW upcallStub(MethodHandle target, FunctionDescriptor PREVIEW function, SegmentScope PREVIEW scope) 创建一个存根,它可以作为函数指针传递给其他外部函数,与给定范围相关联。从外部代码调用这样的函数指针将导致执行提供的方法句柄。返回的内存段地址指向新分配的上行调用存根,并与提供的范围相关联。因此,当范围变为 not 活 时,相应的上行调用存根将被释放PREVIEW .
目标方法句柄不应抛出任何异常。如果目标方法句柄确实抛出异常,则 VM 将以非零退出代码退出。为了避免 VM 由于未捕获的异常而中止,客户端可以将目标方法句柄中的所有代码包装在捕获任何
Throwable的 try/catch 块中,例如通过使用MethodHandles.catchException(MethodHandle, Class, MethodHandle)方法句柄组合器,并根据需要在相应的赶上块。- 参数:
target- 目标方法句柄。function- 向上调用存根函数描述符。scope- 与返回的向上调用存根段关联的范围。- 返回:
- 一个零长度段,其地址是向上调用存根的地址。
- 抛出:
IllegalArgumentException- 如果此链接器不支持提供的函数描述符。IllegalArgumentException- 如果确定目标方法句柄可以抛出异常,或者目标方法句柄的类型与向上调用存根 inferred type 不匹配。IllegalStateException- 如果scope不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则scope.isAccessibleBy(T) == false。
-
defaultLookup
SymbolLookup PREVIEW defaultLookup()返回一组常用库中符号的符号查找。每个
LinkerPREVIEW 负责选择在Linker支持的操作系统和处理器组合上被广泛认为有用的库PREVIEW .因此,符号查找暴露的精确符号集是未指定的;它不同于一个LinkerPREVIEW 给另一个。- 实现注意事项:
-
强烈建议
defaultLookup()的结果公开一组随时间稳定的符号。如果符号查找之前暴露的符号不再暴露,defaultLookup()的客户端可能会失败。如果实施者提供
LinkerPREVIEW 多个操作系统和处理器组合的实现,那么强烈建议defaultLookup()的结果尽可能地公开一组跨所有操作系统和处理器组合的一致符号。 - 返回:
- 在一组常用库中查找符号。
-
Linker。