Cross Compilation, ou compilações entre e para diferentes arquitecturas, é quando um compilador produz um executável para correr num sistema com uma plataforma ou arquitectura diferente daquela presente no computador onde foi compilado. Um exemplo prático.
Exemplo de vida real
A Jane quer compilar o kernel para um dispositivo que corre embedded Linux. Vamos supor que este dispositivo é baseado em PowerPC (PPC). Ela descobre que este dispositivo já tem Linux instalado com várias aplicações. No entanto, quando a Jane tenta actualizar a versão do kernel compilando a ultima versão stable, ela repara que o dispositivo, apesar de eficaz para aquilo que foi pensado, é muito limitado em termos de recursos, e que algo intensivo em recursos com a compilação do kernel, iria demorar várias horas até estar completo.
Visto que ela tem uma máquina (baseada em AMD64) já a correr GNU/Linux e com grandes capacidades de processamento, ela decide compilar o kernel na sua estação de trabalho. Infelizmente, repara que o kernel foi compilado para a arquitectura do computador dela (AMD64), e que por isso não seria possível correr esse mesmo kernel no seu dispositivo (que é baseado em PPC).
Para resolver este problema é necessário um Cross Compiler - Algo que corra na arquitectura A mas que compile para a arquitectura B.
Eu normalmente faço-o para compilar o kernel para o meu NAS da Linksys. O NSLU2, que utiliza a arquitectura ARM.
Para compilar o kernel, é necessário o GCC e o binutils. O GCC é um software inteligente, que é capaz de compilar software numa vasta variedade de arquitecturas, no entanto, é necessário que este seja compilado com essas instruções (para suportar CrossCompilation).
O binutils é uma suite de aplicações presentes numa meta-package chamado de binutils. Exemplo de algumas aplicações:
[om@turyxsrv ~/work]$ rpm -qf /usr/bin/as binutils-2.16.91.0.6-4 [om@turyxsrv ~/work]$ rpm -ql binutils-2.16.91.0.6-4 ... /usr/bin/ar /usr/bin/as /usr/bin/c++filt /usr/bin/gprof /usr/bin/ld /usr/bin/nm /usr/bin/objcopy /usr/bin/objdump /usr/bin/ranlib /usr/bin/readelf /usr/bin/size ...
Caso o seu sistema seja baseado em Debian (como por exemplo o Ubuntu), o seguinte comando vai-lhe informar de quais os ficheiros pertencentes a este pacote:
dpkg -L binutils
Existem vários na Internet, includindo o ELDK (DENX's Embedded Linux Development Kit). Está disponível para várias plataformas, como o PPC, ARM, etc.
Crosstool é um conjunto de scripts que nos ajudam a construir um cross compiler de raíz. Estes scripts também produzem uma correspondente glibc, que nos permite compilar ferramentas de utilizador (user land tools) da mesma forma que kernel.
O GCC é capaz de gerar código tanto para 32 como para 64 bit (exemplo: AMD64 e IA32 ou PPC ou PPC64), usando uma opção (-m32/-m64).
Quando você faz participa no desenvolvimento do kernel, é boa prática compilar os seus patches tanto para 32 como para 64 bit. Esta forma de compilação (cross compilation) também nos pode ajudar nesta altura. Em vez de tentar alterar o makefile do kernel para produzir output de 32 bit usando um compilador de 64 bit (ou vice versa), devemos utilizar esta técnica para compilar o kernel com output tanto para 32 como para 64 bit na mesma plataforma.
Nota:(Funciona no Ubuntu 8.04.1 AMD64) Para compilar um kernel de 32 bit para x86 numa máquina x86_64, corra a aplicação i386 (que é um link para /usr/bin/setarch), e depois continue com a compilação normalmente (make config; make).
Uma vez que já temos o nosso cross compiler a funcionar, podemos testar num pequeno e simples hello.c, para garantir que a compilação funciona.
[om@turyxsrv ~/prg/tmp]$ export cross=i686-unknown-linux-gnu- [om@turyxsrv ~/prg/tmp]$ echo $cross i686-unknown-linux-gnu- [om@turyxsrv ~/prg/tmp]$ ${cross}gcc mathtest.c -o math [om@turyxsrv ~/prg/tmp]$ file math math: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.4.3, dynamically linked (uses shared libs), for GNU/Linux 2.4.3, not stripped
Como funcionou, podemos passar a algo maior, como o kernel:
[om@turyxsrv ~]$ cd src/i386-linux-2.6 [om@turyxsrv /home/src/i386-linux-2.6]$ uname -a Linux turyxsrv 2.6.18-rc3 #3 SMP Sun Jul 30 11:54:32 PDT 2006 x86_64 x86_64 x86_64 GNU/Linux [om@turyxsrv /home/src/i386-linux-2.6]$ make CROSS_COMPILE=i686-unknown-linux-gnu- ARCH=i386 xconfig [om@turyxsrv /home/src/i386-linux-2.6]$ make CROSS_COMPILE=i686-unknown-linux-gnu- ARCH=i386 V=1 -j4 all
Utilizei V=1 para fazer com que a compilação desse mais output (mais verbose) e -j4 para utilizar os 4 CPU do computador.
Depois da compilação, o make deu esta mensagem:
Kernel: arch/i386/boot/bzImage is ready (#1)
Se quiser ainda mais garantias de que a compilação correu com sucesso:
[om@turyxsrv /home/src/i386-linux-2.6]$ file vmlinux vmlinux: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
Compilei em AMD64 um kernel para correr numa máquina i386.
Caso queira compilar módulos para a máquina de destino (neste caso era a máquina i386), é recomendado utilizar o seguinte comando para garantir que depois pode copiar os módulos para o computador de destino:
[om@turyxsrv /home/src/i386-linux-2.6]$ make INSTALL_MOD_PATH=<WhereTheModulesShallGo> modules_install
O kernel permite-nos gerar ficheiros pré-processados. É util quando suspeitamos que há algo de errado com os macros. Nos dias do 2.4, podíamos utilizar a opção -c para redireccionar o resultado do gcc preprocessor para um ficheiro. No 2.6, é algo que faz parte do kernel.
Se quisesse gerar para o kernel/dma.c:
[om@kartel /space/kernel.org/linux-exp]$ make kernel/dma.i CHK include/linux/version.h CHK include/linux/utsrelease.h CALL scripts/checksyscalls.sh CPP kernel/dma.i
Abra kernel/dma.i para ver o que o preprocessor fez ao dma.c.
Isto está disponível para um módulo (não faz parte do kernel):
[root@hydra1 linuxdriver]# make -C /lib/modules/2.6.18-92.el5/build/ M=$(pwd)/src hxge_main.i make: Entering directory `/usr/src/kernels/2.6.18-92.el5-x86_64' CPP [M] /root/hydra-src/linuxdriver/src/hxge_main.i make: Leaving directory `/usr/src/kernels/2.6.18-92.el5-x86_64'