Python中如何解决PyMarshal_ReadObjectFromFile导致的segmentation fault

如题 我的 c++代码:

#include <Python.h>
#include <iostream>
#include <marshal.h>
using namespace std;
int main(){
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue;
    //cout<<233<<endl;
    Py_Initialize();
    FILE *fff = fopen("./a.pyc","br");
    pModule = PyMarshal_ReadObjectFromFile(fff);
    //Py_DECREF(pModule);
    //cout<<PyCode_GetNumFree(pModule);
    //PyImport_ExecCodeModule("asd",pModule);
    pValue = PyLong_FromLong(123);
    cout<<PyCode_Check(pModule);
return 0;

}

只要运行了 PyCode_Check(pModule) 就会导致段错误 进一步测试发现 只要调用 pModule->ob_type 就会导致段错误 同时 a.pyc 文件在 python 中使用 marshal.load 读取 运行 是完全正常的

环境: macos 10.13 测试过 homebrew 的 python 以及 miniconda 的 python 均有这个问题

该如何解决?


Python中如何解决PyMarshal_ReadObjectFromFile导致的segmentation fault

2 回复

这问题我遇到过,PyMarshal_ReadObjectFromFile 导致的段错误通常是因为读取的 .pyc 文件损坏或不兼容。

核心原因有这几个:

  1. Python版本不匹配:用 Python 3.8 生成的 .pyc 文件,用 Python 3.11 去读取
  2. 文件损坏:.pyc 文件在传输或存储过程中损坏
  3. 手动修改了字节码:直接编辑了 .pyc 文件内容

最直接的解决方案:

import marshal
import struct
import sys

def safe_read_pyc_file(filepath):
    """安全读取 .pyc 文件,避免 segmentation fault"""
    try:
        with open(filepath, 'rb') as f:
            # 读取 magic number(Python版本标识)
            magic = f.read(4)
            if len(magic) < 4:
                raise ValueError("文件太小,不是有效的 .pyc 文件")
            
            # 读取时间戳(Python 3.7+)
            if sys.version_info >= (3, 7):
                timestamp = f.read(8)
                if len(timestamp) < 8:
                    raise ValueError("文件格式错误")
            
            # 尝试读取字节码
            data = f.read()
            if not data:
                raise ValueError("文件为空或已损坏")
            
            # 使用 marshal 加载
            code_obj = marshal.loads(data)
            return code_obj
            
    except (EOFError, ValueError, TypeError) as e:
        print(f"文件读取失败: {e}")
        return None
    except Exception as e:
        print(f"未知错误: {e}")
        return None

# 使用示例
code_obj = safe_read_pyc_file("your_file.pyc")
if code_obj:
    print("成功加载字节码对象")

如果问题持续存在,按这个顺序排查:

  1. 重新生成 .pyc 文件
# 删除所有 .pyc 文件
find . -name "*.pyc" -delete
find . -name "__pycache__" -type d -exec rm -rf {} +

# 强制重新编译
python -m py_compile your_script.py
# 或
python -c "import py_compile; py_compile.compile('your_script.py')"
  1. 检查 Python 版本兼容性
import importlib.util
import sys

def check_pyc_compatibility(filepath):
    """检查 .pyc 文件是否与当前 Python 版本兼容"""
    try:
        spec = importlib.util.spec_from_file_location("module", filepath)
        if spec and spec.loader:
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            print("文件兼容,可以正常导入")
            return True
    except ImportError as e:
        print(f"导入失败,可能版本不兼容: {e}")
        return False
  1. 验证文件完整性
import hashlib

def verify_file_integrity(original_py, pyc_file):
    """通过对比源文件和 .pyc 的哈希来验证完整性"""
    with open(original_py, 'rb') as f:
        py_hash = hashlib.md5(f.read()).hexdigest()
    
    with open(pyc_file, 'rb') as f:
        # 跳过 magic number 和时间戳
        if sys.version_info >= (3, 7):
            f.read(12)  # 4字节magic + 8字节时间戳
        else:
            f.read(4)   # 4字节magic
        pyc_data = f.read()
        pyc_hash = hashlib.md5(pyc_data).hexdigest()
    
    print(f"源文件哈希: {py_hash}")
    print(f".pyc 数据哈希: {pyc_hash}")

根本解决方案: 别直接操作 .pyc 文件,用标准导入机制。如果必须处理字节码,用 compile()marshal.dumps() 而不是直接读写文件。

总结:优先重新编译 .pyc 文件,检查版本兼容性。


docker 内运行就能解决这个问题

同时还有个坑: 网上都说 pyc 文件前 8 字节是文件头要去掉 但是 python3.7 中是 16 字节

回到顶部