FatELF ('Universal') Binaries for Haiku

26 Nov 2012, 16:05 PST

Introduction

One of the features that I think has been critical to Apple's ability to leverage improvements in underlying CPUs -- as well as weather migrations to completely new architectures -- has been their Universal Binary Format.

The concept is fairly simple, and dates back to NeXT, where they targeted a plethora of available platforms. Put succinctly, a universal binary is composed of:

For such a seemingly simple idea, the value is huge: universal binaries simplify the user experience for application users and developers alike.

From the user's perspective, they aren't forced to choose between x86-32 and x86-64 binaries depending on what version of the OS they happened to install. 32-bit and 64-bit Macs can use the same installation media, and web sites containing application downloads don't need to list two (or more) different downloads for each version. When new iPhones are released with updated ARM revisions, older binaries that are forwards-compatible can keep running, and newer binaries can be selected when supported by the hardware, maximizing the user (and developer's) ability to seamlessly take advantage of improvements in hardware revisions.

At runtime, the OS can avoid loading a duplicate set of libraries for binaries that are forwards-compatible -- for instance, ARMv6+hardfloat binaries can be linked against ARMv7 libraries on the iPhone -- and are. When hosting plugins, the hosting application can automatically 'downgrade' to the lowest common denominator ABI by relaunching itself, thus allowing for gradual transitions between old ABIs and new ABIs -- this was done by Apple in System Preferences when the entire OS switched over to x86-64, allowing System Preferences to switch back to i386 inthe case where the user still had legacy plugins installed.

From a developer's perspective, the universal binary ecosystem makes it trivial to support building their application for multiple architectures. While testing still needs to be done on the native system, a binary can be built and linked for all supported architectures simply by supplying the appropriate -arch flags to the compiler, using the same set of libraries and headers. Supporting a new target from your existing desktop system is as easy as downloading an updated SDK and including the target architecture in your build. Binaries that are required for the build (such as a protobuf compiler) can be compiled universal and included in the build system for any supported build host.

Even on the App Store, where Apple controls distribution and in theory could automatically supply different versions of an application for different devices, Apple has continued to leverage universal binaries, to Apple's advantage. The simplicity of being able to synchronize a single binary with multiple phones remains, but in addition, Apple has been able to rapidly deploy new ARM hardware features and see immediate developer adoption, because the cost and implementation costs are so low. By comparison, Linux, which traditionally has not supported fat binaries, has had to weigh the advantages of adopting new hardware features with the cost of having to build and deploy a completely new graph of the OS and all available software for that OS.

FatELF - Universal Binaries For the Rest of Us

Apple is largely unique in their use of the Mach-O binary format and fat binaries, but there has been an effort to bring fat binaries to Linux, and other operating systems that use the (much more popular) ELF binary format. Ryan Gordon -- who has been a driving force in porting numerous games to Mac OS X and Linux (including Quake III Arena) -- set out in 2009 to define the FatELF format, which provides support for fat binaries on ELF-using platforms. Ryan implemented a full suite of command line tools for assembling fat binaries, Linux kernel support, and the necessary patches for binutils, gdb, glibc, etc.

Using Ryan's specifications and tools, I went ahead and implemented initial FatELF support for the Haiku kernel, boot loader, and runtime loader. With this work -- assuming the patches are integrated upstream -- Haiku will be able to use universal binaries for the kernel, kernel modules, shared libraries and executables.

The next step will be to extend these additions to the developer tools, with the goal of making it seamless to produce FatELF binaries using the same approach that Apple has used for Mac OS X and iOS. Once completed, I can begin to integrate the new toolchain into the build system, and start on the goal of building a fully universal 32-bit/64-bit Haiku installation.

If you're curious, you can find my FatELF-enabled branch here.