I am trying to load a shared library using CFFI. The code is meant to instantiate a C structure, print it and destroy it.
#!/usr/bin/env python
from cffi import FFI
ffi = FFI()
ffi.cdef("""
typedef struct
{
int numero;
const char* message;
}STRUCTURE, *PSTRUCTURE;
PSTRUCTURE CreateStructure();
int PrintStructure( PSTRUCTURE );
int FreeStructure( PSTRUCTURE );
""")
lib = ffi.dlopen("./lib/libstructure.so")
structure = lib.CreateStructure()
lib.PrintStructure( structure )
lib.FreeStructure( structure )
But I am getting this error:
username@Ubuntu1204VB:~/tests/cffi_tests/structure$ python main.py
Traceback (most recent call last):
File "main.py", line 22, in
structure = lib.CreateStructure()
File "/usr/local/lib/python2.7/dist-packages/cffi/api.py", line 810, in __getattr__ make_accessor(name)
File "/usr/local/lib/python2.7/dist-packages/cffi/api.py", line 806, in make_accessor accessors[name](name)
File "/usr/local/lib/python2.7/dist-packages/cffi/api.py", line 751, in accessor_function raise AttributeError('%s: %s' % (name, e))
AttributeError: CreateStructure: "function 'CreateStructure' not found in library './lib/libstructure.so': ./lib/libstructure.so: undefined symbol: CreateStructure"
So, I have checked what is inside ./lib/libstructure.so, using nm -DC:
@Ubuntu1204VB:~/tests/cffi_tests/structure$ nm -DC ./lib/libstructure.so
................................ w _Jv_RegisterClasses
0000000000000731 T FreeStructure(STRUCTURE*)
0000000000000702 T PrintStructure(STRUCTURE*)
00000000000006bc T CreateStructure()
0000000000201028 A __bss_start
................................ w __cxa_finalize
................................ w __gmon_start__
0000000000201028 A _edata
0000000000201040 A _end
0000000000000788 T _fini
0000000000000588 T _init
................................ U free
................................ U malloc
................................ U printf
CreateStructure seems to be there.
So I created a C main to test the library and it worked. But, I had to include the library and the header (./include/structure.h) of the source code I used to create the shared library (./src/structure.cpp).
Then, I copied and pasted the header into the source code I used to create the shared library, as I could not find a way to load it with the library in the Python code, and built the shared library again. Unfortunately, I am still getting the same error while executing python. This means the issue is not coming from the potentially missing header file.
Hence, I would like to know if I could check the symbols from the python code, after they are loaded using ffi.dlopen("./lib/libstructure.so"), to make sure that they are properly loaded... or not.
Any way to do this? Anything I am missing here?
EDIT : Investigation
I have added the following function to my source code:
int main(void)
{
return printf("%d\n", 42);
}
And "int main(void)" in ffi.cdef, over CreateStructure's prototype.
When I only call lib.main()... it prints 42... If I change int main(void) for int test(void) and call lib.test(), it gives me the error "undefined symbol: test"...
If I change the name of my other functions to "main" (one at a time), they work properly. It is as if cffi could only consume functions named "main"...
EDIT : Answer to comments
I am getting the following error using make
in simple-example/
:
username@Ubuntu1204VB:~/tests/python-cffi-example-how-cffi-works/simple-example$ make
clang -shared add.c -o libadd.so
clang -L. -ladd add-example.c -o add-example.exe
/tmp/add-example-r7jvhJ.o: In function \`main': add-example.c:(.text+0x25): undefined reference to `add'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: \*** [add-example.exe] Error 1
Note that I am using Ubuntu 12.04 and that I have just installed clang using sudo apt-get install clang.
Also, here is the makefile I used to compile the shared library:
CC = gcc
INC_PATH = -I ./include
DEBUGFLAGS = -g
CFLAGS = -c -fPIC $(DEBUGFLAGS) -Wall -Wextra
LDFLAGS = -fPIC $(DEBUGFLAGS) -Wall -Wextra -shared
OBJS = ./obj/structure.o
TARGET_LIB = ./lib/libstructure.so
RM = rm -f
all: $(OBJS) $(TARGET_LIB)
./obj/structure.o : ./src/structure.cpp
$(CC) $(INC_PATH) $(CFLAGS) $< -o $@
./lib/libstructure.so : $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
.PHONY:
clean:
-${RM} ${TARGET_LIB} ${OBJS}
Here is the fix :
Armin Rigo pointed out that nm
showed the code was compiled as C++, by displaying functions like CreateStructure()
instead of functions like printf
from the C standard library (note the parenthesis presence, because C++ depends of arguments for name mangling and C does not allow multiple functions with the same name).
Thus, to compile with C, the name of the source file had to be changed from structure.cpp
to structure.c
, because file extensions matter with GCC.