Let me see if I can flesh out Brent's answer a bit. Scsh calls a whole bunch
of C routines from Scheme code. These C routines are part of the VM -- sitting
in the text pages of the process. In order for Scheme code to call a C
routine, it has to find the address of that C routine, somehow mapping from
the *name* of the routine (e.g., a string like "printf" or "sin") to the
address for the subroutine. Then the Scheme code uses a special VM instruction
that takes one of these addresses and calls the C function for you.
That's how all the system calls are done in scsh.
So the critical problem is that somehow at run-time, the VM needs to
be able to take a string like "sin" or "printf" and look inside itself,
and produce the address of where the corresponding routine lives in its
address space. This is provided by the symbol table information in a.out
(or .o) files; see man 5 a.out for more details. Different flavors of Unix
provide different little libraries for doing exactly this. On some it is
called nlist(), and you sometimes have to tell the linker to keep this
symbol table information around even after it has made the complete a.out
executable. That is the kind of magic Brent was talking about.
Using nlist(), for example, is the reason why you must pass the vm's filename
to the vm when you run it with the -o flag -- so that the nlist() call knows
what file to look inside for the symbol table.
The unix.c file is where some of this machinery is located, if you want to
see what is going on.
The dlopen/dlsym functions to which Brent was referring are for when you
link in the C code at runtime, which we don't actually do right now in
scsh. We just statically link the OS support code in with the VM, which
is lower-tech and easier to port. But the problem of translating C proc names
to addresses at run-time remains the same.
It's an ugly little engineering detail; probably the nastiest in the otherwise
very portable Scheme 48 substrate that we use for scsh.