Embarcados – Introdução a sistemas embarcados IV

Sistemas embarcados
Sistemas embarcados

Embarcados: Remontando um rootfs

A extração de dados de um arquivo cpio foi exemplificada nesse outro post. Então, vamos iniciar esse post remontando o sistema que foi extraído.

Se o sistema extraido for baseado na mesma libc do seu sistema nativo (o sistema Linux que você deve estar utilizando para ler esse post, por exemplo), então com muita facilidade você poderá incluir programas na raiz do sistema que você extraiu do arquivo CPIO. Mas não basta copiar o arquivo binário, tem que carregar também todas as suas dependências. Para isso utiliza-se a ferramenta ldd.

Descobrir dependências com ldd
Obviamente o pré-requisito básico é a já citada libc do sistema em questão. Utilizar o ldd em binários compilados com a uClibc em uma plataforma que utiliza a glibc gerará falsos positivos e/ou falsos negativos.

O ldd deve ser utilizado assim:

Mas esse apontamento deve ser feito diretamente sobre o binário. Para saber onde se encontra o binário, usa-se o comando wich:

Um exemplo para aplicar o ldd sobre o iptraf:

Uma lista de dependências será gerada nesse formato:

O campo 1 diz o nome da dependência, o campo 2 é o divisor e o campo 3 é a localização da dependência. Sabendo disso, a meneira mais simples de recolher o programa e suas dependências é esse:

Depois disso, basta fazer a cópia do conteúdo do diretório iptraf para a raiz do rootfs com cp -rfv iptraf/* /caminho/do_rootfs.

Repare que a base que utilizei para esse exemplo é um sistema 64bits.

Esse método ‘revolucionário’ e prático é bastante útil para economizar tempo na compilação de uma aplicação específica e já foi bastante utilizada no Phantom. Porém, não serve para tudo. Haverão casos em que será necessário customizar um código, ou as libs do sistema alvo são incompatíveis com a versão do program contido no sistema nativo além de outros mais. Por isso você deve ter em mente que é fundamental aprender a construir seus próprios binários. Dito isso, vamos reconstruir o arquivo CPIO (supondo o rootfs em /building/rootfs):

O arquivo initrd será criado um nível abaixo do rootfs. Nesse caso, a compressão está sendo feita com o magnífico lzma, mas é necessário que o kernel também esteja configurado para descomprimir esse formato de arquivo.

Se você pegou um sistema de initrd único, então bastará substituir o initrd da iso pelo seu. Para tal, utilize o programa isomaster.

Depois disso você poderá fazer um boot com o qemu para testá-lo, ou ainda você poderá utilizar o kernel desse sistema para fazer um boot no seu initrd antes mesmo de compilar sua iso. Para isso, leia o procedimento descrito nesse outro post.

BuildRoot
Falamos sobre toolchains na parte 2 desse artigo. O toolchain adotado para seguir com os exemplos será o buildroot, que deve ser baixado daqui.

A versão mais atual até esse post é a 2012.05. As releases tem em média 3 meses de intervalo, sendo que a anterior foi lançada em fevereiro. Na atual versão entram diversos novos programas e inicialmente um bug para compilar o gnutls. Então, o primeiro passo após a descompressão do buildroot é entrar no respectivo diretório seguindo até o diretório de configuração do pacote em [buildroot_dir]/package/gnutls e editar o arquivo gnutls.mk, e simplesmente fazer uma substituição. Troque:

--with-libgcrypt-prefix=$(...)

Por:

--without-libgcrypt-prefix

Parece bobinho, mas isso rendeu sofrimento por alguns dias; tenha essa dica como preciosa!

Estrutura do toolchain
Leia a documentação no site do buildroot para compreender toda sua estrutura. Basicamente vou falar dos resultados da compilação. Da raiz do sistema, os diretório que trataremos são os seguintes descritos:

– dl
Diretório onde se encontrarã os pacotes gzipados que tenham sido baixados. Uma dica para isso; se o download ficar intermitente, você pode copiar a url de onde o buildroot está tentando baixar o pacote e fazê-lo manualmente com o wget utilizando a flag -c, para que possa ser interrompido e continuado o download. Tive que adotar essa prática em vários pacotes.

-output
A saída de toda sua compilação deverá vir para cá. Dentro desse diretório haverão alguns diretórios de maior importância, como host, images e target. Dentro de images estarão os formatos escolhidos para a geração de imagem (pode ser escolhido mais que uma no menu), como rootfs.cpio, rootfs.tar, rootfs.iso9660, rootfs.jffs2, etc.
No diretório target estará o sistema criado, sendo possível acessá-lo via chroot, caso a arquitetura compilada também seja x86.

Como criar um pacote para o buildroot
Mais uma vez recomendo a leitura da documentação, mas para demonstrar basicamente o processo, vou mostrar a compilação (ainda incompleta) do fsarchiver. O processo de criação, considerando que esteja na raiz do buildroot:

A opção select força a inclusão das dependências.
Um arquivo fsarchiver.mk deve ser criado com um conteúdo similar a esse:

Para que o FSArchiver apareça no menu, deve-se editar o Config.in da raiz do diretório package e criar uma entrada tão simples quanto as que lá estão.

Se tudo fosse perfeito, isso compilaria de primeira, mas não está sendo tão simples até o momento em que escrevo este post. A cross-compilação do Partimage foi um verdadeiro parto, incluindo modificações em dois métodos e alguns outros lugares para que a compilação sucedesse sem erros. Consegui montar um pacote para ele, mas como é especifico para o Phantom, não vale a pena exemplificá-lo. Apesar de ter sido compilado, resta saber se funciona, ou seja, depois desse parto todo ainda preciso enviar o sistema compilado para depuração.

Gerando sua configuração do buildroot
Porém, se for compilar os pacotes já contidos dentro do buildroot, pode ser bem menos doloroso – mas certamente haverá problema, pois como citado ao início, tive que mudar o construtor do gnutls para compilar adequadamente. Entãom para acessar o menu e fazer a sua compilação, basta:

E fazer ai sua seleção.

Compilando pacotes individualmente
Se quiser compilar algum pacote sem ter que procurá-lo pelos menus, basta primeiramente listar o diretório de pacotes dentro da raiz do buildroot, selecionando-o no diretório package. Depois basta fazer um ‘make pacote_escolhido’ e aguardar a compilação.
Finalizando essa parte de pacotes, pode ser que alguma dependência seja reclamada, bastando preceder a compilação do programa escolhido pela compilação da dependência, que certamente estará dentro do diretório package.

No próximo post veremos como cross-compilar um programa manualmente, como examinar um binário, como examinar chamadas de sistema para fazer debugging e como analisar leak de memória, tudo isso com as ferramentas de cross-compilação da sua toolchain e alguns recursos extras como o hexdump, strings, ldd, strace e ltrace.

Se o fsarchiver estiver funcional até o próximo post, farei já os demais exclarecimentos. Até a próxima!

Comments

comments

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.

Deixe uma resposta