C function name-dependent segfault with Python ctypes

时间:2015-07-29 00:47:09

标签: python c ctypes

I'm getting a really weird crash when using ctypes in Python, but I'm not sure if the problem comes from Python or C.

Here is the C source (in if (Meteor.isServer) { Meteor.publish("dice",function(){ return DiceResults.find(); }); }; ):

test.c

Then here's what happens when I call #include <stdio.h> void compress(char *a, int b) { printf("inside\n"); } void run() { printf("before\n"); compress("hi", 2); printf("after\n"); } with ctypes:

run()

The weirdest thing is that the crash doesn't happen when I rename $ python -c 'import ctypes; ctypes.cdll.LoadLibrary("./test.so").run()' before Segmentation fault (core dumped) to anything else.

Other things that prevent it from crashing:

  • Calling compress() directly
  • Calling compress() or run() from C directly (If I add a compress(), compile it directly, and execute it)
  • Removing either argument from the signature of main() (but then the function doesn't seem to execute, based on the lack of "compress()" being printed.

I'm pretty new to C, so I'm assuming there's something I'm missing here. What could be causing this?

System info:
Python 2.7.6
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
Ubuntu 14.04
inside: 3.13.0-58-generic

2 个答案:

答案 0 :(得分:5)

根据调试情况,该程序正在尝试拨打compress中的libz.so.1

$ gdb python -c core
...
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `python -c import ctypes; ctypes.cdll.LoadLibrary("./test.so").run()'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007f9ddea18bff in compress2 () from /lib/x86_64-linux-gnu/libz.so.1

接受不同的参数(zlib.h):

ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
                                 const Bytef *source, uLong sourceLen));

ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
                                  const Bytef *source, uLong sourceLen,
                                  int level));
/*

您可以将compress功能修改为static以解决此问题:

static void compress(char *a, int b)
{
    printf("inside\n");
}

答案 1 :(得分:4)

虽然@falsetru已经诊断出这个问题,但是他的解决方案在一般情况下无法工作,在这种情况下你有很多文件要静态链接在一起(因为声明静态的整个点是不能让它们从其他文件)。

虽然@eryksun发布了一个解决方案,当你想要宣布一个与另一个函数同名的函数时,一般来说,你可能有很多你不想要的C函数要导出,你不必担心它们是否与某些Python碰巧导入的库中的某些随机函数发生冲突,并且你不想要为每个内部函数添加前缀有一个属性。

(GCC维护documentation on function attributes,包括此功能可见性功能。)

避免命名空间冲突的更通用的解决方案是告诉链接器默认情况下不导出任何符号,然后仅将要导出的那些函数(如run())标记为可见。

可能有一种标准方法可以为此定义宏,但是我的C已经过时了,我不知道它。无论如何,这将有效:

#include <stdio.h>

#define EXPORT __attribute__((visibility("protected")))

void compress(char *a, int b) {
  printf("inside\n");
}

EXPORT void run() {
  printf("before\n");
  compress("hi", 2);
  printf("after\n");
}

您可以像这样链接和运行它:

$ gcc -x c test.c --shared -fvisibility=hidden -o test.so
$ python -c 'import ctypes; ctypes.cdll.LoadLibrary("./test.so").run()'
before
inside
after