ArchC ARM e Newlib
Autor: Rafael | Publicado: 2009-04-13T23:36:00-03:00

Como fazer binários ARM ligados à newlib funcionar no simulador do ArchC? Durante algum tempo enfrentei esse problema e encarei a responsabilidade de provar que o modelo do ARM estava funcional. Como corrigi alguns erros nesse modelo e acreditei que ele estava funcional, queria por isto à prova. É claro que para mostrar isso, é necessário que as funcões básicas da libc estejam funcionando para que possamos compilar programas de benchmark (a implementacão da libc que usamos para validar modelos do ArchC é a newlib).A minha primeira abordagem, publicada em um post passado, foi a de fazer o port da newlib para as syscalls do ArchC personalizadas para o ARM. No ArchC, as syscalls atendem em enderecos fixos, em uma faixa reservada (0x0-0x100)*. Alterei o arquivo que define as syscalls para o ARM com código em linguagem de montagem ARM para efetivamente realizar um branch para o endereco da syscall adequada. Esta abordagem foi um sucesso parcial, pois algumas funcões não estavam em completa funcionalidade, sem contar que era necessário alterar diretamente o código da newlib. Portanto, esta solucão era claramente temporária e com caráter de testes. Não poderia recomendá-la sempre que alguém precisasse compilar programas para ARM voltados para execucão no simulador ArchC-ARM.Para o propósito de redirecionar a newlib para usar este modo de syscalls peculiar utilizado no simulador do ArchC, existe uma biblioteca específica disponível no ArchC, libac_sysc.a, e um arquivo "specs" para o gcc, ac_specs, que redefine como o ligador deve montar o binário (já prepara para respeitar a faixa reservada) e como ligar as bibliotecas básicas (no caso a libc.a e libac_sysc.a são usadas em conjunto para que tudo funcione). Na árvore do código-fonte do ArchC, tais artefatos se encontram em "src/aclib/ac_syscall/libac_sysc". Lá existe um makefile que deve ser usado de modo a informar a arquitetura alvo (exemplo "make TARGET=arm-elf" - neste caso o cross-compiler "arm-elf-gcc" é usado para compilar a biblioteca, pois este pedaco de código será anexado a todo programa ARM que você compilar). Depois de construída, a biblioteca ficará disponível na mesma pasta, e é sua responsabilidade copiá-la (arquivo libac_syc.a) para a pasta "lib" de seu cross-compiler "raiz_do_compilador/arm-elf/lib", bem como o arquivo ac_specs. Crie também o arquivo ac_start.o incluído no início de todo programa, para substituir o clássico crt0.o (que é fornecido pela newlib). O ac_start pode ser facilmente produzido com um código que executa um branch para o símbolo "main" e em seguida outro branch para o símbolo "exit" (deve ser feito direto em linguagem de montagem e produzido com o montador "arm-elf-as ac_start.s -o ac_start.o").Entretanto, a abordagem do ArchC (utilizando a libac_sysc) estava falhando para o caso do ARM. Após algum tempo de investigacão e com a ajuda de colegas do LSC, descobrimos que a newlib apresenta uma configuracão singular para o caso do ARM (e de poucos outros alvos). As syscalls, que normalmente estavam definidas na libgloss (que pode ser facilmente substituída pela libac_sysc), estavam sendo definidas diretamente na libc. Desse modo era impossível que a libac_sysc entrasse em acão. Ainda, o código da biblioteca libac_sysc não apresenta uma solucão muito robusta, porque quando compilada com o cross-compiler do ARM (gcc 3.3) com a opcão -O3 (como está especificado no makefile), o código produzido é falho. Especificamente, o compilador "suja" um dos registradores de passagem de parâmetro para calcular o endereco da construcão goto utilizada no código C da biblioteca, que é o artifício usado para simular um branch para o endereco específico de uma syscall determinada pelo simulador do ArchC.Contudo, ao compilar a biblioteca com -O2 o problema é eliminado. Assim consegui executar alguns testes com sucesso e verificar que o modelo esteve funcional nesses casos. Preciso executar o benchmark completo para validá-lo completamente.Sumarizando, as modificacões são as seguintes:1. No código-fonte da newlib (usei a versão 1.13), você deve fazer com que o alvo "ARM" seja produzido sem syscalls (pois estas serão definidas pela biblioteca libac_sysc do ArchC). Para isso, altere o arquivo "newlib/configure.host" evitando que para o ARM a variável "sys" seja definida (configurando a pasta onde as syscalls se encontram) e usando newlib_cflags="$newlib_cflags -DMISSING_SYSCALL_NAMES" no local adequado. Esta macro "MISSING_SYSCALL_NAMES" na newlib é importante para fazer com que os acessos sejam feitos a funcões sem underscore (que são os símbolos que a libac_sysc reconhece -  open ao invés de _open, etc.).2. No momento de utilizar o script "configure" do pacote newlib para preparar o build da biblioteca, passe o parâmetro "--disable-newlib-supplied-syscalls".3. Construa e instale a newlib normalmente. Para compilar a libac_sysc, não se esqueca de alterar o Makefile para utilizar "-O2" ao invés de "-O3". Atualmente também é necessário comentar uma linha do arquivo ../ac_syscall.def, que contém fstat, para evitar conflito já que essa funcão já esta definida em /ac_syscall_wrapper.c.Acredito que esses problemas no fonte do ArchC (mudar a flag para O2 e comentar a linha com fstat em ac_syscall.def), se não estiverem corrigidos, estarão em breve, pois isto nós temos controle. Só não temos controle sobre a newlib, que precisa sofrer as alteracões citadas.* Por este motivo, caso esteja com as syscalls habilitadas em seu simulador (gerar o código-fonte do simulador com "acsim -abi"), nunca se esqueca de escolher o ponto de entrada do executável em um endereco a partir de 0x100. Isto é feito automaticamente pelo seu cross-gcc caso você use o arquivo de specs do archc (por exemplo no comando "arm-elf-gcc -specs=ac_specs ..."), mas é sempre importante estar ciente do que está acontecendo.