On Aug 31, 2005, at 08:22 PM, Andrew Gallatin < email@hidden> wrote: 2) ELF support in the kernel, linux ABI support at the level of a userspace ELF dynamic linker (like BSDI's LAP). This again requires modifying the darwin exec code to understand ELF & launch LAP when it encounters a linux ELF binary. LAP then supplies its own version of the linux libc, where all syscalls are translated to native Darwin syscalls. This might be very easy to do; the LAP was open source. But the code may be hard to find now that BSDI is gone.
This would also be very fast, and able to run binaries at native speeds. I have no idea if Apple would allow a patch to exec ELF binaries.
3) ELF support and linux ABI support via a userlevel application like lxrun which loads the ELF binary itself, and uses a signal handler to catch SEGVs when the linux binary makes syscalls. This depends on Darwin/x86 using a different syscall mechanism than linux, which I'm not sure is true. The lxrun project used to be open source, but it has been years since I've looked at it.
This would be a bit slower, especially for an application which makes lots of syscalls.
4. Combination of 2 and 3. Use a custom glibc with syscall() and friends patched to do table lookups for translation of system calls. Then use a launcher executable that to read ELF executable/libraries, link them as needed, and run them.
I might be wrong, but I think you could probably do it something like the following, assuming you can call the relevant vm functions from user space:
a. Create a dummy function and force it to load high enough in the address space that it is out of the way of any reasonable executable. Use a vm_allocate (or similar) call to create such a region if you can't do it with the linker, then slam down the bytes and jump into it with assembly. It's mainly a sleep system call in a loop.
b. The process sets a longjmp vector to point to the start address of the ELF executable, makes a system call to change its name in the process table, then calls the wait_forever() function. That function (loaded high) calls fork(). The child then does a vm_deallocate (or similar) of all its pages except the one(s) used by wait_forever(). It memory maps a file over these pages with a MAP_FIXED argument using mmap.
c. The parent memory maps apertures into the file somewhere else. The child process sits there as the parent process loads the Linux binary over top of the child process's executable.
d. At the magic moment, after the launcher (parent) has fully loaded and linked the executable in the child process's memory space, it sends a SIGSEGV signal to the child, which does a long jump into the code. The parent exits and is reaped by init.
At that point, the code would be running natively, backed by a memory image file (which you could cache for later re-execution), albeit with thin ABI emulation in the library. The advantage is that it should be about as fast as doing it in the kernel, but shouldn't require any changes to the kernel.
Maybe that's too evil....
David
|