To compile a file to disk, we need a format in which to write the compiled code to disk, and later load it into Guile. A good object file format has a number of characteristics:
These characteristics are not specific to Scheme. Indeed, mainstream languages like C and C++ have solved this issue many times in the past. Guile builds on their work by adopting ELF, the object file format of GNU and other Unix-like systems, as its object file format. Although Guile uses ELF on all platforms, we do not use platform support for ELF. Guile implements its own linker and loader. The advantage of using ELF is not sharing code, but sharing ideas. ELF is simply a well-designed object file format.
An ELF file has two meta-tables describing its contents. The first meta-table is for the loader, and is called the program table or sometimes the segment table. The program table divides the file into big chunks that should be treated differently by the loader. Mostly the difference between these segments is their permissions.
Typically all segments of an ELF file are marked as read-only, except that part that represents modifiable static data or static data that needs load-time initialization. Loading an ELF file is as simple as mmapping the thing into memory with read-only permissions, then using the segment table to mark a small sub-region of the file as writable. This writable section is typically added to the root set of the garbage collector as well.
One ELF segment is marked as “dynamic”, meaning that it has data of
interest to the loader. Guile uses this segment to record the Guile
version corresponding to this file. There is also an entry in the
dynamic segment that points to the address of an initialization thunk
that is run to perform any needed link-time initialization. (This is
like dynamic relocations for normal ELF shared objects, except that we
compile the relocations as a procedure instead of having the loader
interpret a table of relocations.) Finally, the dynamic segment marks
the location of the “entry thunk” of the object file. This thunk is
returned to the caller of
load-thunk-from-file. When called, it will execute the “body”
of the compiled expression.
The other meta-table in an ELF file is the section table. Whereas the program table divides an ELF file into big chunks for the loader, the section table specifies small sections for use by introspective tools like debuggers or the like. One segment (program table entry) typically contains many sections. There may be sections outside of any segment, as well.
Typical sections in a Guile
.go file include:
Data that needs initialization, or which may be modified at runtime.
Statically allocated data that needs no run-time initialization, and which therefore can be shared between processes.
The dynamic section, discussed above.
A table mapping addresses in the
.rtl-text to procedure names.
.strtab is used by
Side tables of procedure properties, arities, and docstrings.
Side table of frame maps, describing the set of live slots for ever return point in the program text, and whether those slots are pointers are not. Used by the garbage collector.
Debugging information, in DWARF format. See the DWARF specification, for more information.
Section name string table.
For more information, see the
elf(5) man page. See the DWARF
specification for more on the DWARF debugging format. Or if you are an
adventurous explorer, try running
.go files. It’s good times!