CTypes 允许我们用纯 OCaml 代码绑定 C lib 而不用编写任何 C 代码。这篇文章是以 bonding 项目中的 libseccomp 绑定为例写的。
CTypes 的核心概念是建立 OCaml 类型与 C 类型的双向映射,ctypes 中有如下基本类型:
1 2 3 4 5 6 7 8 9 10 11
openCtypes
(* C 类型与 OCaml 类型的对应关系 *) let c_int : int typ = int(* int *) let c_uint : Unsigned.UInt.t typ = uint (* unsigned int *) let c_long : Signed.Long.t typ = long (* long *) let c_int64 : Unsigned.UInt64.t typ = uint64_t (* int64_t / uint64_t *) let c_float : float typ = float(* float *) let c_double : float typ = double (* double *) let c_char : char typ = char(* char *) let c_void : unit typ = void (* void *)
(* 定义 C 结构体: struct scmp_arg_cmp { unsigned int arg; unsigned int op; uint64_t datum_a; uint64_t datum_b; }; *)
let scmp_arg_cmp : unit structure typ = let s = structure "scmp_arg_cmp"in(* 1. 创建结构体模板 *) let _arg = field s "arg" uint in(* 2. 添加字段 *) let _op = field s "op" uint in let _datum_a = field s "datum_a" uint64_t in let _datum_b = field s "datum_b" uint64_t in seal s; (* 3. 封印结构体 *) s (* 4. 返回类型描述符 *)
注意:
structure "name" 创建一个未完成的类型描述符
field s "fieldname" typ 添加字段,返回字段访问器
seal s 完成结构体定义,之后不可再添加字段
返回的 s 可以作为类型使用
例如:
1 2 3 4 5 6 7 8
let scmp_arg_cmp : unit structure typ = let s = structure "scmp_arg_cmp"in let _arg = field s "arg" uint in let _op = field s "op" uint in let _datum_a = field s "datum_a" uint64_t in let _datum_b = field s "datum_b" uint64_t in seal s; s
定义结构体后,可以通过字段访问器读写值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
(* 写入字段值 *) let set_arg_cmp_values struct_ptr arg_val op_val datum_a_val datum_b_val = let s = structure "scmp_arg_cmp"in let arg = field s "arg" uint in let op = field s "op" uint in let datum_a = field s "datum_a" uint64_t in let datum_b = field s "datum_b" uint64_t in seal s; (* 使用 setf 设置字段值 *) setf struct_ptr arg (Unsigned.UInt.of_int arg_val); setf struct_ptr op (Unsigned.UInt.of_int op_val); setf struct_ptr datum_a (Unsigned.UInt64.of_int64 datum_a_val); setf struct_ptr datum_b (Unsigned.UInt64.of_int64 datum_b_val)
(* C 端的常量定义(seccomp_stubs.ml:58-73) *) moduleConstants = struct let scmp_act_allow = 0x7fff0000l let scmp_act_kill_thread = 0x00000000l let scmp_act_kill_process = 0x00000000l let scmp_act_trap = 0x00030000l let scmp_act_log = 0x7ffc0000l let scmp_act_errno code = Int32.(logor 0x00050000l (logand (of_int code) 0x0000ffffl)) end
let init default_action = let ctx = Stubs.Functions.seccomp_init (action_to_uint32 default_action) in ifCtypes.ptr_compare ctx Ctypes.null = 0then ErrorInit_failed else Ok ctx
(* 使用 Fun.protect 确保清理 *) let with_filter default_action f = match init default_action with | Error e -> Error e | Ok ctx -> try let result = f ctx in release ctx; result with e -> release ctx; raise e