Blog | About

Rethinking libposix in HelenOS

25 Aug 2017

Tags: HelenOS, porting, coastline, libposix and musl

My goal during the last HelenOS Camp was to bring QEMU into mainline. Unfortunately, due to some issues with the to-be-merged code I had to abandon this effort and start thinking whether we are porting POSIX applications to HelenOS the right way.

TL;DR – see summary below.

Porting software to HelenOS now

Currently (August 2017) we port POSIX applications to HelenOS with help of Coastline and libposix. Coastline takes care of running the configure & co. scripts with the right parameters while libposix provides a POSIX-like interface to them. libposix offers the same headers one would see in a UNIX-like distribution and emulates its functionality via HelenOS native API.

This approach worked well so far because generally the requirements on the headers were low – i.e. the applications we ported had required only the basic functions and data types. After all, with rather minimal set of headers we were able to port GCC and Python.

Bringing QEMU to mainline

As stated earlier, my goal during the camp was to finally integrate QEMU into HelenOS mainline (and Coastline). Jan Mareš did the actual porting but his work was not merged yet. Since the code changes were mostly located in libposix, it was rather easy to keep up with mainline changes and still get a functional qemu-system-i386 runnable inside HelenOS.

Jan had to add a lot of new headers because among the ported libraries were ncurses or SDL. Because the complexity of the headers started to pile up, headers were not written from scratch but taken from existing projects. But some of these headers are from GPL-licensed projects and such licensing would not mix well with BSD license we use.

Therefore I decided to postpone the integration and instead solve the issue of headers in libposix first. Obviously, we can reuse headers from some BSD-licensed libraries. The downside would be that we would import huge blobs of code into our repository. But the current state is becoming unmaintainable because of many small workarounds.

For those not familiar with the situation, the problems we face when porting POSIX software to HelenOS are rather subtle. HelenOS offers some POSIX-like interfaces but often with deviations from the standard – as a small example, gettimeofday should return int, though the returned value is always zero. HelenOS also offers gettimeofday with the same signature except it returns void. Seems like a good idea when the function cannot fail but it complicates porting software that expects this function to be declared according to standard.

Better way to port software to HelenOS?

So I started thinking whether we cannot do it better. When looking for a reasonable implementation of POSIX headers I came across a small library called musl. The authors describe it as new standard library to power a new generation of Linux-based devices. It is a complete implementation of a Linux basic C library including all the necessary headers and implementation of standard functions. The library looked pretty well maintained and rather small so I was thinking that using their headers might be a solution for our POSIX emulation layer.

I was nicely surprised how well organized are the source codes of musl and it occurred to me that we may try to use more than just the headers from this library. So the big question was where it ends, i.e. what it requires from the operating system below. The answer is that in the end the library directly executes a system call for operations that cannot be implemented in userspace. This revelation – I admit it is not that big surprise when you think more about it but it took me some time :-) – started a chain of thoughts in a completely new direction. What if we would ditch libposix completely in its current state and instead implement a library that would emulate the Linux syscalls? By several small patches to musl we would be able to replace the actual sysenter etc. with a normal library call. This function would then emulate the syscall via native HelenOS API and return back the Linux-like response (error codes etc.). I am wondering why this have not occured to us before...

Is this approach doable? There are over 338 syscalls in Linux kernel. But we do not definitely need to support all of them and some needs to be supported only partially. My feeling so far is that if we would be able to support calls like open, read and write we would be able to run quite a lot of the currently supported code.

The big disadvantage of this approach is that it would be rather easy to compile the applications against libmusl but things would break at run-time when the syscall would not be implemented. But it seems to me as a rather small drawback compared to the advantages. We would not be hacking the headers to bypass bad configure checks or providing empty implementations for functionality we would not use anyway.

Some of the syscalls would be very difficult to implement. For example, fork() is plainly impossible (for GCC which is the only program that actually needed it, we patched libiberty), supporting ioctl() fully is too much work etc. But some of the syscalls (write, writev etc.) are trivial to implement.

And this approach does not impose any a priori restrictions on how much we want to mimic the POSIX environment. For example, we even could emulate opening of /dev/random (which has no equivalent in HelenOS) by patching the open() syscall etc.

Current status

I played with this approach a little bit and so far it looks promising. I have not pushed any code yet but I hope to do that as soon as I organize the things in a reasonable way.

What worked for me is the following. I simple Hello-World program was compiled against a patched libmusl. All the actual syscalls were replaced by a call to helenos_linux_syscall_emulation() that dispatches the call to the proper implementation. After linking with libmusl and the new HelenOS library providing the above-mentioned function, it was possible to run the Hello-World inside HelenOS and it actually printed the greeting to the terminal.

Last thing I implemented so far was the open() interface but without support for O_CREAT so far. That seems to work well too. I also tried compiling zlib against libmusl and compiling it with the new library. That went well but it (as expected) failed on unimplemented call to write.

Summary

I am playing with the idea of using libmusl (a C standard library for Linux) to replace libposix in HelenOS. The key idea is to replace actual syscalls in musl with a normal library call to a function emulating Linux system calls. This function would be provided by HelenOS and would delegate the implementation to the native API of HelenOS. Applications in Coastline would be linked against the patched version of libmusl – thus using the standard POSIX interfaces during compilation but actually calling the native HelenOS API during run-time.

So far, I am able to successfully compile and run a simple Hello World program and also compile (but not run yet) zlib.

Thanks for reading.

Comments

Watch comments through RSS

Add a comment

The e-mail will be used to get a Gravatar picture and will not be directly displayed.

Please, show me you are not a robot.

You can use basic HTML formatting (following tags are allowed: A, CODE, PRE, B, I, STRONG, P and BLOCKQUOTE). For replies, I recommend using the Quote button next to a post.