O que é compilação cruzada? Deixe um comentário

Você sabe o que é compilação cruzada (do inglês, cross compile)? Você já “cross compilou” alguma coisa? Não? E se eu te dizer que se você já realizou o upload de algum programa pela IDE do Arduino em sua placa você já fez uma “cross compilação”?

Compilação Cruzada – Cross Compile

Na área de sistemas embarcados, tanto micro-processados como micro-controlados, geralmente temos um ambiente de desenvolvimento que está em uma arquitetura diferente da arquitetura da placa em que vamos rodar nosso programa, como nossos notebooks ou desktops, que geralmente possuem processadores Intel ou AMD, arquitetura x86. Em contrapartida a Raspberry Pi é controlada por um processador com arquitetura ARM. O Arduino é controlado por um microcontrolador de arquitetura AVR e assim por diante. Para que nossos notebooks ou desktops Intel possam gerar um binário, programa que rode em outra arquitetura, acontece o “Cross Compile” (Compilação Cruzada da tradução direta do inglês). Toda vez que você realiza o upload de um Skecth pela Arduino IDE acontece uma compilação cruzada.

Demonstração Prática de Compilação Cruzada

Vamos realizar alguns testes práticos para entender melhor o problema e como a compilação cruzada o resolve. Note que para os testes eu estou utilizando uma distribuição Linux Debian rodando em um computador com arquitetura x86_64 Intel.

Vamos começar com o um “Hello World” bem simples escrito em C o hello.c:

#include 

int main()
{
	printf("Hello FilipeFlop\n");
	return 0;
}

Vamos compilar esse programa normalmente:

Agora podemos executar o binário compilado:

Até agora tudo certo, nada de novo compilação e execução normais de um programa em C.

Ok, vamos executar esse mesmo programa então em uma Raspberry Pi? Aqui eu já tenho o SSH da minha Raspberry Pi 3B habilitados e rodando, então vou copiar o binário compilado para a Raspberry Pi e executar o nosso programa via SSH:

Ops, algo deu errado. Como podemos ver no detalhe acima ao tentar executar o programa o sistema da Raspberry Pi vai retornar o erro “Exec format error”. Mas por que? O que está errado com nosso programa? Na verdade nada, só que o processador da Raspberry Pi não entende o formato compilado x86_64.

Para ficar mais claro vamos usar uma analogia. Pense em duas pessoas, uma é brasileira, ou seja sua língua nativa é o português, e uma é norte americana que normalmente fala inglês, lê e entende essa língua nativamente. Agora pense que eu tenho um livro escrito em grego, é em grego pra dificultar as coisas mesmo, e eu traduzo esse livro para português e entrego essa tradução para o brasileiro e pro norte americano. O brasileiro vai conseguir ler a tradução normalmente sem problemas, já o nosso amigo norte americano vai continuar não entendendo nada do que está escrito em português.

O mesmo aconteceu no nosso exemplo acima em que tentamos rodar o binário na Raspberry Pi. Eu tenho um programa escrito em C, mas meu processador não entende C. Então eu tenho que “traduzir”, ou seja, compilar, esse programa em instruções que o meu processador entenda. Quando eu faço isso no meu computador Intel ele compila instruções que processadores Intel entendam. Agora se eu tentar rodar essas mesmas instruções na Raspberry Pi, que tem um processador ARM, ele não vai entender e por consequência retornar com “Exec format error”.

É isso que a compilação cruzada resolve, nos vamos ter um compilador que entende as instruções Intel para gerar instruções ARM, o chamado compilador cruzado (do inglês cross compiler). Então primeiramente precisamos de um compilador cruzado. No caso para a Raspberry Pi, arquitetura ARM, vamos ter que instalar o seguinte no nosso computador:

sudo apt install gcc-arm-linux-gnueabihf

Com esse compilador cruzado, gcc para ARM, podemos compilar um binário com instruções que um processador ARM entenda:

Agora vamos copiar esse novo binário para a Raspberry Pi e executar:

Sucesso, agora nossa Raspberry Pi entende as instruções do programa.

Bonus Round – Instructions Set

Lembra que disse que o processador não entende linguagem C, e então temos que compilar esse programa para instruções que o processador entenda? Geralmente não precisamos nem saber que estas instruções existem porque o compilador traduz a linguagem C para instruções de máquina automaticamente.

No Debian, e em distribuições Debian like que tenham o apt, você pode instalar compiladores cruzados para várias arquiteturas.

Para fins didáticos, e pra você digníssimo leitor desse blog acreditar nesse pobre articulista, podemos ver essas instruções e como o compilador cruzado gera instruções diferentes para um mesmo programa escrito em C. Para isso vamos utilizar nosso compilador, e cross compiladores, com um argumento que irá gerar o código assembly, traduzido, do código C.

Gerando código assembly intel x86_64 (geralmente arquitetura dos nossos computadores desktop e notebooks):

gcc -o hellox86.s hello.c -S

Para ARMv7 (geralmente a arquitetura de sistemas embarcados Linux e celulares Android):

arm-linux-gnueabihf-gcc -o helloArm.s hello.c -S

Para AVR (geralmente a arquitetura dos microcontroladores utilizados no Arduino):

avr-gcc -o helloAvr.s hello.c -S

O argumento -S que faz a mágica, diz ao compilador que ao invés de gerar um binário compilado eu quero como output o assembly gerado da “tradução” do arquivo hello.c

Note na imagem abaixo os três arquivos assembly para cada arquitetura abertos, gerada do mesmo hello.c:

Detalhe que na imagem eu posicionei na linha aonde está a definição da label main:. Note como a quantidade de instruções e conteúdo do código de máquina gerado é totalmente diferente. Cada arquitetura de processador, microcontrolador tem seus próprios padrões de registradores e instruções de operação.

Conclusão

Para o desenvolvimento de aplicações embarcadas, a utilização da compilação cruzada é uma técnica bem comum. Como dito no começo do artigo, geralmente sempre temos um ambiente de desenvolvimento que está em uma arquitetura diferente da arquitetura da placa em que vamos rodar nosso programa. Mesmo que por de baixo dos panos, no caso do Arduino IDE que utiliza o avr-gcc para realizar a compilação cruzada para o ATMega328P do Arduino UNO.

O material desse artigo também está disponível em vídeo, no canal do Youtube do nosso Maker Hero Matheus Castello:

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

{"cart_token":"","hash":"","cart_data":""}