Subject: [clisp-list] Re: Problem when C function targetted twice by FFI modules.

From: "Hoehle, Joerg-Cyril" <[email-masked]>
Date: Thu Jan 16 03:03:06 2003

Kaz Kylheku wrote:

>CLISP contains a foreign function and foreign variable 
>registry keyed on symbol name,
More precisely: On the foreign function name (as seen from C), not on a Lisp symbol name.

Your problem:
Different external modules may attempt to register the same foreign function, e.g. strerror via:
>    void module__unix__init_function_2(module)
>      var module_t* module;
>    {
>      register_foreign_function(&strerror,"strerror",1024);
>      /*...*/
>    }

>    *** - A foreign function "strerror" already exists
>    1. Break [2]>

[...]
>Any other ideas? What if, quite simply, duplicate registrations were
>silently permitted? What would break?
Nothing would break since that did never occur before, as it would have error'ed out.

I think this is the only way to go.

At the level of the executable's symbols, there can be only one with a given name. Prefixing Lisp package names or other random stuff is the wrong way to go.

Only additional check: if the foreign pointer is already valid but now declares a different address then signal an error. If same address as before, everything seems fine.

What's unclear is when the address is the same but the flags are different (e.g. K&R :C vs. ANSI :stdc declaration)
One then would later obtain
      fehler(error,GETTEXT("~: calling conventions for foreign function ~ conflict"));
from FFI::LOOKUP-FOREIGN-FUNCTION

Here I believe people's Lisp sources should be made consistent, e.g. typically enforce :stdc everywhere.

Two addresses for the same name could possibly be different when using dlopen() which may shadow some mapping from name to address?? A problem?

However, having a silent register_foreign_function() is only past the first pitfall you encountered. The next one is FFI::LOOKUP-FOREIGN-FUNCTION which creates the user-accessible foreign function object.

What if the exact :arguments and :return-type definitions differ?
This might signal
      fehler(error,GETTEXT("~: calling conventions for foreign function ~ conflict"));
But this may be impractical. One person could define a parameter as ffi:int, another as ffi:uint etc.

This calls into memory the days where there were few C function prototypes (or only K&R style really incomplete "extern" declarations).

The lesson the C people learnt was: provide correct prototypes (including parameter types, as in ANSI-C) and have everybody use them.

It would be strange if CLISP would permit another behaviour, as if the lesson had not been learnt.

So I think the user's sources must eventually be modified so that they provide the same declarations: :C or :stdC, :arguments and :return-type.

Duplicate registration would then be no problem at all.

But wait, below is a better solution.

			CONCLUSION	-	CONCLUSION

1. please provide patch to register_foreign_function:
  + become silent if both address and flags match
  + error out if flags ("calling conventions ... conflict", as in LOOKUP-FOREIGN-FUNCTION) or address ("ff already exists") differ.

2. Don't modify FFI::LOOKUP-FOREIGN-FUNCTION after all. The flags must agree, but argument types every user may define as s/he likes for the Lisp side of the function.

The ff_flags field which must match includes:
  o :c vs. :stdc
  o :malloc/:alloca for return parameter, not for arguments

As a special exception, the check on matching flags may omit matching :malloc/:alloca return-types in both functions, but probably nobody needs that special case.

An example:

[211]> (ffi:def-c-call-out c-self (:name "ffi_identity") (:arguments (obj ffi:int)) (:return-type ffi:int))
C-SELF
[212]> (ffi:def-c-call-out c-self2 (:name "ffi_identity") (:arguments (obj ffi:c-pointer)) (:return-type ffi:uint))
; cheap FOREIGN-ADDRESS-TO-UNSIGNED [+]
C-SELF2
[213]> (ffi:def-c-call-out c-self3 (:name "ffi_identity") (:arguments (obj ffi:int)) (:return-type ffi:c-pointer))
; cheap UNSIGNED-TO-FOREIGN-ADDRESS [+]
C-SELF3
[214]> (c-self 1)
1
[215]> (c-self3 1)
#<FOREIGN-ADDRESS #x00000001>
[216]> (c-self2 *)
1
[+] both are in core CLISP as built-in functions these days.

BTW, if you want to define or "import" my ffi_identity function (now present in every CLISP >= 2.29/2.30), you'd better use
(EVAL-WHEN (:execute :load-toplevel) (def-c-call-out (:name "ffi_identity") ...))
The compiler should *not* generate an unnecessary external C module for them - keep it out.
Or perhaps use the CLISP extension:
(eval-when ((not compile)) (def-call-out "ffi_identity ...)

Regards,
	Jorg Hohle.