跨语言调 C 库:DragonFFI
文章目录
跨语言调用是很方便实用的操作,但其实现并非想象的那么简单,包含有复杂的 ABI 设计、语言间的兼容交互等,本文将介绍一个跨语言调 C 库:DragonFFI。
1、定义
在官方 github 仓库中就有相关的说明,DragonFFI 是 C 语言的 FFI(Foreign Function Interface,外部函数接口)库,使用 C++ 编写,基于 clang/llvm 来实现。
跨语言调用 C ,一般有通过手写胶水代码的(JNI,Python,Ruby)、生成胶水代码的(SWIG)、扩展 C 的(C++,Objective-C)。
而 FFI 即其他语言可以通过它所提供的 API 和绑定来调用 C 语言的函数。
A foreign function interface (FFI) is a mechanism by which aprogram written in one programming language can call routines ormake use of services written in another.
|
|
2、与其他 FFI 库的对比
libffi 是有名的提供 C 语言 FFI 库。但它不支持最近的一些调用约定(calling convention),比如 Linux x64 系统的 Microsoft x64 ABI(Application Binary Interface),而且其每个 ABI 还需要手写汇编,其 ABI 也变得复杂,尤其是在传递结构体类型时。
cffi 是 libffi 当中所提供的 python 绑定,它还使用了 pycparser 来声明接口和类型。但它所使用的 C 语言的解析器不支持 include 和一些函数的 attribute,因此需要自行维护调用 C 库的头文件。
DragonFFI 基于 clang/llvm 实现,得以使用 clang 解析头文件而不用自行适配、可以动态编译(支持 JIT)、支持很多调用约定和函数的 attribute。
DragonFFI 目前支持 Linux、OSX 和 Windows,支持 Intel 32 位和 64 位 CPU。
3、如何解析 C 语言的类型
为了解析 C 语言的类型,我们需要以下的一些基本信息:
-
函数类型,以及调用约定
-
结构体:偏移量、名字
-
union/enum:域名(值)
一方面,我们看到 LLVM IR 过于底层(low level),十分复杂;另一方面,Clang AST 又过于抽象(high level),没有一些类型信息(比如结构体布局对齐等)。
比较合适的方式是,使用 LLVM metadata (元数据,描述数据的数据),由 Clang 生成 DWARF(debugging with attributed record formats) 或者 PDB(Program Database) 结构体时产生。它们包含有基本的类型描述信息、函数调用约定。
注:PDB 是一种文件格式,和 DWARF 是一个概念,由微软开发出来,包含有调试信息。
这些 LLVM metadata 的具体例子,就是嵌在 IR 里面的那些标注:
|
|
4、实现
DragonFFI 首先会解析 LLVM IR 里所包含的调试信息,然后创建一个自定义的类型系统,来表示各种各样的函数类型、结构体、枚举、typedef。
之所以创建一个自定义的类型系统,一是因为只需要从元数据树(metadata tree)中获取所需信息就够了(不用所有的调试信息),二是因为可以让 DragonFFI 的公开头文件不需要依赖 LLVM 的头文件。
依赖于 Clang,减少了大量的工作(函数重定义、类型错误、函数对外不可见、类型的内存布局等)。
参考
DragonFFI:https://github.com/aguinet/dragonffi
libffi:https://sourceware.org/libffi/
cffi:https://cffi.readthedocs.io/en/latest/
DWARF:http://dwarfstd.org/
PDB:https://llvm.org/docs/PDB/index.html
DragonFFI: FFI/JIT for the C language using Clang/LLVM:https://blog.llvm.org/2018/03/dragonffi-ffijit-for-c-language-using.html
Skip the FFI!:https://llvm.org/devmtg/2014-10/Slides/Skip%20the%20FFI.pdf
Guinet-DragonFFI:https://llvm.org/devmtg/2018-04/slides/Guinet-DragonFFI.pdf
文章作者 calssion
上次更新 2021-07-10