Automatic transfer of iOS applications (ARM) to macOS (x86) using Bitcode
When Apple introduced Bitcode technology and made it mandatory for watchOS and tvOS, the company seemed to shrug off questions about why it was needed at all. She only vaguely said that she helps tune binaries and uses the latest compiler enhancements.
Since then, Bitcode has played an important role in the smooth transition of watchOS to 64 bits, where developers have not even had to recompile their applications in the directory. Apple itself did this automatically: all applications started to work on the Apple Watch Series 4. You probably did not even notice that a migration occurred.
What is Bitcode? Well, bitcode with a small b is an architecture-specific intermediate representation used by LLVM, and Bitcode with a large B refers to a set of functions that allow you to embed this representation into your Mach-O binary file, and mechanisms by which you can give this file in the app store. Bitcode is not as flexible as the source code, but it is much more flexible than the embedded binary, with metadata and annotations for the compiler. In practice, you (or Apple) can easily take Bitcode blobs from an application and recompile them into a fully functioning copy of your application. The transition from armv7 to armv7s or from arm64 to arm64e is very cool and saves developers who have to recompile a binary file every time Apple changes its ARM chips. Bitcode has long been used by Apple in OpenGL drivers, so the driver can be optimized on the fly for various GPU architectures. ')
We have seen how Microsoft effectively uses static recompilation on Xbox One, providing access to a whole library of games originally written for the Xbox 360 (under PowerPC), completely without the participation of developers or access to the source code. And without an intermediary like Bitcode, which simplifies the process.
Of course, for many years the macOS ghost has been hovering around the ARM. Many have wondered if this will simplify the transfer of applications using Bitcode. As a result, they came to the consensus that Bitcode is not suitable for transferring between radically different architectures, such as Intel and ARM.
I was not convinced, so I decided to check it out!
First, we need a simple Objective-C test application with Bitcode; it is usually included only when creating an archive for the App Store, so you need to force it into a regular assembly. You can use the -fembed-bitcode or custom build option:
BITCODE_GENERATION_MODE = bitcode
Create a binary file for the Generic iOS Device or connected device, as usual. It looks like Bitcode is not built into the arm64e builds (for example, if you have an A12 device), so you can disable the Xcode setting “compile only for active architectures” and directly build for arm64.
Using the ebcutil tool , all Bitcode objects are easily retrieved from the compiled binary.
ebcutil -a arm64 -e path/to/MyApp.app/MyApp
Then we recompile every Bitcode object for Intel.
for f in *; do clang -arch x86_64 -c -Xclang -disable-llvm-passes -emit-llvm -x ir -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk $f -o $fo; done
Now link the compiled blobs back to the binary file.
If it worked, now we have the x86 version of the original arm64 app! In theory, it can be placed directly into the iOS simulator window, installed and launched.
This is a very important fact: you can statically transfer binary files between Intel and ARM platforms, if they include Bitcode . It really works!
Pitfalls for more complex projects
It seems that ARC is using the built-in assembler, so to migrate from arm64 to x86, you currently have to disable ARC.
Some block types, such as completion handlers, run the compiler with inappropriate instructions. If you get an X87 error, this is probably the problem.
Why Objective-C? Well, Swift is designed with ARC in mind. I do not think that there is a way to avoid the above-mentioned inline assembler, so recompilation will fail now.
Let's take one more step: use marzipanify to convert this Intel iOS application to a Mac program that works with Marzipan.
It was easy!
In theory, this means that Apple has a way to run any iOS application on the Mac from the App Store, without requiring developers to update or recompile their applications.
What if Mac switches from Intel chips to ARM? Well, as you can see, using Bitcode, it can transfer all Bitcode-enabled applications to the Mac App Store without the help of developers, so it will be ready to change the processor from day one. This gives Apple more freedom. Now there is no need to announce in advance about the transition to new processors a year ahead of time, and a technology like Rosetta is no longer needed.
Obviously, we have not yet reached this point: today Apple does not include Bitcode for applications on the Mac App Store, and today's Bitcode may not be ideal for such an architectural transfer. At Apple’s place, I’d focus on these two factors, and, of course, enabled Bitcode on a mandatory basis for all Marzipan applications in macOS 10.15.