AmigaOS was an operating system that did not support memory protection. All tasks ran in a single (physical) address space.
Of course this is not desirable from a modern OS standpoint. But how it was done can serve as an architectural example to hobbyist OS developers, which is why I wrote a quick abstract on it.
AmigaOS binaries came in a special (non-ELF, non-PE) format that made it possible to split each binary into multiple “hunks”. Code was always PIC (position-independent code), something that was easily done given the address modes of the 680×0 CPU that Amigas were based on. Addressing inside a hunk was IP-relative (IP: Instruction Pointer), addressing across hunks used a relocation table set up by the scatterloader.
The scatterloader took the binaries as they came off the disk, and placed each individual hunk into physical memory wherever it fit. Doing this, it also did build a relocation table for proper across-hunks addressing (see above).
Each library loaded this way had one hunk representing a “jump table”.
The compiler knew (from information in the library headers) how each function in the library could be found: Each function had a corresponding, unique offset in the jump table (e.g.
OpenFile() being at offset -4,
PrintString() at offset -8 and so on). Given that offset and a pointer to the jump table, this would yield a pointer to the function code. (Pointer to, not the function code itself - the size of the functions might change across versions, but the offsets had to remain constant.)
All that was needed was that pointer to the jump table - this was called the “LibraryBase”.
The application had to get the Library Base Pointer, and the OS had to load the desired library into memory. Both was done via the
OpenLibrary( char const * library, long version ) function call. Note the
version parameter: Depending on the version requested, the OS could chose which Base Pointer to return to the application. A single library binary could provide old legacy functions as well as new functions at the same time via seperate jump tables.
The return value of the
OpenLibrary() function was the Base Pointer, giving access to the jump table.
But to access the
OpenLibrary() function, the application needed the Base Pointer of the Exec.library - the core of the AmigaOS system. (Because
OpenLibrary() in turn had a specific offset into the Exec.library jump table.)
Since Exec.library itself could reside at different memory addresses on each start of the OS (think removing or adding RAM between reboots), there had to be some way to access Exec Base. So they chose a fixed address for this Base Pointer, the only fixed address in the whole AmigaOS.
Unfortunately, that fixed address was chosen to be
0x00000004… it is easy to picture how write access to some null-initialized pointer could render the whole system “brainless”. Funny enough, already-running applications (which canonically had cached the
ExecLibraryBase on startup) would continue working flawlessly. It just became impossible to start a new application.