Gerando e usando toolchains em Linux embarcado

- por Sergio Prado

Categorias: Linux Tags: , ,

No último artigo vimos os principais conceitos de toolchain em Linux embarcado: o que é um toolchain, os tipos de toolchain e seus componentes básicos.

Neste artigo iremos aprender a gerar e utilizar um toolchain para cross-compilar aplicações e projetos open-source para Linux embarcado.

ONDE POSSO ENCONTRAR UM TOOLCHAIN?

Você tem duas opções: usar um toolchain pronto ou gerar (compilar) seu próprio toolchain. Cada uma destas opções tem suas vantagens e desvantagens.

Usar um toolchain pronto é fácil. Normalmente vem junto com o BSP (Board Support Package) da sua plataforma de desenvolvimento. Existem também versões gratuitas disponibilizadas por empresas como a Code Sourcery (que foi adquirida pela Mentor Graphics). Mas usar um toolchain pronto engessa sua solução. Como o toolchain depende das bibliotecas do sistema, você vai precisar adaptar seu produto às bibliotecas de sistema usadas no toolchain. Se o toolchain não tem um compilador C++, você não conseguirá desenvolver aplicações nesta linguagem. Conclusão: em vez da ferramenta se adaptar às suas necessidades, você é que vai precisar se adaptar à ferramenta. O que na maioria das vezes não é muito bom, certo?

A segunda opção, gerar um toolchain, pode dar um pouco mais de trabalho, mas te traz toda a flexibilidade necessária para adaptar a ferramenta às necessidades do seu projeto. É o que faremos a seguir.

GERANDO SEU PRÓPRIO TOOLCHAIN

O processo de geração de um toolchain, como você deve imaginar, é bem complicado. Mas existem ferramentas que automatizam este processo. 

Boa parte das ferramentas de Build System, como o OpenEmbedded e o Buildroot, fazem isso. Mas existem algumas ferramentas específicas para gerar toolchains. Dentre elas, a mais famosa é o crosstool-ng, que usaremos para gerar um toolchain para cross-compilar aplicações Linux para ARM.

O crosstool-ng pode ser baixado da página do projeto em http://crosstool-ng.org/. Crie uma pasta de trabalho na sua máquina, baixe e descompacte o crosstool-ng:

$ cd /opt/toolchain
$ wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.13.1.tar.bz2
$ tar jxfv crosstool-ng-1.13.1.tar.bz2
$ cd crosstool-ng-1.13.1/

Primeiramente, precisamos configurar e compilar o crosstool-ng:

$ ./configure --local
$ make && make install

O processo acima deve ter gerado o script "ct-ng", que será usado para configurar o toolchain.

O crosstool-ng vem com algumas configurações de toolchain por padrão, que podem ser visualizadas com o comando abaixo:

$ ./ct-ng list-samples
Sample name                           Status
alphaev56-unknown-linux-gnu           [L X]
alphaev67-unknown-linux-gnu           [L X]
arm-bare_newlib_cortex_m3_nommu-eabi  [L X]
arm-cortex_a15-linux-gnueabi          [L X]
arm-cortex_a8-linux-gnueabi           [L  ]
arm-davinci-linux-gnueabi             [L  ]
armeb-unknown-eabi                    [L  ]
armeb-unknown-linux-gnueabi           [L X]
armeb-unknown-linux-uclibcgnueabi     [L X]
arm-iphone-linux-gnueabi              [L X]
...

A listagem acima esta exibindo apenas uma parte das configurações disponíveis por padrão no crosstool-ng. Ele é capaz de gerar toolchains para ARM, MIPS, PPC, x86 e AVR, dentre outras arquiteturas e plataformas.

Você pode se aventurar a criar uma configuração do zero, mas o mais comum é usar uma das configurações pré-definidas, e então trabalhar em cima dela. No nosso caso, usaremos o “arm-unknown-linux-uclibcgnueabi” e faremos algumas pequenas modificações.

Portanto, carregue esta configuração com o comando abaixo:

$ ./ct-ng arm-unknown-linux-uclibcgnueabi

Vamos agora abrir o menu de configuração do crosstool-ng e fazer alguns ajustes:

$ ./ct-ng menuconfig

Entre na opção “Paths and misc options” e configure a quantidade de threads de execução. Essa configuração ajuda a diminuir o tempo de compilação e geração do toolchain. Multiplique a quantidade de núcleos da sua CPU por dois:

(2) Number of parallel jobs

Ainda neste menu, configure o diretório de instalação do toolchain na opção "Prefix directory":

(/opt/toolchain/x-tools/${CT_TARGET}) Prefix directory

Volte à tela principal e entre na opção “Toolchain options” para configurar o "alias": 

(arm-linux) Tuple's alias

Configurando "arm-linux" como alias, todas as ferramentas começarão com o prefixo "arm-linux". Por exemplo, o gcc do toolchain terá o nome "arm-linux-gcc".

Salve e saia. Agora é só iniciar a geração do toolchain com o comando abaixo:

$ ./ct-ng build

Obs: No momento em que escrevo este artigo, o site do kernel do Linux não esta 100% no ar (os caras tiveram alguns problemas de segurança que tiraram o site do ar durante um bom tempo). Por este motivo, o crosstool-ng deu um erro ao tentar baixar o kernel do Linux (arquivo linux-2.6.33.19.tar.gz). Neste caso, vá para a página do github do kernel, baixe a versão requisitada pelo crosstool-ng, e salve-a no diretório ".build/tarballs/".

O processo de compilação pode demorar um pouco. No final, verifique o toolchain gerado no diretório abaixo:

$ ls /opt/toolchain/x-tools/arm-unknown-linux-uclibcgnueabi/bin
arm-linux-addr2line     arm-linux-g++        arm-linux-ldd       arm-linux-size                             arm-unknown-linux-uclibcgnueabi-c++filt       arm-unknown-linux-uclibcgnueabi-gdb       arm-unknown-linux-uclibcgnueabi-ranlib
arm-linux-ar            arm-linux-gcc        arm-linux-nm        arm-linux-strings                          arm-unknown-linux-uclibcgnueabi-cpp           arm-unknown-linux-uclibcgnueabi-gprof     arm-unknown-linux-uclibcgnueabi-readelf
arm-linux-as            arm-linux-gcc-4.4.3  arm-linux-objcopy   arm-linux-strip                            arm-unknown-linux-uclibcgnueabi-ct-ng.config  arm-unknown-linux-uclibcgnueabi-ld        arm-unknown-linux-uclibcgnueabi-run
arm-linux-c++           arm-linux-gccbug     arm-linux-objdump   arm-unknown-linux-uclibcgnueabi-addr2line  arm-unknown-linux-uclibcgnueabi-g++           arm-unknown-linux-uclibcgnueabi-ldd       arm-unknown-linux-uclibcgnueabi-size
arm-linux-cc            arm-linux-gcov       arm-linux-populate  arm-unknown-linux-uclibcgnueabi-ar         arm-unknown-linux-uclibcgnueabi-gcc           arm-unknown-linux-uclibcgnueabi-nm        arm-unknown-linux-uclibcgnueabi-strings
arm-linux-c++filt       arm-linux-gdb        arm-linux-ranlib    arm-unknown-linux-uclibcgnueabi-as         arm-unknown-linux-uclibcgnueabi-gcc-4.4.3     arm-unknown-linux-uclibcgnueabi-objcopy   arm-unknown-linux-uclibcgnueabi-strip
arm-linux-cpp           arm-linux-gprof      arm-linux-readelf   arm-unknown-linux-uclibcgnueabi-c++        arm-unknown-linux-uclibcgnueabi-gccbug        arm-unknown-linux-uclibcgnueabi-objdump
arm-linux-ct-ng.config  arm-linux-ld         arm-linux-run       arm-unknown-linux-uclibcgnueabi-cc         arm-unknown-linux-uclibcgnueabi-gcov          arm-unknown-linux-uclibcgnueabi-populate

Sempre que você for usar o toolchain, vai precisar configurar a variável de ambiente PATH:

$ export PATH=/opt/toolchain/x-tools/arm-unknown-linux-uclibcgnueabi/bin:$PATH

Agora teste o toolchain gerado:

$ arm-linux-gcc
arm-linux-gcc: no input files

USANDO O TOOLCHAIN

Usar o toolchain para cross-compilar uma aplicação é muito fácil. Crie um programa simples em C:

1
2
3
4
5
6
7
8
// main.c
#include "stdio.h"
 
int main()
{
    printf("Hello Embedded Universe!\n");
    return 0;
}

Se você quisesse compilar nativamente sua aplicação, bastaria usar o gcc conforme abaixo:

$ gcc main.c -o main

Para confirmar que a aplicação foi compilada nativamente, use o comando "file":

$ file main
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

Para cross-compilar, basta trocar o gcc pelo compilador do toolchain, que no nosso caso é o arm-linux-gcc:

$ arm-linux-gcc main.c -o main

Obs: Lembre-se de que a variável PATH deve estar configurada corretamente para o bash encontrar as ferramentas do toolchain.

Use o comando “file” para confirmar que a aplicação foi compilada para ARM:

$ file main
main: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Fácil, não?

CROSS-COMPILANDO APLICAÇÕES MAIS COMPLEXAS

Mas Sergio, compilar um arquivo ".c" é fácil. E no caso de um projeto mais complexo, com diversos arquivos-fonte?

Um projeto deste porte é normalmente baseado em um ou mais Makefiles, ou em um conjunto de ferramentas chamadas de autotools.

Para um projeto baseado em Makefile, você pode abrir o arquivo de Makefile, encontrar a definição das variáveis que definem as ferramentas de compilação (AR, AS, LD, CC, GCC, CPP, CXX, STRIP) e substituir pelas do seu toolchain. Não se esqueça de que a variável PATH também deve estar configurada corretamente.

Para um projeto baseado em autotools, o procedimento comum para compilar nativamente a aplicação é este:

$ ./configure
$ make
$ make install

Mas para cross-compilar um projeto baseado em autotools, alguns cuidados adicionais devem ser tomados, como configurar o compilador através da variável de ambiente CC, e indicar o "host" ao executar a ferramenta "configure".

Para estudar este processo, vamos cross-compilar o lighttpd, um web server bastante usado em Linux embarcado, e cujo projeto é baseado em autotools.

Baixe e descompacte o pacote:

$ cd /opt/toolchain/
$ wget http://download.lighttpd.net/lighttpd/releases-1.4.x/lighttpd-1.4.29.tar.gz
$ tar zxfv lighttpd-1.4.29.tar.gz
$ cd lighttpd-1.4.29/

Para cross-compilar usando o toolchain que geramos, basta executar os comandos abaixo:

$ export PATH=/opt/toolchain/x-tools/arm-unknown-linux-uclibcgnueabi/bin:$PATH
$ export CC=arm-linux-gcc
$ ./configure --host=arm-linux --without-pcre --without-zlib --without-bzip2 --disable-ipv6
$ make

Na primeira linha configuramos a variável de ambiente PATH. Na segunda linha configuramos o compilador do toolchain através da variável de ambiente CC. Na terceira linha configuramos o software indicando qual é o host (máquina que irá executar a aplicação), e desabilitando algumas bibliotecas que não temos disponíveis. Por último, executamos o make para compilar a aplicação.

Confirme se o lighttpd foi compilado para ARM:

$ file ./src/lighttpd
./src/lighttpd: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Percebam que a geração e o uso de toolchains não envolve nenhum tipo de "magia negra". Quanto menos uma ferramenta for uma "caixa preta" para você, melhor. Você será mais produtivo, e resolverá eventuais problemas muito mais rapidamente.

Um abraço,

Sergio Prado

Faça um Comentário

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.