Modules, symbol files and keys
Oberon has no header files. The compiler extracts a module's interface from
the module itself and puts it alongside — in a symbol file with the
extension .smb. The mechanism is small, but it is what holds separate
compilation together, and without understanding keys half the compiler's
messages look cryptic.
What compilation produces
From Math.Mod the compiler makes two files:
Math.rsc— the object code: instructions, data, strings, fixup tables, the import list and the entry points;Math.smb— the symbol file: only the exported names with their types.
An .rsc can be inspected with ORTool.DecObj Math.rsc, which prints the
header and the disassembled code.
The key
In the header of every object and symbol file sits a key — a number computed over the contents of the interface. The log shows it last:
compiling Math 447 0 32C32F12
The key changes if and only if the interface changes: which names are exported, their types, their order. Editing the body of a procedure while its heading stays the same leaves the key alone.
Every module remembers the keys of everything it imports. On loading, the system compares them against the real ones. If they differ the module does not load, and you get a complaint about a mismatch.
This guards against the nastiest error in separate compilation: you build a module, change a library's interface, rebuild only the library — and end up with a program calling a procedure at the old offset. In Oberon that cannot happen: it simply will not start.
The /s option
Hence the meaning of /s in the compile command. By default the compiler
refuses to overwrite a symbol file when the interface has changed — because
doing so would silently break everything already built against it.
/s says "yes, I know, overwrite it". Everything that depends on the module
must then be rebuilt, in order from those that depend on nothing to those that
depend on everything.
Rebuilding an unchanged source leaves the interface alone, so the key stays the
same and /s does no harm. That is why it appears everywhere in the labs.
Build order
The order comes from the imports. For this system it works out like this (the beginning):
Kernel FileDir Files Modules Input Display Viewers
Fonts Texts Oberon ... ORS ORB ORG ORP ...
There is no need to remember it: it follows from the sources themselves by a
topological sort over IMPORT. That is how we do it when rebuilding the whole
system.
What a rebuild shows
If the sources and the compiler agree, rebuilding an unchanged module gives a byte-identical object file. That is a strong property and worth checking: it means the compiler is deterministic and that the shipped binaries correspond to the shipped sources.
We rebuilt the whole system inside itself. Thirty-seven object files out of thirty-nine came out byte-identical. The two exceptions turned out to be findings:
Math.rscon the image is out of date. The shipped file holds 449 words of code, a rebuild gives 447 — with the same key32C32F12. The interface is the same; the code was produced by a different version of the compiler than the one sitting on that disk.PIO.rscandPIO.smbwere absent altogether and appear only after a rebuild.
You will reproduce the first with your own hands in lab 7: look at the length of
Math.rsc before and after.
Three more inconsistencies
It also turned out that three modules from this image do not compile with the compiler from the same image:
| module | message | cause |
|---|---|---|
RISC | pos 926 bad divisor | the text has IR DIV 80000000H, and that constant is negative as a signed integer; the divisor must be positive |
ORC | import not available | imports a module V24 that is on the image neither as source nor as a symbol file |
Net | incompatible parameters, nine times | the signatures of calls to SCC have diverged from the SCC.Mod on the same image |
This is not our machine failing and not a build error: the diagnostics come from
the Oberon compiler itself, and the Math.rsc divergence shows up in a
byte-for-byte comparison of files.
The lesson worth carrying away: a distributed system image need not be consistent with itself, and the only way to find out is to try rebuilding it.