В 2008 году iPhone 3G и первый App Store познакомили многих людей с мобильными приложениями. Мы создали AirBuild3G, чтобы дать этому телефону новую задачу. На iPhone 3G или 3GS можно описать новое приложение, работать с его исходным кодом при помощи модели для написания кода, а затем скомпилировать и запустить его на том же телефоне. Модель работает в выбранном вами сервисе. Файлы проекта и инструменты разработки остаются на телефоне.
Сам AirBuild3G собран в Xcode 27 на Mac. Создаваемые в нём приложения используют отдельный компилятор, установленный на телефоне. Чтобы всё заработало, пришлось внести изменения в четырёх областях: подготовка устройства, компоновка, HTTPS и собственная цепочка инструментов телефона. В следующих разделах описан весь путь до вашего первого приложения.
Подготовка исходного устройства
Небольшой экран и физическая кнопка «Домой» сразу выдают iPhone 3G. Чтобы снова разрабатывать для него, нужно учитывать его исходные пропорции и ограничения, вплоть до элементов управления, благодаря которым приложения выглядели знакомо. Сам AirBuild3G использует элементы UIKit, доступные в iOS 4, включая сгруппированные таблицы настроек и действия на панели навигации.
AirBuild3G работает на iPhone 3G и 3GS. Для 3G нужны двоичные файлы armv6, а 3GS использует armv7. От прошивки зависит, какой пакет среды можно установить на каждый телефон. Наше эталонное устройство armv6, это iPhone 3G с iOS 4.2.1 и пользовательской прошивкой.
- Подготовкавосстановление / активация
- ПодключениеSSH + сеть
- Установкаприложение + пакет среды
- СредаНастройки → Среда
Начните с телефона, который загружается до экрана «Домой» и принимает SSH-подключения с вашего Mac. Для получения и установки пакетов Debian ему нужны рабочее сетевое подключение и менеджер пакетов. Модель устройства и версия прошивки определяют, какие пакеты отправлять, а сетевой адрес определяет, куда их отправлять. Также понадобятся надёжный USB-кабель и питание.
Некоторым старым телефонам нужны дополнительные шаги для активации и доступа к системе. Для поддерживаемого оборудования используйте Legacy iOS Kit и следуйте инструкциям для модели и прошивки вашего телефона. Перед началом создайте резервную копию телефона. Сам AirBuild3G не активирует телефон.
Для поддержки TLS AirBuild3G также нужен Substrate. Сборка для 3G должна включать armv6: пакет только для armv7 на нём не запустится независимо от версии. На телефоне, подготовленном с помощью Legacy iOS Kit, уже может быть совместимая сборка. Если Substrate работает, оставьте его как есть.
Для раздела «Настройки» → «Среда» действует то же правило. Он добавляет инструменты разработки и не затрагивает рабочий менеджер пакетов, службу SSH и другие базовые компоненты. Полное обновление этих компонентов может установить двоичные файлы, которые старая система не сможет запустить.
Сборка с помощью Xcode 27
Компилятор Xcode 27 по-прежнему создаёт код armv6 и armv7 и принимает iOS 4.0 как минимальную целевую версию. В комплекте с Xcode поставляется только iOS 27 SDK, поэтому мы компилируем с заголовками из прежней версии SDK. Передайте путь к SDK в LEGACY_SDK в командной строке make или экспортируйте его. Скрипты сборки пакетов среды тоже считывают это значение, а значение, заданное только внутри Makefile, до них не доходит.
Компоновщик строже. Он отклоняет armv6 как целевую архитектуру компоновки, не принимает минимальную целевую версию ниже iOS 4.3 и сообщает, что библиотеки прежней версии SDK собраны для неизвестной платформы. Поэтому вместо этих библиотек мы компонуем с созданными заглушками, а прежнюю версию SDK продолжаем использовать для заголовков.
- Скомпилированоclang
- архитектура
- armv6
- минимальная iOS
- 4.0
- код
- e92d40f0 …
- Скомпонованосмена метки, затем ld
- архитектура
armv6armv7- минимальная iOS
4.05.0- код
- e92d40f0 …
- Подготовлено к выпускуобратная смена метки, исправление
- архитектура
armv7armv6- минимальная iOS
5.04.0- код
- e92d40f0 …
Сборка компилирует оба среза для iOS 4.0, компонует их для iOS 5.0, а затем возвращает минимальную версию к 4.0. Для armv6 она также меняет метку каждого объектного файла на armv7, чтобы компоновщик принял их, а затем возвращает скомпонованному срезу метку armv6. Смена метки меняет метаданные, но не инструкции. Скомпилированный код и символьные заглушки, которые добавляет компоновщик, уже должны работать на armv6.
MH_MAGIC = 0xFEEDFACE
SUBTYPES = {"armv6": 6, "armv7": 9}
def main():
…
path, want = sys.argv[1], SUBTYPES[sys.argv[2]]
with open(path, "rb") as f:
buf = bytearray(f.read())
if struct.unpack_from("<I", buf, 0)[0] != MH_MAGIC:
…
struct.pack_into("<i", buf, 8, want)
with open(path, "wb") as f:
f.write(buf)- cpusubtype в Mach-O для каждой архитектуры: 6 означает armv6, 9 означает armv7.
- Вся смена метки сводится к четырём байтам со смещением 8, полю cpusubtype в заголовке. Инструкции остаются без изменений.
Сборка сохраняет -no_pie, потому что до iOS 4.3 в iOS не было ASLR. Она также отключает объединение категорий Objective-C, иначе у объединённых методов удаляется бит Thumb и AirBuild3G аварийно завершается на 3GS. make verify проверяет архитектуры, команды загрузки, инструкции armv6 и бит Thumb у методов armv7. Успешная компоновка ничего из этого не гарантирует.
Запустите make, чтобы собрать AirBuild3G и его привилегированный вспомогательный процесс. make payload скачивает и проверяет каждый закреплённый пакет в локальном кэше, а make package без подключения к сети собирает из него пакет AirBuild3G и оба пакета среды. Для упаковки также нужен LEGACY_SDK, потому что каждый пакет среды содержит архив SDK для телефона.
make install-device DEVICE=root@<ip> устанавливает AirBuild3G и пакет среды iOS 4, обновляет экран «Домой» и перезапускает приложение. Для 3GS с iOS 5 или более поздней версией вместо этого передайте ios6 третьим аргументом в tools/deploy_device.sh. Приложение работает от имени пользователя mobile, а небольшой вспомогательный процесс рядом с ним выполняет всё, для чего нужны права root. Теперь пакет среды находится на телефоне, а раздел «Настройки» → «Среда» устанавливает содержащиеся в нём инструменты разработки.
Подключение к сервису моделей
В разделе «Настройки» в группе Endpoint нужно ввести три значения: Base URL, API Key и Model. Запросы отправляются прямо с телефона на эту конечную точку. Между ними нет нашего сервера, регистрации учётной записи или устройства тоже нет. Ваш сервис моделей запускает модель, выставляет счёт за использование и сообщает, какие значения ввести. Если вы получили телефон в подарок от нас, он подключается к air.build.
Стандартная iOS 4 поддерживает через SecureTransport только TLS 1.0, а современной конечной точке может требоваться TLS 1.2. Мы добавляем TLS 1.2 с помощью TLSFix, расширения Substrate для CFNetwork.
Телефону всё равно нужно проверить подлинность сервера. Его старое хранилище доверия не всегда может построить цепочку сертификатов современной конечной точки, а добавление TLS 1.2 не обновляет это хранилище. При сбое подключения рассматривайте шифрование и доверие к сертификату как отдельные проблемы.
По умолчанию AirBuild3G проверяет сертификаты. Если запрос завершается ошибкой, сначала проверьте Base URL, API Key, Model и цепочку сертификатов. Настройка Trust Any Certificate служит явным исключением. При её включении проверка подлинности сервера отключается, поэтому любой, кто сможет выдать себя за конечную точку, потенциально получит ваш API-ключ. Не включайте её, если конечная точка находится за пределами вашей сети. AirBuild3G считывает настройку при каждой оценке доверия, поэтому её отключение действует уже для следующего запроса.
static OSStatus ABLSecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
…
OSStatus status = ABLOriginalSecTrustEvaluate(trust, result);
if (!ABLTrustAnyCertificate()) {
return status;
}
if (result != NULL) {
*result = kSecTrustResultUnspecified;
}
return errSecSuccess;
}- Сначала выполняется собственная проверка телефона.
- По умолчанию Trust Any Certificate выключена, поэтому результат возвращается без изменений. При каждом вызове настройка считывается из
NSUserDefaults.
Потоковый ответ также должен укладываться в возможности телефона. AirBuild3G получает и декодирует поток в фоновом потоке выполнения, а в интерфейс передаёт только готовый текст. Расшифровка обновляется через заданные интервалы, а не пересчитывается и прокручивается для каждого токена. Это снижает нагрузку на компоновку интерфейса во время поступления ответа. Появившийся ответ подтверждает, что подключение работает. Для компиляции написанного моделью кода по-прежнему нужна установленная на телефоне среда.
Установка компилятора, который сможет работать на телефоне
Раздел «Настройки» → «Среда» устанавливает из пакета среды компилятор, заголовки SDK, библиотеки компоновки и инструменты подписи. Пакет iOS 4 содержит GCC для armv6, а пакет для iOS 5 и более поздних версий содержит clang для armv7. Каждый телефон устанавливает только вариант, соответствующий его прошивке.
Для каждого пакета закреплены адрес скачивания и контрольная сумма, а при установке используются именно эти файлы без запроса последних версий из репозитория. Пакеты, уже установленные в закреплённой версии, пропускаются, поэтому повторный запуск установки среды завершается сразу. Затем установленные пакеты удерживаются на текущих версиях, чтобы Cydia не предлагала обновления, нарушающие работу цепочки инструментов.
Для iPhone 3G среда использует сборку iphone-gcc 4.2 от 2008 года. Сборка 2009 года резервирует гигабайт под предварительно скомпилированные заголовки, из-за чего её часть выходит за пределы адресного пространства телефона armv6. Её серверные части компилятора аварийно завершаются ещё до начала компиляции. Обе сборки определяются как Apple build 5555, но только сборка 2008 года помещается в это адресное пространство.
0x30000000iphone-gcc 2008 года · каждый сегмент меньше 6 MBработает
массив 512 MB · тестовый двоичный файлработает
массив 768 MB · тестовый двоичный файлSIGBUS
iphone-gcc 2009 года · область предварительно скомпилированных заголовков 1 GBSIGBUS
Заголовки SDK тоже требуют изменений: этот GCC не может разобрать объявления блоков. Перед упаковкой заголовков iOS 4 мы удаляем эти объявления вместе со всеми объявлениями, которые зависят от удалённого типа. В пакете clang заголовки Apple остаются без изменений, поскольку clang поддерживает блоки. В каждый пакет также входит инструмент подписи, способный подписывать результат работы соответствующего компилятора.
Телефон хранит системные фреймворки в общем кэше dyld, поэтому в каталогах фреймворков нет библиотечных файлов, доступных компоновщику. Мы добавляем созданные заглушки с экспортируемыми символами и настоящими именами библиотек. Компоновщик сопоставляет каждую ссылку с заглушкой и записывает, из какой библиотеки она пришла. При запуске dyld связывает её с настоящей реализацией в общем кэше. Поскольку заглушки содержат настоящие имена символов, отсутствие фреймворка приводит к ошибке компоновки, а не к аварийному завершению приложения при запуске.
Среда также задаёт пути включения, направляет gcc и cc на установленный компилятор и экспортирует флаги компоновки для подключения среды выполнения Objective-C и CoreFoundation. Встроенный Makefile считывает эти настройки, поэтому новому проекту не требуется отдельно настраивать цепочку инструментов. После этого ваши приложения собираются прямо на телефоне.
build/%.o: src/%.m | build
@echo " compile $<"
@gcc -c $(IOS_CFLAGS) $< -o $@
# Compiling and linking take different flags, so they are separate steps.
# A missing -framework is a link error here, which is the point: link stubs
# with real symbol names are what make it one instead of a crash at launch.
build/$(NAME): $(OBJECTS)
@echo " link $@"
@gcc $(OBJECTS) -o $@ $(IOS_LDFLAGS) $(addprefix -framework ,$(FRAMEWORKS))
@# Unsigned is SIGKILL at launch, not a warning.
@ldid -S $@IOS_CFLAGSиIOS_LDFLAGSберутся из/etc/profile.d/airbuild.sh, который устанавливается вместе со средой, поэтому ни в одном проекте не указывается имя SDK или путь к компилятору.- Двоичный файл также подписывается на телефоне. Без подписи система завершает его при запуске.
Создание и запуск первого приложения
Новый проект начинается со встроенного шаблона UIKit. AirBuild3G копирует его в каталог проекта и добавляет файлы в контекст модели. Модель начинает с проекта, который уже собирается, поэтому в первом запросе можно описать приложение, а не просить создать Makefile.
Для первого приложения хорошо подойдёт кнопка, которая увеличивает число на экране. Оно достаточно небольшое, чтобы прочитать код целиком, но при этом охватывает редактирование, компиляцию, упаковку и запуск на телефоне. В запросе укажите iOS 4 как целевую версию и попросите модель собрать и установить приложение. Код интерфейса должен использовать только API, реализованные в установленной на телефоне iOS. Наличие объявления в заголовках не означает, что API существует во время выполнения.
У модели есть четыре инструмента. Execute запускает команды в каталоге проекта. EditFile записывает файл целиком. PatchFile заменяет точный фрагмент. CheckBuild запускает make с загруженными настройками среды и возвращает конец вывода. Результаты работы инструментов возвращаются модели, и она может продолжить работу или исправить неудачное изменение. Все вычисления модели выполняются в вашем сервисе моделей.
вызов инструмента
результат инструмента
Execute EditFile PatchFile CheckBuildЦикл повторяется, пока модель не ответит без вызова инструмента, пока вы не нажмёте «Остановить» или пока не завершатся 16 раундов.
После установки нового приложения откройте его на телефоне. В случае счётчика нажатие кнопки и изменение числа проверяют именно то, о чём вы просили. В следующих запросах можно изменить компоновку или добавить ещё одно действие, а модель продолжит работать с существующими файлами и результатами предыдущих вызовов инструментов.
AirBuild3G хранит каждый проект и его переписку на телефоне, сохраняя их после каждого раунда работы с инструментом. Он также запоминает открытый проект, потому что выход из приложения или нехватка памяти могут завершить его процесс. Когда вы снова откроете AirBuild3G, он вернётся к этому проекту вместе с перепиской и файлами. Следующее сообщение продолжит сохранённый разговор, а исходный код останется на месте для дальнейшего редактирования и сборки.
На этом экране даже такой счётчик связывает приложения, которыми вы когда-то пользовались, с приложениями, которые теперь можете создавать.
Что-то новое на знакомом экране
iPhone 3G вышел в 2008 году, когда открылся App Store. Новое приложение могло превратить его в музыкальный инструмент, блокнот или игру, которую всегда можно было носить с собой. Кнопка «Домой» возвращала к сетке значков, каждый из которых приглашал попробовать что-то ещё.
Сегодня этот телефон даёт почувствовать, как давно был 2008 год. Экран не стал больше. Процессор не стал быстрее. Исходный интерфейс по-прежнему требует от приложений бережно использовать немногочисленные элементы управления и небольшое пространство. Работая в этих пределах, замечаешь детали, которые легко упустить на более новом устройстве: реагирует ли кнопка, плавно ли прокручивается переписка, вовремя ли появляется следующий экран.
AirBuild3G даёт этому знакомому экрану ещё одну задачу. Современная модель помогает написать приложение, а компилятор из эпохи телефона собирает его. Старые значки могут остаться на своих местах.
Рядом с ними найдётся место для приложения, которого раньше не существовало.