Sign in or sign up before continuing. Don't have an account yet? Register now to get started.
Register now

Calling si:call-cfun on function with :cstring in parameter list result in error

Summary

When using dffi, calling a c-function via si:call-cfun results in the following error, when the parameter list contains an argument of type :cstring

NIL is not of type STRING.

Steps to reproduce

  1. Compile following c file as shared library via gcc --shared -o bug.so -fPIC bug.c
int add(int a, int b)
{
    return a+b;
}

const char* str_param(const char* str)
{
    return str;
}
  1. Evaluate following lisp code
(defparameter *module-name* "bug.so")

(defparameter *add-ptr* (si:find-foreign-symbol "add" *module-name* :pointer-void 0))
(defparameter *str-param-prt* (si:find-foreign-symbol "str_param" *module-name* :pointer-void 0))

(si:call-cfun *add-ptr* :int (list :int :int) (list 10 20) :default) ;; Returns 30
(si:call-cfun *str-param-prt* :cstring (list :cstring) (list "bug") :default) ;; <-- Error: NIL is not of type STRING.

Occured in version

(lisp-implementation-version) ;; "16.1.3"
(ext:lisp-implementation-vcs-id) ;; "f331600f84a71d2928ba5a1aef1ab62d7940823f" (current develop) 
(software-type) ;; Linux
(software-version) ;; "5.0.7-arch1-1-ARCH"
(machine-type) ;; "x86_64"
*features*
(:SLYNK :SERVE-EVENT :ASDF-PACKAGE-SYSTEM :ASDF3.1 :ASDF3 :ASDF2 :ASDF :OS-UNIX
 :NON-BASE-CHARS-EXIST-P :ASDF-UNICODE :WALKER :CDR-1 :CDR-5 :LINUX :FORMATTER
 :CDR-7 :ECL-WEAK-HASH :LITTLE-ENDIAN :ECL-READ-WRITE-LOCK :LONG-LONG :UINT64-T
 :UINT32-T :UINT16-T :COMPLEX-FLOAT :LONG-FLOAT :UNICODE :DFFI :CLOS-STREAMS
 :CMU-FORMAT :UNIX :ECL-PDE :DLOPEN :CLOS :THREADS :BOEHM-GC :ANSI-CL
 :COMMON-LISP :IEEE-FLOATING-POINT :PACKAGE-LOCAL-NICKNAMES :CDR-14
 :PREFIXED-API :FFI :X86_64 :COMMON :ECL)

Workaround

Add a dummy parameter to the argument list of si:call-cfun

(si:call-cfun *str-param-prt* :cstring (list :cstring) (list "dummy" "bug") :default)

Possible fix

I am neither a lisp expert nor a c hacker but i think the problem is located in the function prepare_cif in file ffi.d. See following snippet (line 871-879):

if (CONSP(args)) {
    cl_object object = ECL_CONS_CAR(args);
    args = ECL_CONS_CDR(args);
    if (type == ECL_FFI_CSTRING) {
        object = ecl_null_terminated_base_string(CAR(args));
        if (ECL_CONS_CAR(args) != object) {
            ECL_STACK_PUSH(the_env, object);
        }
    }
}

I don't understand the purpose of the function, but i guess the problem is, that the CDR is assigned to args and afterwards the CAR of args is used in the ecl_null_terminated_base_string which is nil if the parameter list only consists of one element. I was able to fix this problem by moving the call to CDR after the type check against ECL_FFI_CSTRING.

Assignee Loading
Time tracking Loading