The linkage table feature is based on how dynamic libraries dispatch. A table of functions is used which is filled in with the appropriate code to jump to the correct address.
For CMUCL, this table is stored at
target-foreign-linkage-space-start
. Each entry is
target-foreign-linkage-entry-size
bytes long.
At startup, the table is initialized with default values in
os_foreign_linkage_init
. On x86 platforms, the first entry is
code to call the routine resolve_linkage_tramp
. All other
entries jump to the first entry. The function
resolve_linkage_tramp
looks at where it was called from to
figure out which entry in the table was used. It calls
lazy_resolve_linkage
with the address of the linkage entry.
This routine then fills in the appropriate linkage entry with code to
jump to where the real routine is located, and returns the address of
the entry. On return, resolve_linkage_tramp
then just jumps to
the returned address to call the desired function. On all subsequent
calls, the entry no longer points to resolve_linkage_tramp
but
to the real function.
This describes how function calls are made. For foreign data,
lazy_resolve_linkage
stuffs the address of the actual foreign
data into the linkage table. The lisp code then just loads the address
from there to get the actual address of the foreign data.
For sparc, the linkage table is slightly different. The first entry is
the entry for call_into_c
so we never have to look this up. All
other entries are for resolve_linkage_tramp
. This has the
advantage that resolve_linkage_tramp
can be much simpler since
all calls to foreign code go through call_into_c
anyway, and
that means all live Lisp registers have already been saved. Also, to
make life simpler, we lie about closure_tramp
and
undefined_tramp
in the Lisp code. These are really functions,
but we treat them as foreign data since these two routines are only
used as addresses in the Lisp code to stuff into a lisp function
header.
On the Lisp side, there are two supporting data structures for the
linkage table: *linkage-table-data*
and
*foreign-linkage-symbols*
. The latter is a hash table whose key
is the foreign symbol (a string) and whose value is an index into
*linkage-table-data*
.
*linkage-table-data*
is a vector with an unlispy layout. Each
entry has 3 parts:
Whenever a new foreign symbol is defined, a new
*linkage-table-data*
entry is created.
*foreign-linkage-symbols*
is updated with the symbol and the
entry number into *linkage-table-data*
.
The *linkage-table-data*
is accessed from C (hence the unlispy
layout), to figure out the symbol name and the type so that the
address of the symbol can be determined. The type tells the C code
how to fill in the entry in the linkage-table itself.