Extensions are written in Modula-3 and are integrated with the rest of the kernel via dynamic linking.
The overall process is conceptually very similar to static linking. Instead of filenames to specify which pieces of code to link together, the programmer uses domains to specify which code to combine. Instead of a Main.m3 that initializes the program, the module bodies (the BEGIN ... END block of a module) are used to initialize an extension. And instead of linking against a system library, the extension programmer links against the Spin public domains which contain user-accessible code exported by the system.
The similarities are slightly misleading, however, in that to keep the overall process simple for the programmer, a whole bunch of mechanisms must conspire transparently under the covers. A review of these mechanisms follows, which explains the terms used in the problems section.
The bulk of the dynamic linker is concerned with interpreting COFF files. The major components of a coff file are sections, relocations, and symbols.
Sections house data, code and symbols. There are about 15 different types of sections. There is a section for text, data, small initialized data, constants, 4, 8-byte and "A" literals, procedure data, BSS, small BSS, etc. Each section contains some data belonging to the program at hand. For instance, the text section will contain machine instructions. However, the instructions will generally not be complete, since they might depend, for instance, on the location of the lita section in virtual memory. Until the lita section is assigned a spot in memory by the linker, the instructions cannot be patched to working order. The dynamic linker first reads in all the sections, and assigns each section to some virtual address in memory.
Relocations are then used to patch the section contents with the runtime values that are assigned by the linker. The relocation language is very powerful - there is even a little stack based calculation mechanism that allows the calculation of arbitrary formulas. Each relocation modifies a particular piece of section data. It also describes what to modify that bit of data to. For instance, a relocation might say, "put the value on top of the calculator stack in the low 32 bits of this instruction." Usually, the relocation will involve a calculation based on the value in the to-be-patched field. This value is quite often a coff address, which has to be translated from coff offsets into the virtual location that now corresponds to that coff offset. The type and class fields in the relocation entry describe when such translations have to be performed. Finally, some relocation entries will refer to symbols.
Symbols allow one to specify a precise location in a coff section. The generator of the coff file, usually a compiler, assigns a symbol to reside at a particular section and creates a symbol entry saying "Procedure X resides at offset 35 in the text segment." Relocations can then say something like "Patch this lda instruction to point to symbol X." The dynamic linker then has to look up X, and subsequently calculate the assigned address of the text segment, and return the new value for X.
There are two symbol tables, one for internal use by the coff module that the symbol appears in (e.g. labels etc. are internal symbols), and an external one for use by other coff modules. The external symbols are visible by other coff modules (domains), who can use that symbol to patch their own relocation entries.
All strings in a coff file are in one large glob, and all textual names appear as offsets into this string table.
Caveats: This is a simplified view of what happens. There are some quite complicated cases, such as a symbol that appears to be exported but is not, or section types which require one to modify the values of the symbols found within, that are too lengthy to go into in a primer. The gp offset calculation deserves a whole treatise on how an innocent little feature can become a boondoggle.
Once a module has been downloaded and linked into the system, two
things have to happen: (1) the types it has introduced must be
registered with the runtime, (2) the main bodies of the extension have
to be executed. This is done in a phase in Domain.Initialize. The
types are assigned typecodes and integrated, and some type problems
are detected at this stage. Then the main bodies of the M3 modules in
the extension are executed in collection order.
It is necessary to bear in mind that the entire dynamic linker was
written with only an outdated coff spec (which has almost nothing
to do with the extended coff used on alphas and mipses) and a lot of
reverse engineering. It is thanks to a group of persistent extension
writers that the dynamic linker is as robust as it is. M3 type linking
has not had the benefit of either stability (shifting the compiler base from
3.3 to 3.5) or a large number of dynamically linked M3
extensions.
Problems with extensions