Em 2008, o iPhone 3G e a primeira App Store apresentaram os apps móveis a muitas pessoas. Criamos o AirBuild3G para dar àquele telefone algo novo para fazer. Em um iPhone 3G ou 3GS, você pode descrever um novo app, trabalhar no código-fonte com um modelo de programação e compilá-lo e executá-lo no mesmo telefone. O modelo roda em um serviço que você escolhe. Os arquivos do projeto e as ferramentas de desenvolvimento permanecem no telefone.
O próprio AirBuild3G é criado com o Xcode 27 em um Mac. Os apps que você cria com ele usam um compilador separado, instalado no telefone. Fazer ambos funcionarem exigiu mudanças em quatro áreas: preparação do dispositivo, vinculação, HTTPS e o conjunto de ferramentas do próprio telefone. As seções abaixo acompanham esse trabalho até seu primeiro app.
Preparação do hardware original
A tela pequena e o botão de Início físico tornam o iPhone 3G imediatamente reconhecível. Voltar a desenvolver para ele significa respeitar suas proporções e limitações originais, até nos controles que faziam seus apps parecerem familiares. O próprio AirBuild3G usa os controles do UIKit disponíveis no iOS 4, incluindo tabelas de ajustes agrupadas e ações na barra de navegação.
O AirBuild3G roda no iPhone 3G e 3GS. O 3G exige binários armv6; o 3GS usa armv7. O firmware determina qual pacote de ambiente cada telefone pode instalar. Nosso dispositivo armv6 de referência é um iPhone 3G com iOS 4.2.1 e firmware personalizado.
- Prepararrestaurar / ativar
- ConectarSSH + rede
- Instalarapp + pacote de ambiente
- AmbienteAjustes → Ambiente
Comece com um telefone que inicialize na Tela de Início e aceite conexões SSH do seu Mac. Ele precisa de uma conexão de rede e de um gerenciador de pacotes que funcionem para receber e instalar os pacotes Debian. O modelo de hardware e a versão do firmware determinam quais pacotes enviar, e o endereço de rede determina para onde enviá-los. Você também precisa de um cabo USB confiável e energia.
Alguns telefones mais antigos precisam de etapas adicionais para ativação e acesso ao sistema. Para hardware compatível, use o Legacy iOS Kit e siga as orientações para o modelo e o firmware do seu telefone. Faça backup do telefone antes de começar. O próprio AirBuild3G não ativa o telefone.
O AirBuild3G também precisa do Substrate para oferecer suporte a TLS. Em um 3G, o build deve incluir armv6: um pacote somente para armv7 não pode rodar nele, independentemente da versão. Um telefone preparado com o Legacy iOS Kit talvez já tenha um build compatível. Se o Substrate funcionar, deixe-o instalado.
Ajustes → Ambiente segue a mesma regra. Ele adiciona ferramentas de desenvolvimento e não altera um gerenciador de pacotes, serviço SSH e outros componentes básicos que estejam funcionando. Atualizar esses componentes em massa pode instalar binários que o sistema antigo não consegue executar.
Desenvolvimento com o Xcode 27
O compilador do Xcode 27 ainda gera código armv6 e armv7 e aceita um destino de implantação iOS 4.0. O próprio Xcode inclui apenas o SDK do iOS 27, então compilamos usando os cabeçalhos de um SDK legado. Passe o caminho do SDK como LEGACY_SDK na linha de comando do make ou exporte-o. Os scripts que criam os pacotes de ambiente também leem esse valor, e um valor definido apenas dentro do Makefile nunca chega até eles.
O vinculador é mais rigoroso. Ele rejeita armv6 como destino de vinculação, recusa qualquer destino de implantação anterior ao iOS 4.3 e informa que as bibliotecas do SDK legado foram criadas para uma plataforma desconhecida. Para essas bibliotecas, vinculamos stubs gerados e mantemos o SDK legado para os cabeçalhos.
- Compiladoclang
- arquitetura
- armv6
- iOS mín.
- 4.0
- código
- e92d40f0 …
- Vinculadorenomear, depois ld
- arquitetura
armv6armv7- iOS mín.
4.05.0- código
- e92d40f0 …
- Distribuídorenomear de volta, aplicar patch
- arquitetura
armv7armv6- iOS mín.
5.04.0- código
- e92d40f0 …
O build compila as duas fatias para iOS 4.0, vincula-as para iOS 5.0 e depois corrige a versão mínima de volta para 4.0. Para armv6, ele também renomeia cada arquivo objeto como armv7 para que o vinculador os aceite e, em seguida, renomeia a fatia vinculada de volta para armv6. A renomeação altera os metadados, não as instruções. O código compilado e os stubs de símbolos que o vinculador adiciona já precisam rodar em 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)- O cpusubtype do Mach-O para cada arquitetura: 6 é armv6, 9 é armv7.
- A renomeação inteira: quatro bytes no deslocamento 8, o campo cpusubtype do cabeçalho. As instruções passam sem alterações.
O build mantém -no_pie porque o iOS não tinha ASLR antes da versão 4.3. Ele também desativa a mesclagem de categorias do Objective-C, que removeria o bit Thumb dos métodos mesclados e faria o AirBuild3G falhar em um 3GS. make verify verifica as arquiteturas, os comandos de carregamento, as instruções armv6 e o bit Thumb nos métodos armv7. Uma vinculação bem-sucedida não comprova nada disso.
Execute make para criar o AirBuild3G e seu auxiliar privilegiado. make payload baixa e verifica cada pacote fixado em um cache local, e make package cria, offline, o pacote do AirBuild3G e os dois pacotes de ambiente a partir dele. O empacotamento também precisa de LEGACY_SDK, pois cada pacote de ambiente contém um arquivo do SDK para o telefone.
make install-device DEVICE=root@<ip> instala o AirBuild3G e o pacote de ambiente do iOS 4, atualiza a Tela de Início e reinicia o app. Para um 3GS com iOS 5 ou posterior, passe ios6 como o terceiro argumento para tools/deploy_device.sh. O app roda como o usuário mobile, e um pequeno auxiliar ao lado dele executa tudo que precisa de root. O pacote de ambiente agora está no telefone; Ajustes → Ambiente instala as ferramentas de desenvolvimento que ele contém.
Conexão com um serviço de modelos
Em Ajustes, você insere três valores em Endpoint: URL base, chave de API e modelo. As solicitações vão diretamente do telefone para esse endpoint. Não executamos nenhum servidor intermediário, e não há cadastro de conta nem de dispositivo. Seu serviço de modelos executa o modelo, cobra pelo uso e informa o que você deve inserir. Se o telefone foi um presente nosso, ele se conecta ao air.build.
O iOS 4 original se comunica apenas por TLS 1.0 através do SecureTransport, e um endpoint atual pode exigir TLS 1.2. Adicionamos TLS 1.2 com o TLSFix, um ajuste do Substrate para CFNetwork.
O telefone ainda precisa verificar a identidade do servidor. Seu repositório de confiança antigo nem sempre consegue montar a cadeia de certificados usada por um endpoint atual, e adicionar TLS 1.2 não atualiza esse repositório. Quando uma conexão falhar, trate a criptografia e a confiança no certificado como problemas separados.
O AirBuild3G valida certificados por padrão. Se uma solicitação falhar, verifique primeiro a URL base, a chave de API, o modelo e a cadeia de certificados. O ajuste Confiar em Qualquer Certificado é uma exceção explícita. Ativá-lo remove uma verificação da identidade do servidor e pode expor sua chave de API a qualquer pessoa capaz de se passar pelo endpoint. Mantenha-o desativado, a menos que o endpoint esteja na sua própria rede. O AirBuild3G lê o ajuste em cada avaliação de confiança, então desativá-lo passa a valer na próxima solicitação.
static OSStatus ABLSecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
…
OSStatus status = ABLOriginalSecTrustEvaluate(trust, result);
if (!ABLTrustAnyCertificate()) {
return status;
}
if (result != NULL) {
*result = kSecTrustResultUnspecified;
}
return errSecSuccess;
}- A avaliação do próprio telefone é executada primeiro.
- Confiar em Qualquer Certificado fica desativado por padrão, então essa resposta retorna sem alterações. O ajuste é lido de
NSUserDefaultsem cada chamada.
A transmissão de uma resposta também precisa caber no telefone. O AirBuild3G recebe e decodifica o fluxo em uma thread em segundo plano e envia apenas o texto concluído para a interface. A transcrição é atualizada em intervalos, em vez de ser medida e rolada novamente a cada token, o que reduz o trabalho de layout enquanto a resposta chega. Uma resposta mostra que a conexão funciona. Compilar o que o modelo escreve ainda exige que o ambiente esteja no telefone.
Instalação de um compilador que o telefone consegue executar
Ajustes → Ambiente instala o compilador, os cabeçalhos do SDK, as bibliotecas de vinculação e as ferramentas de assinatura a partir do pacote de ambiente. O pacote do iOS 4 contém o GCC para armv6; o pacote para iOS 5 e posterior contém o clang para armv7. Cada telefone instala apenas aquele que corresponde ao seu firmware.
Cada pacote é fixado pelo endereço de download e pelo checksum, e a instalação usa esses arquivos exatos, em vez de pedir as versões mais recentes a um repositório. Os pacotes já instalados na versão fixada são ignorados, então uma segunda execução de Ambiente termina imediatamente. Em seguida, os pacotes instalados são retidos, para que o Cydia pare de oferecer atualizações que quebram o conjunto de ferramentas.
Para o iPhone 3G, o ambiente usa o build de 2008 do iphone-gcc 4.2. O build de 2009 reserva um gigabyte para cabeçalhos pré-compilados, o que empurra parte dele para além do espaço de endereçamento de um telefone armv6. Seus backends de compilação falham antes de compilar qualquer coisa. Ambos os builds se identificam como Apple build 5555, mas apenas o de 2008 cabe nesse espaço de endereçamento.
0x30000000iphone-gcc de 2008 · cada segmento abaixo de 6 MBroda
array de 512 MB · binário de testeroda
array de 768 MB · binário de testeSIGBUS
iphone-gcc de 2009 · área de 1 GB para cabeçalhos pré-compiladosSIGBUS
Os cabeçalhos do SDK também precisam de alterações: esse GCC não consegue analisar suas declarações de blocos. Antes de empacotar os cabeçalhos do iOS 4, removemos essas declarações, junto com todas as declarações que dependem de um tipo removido. O pacote do clang mantém os cabeçalhos da Apple inalterados, porque o clang oferece suporte a blocos. Cada pacote também inclui uma ferramenta de assinatura capaz de assinar o que seu compilador produz.
O telefone mantém seus frameworks de sistema no cache compartilhado do dyld, portanto os diretórios de frameworks não contêm arquivos de biblioteca para o vinculador ler. Fornecemos stubs gerados que contêm símbolos exportados e nomes reais de bibliotecas. O vinculador resolve cada referência com um stub e registra de qual biblioteca ela veio; na inicialização, o dyld a associa à implementação real no cache compartilhado. Como os stubs contêm nomes reais de símbolos, omitir um framework faz a vinculação falhar, em vez de fazer o app falhar ao iniciar.
O ambiente também define os caminhos de inclusão, aponta gcc e cc para o compilador instalado e exporta sinalizadores de vinculação que incluem o runtime do Objective-C e o CoreFoundation. O Makefile incluído lê esses ajustes, então um novo projeto não precisa configurar seu próprio conjunto de ferramentas. A partir daí, seus apps são criados no próprio telefone.
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_CFLAGSeIOS_LDFLAGSvêm de/etc/profile.d/airbuild.sh, que o ambiente instala, então nenhum projeto especifica um SDK ou caminho de compilador.- O binário também é assinado no telefone. Sem assinatura, ele é encerrado na inicialização.
Criação e execução do seu primeiro app
Um novo projeto começa com o modelo do UIKit incluído. O AirBuild3G o copia para o diretório do projeto e coloca seus arquivos no contexto do modelo. O modelo parte de um projeto que já compila, então sua primeira solicitação pode descrever o app, em vez de pedir um Makefile.
Um bom primeiro app é um botão que incrementa um número na tela. Ele é pequeno o suficiente para ser lido por completo, mas ainda testa a edição, a compilação, o empacotamento e a execução no telefone. Na solicitação, defina o iOS 4 como destino e peça ao modelo para criar e instalar o app. O código da interface deve se limitar às APIs implementadas pelo iOS do telefone. Uma declaração nos cabeçalhos não significa que a API exista em tempo de execução.
O modelo tem quatro ferramentas. Execute executa comandos no diretório do projeto. EditFile grava um arquivo completo. PatchFile substitui uma passagem exata. CheckBuild executa make com os ajustes do ambiente carregados e retorna o final da saída. Os resultados das ferramentas retornam ao modelo, que pode continuar trabalhando ou revisar uma alteração que falhou. A inferência permanece no seu serviço de modelos o tempo todo.
chamada de ferramenta
resultado da ferramenta
Execute EditFile PatchFile CheckBuildO ciclo se repete até que o modelo responda sem uma chamada de ferramenta, você toque em Parar ou sejam executadas 16 rodadas.
Quando o novo app estiver instalado, abra-o no telefone. No contador, tocar no botão e ver o número mudar verifica exatamente o que você pediu. Solicitações posteriores podem mudar o layout ou adicionar outra interação, e o modelo continua a partir dos arquivos existentes e dos resultados anteriores das ferramentas.
O AirBuild3G mantém cada projeto e sua conversa no telefone, salvando-os após cada rodada de ferramenta. Ele também lembra qual projeto está aberto, pois sair do app ou ficar com pouca memória pode encerrar seu processo. Quando você abre o AirBuild3G novamente, ele retorna a esse projeto, com a conversa e os arquivos. Sua próxima mensagem continua a partir da conversa salva, e o código-fonte ainda está lá para a próxima edição e compilação.
Nesta tela, até mesmo esse contador conecta os apps que você usava aos que agora pode criar.
Algo novo em uma tela familiar
O iPhone 3G chegou em 2008, justamente quando a App Store abriu. Um novo app podia transformá-lo em um instrumento, um caderno ou um jogo que você levava para todo lugar. O botão de Início levava você de volta a uma grade de ícones, cada um um convite para experimentar algo diferente.
Ao usar esse telefone hoje, você percebe há quanto tempo foi 2008. A tela não cresceu. O processador não ficou mais rápido. A interface original ainda exige que os apps usem com cuidado poucos controles e pouco espaço. Ao trabalhar dentro desses limites, você nota detalhes que são fáceis de ignorar em um dispositivo mais novo: se um botão responde, se uma conversa rola sem travar, se a próxima tela aparece quando deveria.
O AirBuild3G dá mais uma função a essa tela familiar. Um modelo atual ajuda você a escrever um app, e um compilador da época do próprio telefone o cria. Os ícones antigos podem permanecer onde estão.
Ao lado deles, há espaço para um app que não existia antes.