Recent from talks
Nothing was collected or created yet.
Binary-code compatibility
View on WikipediaThis article needs additional citations for verification. (November 2013) |
Binary-code compatibility (binary compatible or object-code compatible) is a property of a computer system, meaning that it can run the same executable code, typically machine code for a general-purpose computer central processing unit (CPU), that another computer system can run. Source-code compatibility, on the other hand, means that recompilation or interpretation is necessary before the program can be run on the compatible system.
For a compiled program on a general operating system, binary compatibility often implies that not only the CPUs (instruction sets) of the two computers are binary compatible, but also that interfaces and behaviours of the operating system (OS) and application programming interfaces (APIs), and the application binary interfaces (ABIs) corresponding to those APIs, are sufficiently equal, i.e. "compatible".
A term like backward-compatible usually implies object-code compatibility. This means that newer computer hardware and/or software has (practically) every feature of the old, plus additional capabilities or performance. Older executable code will thus run unchanged on the newer product. For a compiled program running directly on a CPU under an OS, a "binary compatible operating system" primarily means application binary interface (ABI) compatibility with another system. However, it also often implies that APIs that the application depends on, directly or indirectly (such as the Windows API, for example), are sufficiently similar. Hardware (besides the CPU, such as for graphics) and peripherals that an application accesses may also be a factor for full compatibility, although many hardware differences are hidden by modern APIs (often partly supplied by the OS itself and partly by specific device drivers).
In other cases, a general porting of the software must be used to make non-binary-compatible programs work.
Binary compatibility is a major benefit when developing computer programs that are to be run on multiple OSes. Several Unix-based OSes, such as FreeBSD or NetBSD, offer binary compatibility with more popular OSes, such as Linux-derived ones, since most binary executables are not commonly distributed for such OSes.
Most OSes provide binary compatibility, in each version of the OS, for most binaries built to run on earlier versions of the OS. For example, many executables compiled for Windows 3.1, Windows 95 or Windows 2000 can also be run on Windows XP or Windows 7, and many applications for DOS ran on much newer versions of Windows up to Windows 10 for as long as the NTVDM was supported.
Binary compatible hardware
[edit]For a digital processor implemented in hardware, binary compatibility means that (a large subset of) machine code produced for another processor can be correctly executed and has (much) the same effect as on the other processor. This is quite common among many processor families, although it is rather uncommon among the ubiquitous small embedded systems built around such processors. Full machine code compatibility would here imply exactly the same layout of interrupt service routines, I/O-ports, hardware registers, counter/timers, external interfaces and so on. For a more complex embedded system using more abstraction layers (sometimes on the border to a general computer, such as a mobile phone), this may be different.
Binary compatible operating systems
[edit]Binary compatible operating systems are OSes that aim to implement binary compatibility with another OS, or another variant of the same brand. This means that they are ABI-compatible (for application binary interface). As the job of an OS is to run programs, the instruction set architectures running the OSes have to be the same or compatible. Otherwise, programs can be employed within a CPU emulator or a faster dynamic translation mechanism to make them compatible.
For example, the Linux kernel is not compatible with Windows. This does not mean that Linux cannot be binary compatible with Windows applications. Additional software, Wine, is available that does that to some degree. The ReactOS development effort seeks to create an open-source, free software OS that is binary compatible with Microsoft's Windows NT family of OSes using Wine for application compatibility and reimplementing the Windows kernel for additional compatibility such as for drivers whereas Linux would use Linux drivers, not Windows drivers. FreeBSD and other members of the BSD family have binary compatibility with the Linux kernel in usermode by translating Linux system calls into BSD ones. This enables the application and libraries code that run on Linux-based OSes to be run on BSD as well.
Note that a binary compatible OS is different from running an alternative OS through virtualization or emulation, which is done to run software within the alternative OS in the case when the host OS is not compatible. Sometimes virtualization is provided with the host OS (or such software can be obtained), which effectively makes the host OS compatible with programs. For example, Windows XP Mode for Windows 7 allows users to run a 64-bit version of Windows 7 and enable old software to still work in a 32-bit virtual machine running Windows XP; VMware Workstation/VMware Fusion, Parallels Workstation, and Windows Virtual PC allow other OSes to be run on Windows, Linux, and macOS.
For another example, Mac OS X on the PowerPC had the ability to run Mac OS 9 and earlier application software through Classic—but this did not make Mac OS X a binary compatible OS with Mac OS 9. Instead, the Classic environment was actually running Mac OS 9.1 in a virtual machine, running as a normal process inside of Mac OS X.[1][2]
See also
[edit]References
[edit]- ^ "Mac OS X System Architecture". 2002. Archived from the original on August 2, 2002.
- ^ Singh, Amit (June 29, 2006). Mac OS X Internals: A Systems Approach. Addison-Wesley. Section 2.11.8: Classic. ISBN 0-321-27854-2.
Classic Startup is a Mach-O application that runs Mac OS 9 within its address space. It provides a hardware abstraction layer between Mac OS 9 and Mac OS X by virtualizing traps, system calls, and interrupts. It runs in a protected memory environment, with multiple Mac OS 9 processes within it layered on top of a single Mac OS X BSD process.
External links
[edit]- KDE Techbase Policies – a compendium of C++ development rules of thumb (with some examples) for not breaking binary compatibility between releases of a library.
- ABI Analysis Tools – a set of open-source tools for analysis of ABI and backward binary compatibility implementing KDE Techbase Policies
Binary-code compatibility
View on GrokipediaFundamentals
Definition and Scope
Binary-code compatibility refers to the ability of a compiled binary executable to run unchanged on a target system without requiring recompilation or modification, provided that the underlying hardware instruction set and software interfaces remain consistent across the systems involved.[1] This property ensures that machine code produced for one environment can execute identically on another, enabling seamless software distribution and deployment without source code access.[8] The scope of binary-code compatibility is limited to compiled binaries, such as machine code generated from languages like C or C++, and excludes source code portability, which involves recompiling from human-readable code for different targets.[9] It primarily addresses backward compatibility, where legacy binaries continue to function on newer systems, in contrast to forward compatibility, which concerns the adaptability of current binaries to future environments.[1] Key enablers include the Application Binary Interface (ABI), which standardizes how binaries interact with the operating system and libraries, though detailed mechanisms are beyond this introductory scope. Achieving binary compatibility presupposes a standard compilation process, where source code is transformed through multiple stages: preprocessing to handle directives, compilation to generate assembly code, assembly to produce object code, and linking to resolve dependencies and create the final executable.[10] Loaders then play a crucial role at runtime by relocating the executable into memory, resolving any remaining dynamic references, and initiating execution, ensuring the binary aligns with the system's runtime environment.[11] Examples illustrate the scope across hardware and software levels; at the hardware level, x86 binaries compiled for Intel processors can run on AMD systems due to shared instruction set architecture (ISA) compliance, allowing cross-vendor execution without alteration.[8] At the software level, Dynamic Link Libraries (DLLs) in Windows maintain compatibility by preserving interface contracts, enabling applications to load and use updated DLLs without recompilation as long as public APIs remain unchanged.[12]Historical Development
The origins of binary-code compatibility trace back to the 1960s mainframe era, exemplified by IBM's System/360 family, announced on April 7, 1964. This architecture was revolutionary for its unified design, enabling software written for one model to execute on others without modification, including upward scalability from smaller to larger systems. By incorporating features like program compatibility—where central processors could run machine-language programs from equivalent or smaller configurations—and microcode-based emulation for legacy IBM 1401 software, System/360 protected customers' substantial investments in existing programs, addressing the era's frequent need for costly rewrites during hardware upgrades.[13][14][15] In the 1980s and 1990s, binary compatibility advanced with the standardization of the x86 architecture and efforts to unify Unix-like systems. The Intel 8086 microprocessor, introduced in 1978, powered the IBM PC launched in 1981, creating an open platform that spurred a vibrant third-party software ecosystem as compatible clones proliferated, reducing reliance on proprietary hardware. Concurrently, the "Unix wars"—intense competition among vendors like AT&T, BSD, and others—prompted the development of POSIX (IEEE Std 1003.1-1988), which standardized application programming interfaces and utilities for source-level portability across Unix variants, facilitating binary compatibility by ensuring consistent execution environments and mitigating fragmentation.[16][17] The 2000s marked significant transitions in processor architectures, balancing innovation with compatibility. AMD's x86-64 extension, announced in 1999 and first realized in the Opteron processor in 2003, extended the 32-bit x86 instruction set to 64 bits while preserving full backward compatibility through dedicated modes: legacy mode for pure 32-bit operation and compatibility mode within a 64-bit OS to run unmodified x86 applications without performance penalties. In contrast, Intel's Itanium (IA-64), released in 2001, faltered due to its lack of native x86 support, relying instead on emulation or recompilation that hindered software adoption and contributed to its commercial decline. Apple's 2005 shift from PowerPC to Intel x86 processors relied on Rosetta, a dynamic binary translator, to seamlessly execute legacy PowerPC binaries, minimizing disruption during the transition. These developments reduced software vendor lock-in by enabling smoother migrations and broader interoperability.[18][19] From the 2010s onward, binary compatibility has evolved with diverse architectures and virtualization techniques. Apple's adoption of ARM-based silicon culminated in the M1 chip's release on November 10, 2020, integrating a custom ARM core into Macs while employing Rosetta 2—an advanced emulation layer—to run x86-64 applications with near-native performance, supporting the ecosystem's vast legacy software base. Paralleling this, containerization technologies like Docker, launched in 2013, enhanced de facto binary portability by encapsulating applications with their dependencies in isolated environments, allowing consistent execution across heterogeneous servers regardless of underlying OS or hardware variations. These innovations have further diminished lock-in effects, promoting flexible deployments in cloud and edge computing while sustaining compatibility for established binaries.[20]Technical Mechanisms
Application Binary Interface
The Application Binary Interface (ABI) serves as the foundational software interface that governs interactions between compiled binary executables and the underlying runtime environment, including operating systems and shared libraries. It precisely defines low-level details such as function calling conventions—which dictate how parameters are passed (e.g., via registers or stack), return value handling, and callee responsibilities for stack cleanup—along with data type sizes, alignments, and representations (e.g., the layout of structures and unions in memory). Additionally, ABIs specify processor register usage for function calls and returns, as well as the format and numbering of system calls for kernel interactions. For Unix-like systems, the System V ABI provides a comprehensive standard that addresses these components across architectures like x86-64, ensuring consistent binary behavior. Key elements of an ABI include function name mangling, which encodes additional information like parameter types into symbol names to support features such as function overloading in C++; stack alignment rules, which mandate specific memory boundaries (e.g., 16-byte alignment on x86-64 before function entry to optimize SIMD instructions and prevent faults); and exception handling protocols, which outline how runtime errors propagate through call stacks using mechanisms like personality routines and unwind tables. An ABI mismatch, such as incompatible pointer sizes between 32-bit (4-byte) and 64-bit (8-byte) environments, can result in severe issues like segmentation faults or crashes, as code expecting one size misinterprets addresses and accesses invalid memory.[21][22][23] In Linux environments, the ABI is primarily embodied by the GNU C Library (glibc), which supports stable interfaces across versions (e.g., glibc 2.2.5 and later) while allowing for controlled evolution through multiple supported ABI tags per release. On Windows, the Portable Executable (PE) format, built on the Common Object File Format (COFF), defines the ABI by specifying binary layouts for executables (.exe) and dynamic-link libraries (.dll), including sections for code, data, and relocation information. These standards facilitate dynamic linking, where binaries resolve dependencies to shared libraries at load time or runtime without requiring recompilation or source code access, thereby upholding binary compatibility.[24][25] To maintain backward compatibility amid library updates, ABIs incorporate versioning schemes, such as symbol versioning in the Executable and Linkable Format (ELF) used on Linux and Unix-like systems. This mechanism tags symbols with version identifiers (e.g., via .gnu.version sections in ELF files), enabling binaries to bind to specific implementations of functions while allowing newer versions to add features without breaking existing linkages.[26]Instruction Set and Hardware Abstraction
The Instruction Set Architecture (ISA) defines the abstract model of a processor's instruction repertoire, specifying the operations, data types, registers, and addressing modes available to software. It serves as the interface between hardware and software, encapsulating the processor's capabilities in a way that allows binaries compiled for one implementation to run on compatible hardware. For example, the x86 ISA includes opcodes such as ADD, which performs arithmetic addition on operands, and JMP, which enables unconditional jumps to specified addresses.[27][27] ISAs are broadly classified into Reduced Instruction Set Computing (RISC) and Complex Instruction Set Computing (CISC) designs. RISC architectures, like ARM, emphasize a small set of simple, fixed-length instructions that execute in a uniform number of cycles, relying on load/store operations for memory access to simplify pipelining and increase clock speeds. In contrast, CISC architectures, such as x86, feature a larger set of complex instructions with variable-length encodings and multiple addressing modes, allowing more operations per instruction but complicating decoding.[28][28] Hardware abstraction layers, including microcode and firmware, bridge the gap between the high-level ISA and the underlying physical circuitry, enabling consistent binary execution across processor variants. Microcode acts as an interpretive layer in CISC processors like x86, translating user-visible macroinstructions (e.g., complex x86 opcodes) into sequences of simpler micro-operations that control hardware units such as the arithmetic logic unit (ALU) and registers. These micro-operations are stored in on-chip ROM and executed cyclically, providing flexibility for post-silicon updates and maintaining ISA fidelity without altering the binary. Firmware complements this by initializing hardware and managing low-level execution flows, ensuring that ISA instructions map reliably to diverse transistor-level implementations. Virtualization extensions further enhance abstraction; for instance, Intel VT-x introduces VMX modes that allow virtual machines to execute guest binaries directly at native privilege levels, eliminating the need for software-based binary rewriting or paravirtualization.[29][29][30] Binary-code compatibility at the ISA level varies from full superset support to partial emulation via translation. Full compatibility occurs when a newer ISA extends an older one without breaking existing binaries, as in x86-64, which is a superset of x86-32, supporting 32-bit applications through a compatibility mode that preserves legacy opcodes, registers, and addressing while adding 64-bit extensions. Similarly, ARMv8 ensures AArch32 binary support by incorporating the 32-bit execution state from ARMv7, allowing unmodified older ARM binaries to run alongside 64-bit AArch64 code. Partial compatibility relies on dynamic binary translation, where tools like QEMU reinterpret guest instructions on-the-fly for a host ISA lacking native support, such as emulating x86 on ARM hardware, though at a performance cost due to runtime overhead. Opcode encoding influences this: AArch64 uses fixed 32-bit instruction lengths for straightforward decoding, whereas AArch32 supports both fixed 32-bit lengths in the A32 instruction set and variable 16/32-bit lengths in the T32 (Thumb-2) instruction set. In contrast, x86 employs variable lengths (1 to 15 bytes) to pack diverse operand types efficiently but increasing complexity in translation and verification.[31][32][33][32][34]Hardware Compatibility
Cross-Vendor Processor Compatibility
Binary-code compatibility across vendors utilizing the same instruction set architecture (ISA), such as x86, enables software binaries compiled for one processor to execute on hardware from different manufacturers without modification or recompilation. This compatibility stems from the shared foundational ISA, originally developed by Intel and licensed to competitors like AMD and VIA Technologies, allowing them to produce processors that adhere to the same binary instruction format. For instance, x86 processors from Intel and AMD maintain interoperability at the binary level because both implement the core x86 instruction set, ensuring that applications targeting the ISA run seamlessly across vendors.[35][36] Intel plays a central role in defining the x86 ISA through its publicly available documentation, particularly the Intel® 64 and IA-32 Architectures Software Developer's Manuals, which serve as the authoritative reference for instruction sets and extensions. These manuals detail baseline instructions and optional extensions like Streaming SIMD Extensions (SSE) and Advanced Vector Extensions (AVX), which AMD and other vendors adopt to ensure cross-vendor support; for example, AMD integrated SSE and SSE2 as core components in its AMD64 architecture to align with Intel's specifications. While there is no formal certification body akin to those in ARM architectures, compatibility is achieved through rigorous vendor implementation and validation against Intel's reference, with recent collaborations like the x86 Ecosystem Advisory Group (formed in 2024 by Intel and AMD) focusing on standardizing future extensions to enhance predictability and consistency. In October 2025, the group marked its first anniversary and announced new standardized features including AVX10.1, FRED, CHKTAG, and ACE to further improve cross-platform compatibility.[35][37][38][39] A prominent case study is the execution of Windows binaries on AMD hardware, which has been supported since the 1990s following AMD's development of fully compatible x86 processors under licensing agreements with Intel dating back to 1982. AMD's K5 processor, released in 1996, ran Windows 95 and subsequent versions without issues, leveraging the shared x86 ISA to build a viable alternative to Intel's Pentium line. However, challenges arise with proprietary extensions; Intel's Transactional Synchronization Extensions (TSX), introduced in 2013, were not implemented by AMD, leading to binaries using TSX instructions to degrade to no-operation (NOP) behavior on AMD processors, potentially causing performance regressions or unexpected aborts in transactional code. Intel addressed broader TSX vulnerabilities—such as asynchronous aborts exploitable for side-channel attacks—by disabling the feature via microcode updates starting in 2019, mitigating cross-vendor inconsistencies.[36][40][41] This cross-vendor compatibility has driven market competition, enabling AMD to challenge Intel's dominance and expand the x86 ecosystem, which in turn accelerated the growth of the personal computer industry by ensuring a unified software base. The rivalry fostered innovation in processor design while maintaining binary portability, allowing developers to target a broad hardware market without vendor-specific optimizations, ultimately benefiting consumers through lower costs and diverse offerings.[42][43]Backward Compatibility in Processor Generations
Backward compatibility in processor generations refers to the ability of newer processors from the same vendor to execute binary code compiled for earlier generations without modification or recompilation. This is primarily achieved through instruction set architecture (ISA) supersetting, where each new generation extends the previous ISA by adding instructions and features while preserving the full semantics of legacy opcodes. For instance, in the x86 family, Intel processors maintain compatibility by supporting multiple operating modes, such as real-address mode for 16-bit 8086 code and protected mode for 32-bit IA-32 applications, ensuring that software from the 1978 Intel 8086 era can run on modern chips.[44] Similarly, the x86-64 extension introduces 64-bit addressing and additional registers but operates in compatibility mode to execute 32-bit IA-32 binaries seamlessly, using the same instruction encoding and register subsets as prior generations.[45] Another approach involves emulation modes for bridging architectural shifts. Intel's Itanium (IA-64) processor, launched in 2001, attempted this by dedicating a small portion of hardware—less than 1% of the die—to emulate IA-32 instructions via special escape sequences, allowing execution of x86 binaries. However, this mode suffered from severe performance limitations, emulating 32-bit code at speeds comparable to a 75 MHz Pentium on a 667 MHz Itanium, due to the lack of out-of-order execution and poor handling of unaligned memory loads in the emulation layer. This contributed to Itanium's commercial failure, as software emulation alternatives from Microsoft and Linux outperformed Intel's hardware approach by up to 50%, highlighting the challenges of non-supersetting emulation in maintaining efficient backward compatibility.[46] In the ARM architecture, backward compatibility is embedded in the evolutionary design of its profiles, with newer versions like Armv8-M fully compatible with Armv7-M and Armv6-M instructions, preserving 32-bit operations, registers, and data formats across generations. Armv9-A similarly builds on Armv8-A, supporting legacy AArch32 code alongside new AArch64 features. This enables seamless migration of embedded and mobile software. The big.LITTLE heterogeneous architecture exemplifies this by pairing high-performance "big" cores (e.g., Cortex-A57) with efficiency-focused "LITTLE" cores (e.g., Cortex-A53), both based on the Armv8 ISA, ensuring that legacy ARM binaries can execute on either core type without alteration, as the cores share identical instruction sets.[47][48] Implementation of backward compatibility typically relies on dedicated hardware for legacy instruction decoding. In Intel processors, a legacy decode pipeline processes older x86 opcodes, converting them into micro-operations for the execution core, with specialized decoders handling up to four instructions per cycle while supporting fusions for common legacy patterns like compare-and-branch sequences. This hardware integration, including microcode ROMs for complex legacy instructions, adds to the processor's overall design complexity, as maintaining decoders for rarely used opcodes from decades-old ISAs requires additional logic that could otherwise be allocated to modern features.[49][50] Exceptions to intra-vendor backward compatibility are rare but notable, often arising from third-party implementations diverging from the reference design. In the 1990s, Cyrix processors, such as the 6x86, aimed for x86 compatibility with Intel but encountered issues with floating-point unit (FPU) operations and certain instruction timings, leading to failures in applications like id Software's Quake, where imprecise FPU emulation caused crashes and graphical glitches on Cyrix systems while running flawlessly on Intel hardware. These incompatibilities stemmed from Cyrix's custom microcode and optimizations that deviated from Intel's x86 specification, underscoring the importance of strict adherence to ISA definitions for reliable generational compatibility.[51]Operating System Compatibility
Intra-OS Binary Compatibility
Intra-OS binary compatibility ensures that executable binaries compiled for one version of an operating system can execute on other versions within the same OS family without requiring recompilation or modification. This is primarily enabled by maintaining a stable application binary interface (ABI), which defines consistent conventions for interactions between applications and the OS kernel, as well as shared libraries. Such stability allows developers to target a single OS variant while achieving broad portability across its evolutionary releases, reducing maintenance overhead for software vendors and users alike. In the Windows ecosystem, Microsoft upholds robust intra-OS compatibility through the Win32 ABI, which has remained stable across Windows NT-based systems from early versions to contemporary releases like Windows 10 and 11. A cornerstone of this is the WOW64 subsystem, a user-mode emulation layer that transparently executes 32-bit x86 applications on 64-bit Windows architectures by intercepting system calls and managing bitness-specific behaviors, such as registry redirection and file system isolation.[52] For legacy applications facing compatibility issues due to evolving system behaviors, Microsoft employs the Application Compatibility Database, which applies lightweight shims—small intercepting libraries that modify API calls at runtime—to bridge gaps without altering the original binary.[53] This approach exemplifies how Windows prioritizes backward compatibility, enabling even Windows 95-era executables to function on modern systems via layered emulation. Apple's macOS maintains strong intra-OS binary compatibility through stable ABIs in its core frameworks, such as Cocoa and Carbon (now deprecated), allowing applications compiled for earlier versions to run on subsequent releases without modification, provided they avoid deprecated APIs. This is supported by guidelines ensuring ABI stability in dynamic libraries, where updates preserve binary interfaces to avoid breaking existing clients.[54] For architecture transitions, macOS employs Rosetta 2, a translation layer that enables Intel-based x86_64 binaries to execute on Apple Silicon (ARM64) processors, facilitating seamless upgrades from macOS Big Sur (2020) onward. Additionally, macOS versions like Ventura (13, 2022) and Sonoma (14, 2023) continue to support binaries from macOS 10.x eras, balancing innovation with legacy support, though 32-bit apps were dropped starting with Catalina (10.15, 2019).[55] Linux distributions achieve intra-OS binary compatibility through mechanisms like shared library versioning, particularly via sonames in the GNU C Library (glibc), which encodes ABI versions in filenames such aslibc.so.6 to prevent conflicts from interface changes.[56] The soname remains fixed for backward-compatible updates (e.g., glibc 2.x series under libc.so.6 since 1997), allowing binaries linked against it to run on any system providing the same or newer compatible implementation, while incompatible changes prompt a new soname to coexist alongside the old one. Enterprise-focused distributions like Red Hat Enterprise Linux (RHEL) formalize this with structured compatibility levels: for example, Compatibility Level 1 (CL1) guarantees ABI stability for core libraries like glibc across three major releases (e.g., binaries from RHEL 8 run unchanged on RHEL 9 and 10), enforced through policies that restrict disruptive changes and provide separate library variants for legacy support.[57]
In Debian and its derivatives like Ubuntu, intra-OS binary compatibility is maintained within the lifetime of a single stable release branch, where the package set is frozen to avoid ABI changes, in adherence to the Linux Filesystem Hierarchy Standard. This ensures that binaries built for a specific release, such as Debian 13 or Ubuntu 24.04 LTS, execute reliably on point-release updates of the same version without ABI breaks. However, between major releases, ABI changes may occur, often requiring recompilation. Debian packages (.deb files) bundle dependencies and metadata to aid installation within the same release, but portability across major versions or between distributions like Debian and Ubuntu is limited due to library version differences and configurations.[58]
