Introdução a cross-compiling com a Raspberry Pi

Devido à contínua evolução tecnológica e acessibilidade a hardware com cada vez mais "poder de fogo", os sistemas embarcados estão cada vez mais contando com sistemas operacionais completos. Destes, o Linux embarcado (e suas mais diversas distribuições) se destaca em popularidade e aceitação. Um dos grandes motivos para isso é a capacidade de rodar muito bem em hardwares baratos, com poucos recursos computacionais, o que implica em redução de custo de projeto final.

 

Dado este cenário, há uma situação delicada: devido à restrição de recursos computacionais, o desenvolvimento (principalmente a compilação) de projetos grandes nos próprios sistemas embarcados é inviável, ou até mesmo impossível. Neste caso, como é feito o desenvolvimento e compilação de projetos para estes sistemas? É exatamente isso que este artigo irá mostrar.

 

Primeiro, algumas definições

 

Antes de prosseguir com o artigo, é necessário deixar claro as seguintes definições:

  • Host: máquina que compilará um código-fonte/projeto;
  • Target: máquina (ou máquinas) que executará o projeto compilado pelo host.

 

Compilação de um projeto - modalidades

 

A compilação de qualquer código-fonte / projeto pode ser dividida em duas modalidades:

  1. Compilação nativa: é a modalidade de compilação onde o host é da mesma arquitetura (utiliza mesmo processador ou linha de processadores) do(s) target(s);
  2. Compilação cruzada (ou cross-compiling): nesta modalidade, o host não é da mesma arquitetura do(s) target(s).

 

Na modalidade de compilação nativa, intuitivamente nota-se que não há segredos. Como as arquiteturas do host e do(s) target(s) são iguais (ou, ao menos, compatíveis), o executável do projeto será gerado com código de máquina perfeitamente executável por qualquer uma das máquinas-alvo. No mundo "não embarcado" (não se tratando de sistemas embarcados) esta modalidade é, de longe, a mais comum. Um grande exemplo disso é o desenvolvimento de jogos para computador, os quais normalmente são compilados em computadores da mesma arquitetura. Já na modalidade de compilação cruzada, as arquiteturas do host e target(s) são distintas. Portanto, se for feita uma simples compilação de um projeto no host, o executável do projeto não funcionará no target.

 

E agora, como proceder para compilar algo para um target de arquitetura diferente do host?

 

Para isso, é adotada a técnica de cross-compiling (em português, compilação cruzada). Nesta técnica, no host é utilizado um conjunto de ferramentas especial para compilação chamado toolchain. Dessa forma, o host será capaz de gerar um executável do projeto compatível com a arquitetura do(s) target(s).

 

Pareceu complicado? Veja a seguinte analogia: imagine que você fala somente português e precisa se comunicar com uma pessoa que fala somente inglês. Naturalmente, se você falar somente em português, a outra pessoa não irá compreender uma só palavra. Mas, se você utilizar um dicionário português-inglês e traduzir suas frases para o inglês, a outra pessoa será capaz de entender o que você quis dizer, mesmo o inglês não sendo sua língua nativa.

Analisando com calma, constatamos duas coisas nesta situação:

  • Você (host) precisou de um dicionário português-inglês (toolchain) para falar (cross-compiling) com a pessoa que só fala inglês (target). Dessa forma, ela entendeu sua mensagem (executável do projeto, compilado para rodar no target);
  • O dicionário (toolchain) utilizado precisa ser da língua (arquitetura) da outra pessoa desta conversação (target). Portanto, ele precisa ser utilizável por você (host) e produzir como resultado frases (programa executável do projeto, compilado para rodar no target) que a outra pessoa (target) possa entender.

Portanto, em poucas palavras, um toolchain funciona como um dicionário. Este dicionário é capaz de "traduzir" a compilação de um host de forma que seja gerado um executável do projeto compatível com a arquitetura do(s) target(s). Em termos simples, o que foi explicado aqui é o processo de cross-compiling.

 

Exemplo prático de cross-compiling

 

Até agora foi dada uma noção de cross-compiling, sobretudo do que ele se trata e porque ele é necessário. Agora, será visto na prática um exemplo de cross-compiling, utilizando um computador (host, de arquitetura x86-64 bits) para compilar um programa simples em C para uma Raspberry Pi (target, de arquitetura ARM), modelo 3B ou Zero W. Segue abaixo mais informações do ambiente utilizado:

  • Ambiente do host: computador pessoal (arquitetura x86-64 bits), rodando como sistema operacional o Linux (distribuição: Ubuntu 16.04);
    Ambiente do target: Raspberry Pi (arquitetura ARM), rodando como sistema operacional o Linux (distribuição: Raspbian OS Jessie).

 

Primeiro passo: código-fonte

 

Primeiramente, precisamos de algo a ser compilado. Para isso, vamos utilizar um código-fonte muito simples: o bom e velho Hello World.

Segue abaixo o código-fonte. Salve-o como helloworld.c.

 

 

Segundo passo: obtenção do toolchain para cross-compilar para Raspberry Pi

 

Um toolchain pode ser obtido de duas maneiras:

  1. Fazer seu próprio toolchain (algo que pode ser realmente complicado dependendo do target, além de ser algo fora do escopo deste artigo);
  2. Utilizar um toolchain pronto (opção disponível na grande maioria das vezes, seja em repositórios-padrão ou site/SDK das plataformas de desenvolvimento).

 

Neste caso, vamos seguir na opção número 2. No caso da Raspberry Pi, os toolchains necessários estão disponíveis gratuitamente no repositório GitHub deste link. Portanto, primeiramente é preciso clonar o repositório. Para termos um norte neste artigo (padronizarmos os paths, sem criar confusões e facilitar seu entendimento), na sequência de comandos dada, será criado dentro do seu diretório home o diretório RaspPiCrossCompiling, e nele será clonado o repositório. Portanto, a sequência de comandos para isso fica assim:

 

 

O repositório é grande (pois contém muita coisa além do toolchain que usaremos), portanto o processo de clone do mesmo pode demorar alguns minutos.

 

Feito o clone do repositório, o próximo passo será adicionarmos à variável de ambiente PATH o caminho para o toolchain que usaremos. Isso fará com que a cada chamada do toolchain, o Linux procure na pasta em que ele está de fato (sem isso, teríamos que referenciar o path todo até o toolchain a cada processo de cross-compiling, o que deixaria os comandos de compilação desnecessariamente longos). Para isso, execute o comando abaixo:

 

 

Está feito! O toolchain agora está instalado. Para termos certeza que ele está sendo corretamente chamado, utilize o comando abaixo para verificar sua versão:

 

 

Este comando retorna informações gerais do toolchain, como pode ser visto abaixo:

 

 

Se este comando não retornar nenhum erro, significa que o toolchain está pronto para utilização. 

 

Terceiro passo: cross-compiling e análise do resultado do processo

 

É chegada a hora de cross-compilar o código-fonte. Para isso, utilize o seguinte comando:

 

 

Observe que a sintaxe é a mesma do GCC "padrão". A compilação deve acontecer sem problemas.

 

Agora vem a questão: como verificar que a compilação foi feita realmente para ARM? Felizmente, há uma maneira bem fácil de verificar isso, com um comando nativo do Linux, o comando file. Este comando tem como função informar o tipo de qualquer arquivo e, no caso de executáveis, informa inclusive a arquitetura para o qual foi compilado (ou seja, arquitetura do target).

 

 

A resposta a esse comando é a seguinte:

 

 

Logo, está claro que o executável está compilado para ARM. Para ter ainda mais certeza, tente rodá-lo na sua máquina (o host do cross-compiling, de arquitetura diferente do target). A execução falhará (erro mostrado abaixo), reforçando ainda mais que o processo de cross-compiling foi um sucesso.

 

 

Quarto passo: teste, na Raspberry Pi, do programa cross-compilado

 

Agora, basta passar o executável (código compilado) para a Raspberry Pi (via SSH, SFTP, enfim, da forma que desejar) e executar os comandos abaixo, os quais dão permissão de execução e executam o programa:

 

 

A execução ocorrerá sem problemas, produzindo a seguinte saída:

 

Cross-compiling na Raspberry Pi: saída do programa cross-compilado, rodando na Raspiberry Pi.
Figura 1 - Saída do programa cross-compilado, rodando na Raspberry Pi (target). Aqui, o acesso à Raspberry Pi foi feito via SSH

 

Portanto, o processo de cross-compiling foi feito com sucesso!

 

Conclusão

 

Neste artigo, foram abordados os conceitos básicos de cross-compiling, algo muito comum (e extremamente necessário) no desenvolvimento de sistemas embarcados em geral. Como exemplo prático, foi visto como se faz a obtenção e instalação de um toolchain para a Raspberry Pi (aplicável aos modelos 3B e Zero W), além do processo de cross-compiling em si.

 

Saiba mais sobre cross-compiling

 

GNU Cross-toolchain - Processo de build

Série "GNU ARM Cross-toolchain"

 

Referências

 

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Pedro Bertoleti
Sou engenheiro eletricista formado pela Faculdade de Engenharia de Guaratinguetá (FEG - UNESP) e trabalho com desenvolvimento de sistemas embarcados em São Paulo capital. Curioso e viciado em tecnologia, sempre busco me aprimorar na área de eletrônica e programação, em especial em desenvolvimento de firmware (área que mais gosto de trabalhar e estudar).Para mais informações, acesse minha página no Facebook:https://www.facebook.com/pbertoleti

Deixe um comentário

1 Comentário em "Introdução a cross-compiling com a Raspberry Pi"

avatar
 
  Notificações  
recentes antigos mais votados
Notificar
Sandro Vieira
Membro
Sandro Vieira

Isso poupa o tempo significativo, muito grato pelo artigo