我正在尝试使用 ctypes 中的 cdll 接口(interface)从 OCaml 库构建一个共享对象,我可以在 Python 下运行它.
有没有办法在 OCaml 工具链中引导某些东西来生成一个共享对象,并链接到它的依赖项?
我开始怀疑 OCaml 根本不支持我正在尝试做的事情。我想调用一个 OCaml 函数,然后让它产生某种结果并将控制权返回到调用它的地方。
这是我的 OCaml 库
(* foo.ml *)
let add x y = x + y
我在 OS X 上编译它时使用的命令。(使用 .so 而不是 .dylib 稍微违反了平台的命名约定,但是 ocamlopt 将只接受 .o 或 .so 的扩展名。)
$ ocamlopt -output-obj -fPIC -o foo.so foo.ml
成功并生成 foo.so。
这是我第一次尝试将共享对象加载到 python 程序(python 3.6)中。
# foo.py
import ctypes
lib = ctypes.cdll.LoadLibrary('./foo.so')
print("Loaded")
运行时出现如下错误
节选:
self._handle = _dlopen(self._name, mode)
OSError: dlopen(./foo.so, 6): Symbol not found: _caml_code_area_end
完整错误:
Traceback (most recent call last):
File "foo.py", line 3, in <module>
lib = ctypes.cdll.LoadLibrary('./foo.so')
File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ctypes/__init__.py", line 426, in LoadLibrary
return self._dlltype(name)
File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ctypes/__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: dlopen(./foo.so, 6): Symbol not found: _caml_code_area_end
Referenced from: ./foo.so
Expected in: flat namespace
in ./foo.so
我在编译器的目录 ocamlopt -where 中四处寻找符号。
$ find `ocamlopt -where` | xargs -I% bash -c 'nm % | sed -e "s|^|%:|"' | & grep _caml_code_area_end
/usr/local/lib/ocaml/libasmrun_pic.a: 0000000000000008 C _caml_code_area_end
/usr/local/lib/ocaml/libasmrun_pic.a: U _caml_code_area_end
/usr/local/lib/ocaml/libasmrunp.a: 0000000000000008 C _caml_code_area_end
/usr/local/lib/ocaml/libasmrunp.a: U _caml_code_area_end
/usr/local/lib/ocaml/libasmrun_shared.so: 000000000001ded0 S _caml_code_area_end
/usr/local/lib/ocaml/libasmrun.a: 0000000000000008 C _caml_code_area_end
/usr/local/lib/ocaml/libasmrun.a: U _caml_code_area_end
/usr/local/lib/ocaml/libasmrund.a: 0000000000000008 C _caml_code_area_end
/usr/local/lib/ocaml/libasmrund.a: U _caml_code_area_end
并首先尝试加载 /usr/local/lib/ocaml/libasmrun_shared.so,给出
import ctypes
lib = ctypes.cdll.LoadLibrary('/usr/local/lib/ocaml/libasmrun_shared.so')
lib = ctypes.cdll.LoadLibrary('./foo.so')
print("Loaded")
产生错误的地方:
OSError: dlopen(/usr/local/lib/ocaml/libasmrun_shared.so, 6): Symbol not found: _caml_apply2
Referenced from: /usr/local/lib/ocaml/libasmrun_shared.so
Expected in: flat namespace
in /usr/local/lib/ocaml/libasmrun_shared.so
_caml_apply2 符号在 OCaml 的运行时中很深,我无法为它找到扩展名为 .so 的文件 ... 和 cdll 似乎没有公开加载 .as 的能力。
请您参考如下方法:
出于某种原因,-fPIC 标志还不够,您还需要一个 PIC 运行时。以前,这需要一个特殊的开关(例如 4.04.1+fPIC),但现在您可以选择要链接到哪个运行时。
以下对我有效(Debian buster,opam 4.06.1+rc2 switch):
% ocamlopt -output-obj -runtime-variant _pic -o foo.so foo.ml
% python foo.py
Loaded
I'm trying to build a shared object out of an OCaml library that I can run under Python using the cdll interface in ctypes.
请注意,您不能只从 Python(或其他接口(interface))调用 OCaml 符号,您需要编写一些 C 来设置运行时和管理 OCaml 对象。一个简单的方法是使用 Cstubs_inverted对于这部分。
