Maintaining Seamless Compatibility with Apple and LLVM Compiler Technology
Why did Apple deprecate bitcode from the App Store?
Bitcode is a core component of the LLVM code compilation technology, and it continues to exist despite the decisions Apple has made in the later part of the build/release process. Apple has now transitioned all of its product lines, including Mac, iPhone, iPad, and Apple Watch, among others, to ARM-based architectures, at the same time removing the ability to upload bitcode when submitting for the App Store (a detailed explanation below).
Given that Apple does not need developers submitting LLVM bitcode to the App Store anymore, the easiest way to stop new submissions is to change the default settings in Xcode to stop embedding bitcode and eventually remove the option to embed bitcode altogether.
The exact reason for the deprecation of bitcode from the App Store, may not be known, but we could hypothesize a few compelling reasons:
- Apple no longer requires it, so simplifying the options and UI around Xcode improves developer usability.
- Having bitcode uploaded to the AppStore likely results in significant unnecessary resources (network, compute, and storage) for something no longer needed.
But how did we get here? Let’s have a look at the brief history of Apple’s use of compiler technologies over the last few years.
A brief history of Apple’s use of compiler technologies
GCC compiler in earlier times
Previously, Apple's main compiler, GCC, generated processor-specific native code for Objective-C apps. This resulted in "fat binaries" containing multiple program versions, allowing cross-processor compatibility during transitions from 68k to PowerPC, PowerPC64, Intel, and Intel64. However, this approach stored numerous unused copies within "Universal Binary" files. Today, they can incorporate both ARM and Intel (32-bit and 64-bit) support, with the dynamic linker selecting the correct architecture when run on a different processor.
Apple's shift towards LLVM, away from GCC, occurred several years ago. This move made Clang, LLVM's C-based language front-end, Xcode's default, driving Objective-C enhancements and enabling a comprehensive LLVM-based toolchain for iOS app compilation.
Introducing LLVM
LLVM is a compiler backend that is used by multiple compilers and programming languages to compile code. LLVM-based compilers first compile to an Intermediate Representation (LLVM IR). The theory is that this will make it easier to reuse optimization techniques and to make it easier to compile for multiple target platforms in one unified way. LLVM bitcode is just a binary representation of the LLVM IR.
Apple is a big contributor to LLVM technology, but not the only one. Many big companies heavily contribute to and rely on this open-source project. It is very much used outside the Apple ecosystem both as a research tool in the compiler domain as well as a basis for developing (proprietary and open-source) compilers for a wide array of use cases.
Some examples of compilers that use LLVM are:
- Clang (Objective-C and C++)
- Rust
- Swift
- Kotlin Native
What could be the reasons that Apple decided to upload the bitcode when they transitioned to LLVM?
Bitcode has always been a part of the LLVM compile, optimization, and code generation phases. Moving the back-end logic to the Apple servers created the opportunity for additional server-side optimizations and analyses. This unlocked the potential of future re-translation to support newer and faster processors.
Bitcode deployments could be conditionally enabled for existing iOS deployments with the "Enable Bitcode" option in the project settings. This would add debug builds and embed bitcode for archive/device builds. These would be passed to the compiler.
Bitcode is the core mechanism in code compilation
Since Xcode 14, the bitcode enablement option was removed from the UI. It remained available, but as a hidden option, so bitcode could still be embedded in the final application or SDK by using the Xcode environment variable ENABLE_BITCODE. With Xcode 15 and henceforth, bitcode enablement is completely deprecated. It is not available in UI nor as a hidden option.
iXGuard, Guardsquare’s comprehensive mobile application protection product, uses the most relevant technology abstraction level to deliver the best result for code obfuscation and runtime integrity. One of the important abstraction levels that iXGuard uses is the LLVM bitcode that is produced at the intermediate stages of compilation. As an integral component of the compiler, bitcode remains a steadfast foundation upon which iXGuard can confidently depend. We have diligently anticipated and prepared for the deprecation of bitcode from AppStore and Xcode for the past 1.5 years.
Notably, iXGuard streamlines the process of configuring Xcode projects for obfuscation and incorporates enhanced analytical capabilities. This ensures minimal disruption to the security of the primary application, particularly when vendors cease providing SDKs with bitcode support in the future.
To build the app, utilize the Guardsquare option; simply select it from Xcode's menu or set an environment variable in your CI, and iXGuard will handle all intermediate steps for you. iXGuard transparently sets the compiler flags to produce a bitcode enabled app for protection. Furthermore, iXGuard is aligned with the current way of working and will not export bitcode anymore post-implementation of its protection mechanisms. Guardsquare chose the option that ensured a more user-friendly interface among many alternatives.