使用ctypes.cdll.LoadLibrary加载库时出现无效的ELF头部

3 投票
1 回答
10158 浏览
提问于 2025-04-17 07:01

我刚开始在Linux上使用gcc编译器。我正在按照这个教程学习,不过我用的是g++编译器。

这是我的代码文件hello_fn.cpp:

#include <stdio.h>
#include "hello.h"

void 
hello (const char * name)
{
  printf ("Hello, %s!\n", name);
}

这是另一个代码文件bye_fn.cpp:

#include <stdio.h>
#include "hello.h"

void 
bye (void)
{
  printf ("Goodbye!\n");
}

还有一个头文件hello.h:

void hello (const char * name);
void bye (void);

然后我在命令行中运行了以下命令:

$ g++ -Wall -c hello_fn.cpp
$ g++ -Wall -c bye_fn.cpp
$ ar cr libhello.a hello_fn.o bye_fn.o

接着我在Python中尝试了以下内容:

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes 
>>> test = ctypes.cdll.LoadLibrary(r'/home/oob/development/libtest/libhello.a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/ctypes/__init__.py", line 431, in LoadLibrary
    return self._dlltype(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 353, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: /home/jeff/development/libtest/libhello.a: invalid ELF header

我的想法是写一些C++的函数,然后从Python中调用它们。你有什么建议吗?

更新:我已经让事情“运作”起来了。根据Cat Plus Plus的建议,我可能不会为新代码采用这种方法,但我成功地让它在一个我从Windows移植到Linux的大型旧C++库中工作。我们需要一个前端来调用这个库中的一些长时间运行的函数,所以我觉得用Python可能是最简单的选择。这些函数会产生很多输出,只返回一个整数代码,所以也许我可以避免Cat Plus Plus所说的“痛苦”的部分。

这是我所做的。

修改了hello_fn.cpp:

#include <stdio.h>
#include "hello.h"

extern "C" int 
hello (void)
{
  return 16;
}                                                                                

修改了bye_fn.cpp:

#include <stdio.h>
#include "hello.h"

extern "C" void 
bye (void)
{
  printf ("Goodbye!\n");
}

修改了hello.h:

extern "C" int hello (void);
extern "C" void bye (void);

这是我的构建脚本buildscript.sh:

#!/bin/bash

rm *.o
rm *.so

g++ -fpic -g -c -Wall hello_fn.cpp
g++ -fpic -g -c -Wall bye_fn.cpp
#make a shared library, not a static library (thanks cat plus plus)
g++ -shared -o libhello.so hello_fn.o bye_fn.o

还有我的测试脚本test.py:

#!/usr/bin/python

import ctypes

c = ctypes.cdll.LoadLibrary(r'/home/jeff/development/libtest/libhello.so')
a = c.hello()
print 'hello was ' + str(a)
c.bye()

在终端中试试这个……

oob@ubuntu:~/development/libtest$ ./build_script.sh 
oob@ubuntu:~/development/libtest$ python test.py 
hello was 16
Goodbye!

我们的旧库其实并没有使用任何Windows特有的C++内容(多亏了写这段代码的人),所以移植过程相对简单。我们有几个函数使用了extern "C"来暴露函数。为了移植,我做了以下更改:

#ifdef LINUX
#define __stdcall
#endif
#ifdef WINDOWS
#define __stdcall __stdcall
#endif

对于我们的一个函数,我可以直接保持不变,例如:

extern "C" long __stdcall reform_proj {
    //do a bunch of stuff
    return 0;
}

1 个回答

7

ctypes 是用来加载共享库的工具。ar 则是用来创建对象文件的归档,也就是我们说的 静态 库。你不能用 ctypes 来加载这个文件,因为它只能被链接器理解。

还有一个问题是,如果你想通过 ctypes 使用 C++ 的共享库,那会非常麻烦,甚至几乎不可能。建议你别这样做。可以使用 Cython,这样你可以写一个合适的 Python 扩展,来和你的 C++ 代码进行交互(这样你就可以选择静态链接或动态链接,它都会正常工作)。

另一个选择是 Boost.Python,不过它的文档相对较少,但它的好处是可以直接在你的 C++ 代码中定义 Python 模块,而不是使用其他语言写的包装器。

第三个选择是 SWIG,不过我自己没用过,所以不能告诉你它在实际使用中效果如何。

撰写回答