Skip to content

OSv Linux ABI Compatibility

Nadav Har'El edited this page Mar 23, 2014 · 14 revisions

OSv is mostly implements Linux's ABI. This means that most unmodified executable code compiled for Linux can be run in OSv. There are only a few areas where OSv is known to be imperfectly compatible with Linux. This document will list these cases.

Executable formats

Currently, OSv can only run executables in shared object (".so") form. Support for running position-independent executables ("PIE") is planned, but not yet available. Support for running a standard Linux executable (position-dependent dynamically linked executable) is also planned. Support for running statically linked executables will be harder.

Processes

OSv supports only a single process. Therefore, fork(), vfork() and clone() are not supported (their use in an executable will cause a crash because of a missing symbol).

Moreover, in OSv there is no isolation between the single process and the kernel - we do not track which memory, and which resources (threads, mutexes, etc.) belong to the process and which to the kernel. So exec() in an attempt to switch only the process but not the kernel, is not supported. So all exec() variants - execl(), execlp(), execle(), execv(), execvp(), execvpe(), and execve() - are not supported. Instead, if you want to run another executable, you can load it as a new shared object (with dlopen() or equivalent) and run its main() (see also osv::run(3o)), and attempt to free the old shared object's resources, and to unload it.

Users

OSv is designed to support a single application on a VM, and with only a single application trying to isolate different users is pointless. Therefore, OSv only supports a single user - with uid=0 and gid=0. Trying to set a different user will fail. Permission bits on files are ignored (TODO: only the owner/group/other difference, or also the writable bit?)

Signals

In Linux, when a signal is sent to a process with kill(), it is delivered to one of this process's threads which hasn't masked this signal - preferably to the main thread but if it masked the signal, then one of the other threads is chosen.

In contrast, in OSv signals are delivered in a new thread, not one of the process's existing threads. The signal handler does not preempt an existing thread, and cannot take over it (as single-thread applications sometimes used longjmp()). We do support, however, signal delivery interrupting a long-running system call (such as sleep() or a blocking read() from the network) in a thread.

It is hoped that this difference will not matter to most actual uses of kill() (and related functions, such as alarm()) in cloud applications. The reason why signal handling was implemented differently in OSv is system call reentrancy and interruption: If a signal handler was to be run in an existing thread, we would need to handle the case where the signal handler is run in the middle of a system call, and the handler also calls a system call - and the system call might not be reentrant. It is possible to track calls to "system calls" (calls from a shared object to the main program), and avoid running a signal handler while a system call is in progress (and run it when it returns), but then we have a problem of system call interruption - if we are sleeping on "system calls" like sleep(), poll(), read(), etc., or internally on OSv functions like mutex_wait(), condvar_wait(), msleep(), the signal handler can be delayed indefinitely, so in Unix the signal first interrupts the system call, which needs to unravel its stack and return an EINTR from the system call. All of these are doable, but will require extensive changes to the code, and make it slower and uglier just to support an archaic Unix API, kill().

Special files

Most linux /proc/* files are not yet supported on OSV. /dev/urandom is not yet available (/dev/urandom is, implemented using a hardware random number generator, not using software sources of entropy like is done in Linux).

Clone this wiki locally