Когда 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, поэтому нужно принудительно включить его в обычную сборку. Можно использовать флаг
Создайте двоичный файл для Generic iOS Device или подключённого устройства, как обычно. Похоже, Bitcode не встроен в сборки arm64e (например, если у вас устройство на A12), поэтому можете отключить настройку Xcode «компилировать только для активных архитектур» и напрямую собирать для arm64.
С помощью инструмента ebcutil все объекты Bitcode легко извлекаются из скомпилированного бинарника.
Затем перекомпилируем каждый объект Bitcode для Intel.
Теперь свяжем скомпилированные блобы обратно в двоичный файл.
Если получилось, теперь у нас x86-версия оригинального приложения arm64! По идее, его можно поместить непосредственно в окно симулятора iOS, установить и запустить.
Это очень важный факт: вы можете статически переносить двоичные файлы между платформами Intel и ARM, если они включают Bitcode. Это действительно работает!
Это было легко!
Теоретически это означает, что у Apple есть способ запускать на Mac любое приложение iOS из App Store, не требуя от разработчиков обновлять или перекомпилировать свои приложения.
Что если Mac перейдёт с чипов Intel на ARM? Ну, как видите, с помощью Bitcode она может перенести все приложения с поддержкой Bitcode в Mac App Store без помощи разработчиков, поэтому будет готова к смене процессора с первого дня. Это даёт Apple большую свободу. Теперь не требуется заранее объявлять о переходе на новые процессоры на год раньше времени, а технология вроде Rosetta теперь не нужна.
Очевидно, что мы ещё не достигли этой точки: сегодня Apple не включает Bitcode для приложений в Mac App Store, а сегодняшний Bitcode может не идеально подходить для такого архитектурного переноса. На месте Apple я бы сфокусировался на этих двух факторах, и, конечно, включил Bitcode в обязательном порядке для всех приложений Marzipan в macOS 10.15.
С тех пор 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. Это действительно работает!
Сделаем ещё один шаг: применим marzipanify для преобразования этого приложения Intel iOS в программу Mac, которая работает с помощью Marzipan.Подводные камни для более сложных проектов
Похоже, ARC использует встроенный ассемблер, поэтому для переноса с arm64 на x86 в данный момент придётся отключить ARC.
Некоторые типы блоков, такие как обработчики завершения, запускают компилятор с неприемлемыми инструкциями. Если попадётся ошибка X87, вероятно, проблема в этом.
Почему Objective-C? Ну, Swift разработан с учётом ARC. Не думаю, что есть способ избежать вышеупомянутого встроенного ассемблера, поэтому перекомпиляция в настоящее время завершится неудачей.
Это было легко!
Теоретически это означает, что у Apple есть способ запускать на Mac любое приложение iOS из App Store, не требуя от разработчиков обновлять или перекомпилировать свои приложения.
Что если Mac перейдёт с чипов Intel на ARM? Ну, как видите, с помощью Bitcode она может перенести все приложения с поддержкой Bitcode в Mac App Store без помощи разработчиков, поэтому будет готова к смене процессора с первого дня. Это даёт Apple большую свободу. Теперь не требуется заранее объявлять о переходе на новые процессоры на год раньше времени, а технология вроде Rosetta теперь не нужна.
Очевидно, что мы ещё не достигли этой точки: сегодня Apple не включает Bitcode для приложений в Mac App Store, а сегодняшний Bitcode может не идеально подходить для такого архитектурного переноса. На месте Apple я бы сфокусировался на этих двух факторах, и, конечно, включил Bitcode в обязательном порядке для всех приложений Marzipan в macOS 10.15.