Python与C语言交互——libffi库介绍

350 阅读3分钟

相关知识

函数调用约定涉及的方面:

  • 参数的传递:参数是通过栈传递还是寄存器传递;
    参数的传递顺序:是从左到右,还是从右到左
  • 返回值处理:通过寄存器传递 或 通过指针 或 栈空间
  • 栈的维护方式:有调用者清理栈(caller-cleanup)或 被调用者清理栈(callee-cleanup)

函数调用典型方式:

  • cdecl:c 语言使用
  • stdcall:windows api
  • fastcall:前几个参数通过寄存器传递
  • thiscall:C++ 语言使用,包含 this 指针传递
  • System V AMD64:x86-64 的 linux 和 macOS
  • CPython:python 解释器

Hook 技术分类:

  • 编译期 Hook:在编译时插入钩子代码。
  • 动态库加载期 Hook:在动态加载库时通过符号重定向或链接替换来实现钩子(如使用 LD_PRELOAD、dlopen)。
  • 运行时 Hook:在程序执行过程中动态修改函数指针或内存,实时拦截函数调用。

Python 程序内存模型:

  • 堆:实现对象分配和垃圾回收,基于 pymalloc 内存池。
  • 栈:保存局部变量、函数调用信息、python 对象引用(python 全部数据都是对象),由操作系统分配空间,由 python 解释器使用。

C 程序内存模型:

  • 堆:通过 malloc、free 操作,由用户态操作,基于 ptmalloc 内存池
  • 栈:存储局部变量、函数参数、函数返回值,由操作系统分配空间,由 C 程序本身处理栈使用

概述

Libffi 通常用于支持高级语言(python)动态的调用 c 库,与 hook 用于拦截 API 调用的功能不同,是支持运行期跨语言调用的库。Libffi 能够处理统一进程中执行不同语言的“函数调用约定”带来的问题,能够处理各种不同平台/编译期带来的“函数调用约定”差异。

使用环境

支持 Linux、iOS、Windows 等操作系统下的 GCC、Clang、MSVC 等编译器在 ARM、X86 等硬件架构上 cdecl 等函数调用约定的函数调用。具体可见:

github.com/libffi/libf…

实现原理

以实现 Python 调用 C 函数为例。

Python 解释器与 C 函数的函数调用约定不同,对堆栈的访问、构建方式不同。libffi 实现同进程下的二者兼容。

  1. 栈帧管理:libffi 会创建符合 C 调用约定的栈帧,将 Python 参数正确地传递给 C 函数。
  2. 内存管理与转换:libffi 负责将 Python 数据类型转换为 C 数据类型,并处理内存分配和释放,避免内存冲突。
  3. 栈空间隔离:Python 和 C 的栈空间是独立的,libffi 确保两者的栈操作不会互相干扰。
  4. 堆管理:C 函数运行或返回值可能涉及堆内存使用,libffi 会处理这部分内存的分配和释放。

因此,libffi 作为 Python 和 C 之间的桥梁,自动处理了栈空间、内存管理、数据类型转换等复杂的细节,使得 Python 程序能够顺利地调用 C 函数。

接口

建议参考这个开源库进行学习:github.com/faimin/ZDLi…