如何用CFFI实现Ctypes中in_dll()的运行时符号查找功能?
macOS框架动态库常量的CFFI运行时符号查找方案
当访问macOS上的框架动态链接库时,这类库通常包含大量字符串常量。使用Ctypes时,可直接在运行时动态获取任意常量,无需预先定义全部:
from ctypes import CDLL, c_void_p, POINTER CFString = c_void_p CFStringRef = POINTER(CFString) my_dll = CDLL(...) s1 = CFStringRef.in_dll(my_dll, "kSomeSpecificConstant") s2 = CFStringRef.in_dll(my_dll, "kSomeOtherConstant")
但使用CFFI时,常规做法只能通过显式声明来获取常量:
import cffi my_dll = cffi.FFI() my_dll.cdef(""" typedef void *CFTypeRef; typedef CFTypeRef CFStringRef; extern CFStringRef kSomeSpecificConstant; extern CFStringRef kSomeOtherConstant; """)
CFFI其实支持运行时动态查找符号,只是需要借助系统的动态链接器API(dlopen/dlsym)来实现,具体方式如下:
实现步骤
- 用CFFI声明
dlopen和dlsym的接口,这两个是POSIX标准的动态链接器函数,macOS原生支持 - 打开目标框架库,获取句柄
- 调用
dlsym查找指定符号,再转换成对应的类型
代码示例
import cffi ffi = cffi.FFI() # 声明必要的C接口 ffi.cdef(""" void *dlopen(const char *path, int mode); void *dlsym(void *handle, const char *symbol); typedef void *CFTypeRef; typedef CFTypeRef CFStringRef; """) # 加载系统动态链接器库 libdl = ffi.dlopen("libdl.dylib") # 打开目标macOS框架(以CoreFoundation为例) # RTLD_LAZY模式:延迟加载符号,数值为1 framework_handle = libdl.dlopen( "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", 1 ) # 运行时动态查找并转换符号 kSomeSpecificConstant = ffi.cast( "CFStringRef", libdl.dlsym(framework_handle, "kSomeSpecificConstant") ) kSomeOtherConstant = ffi.cast( "CFStringRef", libdl.dlsym(framework_handle, "kSomeOtherConstant") )
简化写法(已加载框架时)
如果已经通过CFFI的ffi.dlopen加载了目标框架,可以直接获取其底层句柄来查找符号:
# 先加载目标框架 core_foundation = ffi.dlopen( "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" ) # 获取框架的底层句柄并查找符号 symbol_ptr = libdl.dlsym(ffi.from_handle(core_foundation), "kSomeSpecificConstant") constant = ffi.cast("CFStringRef", symbol_ptr)
这种方式和Ctypes的in_dll本质逻辑一致,都是通过动态链接器在运行时查找符号,只是CFFI需要手动处理库打开和类型转换的步骤。
内容的提问来源于stack exchange,提问作者YKdvd
相关产品推荐
相关产品推荐

