9ee - Plan9 Execution Environment


At Bay Area Plan 9 Users Group Meeting (August '07) I gave a short, hurried, almost incomprehensible, description of 9ee - a mechanism for running plan9 programs on other platforms. It was presented using my signature "scrawl all over the whiteboard" method which proved so entertaining (at least to me) at IWP92006.

The method is simple, and hardly original, but seems basic enough to actually work.

Compiling stuff

The compilation for 9ee (either on a plan9 machine, or a 9ee machine) uses the standard plan9 compilers and libraries, unmodified. The issues involved in creating a useable output file from the loader are:

  1. _main, the startup code
  2. intercepting system calls
  3. relocation

Let's look at number 2 first. A short awk script was used to generate a stub file to catch system calls. Here it is: stub.c.

It starts like this:

#include <u.h>
#include <libc.h>
#include "sys.h"

extern void (*_syscall)(int, void*, ...);

and contains a stub, like this one, for every system call:

int
open(char* a, int b)
{
        int r;

        (*_syscall)(OPEN, (void*)&r, a, b);
        return r;
}

(the file sys.h is in /sys/src/libc/9syscall).

i.e. all system calls are redirected through the _syscall function pointer.

Now back to number 1. _main() is the (default) real entry point used by compiler. The modified _main() is passed _syscall as an additional parameter and stashes it in a global. Number 3 is easy, the linker accepts the needed flags. Conveniently there is a system call in _main() - exits(), so we can put stub and _main in a library (9ee.a), listed first, and the system call definitions in libc will not be linked in.

Then we do something like:

8l -T$ENTRY 9ee.a wc.8

Running Stuff

Now we have an 8.out. To run it on the target (non-plan9) machine we need a helper program to load and execute, and we need a library that implements the system calls. The helper loads (or appropriately memory maps) the 8.out at the conventional address and then calls its _main - passing a pointer to the targets syscall function.

You might me thinking "What are you thunking?". Yes, I know that most other compilers won't cope very well with the plan9 calling conventions. But we only need two thunks. _main() has to be thunked on the way in, and (*_syscall)() needs to be thunked as an incoming hostile varargs call.

OK, this is still sounding obvious but it gets to be more fun.

To get things rolling write a simple 9eelib for the target which only copes with posix type system calls. Now you have a 9ee that will run a large number of simple plan9 programs.

For anything serious we need hard calls, like rfork(), and functioning devices (#p).

This is where things become fun. The intention is to use 9/port code directly to implement a user mode kernel (which is compiled on plan9 and mapped somewhere on the target). An observation is that most of the source files are fine as is and that the major differences will be timers, traps and the scheduler (and of course rfork() and #p).

That's all for now folks.


© 2007, Bruce Ellis: brucee@chunder.com, Home.