Sintaxe do GAS
Aprendendo a sintaxe AT&T e a usar o GAS
O GNU assembler (GAS) usa por padrão a sintaxe AT&T e neste tópico irei ensiná-la. Mais abaixo irei ensinar a diretiva usada para usar sintaxe Intel meramente como curiosidade e caso prefira usá-la.
Diferenças entre sintaxe Intel e AT&T
A primeira diferença notável é que o operando destino nas instruções de sintaxe Intel é o mais à esquerda, o primeiro operando. Já na sintaxe da AT&T é o inverso, o operando mais à direita é o operando destino. Conforme exemplo:
E como já pode ser observado valores literais precisam de um prefixo $, enquanto os nomes dos registradores precisam do prefixo %.
Tamanho dos operandos
Na sintaxe da Intel o tamanho dos operandos é especificado com base em palavra-chaves que são adicionadas anteriormente ao operando. Na sintaxe AT&T o tamanho do operando é especificado por um sufixo adicionado a instrução, conforme tabela abaixo:
Exemplos:
Assim como o NASM consegue identificar o tamanho do operando quando é usado um registrador e a palavra-chave se torna opcional, o mesmo acontece no GAS e o sufixo também é opcional nesses casos.
Far jump e call
Na sintaxe Intel saltos e chamadas distantes são feitas com jmp far [etc] e call far [etc] respectivamente. Na sintaxe da AT&T se usa o prefixo L nessas instruções, ficando: ljmp e lcall.
Na sintaxe Intel é bem intuitivo já que ele é escrito em formato de expressão matemática. Na sintaxe AT&T é um pouco mais confuso e segue o seguinte formato:
segment:displacement(base, index, scale).
Exemplos com o seu equivalente na sintaxe da Intel:
Como demonstrado no último exemplo o endereço relativo na sintaxe do GAS é feito explicitando RIP como base, enquanto na sintaxe do NASM isso é feito usando a palavra-chave rel.
Na sintaxe da AT&T os saltos para endereços armazenados na memória devem ter um * antes do rótulo para indicar que o salto deve ocorrer para o endereço que está armazenado naquele endereço de memória. Sem o * o salto ocorre para o rótulo em si. Exemplo:
Saltos que especificam segmento e offset separam os dois valores por vírgula. Como em:
Aprendendo a usar o GAS
As diretivas do GAS funcionam de maneira semelhante as diretivas do NASM com a diferença que todas elas são prefixadas por um ponto.
No GAS comentários de múltiplas linhas podem ser escritos com /* e */ assim como em C. Comentários de uma única linha podem ser escritos com # ou //.
Pseudo-instruções de dados
No NASM db, dw, dd, dq etc. que servem para despejar bytes no arquivo binário de saída. No GAS isso é feito usando as seguintes pseudo-instruções:
Exemplos:
Diretivas de seções e alinhamento
O GAS tem diretivas específicas para declarar algumas seções padrão. Conforme tabela:
Porém ele também tem a diretiva .section que pode ser usada de maneira semelhante a section do NASM. Os atributos da seção porém são passados em formato de flags em uma string como segundo argumento. As flags principais são w para dar permissão de escrita e x para dar permissão de execução. Exemplos:
A diretiva .align pode ser usada para alinhamento dos dados. Você pode usá-la no início da seção para alinhar a mesma, conforme exemplo:
Usando sintaxe Intel
A diretiva .intel_syntax pode ser usada para habilitar a sintaxe da Intel para o GAS. Opcionalmente pode-se passar um parâmetro noprefix para desabilitar o prefixo % dos registradores.
Uma diferença importante da sintaxe Intel do GAS em relação ao NASM é que as palavra-chaves que especificam o tamanho do operando precisam ser seguidas por ptr, conforme exemplo abaixo:
Exemplo de código na sintaxe AT&T
O exemplo abaixo é o mesmo apresentado no tópico sobre porém reescrito na sintaxe do GAS/AT&T: