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