Usando a GNU Compiler Collection (GCC) para ARM Cortex-M3

Esse é o nono artigo da série escrita pelo engenheiro Ismael Lopes da Silva, exclusivamente para o portal Embarcados.Nessa série focarei no Microcontrolador da STMicroelectronics, o MCU STM32F103C8T6, que é um ARM Cortex-M3. Os pré-requisitos para uma boa compreensão dos artigos é ter o domínio da Linguagem C Embedded e conceitos de eletrônica.

Diferentes seções de Memória

Seções da memória de programa/código (FLASH ou ROM):

  • Tabela de vetores;
  • Seção .text;
  • Seção .rodata;
  • Seção .data.

Seções da memória de dados (SRAM):

  • Seção .data;
  • Seção .bbs;
  • Heap;
  • Stack (pilha).

Seção “.text” – é a seção de código, também conhecido como segmento de texto ou simplesmente texto, é onde uma parte de um arquivo de objeto ou a seção correspondente do espaço de endereço do programa que contém instruções executáveis é armazenada e geralmente é somente de leitura e de tamanho fixo.

Seção “.data” – é a seção de dados que contém quaisquer variáveis globais ou estáticas que possuem um valor predefinido e podem ser modificadas. Ou seja, quaisquer variáveis que não são definidas em uma função, portanto, podem ser acessadas de qualquer lugar, ou são definidas em uma função, mas são definidas como estáticas para que retenham seu endereço nas chamadas subsequentes. Os valores para essas variáveis são inicialmente armazenados na memória FLASH e são copiados para o segmento “.data”, na memória SRAM, durante a rotina de inicialização do programa.

Seção “.bss” – significa “block started by symbol” – Toda variável global não inicializada, variável estática não inicializada e variável inicializada com zero são armazenadas na seção “.bss”.

Seção “.rodata” – é a seção com todas as variáveis globais constantes.

Diferentes Dados de Programa

Existem diferentes tipos de dados, que são:

  • Global não inicializado;
  • Global inicializado;
  • Global estático não inicializado;
  • Global estático inicializado;
  • Local não inicializado;
  • Local inicializado;
  • Local estático não inicializado;
  • Local estático inicializado;
  • Global constante;
  • Local constante.

Dado global não inicializado é armazenado na seção “.bss” da memória de dados (SRAM) e inicializado com o conteúdo zero. Esse dado não carrega informação importante. Dado global não inicializado não é armazenado na FLASH para não consumir espaço da memória de programa com informação não relevante.

Dado global inicializado é armazenado na seção “.data” da memória de programa (FLASH), e também é copiado para a seção “.data” da memória de dados (SRAM), durante o processo de inicialização (startup code). Esse dado carrega informação importante.

Dado global estático não inicializado é um dado privado, e é armazenado na seção “.bss” da memória de dados (SRAM) e inicializado com o conteúdo zero. Esse dado não carrega informação importante.

Dado global estático inicializado é armazenado na seção “.data” da memória de programa (FLASH), e também é copiado para a seção “.data” da memória de dados (SRAM), durante o processo de inicialização (startup code). Esse dado carrega informação importante.

Dado local não inicializado e inicializado é armazenado no “stack” da memória de dados (SRAM). Esse dado está relacionado com o escopo da função, então, é um dado transiente que é criado e destruído dinamicamente. Quando o programa entra na função o dado é criado e quando retorna da função o dado é destruído.

Dado local estático não inicializado é um dado global que é privado para o escopo da função, e é armazenado na seção “.bss” da memória de dados (SRAM), e inicializado com o conteúdo zero.

Dado local estático inicializado é um dado global que é privado para o escopo da função, e é armazenado na seção “.data” da memória de programa (FLASH), que também é copiado para a seção “.data” da memória de dados (SRAM), durante o processo de inicialização (startup code).

Dado global constante é armazenado na seção “.rodata” da memória de programa/código (FLASH).

Dado local constante é armazenado no “stack” da memória de dados (SRAM).

GCC: GNU Coleção do Compilador (GNU Compiler Collection)

Escrevemos nossa aplicação em linguagem “C”, então, nosso arquivo-fonte é escrito numa linguagem de alto nível. Uma linguagem de alto nível é uma linguagem de programação com alto grau de abstração dos detalhes da linguagem de máquina.

Na programação embarcada, denominamos “host” o computador onde escrevemos nossa aplicação e compilamos, e “target” o MCU que transferimos nossa aplicação convertida em código de máquina, onde a mesma rodará.

O processo de compilação é converter um programa de linguagem de alto nível para um arquivo executável que contém código de máquina. Depois transferimos esse arquivo executável para o “target”. Nossa IDE, STM32CubeIDE, tem incorporado um compilador, conhecido como GCC. Esse compilador é gratuito de código aberto, denominado GNU Tools (GCC) for ARM Embedded Processors – GCC significa GNU Compiler Collection.

Mesmo que o GCC vem incorporador no STM32CubeIDE, vamos compilar, criar executável, converter arquivo e analisar executável na linha de comando para entender o que passa por trás dos bastidores, então, faça um download do GNU Cross Toolchain, através do seguinte link https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads, e instale o GCC em seu “host”. Minha plataforma é Windows.

Aplicação (Linguagem de Alto Nível)>Cross Compilation>Executável (código de máquina)
Tabela 1 – Fluxo de compilação

O que é “GCC”? É um processo na qual um conjunto de ferramentas roda no “host” e cria executável que rodará em diferentes “target”. Em meu contexto o “host” é meu desktop e o “target” é o MCU STM32F103C8T6, baseado na arquitetura ARM Cortex-M3. O GCC, é uma coleção de arquivos binários que permite compilar, converter para linguagem Assembly e criar arquivo executável (aplicação). Também contém arquivo binário para depurar (debug) a aplicação no “target”. Essas ferramentas veem com outros arquivos binários que ajudam analisar o executável, então, podemos:

  • Dissecar as diferentes seções do executável;
  • Ver o código em linguagem de baixo nível (Assembly) – Disassemble;
  • Extrair símbolos e informações de tamanho;
  • Converter executável em outros formatos como “.bin” e “ihex”;
  • Prover as bibliotecas padrões da linguagem “C”.

A tabela a seguir mostra os arquivos binários importantes das ferramentas do GCC.

ArquivoFunção
arm-nome-eabi-gccCompilar, Converter para Assembly e Criar o Executável (*.elf – Linker)
arm-nome-eabi-asConverter para Assembly
arm-nome-eabi-ldCriar o Executável (*.elf – Linker)
arm-nome-eabi-objcopyConverte (*.elf) para outros formatos (*.bin, *.ihex)
arm-nome-eabi-objdumpAnalisar arquivo executável (*.elf)
arm-nome-eabi-readelfAnalisar arquivo executável (*.elf)
arm-nome-eabi-nmAnalisar arquivo executável (*.elf)
arm-nome-eabi-gdbDepurar a aplicação (Debugging)
Tabela 2 – Arquivos binários importantes do GCC

A figura 1 ilustra onde estão instalados os arquivos binários importantes do GNU Cross Toolchain. Eles são localizados na subpasta [bin] no path de instalação.

GCC - Arquivos binários importantes instalados no “host”
Figura 1 – Arquivos binários importantes instalados no “host”

Processo de Converter Aplicação de Linguagem C para Código de Máquina

O primeiro estágio é chamado de pré-processamento. Todas as diretivas do arquivo-fonte ‘*.c’ será resolvida, e a saída é o arquivo ‘*.i’. Esse estágio não é uma compilação, é um pré-processamento da compilação. No pré-processamento diretivas como todos os ‘#includes’, macros de ‘C’ e todas as macros de compilação condicional serão resolvidas e o arquivo de pré-processamento é criado, com a extensão ‘.i’.

O segundo estágio é o ‘Parser’, onde as sintaxes da linguagem ‘C’ será verificada. O terceiro estágio gera o código em linguagem Assembly, que é a tradução do arquivo-fonte para a linguagem Assembly. A entrada para esse estágio é o arquivo ‘*.i’, e a saída é o arquivo ‘*.s’. Nesse estágio as instruções da linguagem ‘C’, de alto nível, será convertida para mnemônicos da arquitetura do processado, em linguagem Assembly.

O quarto estágio, denominado ‘Assembler’, os mnemônicos da linguagem Assembly são convertidos em opcodes. Opcodes nada mais são do que código de máquina para várias instruções, porque o ‘target’, ou o processador, entende números ou código de máquina. Nesse estágio um arquivo objeto realocável (sem endereços absolutos) é criado. A entrada para esse estágio é o arquivo ‘*.s’, e a saída é o arquivo ‘*.o’.

O último estágio do processo de compilação é a criação do arquivo executável ‘*.elf’, que significa executável e formato linkavel (executable and linkable format). Nesse estágio todos os símbolos e outras informações são resolvidas, e também faz um merge de todas as diferentes seções do arquivo objeto realocável ‘*.o’, criando um único arquivo executável.

GCC - processo de compilação
Figura 2 – Diagrama do GNU Cross Toolchain

A figura 2 ilustra todo o processo de compilação realizada pelo GCC. Nesse diagrama foi acrescentado um bloco utilitário, chamado ‘objcopy’, que converter o arquivo executável ‘*.elf’ em outros formatos. Como já vimos a coleção de ferramentas do GCC tem outros utilitários, que podem ser revistos na tabela 2.

Resumindo o processo de construção de um arquivo executável, conhecido como ‘build’, tem as seguintes etapas principais, executadas pelo arquivo binário ‘arm-nome-eabi-gcc’, que são:

  • Pré-processamento (Preprocessor);
  • Compilação (Parser, Code Generator e Assemble);
  • Linkagem (Linker).

Compilação na Linha de Comando do DOS

Depois de baixado e instalado o GNU Tools (GCC) for ARM Embedded Processors em seu ‘host’, e também garantindo que a subpasta [bin] esteja no PATH do seu ‘host’, vamos aplicar o que vimos até aqui.

Segue o link da documentação do GCC https://gcc.gnu.org/onlinedocs/gcc/index.html, para obter mais detalhes. Na tabela de conteúdo desse site podemos encontrar a opção ‘GCC Command Options – Options Controlling the Kind of output e Machine-Dependent Options – ARM Options’, onde tem as opções que usaremos aqui.

Abra uma janela de comando do DOS e crie uma simples aplicação ‘main.c’, como por exemplo:

As ferramentas de compilação e analise nos oferecem muitas opções, então, veremos algumas. Para compilar e criar o arquivo objeto realocável ‘main.o’, digite o seguinte comando na janela do DOS.

O seguinte comando permite vermos que o arquivo ‘main.o’ foi criado

Na linha de comando o argumento ‘-c’ determina que o GCC compile e crie o arquivo objeto realocável (main.o), mas, não processa no Linker, então, não cria o arquivo executável ‘main.elf’. O argumento ‘-mcpu=cortex-m3’ informa a arquitetura do processador ‘target’. O argumento ‘-mThumb’ informa que as instruções do código são ‘Thumb’. O processador ARM Cortex-M3 não suporta instruções de código no padrão ‘ARM’, somente ‘Thumb’. O argumento ‘-o0’ determina que o compilador não otimize o processo de compilação. O argumento ‘-o’ determina que será criado um arquivo de saída, que nesse caso ‘main.o’, nosso arquivo objeto realocável.

Na documentação do GCC https://gcc.gnu.org/onlinedocs/gcc/index.html, na tabela de conteúdo podemos encontrar a opção ‘GCC Command Options – Options Controlling the Kind of output’. Sobre o argumento ‘-c’ encontramos:

Compile or assemble the source files, but do not link. The linking stage simply is not done. The ultimate output is in the form of an object file for each source file. By default, the object file name for a source file is made by replacing the suffix ‘.c’, ‘.i’, ‘.s’, etc., with ‘.o’. Unrecognized input files, not requiring compilation or assembly, are ignored.”

Outra opção é quando queremos obter apenas o arquivo ‘main.s’, que é a aplicação convertida em linguagem Assembly.

O seguinte comando permite vermos que o arquivo ‘main.o’ foi criado

Para ver o conteúdo do arquivo criado ‘main.s’, digite o seguinte comando:

Na documentação do GCC https://gcc.gnu.org/onlinedocs/gcc/index.html, na tabela de conteúdo podemos encontrar a opção ‘GCC Command Options – Options Controlling the Kind of output’. Sobre o argumento ‘-S’ encontramos:

-S

Stop after the stage of compilation proper; do not assemble. The output is in the form of an assembler code file for each non-assembler input file specified.

By default, the assembler file name for a source file is made by replacing the suffix ‘.c’, ‘.i’, etc., with ‘.s’.

Input files that don’t require compilation are ignored.”

Outra opção que veremos são algumas análises que podemos fazer, no arquivo objeto realocável ‘main.o’.

O seguinte comando permite analisarmos as principais seções do arquivo ‘main.o’, que são ‘.text’, ‘.data’, ‘.bss’ e ‘rodata’.

O argumento ‘-h’ mostra os conteúdos das seções do arquivo objeto realocável.

São muitas possibilidades, mas, já temos uma ideia de como funciona atrás dos bastidores.

Saiba mais

Desenvolvendo em C para ARM Cortex-M usando GCC e Make

GNU ARM Cross-toolchain – Eclipse + FreeRTOS + GCC – Parte 1

STM32CubeIDE: Primeiros passos e CMSIS Core com GPIO

Outros artigos da série

<< Alguns Registradores do Núcleo (Core) do Processador ARM Cortex-M3 na Pratica
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

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

Software » Usando a GNU Compiler Collection (GCC) para ARM Cortex-M3
Comentários:
Notificações
Notificar
guest
0 Comentários
Inline Feedbacks
View all comments
Talvez você goste:

Nenhum resultado encontrado.

Séries



Outros da Série

Menu