Propagating exceptions through dlsym cython

烂漫一生 提交于 2020-01-02 15:06:56

问题


I am unable to propagate exceptions through dlsym. I use dlsym to load a cythonized python file. I made a minimal working example below so you can try it yourself:

I have a pyx file, c_fun.pyx, which I compile to a C file using Cython. Then I'm using dlsym to load in the so file in another program, say use_fun.c++. You can use ./compile.sh to compile the files. On executing ./test, the program crashes with a segmentation fault.

#c_fun.pyx:
cdef public double _myfunction(double x) except*:
    a=1/0          # This does not propagate an exception. Comment to make the example work
    return x**2-x  # This works.

#use_fun.c++
#include <dlfcn.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    void* handle = dlopen("./c_fun.so", RTLD_NOW | RTLD_GLOBAL);
    if(handle==NULL) {
        printf("%s \nat line number: %i\n",dlerror(),__LINE__); return;
    }

    double (*f)(double) = (double (*)(double))dlsym(handle, "_myfunction");
    if(f==NULL) {
        printf("%s\n",dlerror()); return;
    }

    double res = 0;
    try {
        res = (*f)((double)99);
    } catch(char *err) {
        printf("Got exception: %s.\n", err);
    }
    printf("res = %f\n", res);
}

#setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("c_fun", ["c_fun.pyx"], libraries = ['python2.7'], extra_compile_args= ['-fexceptions'])]

setup(
  name = 'Hello world app', cmdclass = {'build_ext': build_ext}, ext_modules = ext_modules
)

# compile.sh
python setup.py build_ext --inplace
echo "gcc use_fun.c++ -g -O0 -o test -ldl"
g++ use_fun.c++ -g -O0 -o test -ldl

Initially, I tried without the "except*" at the end of my function, and without the "-fexceptions" compiler flag. But adding those do not change the behavior. gdb doesn't even allow me to backtrace the problem, and says: "Cannot find new threads: generic error ". I have looked on the internet for exception handling in combination with dlsym, but have found little information.

The rest of the story: To make it more complicated, actually this use_fun.c++ file is a Python module which I imported. So: I'm using Python to load in a the module use_fun, and in that C++ module, _myfunction is called. But also then, I cannot get the exception handling right. However, in that case, I did successfully use gdb to get into the C code and see that PyErr_Format was successfully called. But the error is not triggered nor caught in the C++ code.

Note that to be able to expose the file _myfunction, I have specified the 'public' keyword in the pyx file. Without it, the name mangling would cause the dlsym function call to fail. I tried looking for documentation on these two links:

http://docs.cython.org/src/userguide/external_C_code.html#using-cython-declarations-from-c

http://docs.cython.org/src/userguide/language_basics.html#error-return-values

Edit: I have found the solution. I'll keep the original question above so it can help others. There were essentially 2 problems

1) Of course, since C does not have exceptions, it was wrong to set a try/catch over the function! What does work, is checking whether an error occurred in Python using PyErr_Occurred().

2) Since cython generates a module, it has to be initialized before you can properly use it. This means: calling Py_Initialize/Py_Finalize, and also calling the init_cfun method.

The solution is shown below:

#include "Python.h"
#include <dlfcn.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    Py_Initialize();
    void* handle = dlopen("./c_fun.so", RTLD_NOW | RTLD_GLOBAL);
    if(handle==NULL) {
        printf("%s \nat line number: %i\n",dlerror(),__LINE__); return -1;
    }
    void (*init_func)();
    *(void**)(&init_func) = dlsym(handle, "initc_fun");
    init_func();

    double (*f)(double) = (double (*)(double))dlsym(handle, "_myfunction");
    if(f==NULL) {
        printf("%s\n",dlerror()); return -1;
    }

    double res = 0;
    res = (*f)((double)99);
    if(PyErr_Occurred()) {
        PyErr_Print();
    }
    printf("res = %f\n", res);
    Py_Finalize();
    return 0;
}

来源:https://stackoverflow.com/questions/14280421/propagating-exceptions-through-dlsym-cython

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!