Skip to main content
 首页 » 编程设计

python之OCaml 生成与 OCaml 运行时链接的共享对象

2025年05月04日22sxdcgaq8080

我正在尝试使用 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对于这部分。