Universal Binaries (auf Deutsch in etwa universelle Binärdateien), oft abgekürzt mit UB, sind im Apple-Jargon ausführbare Dateien (d. h. Programme), die nativ ausführbare Maschinensprache für mehr als eine Prozessorarchitektur enthalten. Apple hat Universal Binaries bei der Umstellung von PowerPC- auf Intel-x86-Prozessoren ab 2005 verwendet. Die Technik wurde zudem in Xcode integriert, damit erstellte und entsprechend kompilierte Applikationen konnten nativ sowohl auf PowerPC- als auch auf Intel-Macs laufen. Bei der Umstellung von Intel auf Arm seit 2020 wird mit Universal Binary 2 dieselbe Technik noch einmal verwendet und ebenfalls in Xcode integriert.
Universal Binaries basieren auf dem mit MACH Mitte der 1980er Jahre entworfenen Mach-O-Binärformat. Unter NeXTStep, ab 1987 auf der Basis von Mach Version 2 entwickelt, wurde das Format auf die vom Betriebssystem (ab NeXTStep 3.1 von 1993) unterstützten Architekturen m68k, IA-32 „i386“ (32-Bit-x86), PA-RISC und SPARC erweitert und Multi-Architecture Binaries genannt.
Auch Apple hatte ab 1994 bereits eine Umstellung der Prozessorarchitektur unter dem Betriebssystem System 7 von der m68k auf die PowerPC-Architektur durchgeführt und dabei mit Fat Binaries ein ähnliches Konzept umgesetzt. Das verwendete Binärformat ist jedoch nicht mit dem von Mach-O verwandt.
Apple erwarb 1997 NeXT samt dem von NeXTSTEP in OPENSTEP umbenannten Mach-Betriebssystem und portierte es im Project Rhapsody auf die von Apple damals genutzte PowerPC-Plattform. Rhapsody hätte das klassische Mac OS vollständig ersetzen sollen, war jedoch ein vollkommen anderes Betriebssystem und mit bestehenden Mac-OS-Programmen nicht kompatibel. Als Rhapsody von den Herstellern wichtiger Anwendungssoftware nicht angenommen wurde, portierte Apple große Teile der Mac-OS-Programmierschnittstelle unter dem Namen Carbon auf das nun von Rhapsody in Mac OS X umbenannte neue Betriebssystem. Das Mach-O-Format besteht seither mit Mac OS X weiter, wurde anfangs jedoch nur mehr für eine einzige Architektur verwendet: die PowerPC-Architektur.
2005, mit der Umstellung von der PowerPC- auf die IA-32-Architektur, griff Apple die ohnehin noch vorhandene Technik der Multi-Architecture Binaries wieder auf: Auf der Worldwide Developers Conference (WWDC) wurde es, umbenannt in Universal Binaries, der Öffentlichkeit vorgestellt. Apple integrierte die Technik in die eigene Entwicklungsumgebung Xcode, um es den Entwicklern von Anwendungsprogrammen zu erleichtern, nativen Binärcode für beide Architekturen in ihre Softwareprodukte zu integrieren. In diesem Zuge war es auch möglich, sowohl 32- als auch 64-Bit-Binärcode für dieselbe Architektur in einem Universal Binary unterzubringen, also sowohl 32-Bit- und 64-Bit-x86 als auch 32-Bit- und 64-Bit-PowerPC. Nach dem erfolgten Übergang auf IA-32 (32-Bit-x86) bzw. x64 (64-Bit-x86) wurde die Unterstützung für PowerPC/x86-Universal-Binaries wieder aus Xcode entfernt. iOS unterstützt Universal Binaries ebenfalls, wodurch Mobile Apps für verschiedene Arm-Architekturen ermöglicht werden.
Umsetzung
Um Universal Binaries zu verwenden, muss der Kernel eines Betriebssystems mit dem von NeXT erweiterten Mach-O-Binärformat umgehen können. Gegenüber dem einfachen Mach-O-Format sind Multi-Architecture Binaries in einander gekapselte Mach-O-Dateien, mit zusätzlichen Metadaten. Die Header-Struktur von Mach-O selbst wurde dabei nicht verändert, aber um zusätzliche Flags erweitert:[1]
MH_CIGAM steht für Binärcode in Little-Endian-Byte-Reihenfolge in 32-Bit
MH_MAGIC_64 repräsentiert Binärcode in Big-Endian-Byte-Reihenfolge in 64-Bit
MH_CIGAM_64 steht für Binärcode in Little-Endian-Byte-Reihenfolge in 64-Bit
CPU-Typ, beispielsweise:
CPU_TYPE_POWERPC für 32-Bit-PowerPC
CPU_TYPE_POWERPC64 für 64-Bit-PowerPC
CPU_TYPE_I386 für 32-Bit-x86 bzw. 32-Bit-IA-32 (ab dem Intel 80386, darum i386)
CPU_TYPE_X86_64 für 64-Bit-x86 bzw. x64 (auch x86-64, 64-Bit-IA-32)
Eine Universal Binary wird beim Ausführen vom Betriebssystem als solche an ihrem Header erkannt, wodurch das Betriebssystem anschließend anhand der vorhandenen Architektur den jeweiligen ausführbaren Code abarbeiten kann. Noch heute enthalten Open-Source-Bestandteile von macOS Hinweise auf m68k, SPARC und weitere CPUs. 2020 wurden 17 verschiedene Architekturen gezählt.[2]
Dieser Vorgang ermöglicht es nun, eine Anwendung sowohl auf einem Apple Computer mit PowerPC- als auch mit Intel-Architektur (Universal Binary von 2005) oder mit Intel- als auch mit ARM-Architektur (Universal Binary von 2020) ohne Geschwindigkeitsverlust auszuführen.
Technisches
Realisiert werden Universal Binaries über das Binärformat Mach-O, das im Gegensatz zum ELF-Format, welches unter Linux und anderen unixähnlichen Betriebssystemen verbreitet ist, Binärcode für mehrere Architekturen enthalten kann. Mit dem Tool lipo[3] aus Xcode und objdump[4] aus den GNU Binutils kann man die Binärcodes eines Universal Binaries auslesen. Auch file[5] gibt einen Überblick über die enthaltenen Architekturen.
Der universale Safari-Webbrowser enthielt (ca. 2005/2006) sowohl Code für Intel (i386) als auch für PowerPC (powerpc:common). „mach-o-le“ und „mach-o-be“ stehen für die Byte-ReihenfolgenLittle Endian und Big Endian.
$ objdump -f /Applications/Safari.app/Contents/MacOS/Safari
In archive /Applications/Safari.app/Contents/MacOS/Safari:
Es gibt auch Programme, die in zwei getrennten Versionen (Intel-Binary und PPC-Binary) angeboten werden, hier muss man sich (sofern man nicht doppelt herunterladen möchte) bereits beim Herunterladen für die richtige Datei entscheiden, was jedoch den Vorteil kleinerer Dateien hat.
Bei Programmen für nur eine Architektur wird entsprechend nur ein Binärcode angezeigt:
Ohne zusätzliche Programme lässt sich der Binärcode mit dem Befehl file auslesen, also für das obige Beispiel Safari: file /Applications/Safari.app/Contents/MacOS/Safari.
„Klassische“ Fat Binaries
Schon beim Wechsel von der Motorola-68k- auf die PowerPC-Prozessorarchitektur verwendete Apple das Konzept, in derselben Datei Code für mehrere Prozessoren unterzubringen. Damals wurde der Begriff Fat Binary verwendet. Realisiert wurde dies unter klassischem Mac OS jedoch nicht als Mach-O-Datei, sondern als das eigentlich modernere PEF-Dateiformat (Preferred Executable Format). Dabei lag der m68k-Code in der resource fork und der PowerPC-Code in der data fork.
Trivia
Theoretisch ist es möglich, weit mehr als zwei Architekturen in eine sogenannte Super-Universal Binary zu packen, sodass das resultierende Programm anschließend auf zahlreichen Architekturen nativ lauffähig ist.[2] Praktisch wurde das z. B. bei der Umstellung von PowerPC auf Intel für bis zu vier Architekturen umgesetzt.[6]