En 2008, el iPhone 3G y la primera App Store acercaron las apps móviles a muchas personas. Creamos AirBuild3G para darle a ese teléfono algo nuevo que hacer. En un iPhone 3G o 3GS, puedes describir una nueva app, trabajar en su código fuente con un modelo de programación, compilarla y ejecutarla en el mismo teléfono. El modelo se ejecuta en un servicio que tú eliges. Los archivos de tu proyecto y las herramientas de desarrollo permanecen en el teléfono.
AirBuild3G está creado con Xcode 27 en un Mac. Las apps que creas con él usan un compilador aparte, instalado en el teléfono. Para que ambos funcionaran tuvimos que hacer cambios en cuatro áreas: preparación del dispositivo, enlazado, HTTPS y la cadena de herramientas del propio teléfono. Las siguientes secciones recorren ese trabajo hasta llegar a tu primera app.
Preparar el hardware original
La pantalla pequeña y el botón de inicio físico hacen que el iPhone 3G sea reconocible al instante. Volver a desarrollar para él implica respetar sus proporciones y límites originales, incluidos los controles que daban a sus apps un aspecto familiar. AirBuild3G usa los controles de UIKit disponibles en iOS 4, como las tablas de ajustes agrupadas y las acciones de la barra de navegación.
AirBuild3G funciona en el iPhone 3G y 3GS. El 3G necesita binarios armv6; el 3GS usa armv7. El firmware determina qué paquete de entorno puede instalar cada teléfono. Nuestro dispositivo armv6 de referencia es un iPhone 3G con iOS 4.2.1 y firmware personalizado.
- Prepararrestaurar / activar
- ConectarSSH + red
- Instalarapp + paquete de entorno
- EntornoAjustes → Entorno
Empieza con un teléfono que arranque hasta la pantalla de inicio y acepte conexiones SSH desde tu Mac. Necesita una conexión de red y un gestor de paquetes que funcionen para recibir e instalar los paquetes Debian. El modelo de hardware y la versión del firmware determinan qué paquetes enviar, mientras que la dirección de red determina adónde enviarlos. También necesitas un cable USB fiable y alimentación eléctrica.
Algunos teléfonos antiguos necesitan pasos adicionales para la activación y el acceso al sistema. Si el hardware es compatible, usa Legacy iOS Kit y sigue las indicaciones para el modelo y el firmware de tu teléfono. Haz una copia de seguridad del teléfono antes de empezar. AirBuild3G no activa el teléfono.
AirBuild3G también necesita Substrate para admitir TLS. En un 3G, el build debe incluir armv6: un paquete que solo incluya armv7 no puede ejecutarse en él, sin importar su versión. Es posible que un teléfono preparado con Legacy iOS Kit ya tenga un build compatible. Si Substrate funciona, déjalo instalado.
Ajustes → Entorno sigue la misma regla. Añade herramientas de desarrollo y no modifica el gestor de paquetes, el servicio SSH ni otros componentes básicos que ya funcionan. Actualizar todos esos componentes puede instalar binarios que el sistema antiguo no puede ejecutar.
Desarrollar con Xcode 27
El compilador de Xcode 27 todavía genera código armv6 y armv7 y acepta iOS 4.0 como destino de despliegue. Xcode solo incluye el SDK de iOS 27, así que compilamos con los encabezados de un SDK antiguo. Pasa la ruta del SDK como LEGACY_SDK en la línea de comandos de make o expórtala. Los scripts que crean los paquetes de entorno también la leen, y un valor definido solo dentro del Makefile nunca les llega.
El enlazador es más estricto. Rechaza armv6 como destino de enlazado, no acepta ningún destino de despliegue anterior a iOS 4.3 e indica que las bibliotecas del SDK antiguo están creadas para una plataforma desconocida. Para esas bibliotecas, enlazamos con stubs generados y conservamos el SDK antiguo para los encabezados.
- Compiladoclang
- arq.
- armv6
- iOS mín.
- 4.0
- código
- e92d40f0 …
- Enlazadoreetiquetar, después ld
- arq.
armv6armv7- iOS mín.
4.05.0- código
- e92d40f0 …
- Distribuidovolver a etiquetar, aplicar parche
- arq.
armv7armv6- iOS mín.
5.04.0- código
- e92d40f0 …
El proceso de build compila ambas slices para iOS 4.0, las enlaza para iOS 5.0 y después vuelve a cambiar su versión mínima a 4.0. Para armv6, también vuelve a etiquetar cada archivo objeto como armv7 para que el enlazador lo acepte y, después, vuelve a etiquetar la slice enlazada como armv6. El reetiquetado cambia los metadatos, no las instrucciones. El código compilado y los stubs de símbolos que añade el enlazador ya deben poder ejecutarse en 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)- El cpusubtype de Mach-O para cada arquitectura: 6 es armv6 y 9 es armv7.
- Todo el reetiquetado se reduce a cuatro bytes en el offset 8, el campo cpusubtype del encabezado. Las instrucciones pasan sin cambios.
El proceso de build conserva -no_pie porque iOS no tenía ASLR antes de 4.3. También desactiva la combinación de categorías de Objective-C, que de otro modo eliminaría el bit Thumb de los métodos combinados y bloquearía AirBuild3G en un 3GS. make verify comprueba las arquitecturas, los comandos de carga, las instrucciones armv6 y el bit Thumb de los métodos armv7. Un enlazado correcto no demuestra nada de esto.
Ejecuta make para crear AirBuild3G y su helper con privilegios. make payload descarga y verifica cada paquete fijado en una caché local, y make package crea a partir de ella el paquete de AirBuild3G y los dos paquetes de entorno, sin conexión. El empaquetado también necesita LEGACY_SDK, porque cada paquete de entorno contiene un archivo del SDK para el teléfono.
make install-device DEVICE=root@<ip> instala AirBuild3G y el paquete de entorno de iOS 4, actualiza la pantalla de inicio y reinicia la app. Para un 3GS con iOS 5 o posterior, pasa ios6 como tercer argumento a tools/deploy_device.sh. La app se ejecuta como el usuario mobile, y un pequeño helper instalado junto a ella ejecuta todo lo que necesita root. El paquete de entorno ya está en el teléfono; Ajustes → Entorno instala las herramientas de desarrollo que contiene.
Conectar con un servicio de modelos
En Ajustes, introduces tres valores en Punto de conexión: URL base, Clave de API y Modelo. Las solicitudes van directamente del teléfono a ese punto de conexión. No ejecutamos ningún servidor intermedio y no hay cuentas ni registro de dispositivos. Tu servicio de modelos ejecuta el modelo, cobra por el uso y te indica qué debes introducir. Si nosotros te regalamos el teléfono, se conecta a air.build.
La versión original de iOS 4 solo admite TLS 1.0 mediante SecureTransport, y un punto de conexión actual puede requerir TLS 1.2. Añadimos TLS 1.2 con TLSFix, un tweak de Substrate para CFNetwork.
El teléfono también debe verificar la identidad del servidor. Su antiguo almacén de confianza no siempre puede construir la cadena de certificados que usa un punto de conexión actual, y añadir TLS 1.2 no actualiza ese almacén. Cuando falle una conexión, trata el cifrado y la confianza del certificado como problemas separados.
AirBuild3G valida los certificados de forma predeterminada. Si falla una solicitud, comprueba primero la URL base, la Clave de API, el Modelo y la cadena de certificados. El ajuste Confiar en cualquier certificado es una excepción explícita. Al activarlo, se elimina una comprobación de identidad del servidor y tu clave de API podría quedar expuesta a cualquiera que suplante el punto de conexión. Déjalo desactivado salvo que el punto de conexión esté en tu propia red. AirBuild3G lee el ajuste en cada evaluación de confianza, así que desactivarlo se aplica a la siguiente solicitud.
static OSStatus ABLSecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
…
OSStatus status = ABLOriginalSecTrustEvaluate(trust, result);
if (!ABLTrustAnyCertificate()) {
return status;
}
if (result != NULL) {
*result = kSecTrustResultUnspecified;
}
return errSecSuccess;
}- La evaluación del propio teléfono se ejecuta primero.
- Confiar en cualquier certificado está desactivado de forma predeterminada, así que esa respuesta se devuelve sin cambios. El ajuste se lee desde
NSUserDefaultsen cada llamada.
La transmisión de una respuesta también debe adaptarse al teléfono. AirBuild3G recibe y decodifica el flujo en un hilo en segundo plano y solo pasa a la interfaz el texto terminado. La transcripción se actualiza a intervalos en vez de volver a medirla y desplazarla con cada token, lo que reduce el trabajo de disposición mientras llega la respuesta. Una respuesta confirma que la conexión funciona. Para compilar lo que escribe el modelo, el teléfono todavía necesita el entorno.
Instalar un compilador que el teléfono pueda ejecutar
Ajustes → Entorno instala desde el paquete de entorno el compilador, los encabezados del SDK, las bibliotecas de enlace y las herramientas de firma. El paquete de iOS 4 incluye GCC para armv6; el paquete para iOS 5 y versiones posteriores incluye clang para armv7. Cada teléfono instala únicamente el que coincide con su firmware.
Cada paquete queda fijado mediante su dirección de descarga y suma de comprobación, y la instalación usa esos archivos exactos en lugar de pedir las versiones más recientes a un repositorio. Los paquetes que ya están instalados en la versión fijada se omiten, por lo que una segunda ejecución de Entorno termina de inmediato. Después, los paquetes instalados quedan retenidos para que Cydia deje de ofrecer actualizaciones que rompan la cadena de herramientas.
Para el iPhone 3G, el entorno usa el build de 2008 de iphone-gcc 4.2. El build de 2009 reserva un gigabyte para encabezados precompilados, lo que lleva parte de él más allá del espacio de direcciones de un teléfono armv6. Sus backends del compilador fallan antes de compilar nada. Ambos builds se identifican como Apple build 5555, pero solo el de 2008 cabe en ese espacio de direcciones.
0x300000002008 iphone-gcc · cada segmento por debajo de 6 MBse ejecuta
array de 512 MB · binario de pruebase ejecuta
array de 768 MB · binario de pruebaSIGBUS
2009 iphone-gcc · área de 1 GB para encabezados precompiladosSIGBUS
Los encabezados del SDK también necesitan cambios: este GCC no puede analizar sus declaraciones de bloques. Antes de empaquetar los encabezados de iOS 4, eliminamos esas declaraciones junto con todas las que dependen de un tipo eliminado. El paquete de clang conserva los encabezados de Apple sin cambios porque clang admite bloques. Cada paquete también incluye una herramienta de firma capaz de firmar lo que genera su compilador.
El teléfono mantiene los frameworks del sistema en la caché compartida de dyld, así que los directorios de los frameworks no contienen archivos de biblioteca que el enlazador pueda leer. Proporcionamos stubs generados que contienen los símbolos exportados y los nombres reales de las bibliotecas. El enlazador resuelve cada referencia con un stub y registra de qué biblioteca procede; al iniciarse, dyld la enlaza con la implementación real de la caché compartida. Como los stubs contienen nombres de símbolos reales, omitir un framework hace que falle el enlazado en lugar de bloquear la app al iniciarse.
El entorno también configura las rutas de inclusión, dirige gcc y cc al compilador instalado y exporta indicadores de enlace que incorporan el runtime de Objective-C y CoreFoundation. El Makefile incluido lee esos ajustes, así que un proyecto nuevo no necesita configurar su propia cadena de herramientas. A partir de entonces, tus apps se compilan en el propio teléfono.
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_CFLAGSyIOS_LDFLAGSproceden de/etc/profile.d/airbuild.sh, que instala el entorno, así ningún proyecto especifica la ruta de un SDK o compilador.- El binario también se firma en el teléfono. Si no está firmado, el sistema detiene su ejecución al iniciarse.
Crear y ejecutar tu primera app
Un proyecto nuevo parte de la plantilla de UIKit incluida. AirBuild3G la copia en el directorio del proyecto e incorpora sus archivos al contexto del modelo. El modelo parte de un proyecto que ya compila, así que tu primera solicitud puede describir la app en vez de pedir un Makefile.
Una buena primera app es un botón que incrementa un número en pantalla. Es lo bastante pequeña para leerla entera, pero permite probar la edición, la compilación, el empaquetado y la ejecución en el teléfono. En tu solicitud, indica iOS 4 como destino y pide al modelo que compile e instale la app. El código de la interfaz debe limitarse a las API que implementa el iOS del teléfono. Que una API aparezca declarada en los encabezados no significa que exista en tiempo de ejecución.
El modelo dispone de cuatro herramientas. Execute ejecuta comandos en el directorio del proyecto. EditFile escribe un archivo completo. PatchFile sustituye un pasaje exacto. CheckBuild ejecuta make con los ajustes del entorno cargados y devuelve la parte final de su salida. Los resultados de las herramientas vuelven al modelo, que puede seguir trabajando o corregir un cambio fallido. La inferencia permanece en tu servicio de modelos durante todo el proceso.
llamada a herramienta
resultado de herramienta
Execute EditFile PatchFile CheckBuildEl bucle se repite hasta que el modelo responde sin una llamada a herramienta, tocas Detener o se completan 16 rondas.
Una vez instalada la nueva app, ábrela en el teléfono. En el contador, tocar el botón y ver cómo cambia el número comprueba exactamente lo que pediste. Las solicitudes posteriores pueden cambiar la disposición o añadir otra interacción, y el modelo continúa a partir de los archivos y los resultados anteriores de las herramientas.
AirBuild3G conserva cada proyecto y su conversación en el teléfono y los guarda después de cada ronda de herramientas. También recuerda qué proyecto está abierto, porque salir de la app o quedarse con poca memoria puede finalizar su proceso. Cuando vuelves a abrir AirBuild3G, regresa a ese proyecto con su conversación y sus archivos. Tu siguiente mensaje continúa desde la conversación guardada y el código fuente sigue ahí para la siguiente edición y compilación.
En esta pantalla, incluso ese contador conecta las apps que usaste en otro tiempo con las que puedes crear ahora.
Algo nuevo en una pantalla conocida
El iPhone 3G llegó en 2008, justo cuando abrió la App Store. Una nueva app podía convertirlo en un instrumento, un cuaderno o un juego que llevabas a todas partes. El botón de inicio te devolvía a una cuadrícula de iconos, cada uno una invitación a probar algo distinto.
Al usar ese teléfono hoy, notas cuánto tiempo ha pasado desde 2008. La pantalla no ha crecido. El procesador no se ha vuelto más rápido. La interfaz original todavía exige que las apps aprovechen con cuidado unos pocos controles y un espacio reducido. Al trabajar dentro de esos límites, reparas en detalles que es fácil pasar por alto en un dispositivo más reciente: si un botón responde, si una conversación se desplaza con fluidez o si la siguiente pantalla aparece cuando debe.
AirBuild3G le da una tarea más a esa pantalla conocida. Un modelo actual te ayuda a escribir una app y un compilador de la época del teléfono la crea. Los iconos antiguos pueden quedarse donde están.
Junto a ellos, hay espacio para una app que antes no existía.