Search code examples
pythonshared-librarieswrapperword-wrappython-cffi

CFFI cannot find functions even though they are in the library


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}

Solution

  • 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.