Cross-compiling fácil fácil

Postado por Osvaldo Santana

Como eu já contei no post anterior no meu novo trabalho a gente tem que lidar com cross-compiling (compilação cruzada) o tempo todo. A idéia da compilação cruzada é simples: você compila um programa P em uma plataforma A e o binário produzido deverá rodar em uma plataforma B.

O conceito é simples, o seu funcionamento na teoria também. Para compilar um típico programa Linux em um computador x86 para rodar na plataforma ARM bastaria ter o toolchain, que é o conjunto de ferramentas que engloba o binutils (onde fica o linker) o gcc (onde fica o compilador C/C++) e em algumas bibliotecas básicas já como binários ARM (a libc é uma delas).

O problema do ovo e da galinha dificulta um pouco a construção de um toolchain (você precisa compilar o compilador) mas não é incomum que esses toolchains já sejam distribuídos com a plataforma ‘alvo’, logo, esse problema não é muito grande.

Com cross-toolchain já instalado a teoria diz que bastariam os seguintes comandos (assumindo que a nossa plataforma alvo seja ARM) para compilar um programa:

$ ./configure --host=arm-linux
$ make
$ make install

Com algumas pequenas variações disso conseguiríamos fazer a compilação cruzada de ‘todo o Linux’, mas na prática a teoria não funciona… :)

O que acontece é que um grande percentual das aplicações (regra e não exceção) simplesmente ignora o fato de que no futuro elas serão submetidas à compilação cruzada e simplesmente não funcionam nessas circunstâncias.

O Python é um desses programas. O interpretador compila perfeitamente, mas as extensões em C da biblioteca padrão não. O problema é que o Python usa o módulo distutils para executar tal tarefa e o mesmo é feito em Python. Neste caso precisamos de um Python ARM para executar a segunda fase do processo de build. Como executar um Python ARM em uma máquina x86?

Uma das maneiras é compilar primeiro um interpretador Python x86, renomeá-lo para algo como ‘hostpython‘, depois compilar um interpretador Python para ARM e aplicar uns patches no Makefile.in do Python para que ele chame o ‘hostpython‘ para compilar as extensões C. Mas os efeitos colaterais dessa solução são enormes porque existem extensões que usam bibliotecas do sistema (OpenSSL, Socket, SQLite, …) e o distutils não irá procurá-las no lugar correto pois não sabe o que é compilação cruzada.

Aí então entra uma técnica de transparência de CPU que aprendi a fazer com a turma do projeto Scratchbox (aperfeiçoada pela turma do projeto Mamona) que é bem simples e permite fazer compilação cruzada sem modificar nada nas aplicações que estão sendo compiladas.

A idéia é usar o binfmt do Linux para dizer que todos os binários ARM deverão ser executados pelo qemu (pegadinha 1: esse qemu deve ser estático e estar instalado dentro do chroot no path definido nas configurações do binfmt) e criar um ambiente chroot com tais binários.

Dentro desse ambiente todos os binários ARM rodam com o qemu e todos os binários x86 rodam nativamente na sua plataforma (assumindo que ela é x86) sem que você sequer note a diferença entre o funcionamento deles. Desta forma podemos colocar então o nosso cross-toolchain dentro desse chroot fingindo ser um toolchain nativo ARM (tem uma pegadinha aqui: esse toolchain precisa ser estático e não dinâmico pois as bibliotecas nesse nosso ambiente são ARM e não mais x86).

Você está me perguntando “porque não compilar um toolchain nativo pra rodar dentro desse chroot“? Só por questão de velocidade. O gcc rodaria muito devagar sendo emulado pelo qemu.

Agora é só sair compilando os programas normalmente. Mesmo aqueles que não estão preparados para compilação cruzada:

$ ./configure
$ make
$ make install

Neste exato momento estou compilando o SQLite3 (após ter terminado o OpenSSL) dentro do ambiente chroot com binários ARM (XScale) que rodam emulados pelo qemu. Tudo isso para que no final eu tenha um Python com Pygame 100% funcional.

Ok. Agora é a hora das notícias ruins:

  • O qemu não emula 100% das syscalls, logo, você poderá esbarrar com uma das famigeradas mensagens “Unsuported syscall XX“. Nestes casos verifique se já não existe um patch que implementa o suporte à essa syscall no qemu e recompile-o (lembrando que o qemu precisa ser estático).
  • O qemu não lida muito bem com threads, logo, se os scripts de build do seu programa testam threads eles podem quebrar (ou bloquear). Neste caso a sugestão é: retire esses testes dos scripts e assuma que sua plataforma tem sim (ou não) suporte à threads.
  • Lembre-se sempre que o kernel não é emulado pelo qemu! Se o seu programa usa ferramentas como o uname, por exemplo, ele irá retornar informações da sua plataforma nativa e não da plataforma emulada. Desenvolvimento para o kernel também não é muito viável.
  • Pode acontecer da tua aplicação quebrar ’silenciosamente’. Lembre-se que pode ser o qemu quebrando e ocultando a real causa do problema. Nesses casos utilize as funções de depuração do qemu.
  • Lembre-se de ter o /proc e o /sys montados dentro do seu chroot. Alguns programas usam as informações disponíveis nesse lugar para a sua construção. Lembre-se também que esse /proc e /sys são da sua plataforma nativa e não da plataforma emulada, logo, eles poderão fornecer informações incorretas.
  • Dica: tenha a sua área de trabalho ($HOME) montada via mount –bind dentro de seu chroot para que você possa ter vários chroots compartilhando o mesmo $HOME.

Mais notícias do front

Postado por Osvaldo Santana

Como todos já devem saber eu não estou trabalhando mais no INdT. O que poucos devem saber é que já estou trabalhando em para outra empresa.

Quando saí do INdT o meu plano era o de encontrar um trabalho que não exigisse muito tempo para que eu pudesse levar adiante o desenvolvimento de um projeto pessoal que “me persegue” a muito tempo.

Queria também que fosse possível trabalhar em casa pois queria ver se eu conseguiria trabalhar direito nessas condições. Se teria a disciplina necessária para isso.

Esse novo trabalho me proporcionou tudo isso exceto pelo meio-expediente que ainda não foi possível colocar em prática. Espero que assim que terminar uma das tarefas grandes em que estou trabalhando o ritmo caia pela metade.

Tenho gostado bastante de trabalhar em casa e sinto falta apenas da famosa ‘pausa para o café/bate-papo com os amigos’. Quando essa vontade aperta eu dou um pulinho lá na Haxent e trabalho lá com eles (eles me emprestam uma mesa e conexão com a Internet ‘di grátis’.

Esse trabalho novo é muito semelhante ao que eu tinha no INdT e envolve muito cross-compiling, ambientes emulados e Linux igual no INdT.

Estou trabalhando no meu Mac que ainda está com o Leopard instalado (não cometi a heresia de instalar Linux/Windows num Macbook). Como eu virei usuário Mac por gosto e uso Linux por profissão comprei o VMWare Fusion que está rodando uma imagem com o Ubuntu 8.04. Ferramenta fantástica que valeu cada um dos dólares gastos.

Para concluir as notícias vou colocar aqui uma foto do meu escritório no INdT…

Meu escritório antigo…

…e uma foto do meu escritório atual…

Meu escritório novo…

…. Estou melhor ou não estou? :)