vx32-gccとlibvxcとvxrun
と、ここでしばらく考えてみる。vx32-gccがどんなバイナリを出力するとかというと、
$ vx32-gcc hello.c $ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
ELF形式だ。当然、実行はできない。
$ ./a.out -bash: ./a.out: cannot execute binary file
vx32-gccが普通のgccと何が違うのか、まだわかっていないが、libcにはlibvxcという独自のものが使われている。ベースになっているのはFreeBSDのlibcみたいだけど、システムコール呼び出しはすべてvx32のトラップ(正確にはsyscall命令を実行する)に置き換えられる。例えば、openは、こんな感じ。
int open(const char *path, int flags, int mode) { return syscall(VXSYSOPEN, (int)path, flags, mode, 0, 0); }
vx32にはこのa.outを実行できる、vxrunというプログラムが同梱されている。これもvxlinuxと同様なループになっていて、vxproc_runがVXTRAP_SYSCALLで中断されたら、システムコール処理をする。(vxlinuxとvxrunの違いは前者がLinuxホストOS上でLinuxの実行バイナリを変更なしで実行できるのに対して、vxrunはOS非依存な仕組みである。)
static void dosyscall(vxproc *proc) { : switch (NUM) { : case VXSYSOPEN: addr = ARG1; mode = ARG2; umode = mode&3; if(mode & VXC_O_CREAT) umode |= O_CREAT; if(mode & VXC_O_EXCL) umode |= O_EXCL; if(mode & VXC_O_NOCTTY) umode |= O_NOCTTY; if(mode & VXC_O_TRUNC) umode |= O_TRUNC; if(mode & VXC_O_APPEND) umode |= O_APPEND; if(mode & VXC_O_NONBLOCK) umode |= O_NONBLOCK; if(mode & VXC_O_SYNC) umode |= O_SYNC; if (!checkstring(proc->mem, m->base, addr)) goto einval; ret = open((char*)m->base+addr, umode, ARG3); if(trace) fprintf(stderr, "open %s %#x %#o => %d\n", (char*)m->base+addr, ARG2, ARG3, ret); break;
見てわかるように、vx32のシステムコールをホストOSへのシステムコール呼出しに書き換えている。
9vxも似たような流れになるんだろう。
こんな感じでユーザレベルOS書いてみません!?