clouds

ffidl-0.5.tar.gz
ffidl05.zip

Commands, et al.
  ::ffidl::callout
  ::ffidl::callback
  ::ffidl::symbol
  ::ffidl::typedef
  ::ffidl::info
  ffidl_pointer_pun
  ::ffidl::find-lib
  ::ffidl::find-type
Types

Install
Demos
Performance
Issues
Credits
  ::dll
  libffi
Copyright
License
No Warranty
Ffidl 0.1

Ffidl Version 0.5

Ffidl allows you to call C functions using pure Tcl wrappers. You specify a function name, a library, a list of argument types, and a return type, and Ffidl takes care of the nasty details of converting a Tcl command call into a C function call for you. So, if you have a shared library and a specification of the entries in the library, you can wrap the library into a Tcl extension with Ffidl and pure Tcl.

Ffidl 0.5 adds support for Windows and synchronous callbacks from C functions into Tcl on the x86 architecture, the option of using the GPL'ed ffcall package, introduces a ::ffidl:: namespace, renames ffidl-proc to ::ffidl::callout, and adds some options to ::ffidl::info.

Ffidl uses libffi-2.00-beta , with substantial patches, to dynamically construct calls to C functions from Tcl, and on the x86 architecture, to dynamically construct calls from C functions into Tcl. Libffi has officially been ported to:

  • SunOS 4.1.3 & Solaris 2.x (Sparc v8)
  • Irix 5.3 & 6.2 (System V/o32 & n32)
  • Intel x86 - Linux (System V ABI)
  • Alpha - Linux and OSF/1
  • m68k - Linux (System V ABI)
  • PowerPC - Linux (System V ABI)
  • ARM - Linux (System V ABI)
This release of Ffidl also includes support for
  • Intel x86 - Mingw32 (cdecl and stdcall)

Ffidl can also be configured (--enable-ffcall) to use ffcall-1.6 a GPL'ed foreign function package which implements both callouts and callbacks on:

  • i486-unknown-linux (gcc),
  • i386-unknown-sysv4.0 (gcc, /usr/bin/cc, /usr/ucb/cc),
  • i386-pc-solaris2.6 (gcc),
  • i486-unknown-sco3.2v4.2 (gcc, cc -Of),
  • i486-unknown-os2emx (gcc), i386-pc-cygwin32 (gcc),
  • i386-pc-win32 (msvc4, msvc5)
  • m68k-next-nextstep3 (cc), m68k-sun-sunos4.0 (cc),
  • m68k-unknown-linux (gcc)
  • mips-sgi-irix4.0.5 (gcc, cc -ansi, cc -D__STDC__, cc -cckr),
  • mips-sgi-irix5.2 (gcc, cc -ansi, cc -D__STDC__, cc -cckr),
  • mips-sgi-irix5.3 (gcc, cc -ansi, cc -D__STDC__, cc -cckr),
  • mips-sgi-irix6.2 (cc -32),
  • mips-sgi-irix6.4 (cc -32, cc -n32, cc -64)
  • sparc-sun-sunos4.1.1 (gcc, cc)
  • sparc-sun-solaris2.3 (gcc)
  • sparc-sun-solaris2.4 (gcc, cc)
  • alpha-dec-osf3.0 (gcc, cc)
  • alpha-dec-osf4.0 (gcc, cc)
  • hppa1.0-hp-hpux8.00 (gcc, cc),
  • hppa1.1-hp-hpux9.05 (cc),
  • hppa1.1-hp-hpux10.01 (cc),
  • hppa2.0-hp-hpux10.20 (cc +DA1.1)
  • arm -- untested
  • rs6000-ibm-aix3.2.5 (gcc, c89, xlc),
  • powerpc-ibm-aix4.1.4.0 (cc)
  • m88k -- untested
  • convex -- untested

Ffidl uses dlopen() and dlsym(), or their Windows approximations LoadLibrary() and GetProcAddress, to load dynamic libraries and discover the locations of functions.

Ffidl was developed under linux-2.0.36, linux-2.2.5, and Windows 95 using Tcl8.2.1. It should be able to run on any system with a stubs enabled Tcl, libffi or ffcall support, and a libdl implementation.

Ffidl 0.5 is a second alpha release. There have been about 50 downloads of Ffidl 0.1, the first alpha, in the past weeks, but only one person has reported on his experiences. There are configuration details which you will need to attend to by hand in the current release. The initial development turned up a few bugs in libffi under linux-x86, so users on other architectures should be alert for similar problems. There are several open design issues still to be resolved, so there may be changes in the interfaces in future releases. There are several common Tcl/Tk platforms which need ports of libffi or implementations of libdl.

I'm releasing at this early point because it's really too much fun to keep to myself, I need assistance verifying that the implementation works on all the architectures supported by libffi, and the open design issues could use some discussion.

Commands, Functions, and Procs

Ffidl defines five Tcl commands in the Ffidl package: ::ffidl::callout, ::ffidl::callback, ::ffidl::symbol, ::ffidl::typedef, and ::ffidl::info; exports one function from the Ffidl shared library: ffidl_pointer_pun; and defines two helper procs in the Ffidlrt package in demos/ffidlrt.tcl: ::ffidl::find-lib, and ::ffidl::find-type, which are currently just stubs of their true form.

These interfaces should be considered subject to revision.

::ffidl::callout name {?arg_type1 ...?} return_type address ?protocol?
::ffidl::callout defines a Tcl command with the specified name which, when invoked, converts its arguments according to the arg_types specified, calls the function at the specified address, and converts the specified return_type into a Tcl result. The allowed types are described below.

The protocol specifies a calling convention to be used. The only platform which supports protocols is Windows where protocol may be cdecl, which is the default, or stdcall.

::ffidl::callback name {?arg_type1 ...?} return_type ?protocol?
::ffidl::callback declares that a Tcl proc with the specified name will be invoked as a callback from C code. When invoked the arguments will be converted to Tcl values according to the arg_types specified, passed to name, the return value from name will be converted back into the specified return_type, and the value will be returned to the caller. The allowed types are described below.

The protocol specifies a calling convention to be used. The only platform which supports protocols is Windows where protocol may be cdecl, which is the default, or stdcall.

::ffidl::symbol library symbol
::ffidl::symbol loads, if necessary, a dynamically linked library of name library and fetches the loaded address of symbol from the library. The kinds of symbols available vary with the implementation of dynamic loading.

::ffidl::typedef name type1 ?...?
::ffidl::typedef defines a new Ffidl type name. This may be either a simple alias for an existing type, or a list of types which form a structured aggregate. To pass a structure by value or return a structure by value, you must make a ::ffidl::typedef for it. But even if you only pass or receive structures by reference, you might want to define a structure in order to use the format, sizeof, and alignof options of ::ffidl::info on it.

::ffidl::info option ?...?
::ffidl::info implements a variety of information functions.
::ffidl::info alignof type
returns the alignment modulus for type.
::ffidl::info callbacks
returns a list of ::ffidl::callback declared names.
::ffidl::info callouts
returns a list of ::ffidl::callout defined names.
::ffidl::info canonical-host
returns the canonical host name as determined by autoconf.
::ffidl::info format type
returns a format string for type, in the style of the Tcl binary format and binary scan commands, using the correct endian format for integers and, for structures, including any pad bytes required for alignment of fields.
::ffidl::info have-int64
returns true if the host implements a 64 bit integer.
::ffidl::info have-long-double
returns true if the host implements the "long double" type.
::ffidl::info have-long-long
returns true if the host implements the "long long" type.
::ffidl::info interp
returns the current Tcl_Interp as an integer value.
::ffidl::info libraries
returns the list of libraries opened by ::ffidl::symbol.
::ffidl::info signatures
returns the list of function call signatures used by ::ffidl::callout.
::ffidl::info sizeof type
returns the size of type.
::ffidl::info typedefs
returns a list of ::ffidl::typedef defined names.
::ffidl::info use-callbacks
returns true if Ffidl was configured to use callbacks.
::ffidl::info use-ffcall
returns true if Ffidl was configured to use ffcall.
::ffidl::info use-libffi
returns true if Ffidl was configured to use libffi.
::ffidl::info use-libffi-raw
returns true if libffi implements the raw api.

EXTERN void *ffidl_pointer_pun(void *pointer);
ffidl_pointer_pun is exported from the Ffidl shared lib to allow conversions between pointer representations to be coded as Ffidl bindings. There are some examples in ffidlrt.tcl.

::ffidl::find-lib library
::ffidl::find-lib converts a conventional name for a library into the path name for the library name appropriate to the host system. It is currently implemented in ffidlrt.tcl as a table lookup which returns the libraries appropriate to my Linux system.

::ffidl::find-type type
::ffidl::find-type converts a standard types such as size_t and time_t into real types appropriate to the host system. It is currently implemented in ffidlrt.tcl as a table lookup which returns the types appropriate to my Linux system.

Types

The Ffidl builtin types include the scalar C types in both their unsized forms and as explicitly bit sized types, and a variety of pointer treatments. Note that some types are only valid in certain contexts: arguments (arg), return (ret), or struct elements (elt).

In addition to the builtin types, the ::ffidl::typedef command may be used to define new types. Aliases for existing types may be used where ever the existing type may be used. Structured aggregates may be used as arguments, returns, or elements of other structures.

proc callback elt type definition
arg ret arg ret
-+-+-voidvoid
+++++intint
+++++unsignedunsigned int
+++++shortsigned short int
+++++unsigned shortunsigned short int
+++++longsigned long int
+++++unsigned longunsigned long int
+++++floatfloat
+++++doubledouble
+++++long doublelong double
+++++sint8signed 8 bit int
+++++uint8unsigned 8 bit int
+++++sint16signed 16 bit int
+++++uint16unsigned 16 bit int
+++++sint32signed 32 bit int
+++++uint32unsigned 32 bit int
+++++sint64signed 64 bit int
+++++uint64unsigned 64 bit int
+++++pointerpointer as an integer value
++++-pointer-objpointer from Tcl_Obj
+++--pointer-utf8pointer from String
+++--pointer-utf16pointer from Unicode
+----pointer-bytepointer from ByteArray
+----pointer-varpointer from ByteArray stored in variable. If the ByteArray is shared, then an unshared copy is made and stored back into the variable.
+----pointer-proc pointer to callback function constructed to call a Tcl proc.

Installation

Building on linux has been painless, but building on Windows has yet to be replicated by anyone but myself. The binaries directory contains some prebuilt libs.

Installation on linux consists of:

tar xzvf ffidl-0.5.tar.gz
cd ffidl-0.5
configure && make
Installation under windows consists of:
unzip ffidl05.zip
cd ffidl-0.5
CC="gcc -mno-cygwin" ./configure
make -f Makefile.gnu
The Windows build is very, very picky. It assumes that you have Cygwin-B20 installed with Mumit Khan's gcc-2.95 update, and with the libtclstub82.a from an install of Jan Nijtman's tcl82*plus.exe .

The Windows Makefile.gnu contains instructions for compiling ffcall, but it doesn't link as a dll under cygwin.

Three custom configure options are implemented for selecting between libffi and ffcall, --enable-libffi and --enable-ffcall, and for excluding callbacks, --disable-callbacks.

(cd libffi-2.00-beta && ./ffitest)
will run libffi's test suite.
(cd ffcall-1.6 && make check)
will run ffcall's self test.

At this point you should edit demos/ffidlrt.tcl and look at the table of libraries in ::ffidlrt::libs and the table of types in ::ffidlrt::types. Either or both of these tables will probably need attention if you go further. You may need, for instance, to find, build, or install libraries for gmp and gdbm, or to adjust the pathnames for libc, libm, tcl, and tk. (The ever useful Mumit Khan has a gmp port for Windows, gmp-dll-2.0.2-mingw32.zip, available for download, which will allow Windozens to run the multiprecision demos.)

Running

make test
or
make -f Makefile.gnu test
will build a test shared lib and run a series of scripts in demos/ which run to completion without error messages on my system. It is not quite a systematic test suite, but it does exercise a good deal of Ffidl's capabilities, and it has turned up some problems with libffi.

The configure for Ffidl started from the TEA sample configuration, but it needs more work. It doesn't test that libdl is actually available. It builds the ffidl_test library and runs the test scripts in demos/, but it wasn't clear how Makefile.in was supposed to support this or cleaning up test binaries. Most glaringly, I couldn't get the Windows configure to work at all, so I gave up and wrote a makefile.gnu by hand.

The included copy of libffi-2.00-beta has been rewritten from a copy retrieved via cvs on October 28, 1999. The distributed libffi-2.00-beta erroneously makes "long int" a 64 bit integer on x86, and the inline assembly for implementing closures miscompiled on two out of two gcc's. If you have installed a copy of libffi on your machine, beware of finding the unpatched headers in /usr/local/include before the patched ones.

Demos

The demos directory contains several small and medium size examples of Ffidl bindings to shared libraries, and some code for making comparisons to other ways of doing the same thing.

atol.tcla Ffidl binding to atol() and congeners
ffidlrt.tclrun time support for Ffidl bindings
gdbm.tcla Ffidl binding to gdbm-1.8
getrusage.tcla Ffidl binding to getrusage()
gmp.tcla Ffidl binding to gmp-2.0.2
gmpz.tclarbitrary precision integers via gmp.tcl
gmpq.tclarbitrary precision rationals via gmp.tcl
gmpf.tclarbitrary precision floats via gmp.tcl
libm.tcla Ffidl binding to libm
qsort.tcla Ffidl binding to qsort
tkphoto.tcla Ffidl binding to the Tk photo image.
pkgIndex.tclhand built package index
test-callback.tcla general test of callback
test-ffidl.tcla test of Ffidl using ffidl_test.c
test-gdbm-1.tcla test of the gdbm binding
test-gdbm-2.tcla test of the gdbm binding
test-gmpz.tcla test of the gmpz routines
test-libm.tcla test of the libm binding
test-qsort.tcla test of the qsort binding
time-libm.tcla timing comparison of Ffidl and expr
test-tkphoto.tcla short example of tkphoto.tcl usage

ffidlrt.tcl will need attention unless you're running on my machine. There are two functions, ::ffidl::find-lib and ::ffidl::find-type, which abstract library names and system typedefs out of the rest of the code. However, the abstraction is currently limited to the correct results for my Linux box. You'll need to rewrite the mapping for your own machine.

ffidlrt.tcl also contains some examples of binding into the Tcl core itself.

tkphoto.tcl allows extraction and insertion of photo image pixels as binary data. See test-tkphoto.tcl for an example.

The gdbm.tcl extension should be plug compatible with Tclgdbm0.6, a C coded Tcl extension for manipulating gdbm files. Since gdbm passes and returns structures, it also tests the Ffidl struct code.

The gmp*.tcl extensions make a nice example. The main Gmp package wraps all the exported mpz_*, mpq_*, and mpf_* entries from the Gnu multiple precision library. The subsidiary Gmp[zqf] packages use the Gmp package to define arbitrary precision integers, rationals, and floats which are represented as strings. This isn't the most efficient way to do arbitrary precision arithmetic, but it is convenient, it does avoid needing to know what type mp_limb_t and mp_size_t actually are, and it does show how to use the underlying library if you want to build something more efficient.

Performance

Performance appears to be excellent, but I can't take any credit because libffi is doing most of the work. The demos/time-libm.tcl script compares ::ffidl::callout wrapped libm functions to the Tcl expr versions of the same functions. On UNIX, the "make test" target will build libmathswig0.5.so to provide a SWIG wrapped libm for comparison purposes. If you're running on Linux-x86 or Windows you can install Robin Becker's ::dll for another data point. time-libm.tcl will time them on the same functions.

The Ffidl bindings to libm run a little faster than ::dll bindings and a little slower than SWIG wrappers, all of them coming in slower than expr itself. The bottom line is that all three extensions run better than 1.5 times the speed of expr over the same functions.

Open Issues

A few issues have been closed since the initial release.

The Windows port is done.

Callbacks are implemented for the x86 architecture.

But there are many open issues.

Port to macintosh and other architectures.

Importing libdl compatability code from Tcl. It would be real nice if the Tcl core exported some neatly packaged libdl abstraction, since it has the wherewithal implemented for load already.

Finding the right library is a pain. dlopen("libm.so") finds libm on my machine, but dlopen("libc.so") returns an error string decorated with binary characters while dlopen("libc.so.6") works. If you work with shared libraries you build yourself, it's not an issue, but for all the standard stuff there is no standard. In demos/ffidlrt.tcl the ::ffidl::find-lib function provides an abstraction for at least removing these issues one layer away from your Ffidl bindings to the library, but the implementation of the abstraction hasn't gone farther than listing where I find my standard libraries.

Discovering what type a type is is also a pain. Include headers are typically so heavily conditionalized, that one needs to search and search to find which typedef is actually implemented. In demos/ffidlrt.tcl the ::ffidl::find-type function abstracts these issues out of the Ffidl bindings, but again the implementation of the abstraction will need some work.

A backend for SWIG which generates Ffidl bindings might be useful.

There are some more pointer types which ought to be defined: a variant of pointer-var which requires an unshared value; a pointer to a native character string -- but couldn't that be pushed back to the Tcl layer?

Writing Tcl extensions with Ffidl is very much like writing C code in Tcl. I'm not sure what the actual required skill set is. But if you're not sure what you're doing, you might be in over your head. In any case, try not to take the core dumps personally.

Loading snippets of code into a Tcl interpreter with Ffidl loaded could be very hazardous, as in downloading "Try ME!" scripts from the web. There is no Ffidl_SafeInit(), we'd probably need signed scripts to even begin to consider such a thing.

I've looked at SWIG and at dll and seen that they very carefully duplicate any shared Tcl_Obj before attempting a conversion to Int or Double. I've also looked at the source for Tcl's expr command, and it converts objects to Double or Int and only duplicates shared objects when it finds a valid Int or a Double with an existing string representation. Ffidl only duplicates shared objects when processing pointer-var, though I'm open to explanations why it should do otherwise. It seems that if you pass a parameter to a typed function that you shouldn't be upset if the parameter is converted to that type.

Hmm, this is a really pared to the bone. It would be nice for newbies and experimenters and the careless if Ffidl implemented a debugging mode which verified that constraints were observed: 1) that Tcl_Obj string reps were not modified, 2) that Tcl_Obj bytearray reps were not modified outside their allocated sizes, and so on. This could be done by switching in an alternate implementation of tcl_ffidl_call() which made copies and verified the constraints after the call.

Some naming consistency in the demos. I seem to be reinventing my Ffidl extension style each time I start a new example.

Some style consistency in the tests. The tests just run, some generate descriptions, some report what they've done, some say nothing, some give summaries.

Credits

Robin Becker's ::dll package, which does much the same thing as Ffidl, provided the immediate inspiration for this work and pointed to the solution of some of the design issues for me. And Robin hisself has been very helpful.

Anthony Green's libffi package provided most of the initial implementation of Ffidl.

Bruno Haible's ffcall-1.6 package for the clisp system provided an alternate implementation and a truly amazing example of cpp macrology.

Copyright, License, & No Warranty

Ffidl Version 0.5, Copyright © 1999 by Roger E Critchlow Jr, Santa Fe, NM, USA

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROGER E CRITCHLOW JR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Note that libffi is Copyright © 1996-1999 Cygnus Solutions and bears a similar copyright notice, license, and non-warranty.

Note that ffcall-1.6 is Copyright © 1995-1999 by Bruno Haible and is licensed under the GNU General Public License .



Roger E Critchlow Jr
Last modified: Fri May 16 19:48:33 MDT 2003
elf.org