[Перевод] Автоматический перенос приложений iOS (ARM) на macOS (x86) с помощью Bitcode

Когда Apple представила технологию Bitcode и сделала её обязательной для watchOS и tvOS, компания словно отмахнулись от вопросов, зачем она вообще нужна. Лишь туманно сказала, что она помогает настраивать двоичные файлы и использует последние улучшения компилятора.

С тех пор Bitcode сыграл важную роль в плавном переходе watchOS на 64 бит, где разработчикам даже не пришлось перекомпилировать свои приложения в каталоге. Сама Apple сделала это автоматически: все приложения начали работать на Apple Watch Series 4. Вероятно, вы даже не заметили, что произошла миграция.

Что такое Bitcode? Ну, bitcode с маленькой b — это специфичное для архитектуры промежуточное представление, используемое LLVM, а Bitcode с большой B относится к набору функций, позволяющих встроить это представление в ваш двоичный файл Mach-O, и механизмы, с помощью которых вы можете отдать этот файл в App Store.
Bitcode не такой гибкий, как исходный код, но он гораздо более гибкий, чем встроенный двоичный файл, с метаданными и аннотациями для компилятора. На практике вы (или Apple) можете легко взять блобы Bitcode из приложения и перекомпилировать их в полностью функционирующую копию вашего приложения. Переход от armv7 к armv7s или от arm64 к arm64e — это очень классно и экономит время разработчиков, которым приходилось перекомпилировать двоичный файл каждый раз, когда Apple меняет чипы ARM. Bitcode уже давно используется Apple в драйверах OpenGL, так что драйвер можно оптимизировать на лету для различных архитектур GPU.

Мы видели, как Microsoft эффективно использует статическую перекомпиляцию на Xbox One, предоставляя доступ к целой библиотеке игр, первоначально написанных для Xbox 360 (под PowerPC), совершенно без участия разработчиков или доступа к исходному коду. И без посредника вроде Bitcode, что упрощает процесс.

Конечно, в течение многих лет вокруг витает призрак macOS на ARM. Многие размышляли, упростит ли это перенос приложений с помощью Bitcode. В итоге пришли к консенсусу, что Bitcode не подходит для переноса между кардинально разными архитектурами, такими как Intel и ARM.

Меня это не убедило, так что я решил проверить!

Для начала, нам нужно простое тестовое приложение на Objective-C с Bitcode; он обычно включается только при создании архива для App Store, поэтому нужно принудительно включить его в обычную сборку. Можно использовать флаг -fembed-bitcode или кастомный параметр сборки:

BITCODE_GENERATION_MODE = bitcode


Создайте двоичный файл для Generic iOS Device или подключённого устройства, как обычно. Похоже, Bitcode не встроен в сборки arm64e (например, если у вас устройство на A12), поэтому можете отключить настройку Xcode «компилировать только для активных архитектур» и напрямую собирать для arm64.

С помощью инструмента ebcutil все объекты Bitcode легко извлекаются из скомпилированного бинарника.

ebcutil -a arm64 -e path/to/MyApp.app/MyApp


Затем перекомпилируем каждый объект Bitcode для 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 $f.o;
done


Теперь свяжем скомпилированные блобы обратно в двоичный файл.

clang -arch x86_64 -mios-version-min=12.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk *.o -o path/to/MyApp.app/MyApp


Если получилось, теперь у нас x86-версия оригинального приложения arm64! По идее, его можно поместить непосредственно в окно симулятора iOS, установить и запустить.

Это очень важный факт: вы можете статически переносить двоичные файлы между платформами Intel и ARM, если они включают Bitcode. Это действительно работает!

Your browser does not support HTML5 video.


Подводные камни для более сложных проектов


Похоже, ARC использует встроенный ассемблер, поэтому для переноса с arm64 на x86 в данный момент придётся отключить ARC.

Некоторые типы блоков, такие как обработчики завершения, запускают компилятор с неприемлемыми инструкциями. Если попадётся ошибка X87, вероятно, проблема в этом.

Почему Objective-C? Ну, Swift разработан с учётом ARC. Не думаю, что есть способ избежать вышеупомянутого встроенного ассемблера, поэтому перекомпиляция в настоящее время завершится неудачей.

Сделаем ещё один шаг: применим marzipanify для преобразования этого приложения Intel iOS в программу Mac, которая работает с помощью Marzipan.

187e0e18d39520cd7837887938f44f7b.jpg

Это было легко!

Теоретически это означает, что у Apple есть способ запускать на Mac любое приложение iOS из App Store, не требуя от разработчиков обновлять или перекомпилировать свои приложения.

Let’s automate things! How about a single command that takes an arm64 iOS app and transpiles it into a Marzipan app that runs on Intel via its embedded bitcode pic.twitter.com/sxWfXR9MOP

— Steve Troughton-Smith (@stroughtonsmith) May 18, 2019


Что если Mac перейдёт с чипов Intel на ARM? Ну, как видите, с помощью Bitcode она может перенести все приложения с поддержкой Bitcode в Mac App Store без помощи разработчиков, поэтому будет готова к смене процессора с первого дня. Это даёт Apple большую свободу. Теперь не требуется заранее объявлять о переходе на новые процессоры на год раньше времени, а технология вроде Rosetta теперь не нужна.

Очевидно, что мы ещё не достигли этой точки: сегодня Apple не включает Bitcode для приложений в Mac App Store, а сегодняшний Bitcode может не идеально подходить для такого архитектурного переноса. На месте Apple я бы сфокусировался на этих двух факторах, и, конечно, включил Bitcode в обязательном порядке для всех приложений Marzipan в macOS 10.15.

© Habrahabr.ru