The method for creating a loadable object module varies from platform to platform. In the following, assume you have to C source files file1.c and file2.c that define functions that you want to make available as foreign functions in Larceny.
gcc -fPIC -shared file1.c file2.c -o my-library.soThe command creates my-library.so in the current directory. This library can now be loaded into Larceny using foreign-file. Any other shared libraries used by your library files should also be loaded into Larceny using foreign-file before any procedures are linked using foreign-procedure.
By default, /lib/libc.so is made available to the dynamic linker and to the foreign function interface, so there is no need for you to load that library explicitly.
gcc -fPIC -shared file1.c file2.c -lc -lm -lsocket -o my-library.soNow you can use foreign-file to load my-library.so into Larceny.
By default, /lib/libc.so is made available to the foreign
function interface, so there is no need for you to load that library
explicitly.
(foreign-file filename) => unspecified
Larceny uses the operating system provided dynamic linker to do dynamic
linking. The operation of the dynamic linker varies from platform to
platform:
(foreign-procedure name (arg-type ...) return-type) => unspecified
Types are described below.
The address of the foreign procedure is obtained by searching for
name in the symbol tables of the foreign files that have been
loaded with foreign-file.
(foreign-null-pointer) => integer
(foreign-null-pointer? integer) => boolean
A type is denoted by a symbol. The following is a list of
the accepted types and their conversions at the call-out to the foreign
procedure:
Additionally, the types can be used as the return type, where conversions
back to Scheme values take place:
(The use of peek-bytes and poke-bytes can often be
avoided by keeping foreign data in a Scheme bytevector and passing the
bytevector to a call-out using the boxed parameter type. However,
this technique is inappropriate if the foreign code retains a pointer
to the Scheme datum, which may be moved by the garbage collector.)
(peek-bytes addr bytevector count) => unspecified
Addr must be an exact nonnegative integer. Count
must be a fixnum. The bytes in the range from addr through
addr+count-1 are copied into bytevector, which must be
long enough to hold that many bytes.
If any address in the range is not an address accessible to the
process, unpredictable things may happen. Typically, you'll get a
segmentation fault. Larceny does not yet catch segmentation faults.
(poke-bytes addr bytevector count) => unspecified
Addr must be an exact nonnegative integer. Count
must be a fixnum. The count first bytes from bytevector
are copied into memory in the range from addr through
addr+count-1.
If any address in the range is not an address accessible to the
process, unpredictable things may happen. Typically, you'll get a
segmentation fault. Larceny does not yet catch segmentation faults.
Also, it's possible to corrupt memory with poke-bytes.
Don't do that.
A number of utility procedures that make reading and writing data
of common C primitive types have been written for both these kinds
of foreign objects.
(%get16 bv i) => integer
(%get-int bv i) => integer These procedures decode bytevectors that contain the bytes of foreign
objects. In each case, bv is a bytevector and i is
the offset of the first byte of a field in that bytevector. The
field is fetched and returned as an integer (signed or unsigned as
appropriate).
(%set16 bv i val) => unspecified
(%set-int bv i val) => unspecified These procedures update bytevectors that contain the bytes of foreign
objects. In each case, bv is a bytevector, i is
an offset of the first byte of a field in that bytevector, and val
is a value to be stored in that field. The values must be exact integers
in a range implied by the data type.
(%peek8 addr) => integer
(%peek-int addr) => integer
(%peek-string addr) => string These procedures read raw memory. In each case, addr is an
address, and the value stored at that address (the size of which is
indicated by the name of the procedure) is fetched and returned as an
integer.
%Peek-string expects to find a NUL-terminated string
of 8-bit bytes at the given address. It is returned as a Scheme string.
(%poke8 addr val) => unspecified
(%poke-int addr val) => unspecified These procedures update raw memory. In each case, addr is an
address, and val is a value to be stored at that address.
2. The Interface
2.1. Procedures
2.2. Types
3. Foreign Data Access
3.1. Raw memory access
The two primitives peek-bytes and poke-bytes are
provided for reading and writing memory at specific addresses. These
procedures are typically used for copying data from foreign data
structures into Scheme bytevectors for subsequent decoding.
3.2. Foreign data sizes
The following variables constants define the sizes of basic C data types:
3.3. Decoding foreign data
Foreign data is visible to a Scheme program either as an object pointed
to by a memory address (which is itself represented as an integer), or
as a bytevector that contains the bytes of the foreign datum.
(%get16u bv i) => integer
(%get32 bv i) => integer
(%get32u bv i) => integer
(%get-unsigned bv i) => integer
(%get-short bv i) => integer
(%get-ushort bv i) => integer
(%get-long bv i) => integer
(%get-ulong bv i) => integer
(%get-pointer bv i) => integer
(%set16u bv i val) => unspecified
(%set32 bv i val) => unspecified
(%set32u bv i val) => unspecified
(%set-unsigned bv i val) => unspecified
(%set-short bv i val) => unspecified
(%set-ushort bv i val) => unspecified
(%set-long bv i val) => unspecified
(%set-ulong bv i val) => unspecified
(%set-pointer bv i val) => unspecified
(%peek8u addr) => integer
(%peek16 addr) => integer
(%peek16u addr) => integer
(%peek32 addr) => integer
(%peek32u addr) => integer
(%peek-long addr) => integer
(%peek-unsigned addr) => integer
(%peek-ulong addr) => integer
(%peek-short addr) => integer
(%peek-ushort addr) => integer
(%peek-pointer addr) => integer
(%poke8u addr val) => unspecified
(%poke16 addr val) => unspecified
(%poke16u addr val) => unspecified
(%poke32 addr val) => unspecified
(%poke32u addr val) => unspecified
(%poke-long addr val) => unspecified
(%poke-unsigned addr val) => unspecified
(%poke-ulong addr val) => unspecified
(%poke-short addr val) => unspecified
(%poke-ushort addr val) => unspecified
(%poke-pointer addr val) => unspecified
4. Heap dumping and the FFI
If foreign functions are linked into Larceny using the FFI, and a
Larceny heap image is subsequently dumped (with dump-interactive-heap or
dump-heap), then the foreign
functions are not saved as part of the heap image. When the heap image
is subsequently loaded into Larceny at startup, the FFI will attempt to
re-link all the foreign functions in the heap image.
During the relinking phase, foreign files will again be loaded into Larceny, and Larceny's FFI will use the file names as they were originally given to the FFI when it tries to load the files. In particular, if relative pathnames were used, Larceny will not have converted them to absolute pathnames.
An error during relinking will result in Larceny aborting with an
error message and returning to the operating system. This is considered
a feature.
5. Examples
5.1. Change directory
This procedure uses the chdir() system call to set the
process's current working directory. The string parameter
type is used to pass a Scheme string to the C procedure.
(define cd
(let ((chdir (foreign-procedure "chdir" '(string) 'int)))
(lambda (newdir)
(if (not (zero? (chdir newdir)))
(error "cd: " newdir " is not a valid directory name."))
(unspecified))))
5.2. Print Working Directory
This procedure uses the getcwd() (get current working
directory) system call to retrieve the name of the process's current
working directory. A bytevector is created and passed in as a buffer in
which to store the return value -- a 0-terminated ASCII string. Then
the FFI utility function ffi/asciiz->string is called to
convert the bytevector to a string.
(define pwd
(let ((getcwd (foreign-procedure "getcwd" '(boxed int) 'int)))
(lambda ()
(let ((s (make-bytevector 1024)))
(getcwd s 1024)
(ffi/asciiz->string s)))))
5.3. Other examples
The Experimental directory contains several examples of use of
the FFI. See in particular the files unix.sch (Unix system
calls) and socket.sch (procedures for communicating over
sockets).
$Id: ffi.html,v 1.5 1999/11/23 23:33:17 lth Exp $
larceny@ccs.neu.edu