使用 ctypes.cdll.LoadLibrary 从 Python 加载库时,ELF 标头无效

发布于 2024-12-18 02:25:32 字数 3125 浏览 2 评论 0原文

我刚刚开始在 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);

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

$ 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;
}                                                                                

修改了 by_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;
}

I am just getting started with gcc on Linux. I am following the tutorial here, except that I am using the g++ compiler.

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);

I then run the following in the shell:

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

Then I try the following from 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

My idea was to write some functions in c++ and call them from Python. Any ideas?

UPDATE: I was able to get things "working". Based on what Cat Plus Plus said, I may not go this direction for new code, but I was able to get this to work with a large legacy c++ library that I was porting from Windows to Linux. We need a frontend to call some long running functions from this library, so I thought Python might be easiest. The functions create a lot of output and only return an integer return code, so maybe I can avoid the "painful" stuff Cat Plus Plus was saying.

Here is what I did.

Modified hello_fn.cpp

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

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

Modified by_fn.cpp

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

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

Modified 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()

Try it in the terminal....

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

Our legacy library doesn't really use any windows-specific c++ stuff (thanks to the guy who wrote that code), so it has been a pretty easy port. We had several functions that used extern "C" to expose functions. For the port, I have made the following changes:

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

And for one of our functions, I can just leave it unchanged, for example:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

且行且努力 2024-12-25 02:25:33

ctypes 用于加载共享库。 ar 创建目标文件的档案,也称为静态库。您无法使用 ctypes 加载该文件,它只能被链接器理解。

另一个问题是,通过 ctypes 使用 C++ 共享库即使不是完全不可能,也是痛苦的。只是不要。请改用 Cython,并编写一个与 C++ 代码交互的正确 Python 扩展(然后您可以静态或动态链接它) ,并且它会起作用)。

另一个选择是 Boost.Python,但它的记录较少,但有一个好处是直接在 C++ 代码中定义 Python 模块,而不是使用用其他语言编写的包装器。

第三个是 SWIG,但我从未使用过它,所以无法告诉你它在实践中的效果如何。

ctypes is for loading shared libraries. ar creates archives of object files, also known as static libraries. You can't load that file with ctypes, it will only be understood by the linker.

Another issue is that using C++ shared libraries via ctypes is painful if not downright impossible. Just don't. Use Cython instead, and write a proper Python extension that interfaces with your C++ code (then you can link it either statically or dynamically, and it'll work).

Another option is Boost.Python, but it's somewhat less documented, but has a benefit of defining Python module directly in your C++ code, instead of using wrappers written in another language.

Third is SWIG, but I've never used it, so can't tell you how well it works in practice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文