Pergunta

A menos que eu copiado errado, o código acima foi escrito no quadro-negro em uma classe por um aluno com a ajuda / correções do professor:

int array[100], sum, i;

void ini() {
  for(i = 0; i < 100; i++)
    array[i] = i;
}

int main() {
  ini();

  sum = 0;

  for(i = 0; i < 100; i++)
    sum += array[i];
}

.pos 0
  irmovl Stack, %esp
  rrmovl Stack, %ebp

  jmp main

array:
.pos 430

sum: .long 0
i: .long 0

main:
  call ini                     //

  irmovl $0, %eax              // %eax = 0
  irmovl sum, %esi             // %esi = 0xsum
  rmmovl %eax, 0(%esi)         // 0(%esi) = %eax <=> 0(0xsum) = 0 [sum = 0]
  rmmovl %eax, 4(%esi)         // 4(%esi) = %eax <=> 4(0xsum) = 0 [i = 0]

compare:
  irmovl $100, %ebx            // %ebx = 100
  subl %eax, %ebx              // %ebx = %ebx - %eax <=> %ebx = 100 - i
  jle finish                   // Jumps to "finish" if SF=1 pr ZF=0

  mrmovl 0(%esi), %edx         // %edx = 0(%esi) <=> %edx = 0(0xsum) = sum
  addl %eax, %edx              // %edx = %edx + %eax <=> %edx = sum + i => sum
  rmmovl %edx, 0($esi)         // 0(%esi) = %edx <=> 0(0xsum) = sum

  irmovl $1, %ecx              // %ecx = 1
  addl %ecx, %eax              // %eax = %eax + %ecx <=> %eax = i + 1 => i
  rmmovl %eax, 4(%esi)         // 4($esi) = %eax <=> 4(0xsum) = i

  jmp compare                  // Jumps unconditionally to "compare"

ini:
  pushl %ebp                   //
  rrmovl %esp, %ebp            //
  pushl %ebx                   //
  pushl %eax                   //

  irmovl $0, %eax              // %eax = 0
  rmmovl %eax, -8(%ebp)        //

ini_compare:
  irmovl $100, %ecx            // %ecx = 100
  subl %eax, %ecx              // %ecx = %ecx - %eax <=> %ecx = 100 - i
  jle ini_finish               // Jumps to "ini_finish" if SF=1 pr ZF=0

  rrmovl %eax, %ebx            // %ebx = %eax <=> %ebx = i
  addl %eax, $ebx              // %ebx = %ebx + %eax <=> %ebx = i + i = 2i
  addl %ebx, %ebx              // %ebx = %ebx + %ebx <=> %ecx = 2i + 2i = 4i
  rmmovl %eax, array(%ebx)     // array(%ebx) = %eax <=> array(0x4i) = i

  irmovl %1, %ecx              // %ecx = 1
  addl %ecx, %eax              // %eax = %eax + %ecx <=> %eax = i + 1 => i
  rmmovl %eax, -8(%ebp)        //

  jmp ini_compare              // Jumps unconditionally to "ini_compare"

ini_finish:
  irmovl $4, %ebx              //
  addl %ebx, %esp              //
  popl %ebx                    //
  popl %ebp                    //

  ret                          //

.pos 600
  Stack .long 0

Como você pode ver, há um monte de comentários em todas as instruções e eu tenho (eu acho) a maioria deles, o que está me confundindo é a chamada, pushl / popl e ret instruções. Eu não entendo muito bem deles e eu também não entendo o que está acontecendo com a pilha e onde todos os registros estão apontando. Basicamente, as linhas com comentários (//) que não têm nada escrito sobre eles.

É realmente importante que eu entendo como tudo isso funciona, espero, alguns de vocês podem lançar alguma luz sobre toda essa confusão.

Algumas notas sobre meus comentários:

  • 0xsum: Isso não significa que o endereço é "sum", que seria impossível. É apenas um meio para entender o que eu estou falando sem usar o endereço de memória exata.
  • [soma = 0]:. Isto significa que no nosso código C, a soma variável serão definidas como 0, neste ponto
  • i + 1 => i:. Isto significa que estamos incrementando o valor do 'i' por um e que na seguinte linha 'i' vai realmente representar esse valor incrementado
Foi útil?

Solução

Vamos olhar algum do código:

main:
  call ini

Este vai empurrar o valor do ponteiro de instrução para a pilha (de modo que você possa mais tarde voltar a esta posição no código), e saltar para o endereço do rótulo ini. A instrução 'ret' usa o valor armazenado na pilha para retornar a partir da sub-rotina.

O seguinte é a sequência de inicialização de uma sub-rotina. Ele salva os valores de alguns registros na pilha e estabelece um quadro de pilha, copiando o ponteiro da pilha (ESP) para o registro de ponteiro de base (EBP). Se a sub-rotina tem variáveis ??locais, o ponteiro da pilha é reduzido para dar espaço para as variáveis ??na pilha, eo ponteiro base é usada para acessar as variáveis ??locais no quadro de pilha. No exemplo, a variável só local é a (não utilizado) valor de retorno.

A instrução PUSH decrementa o ponteiro de pilha (ESP) com o tamanho dos dados que vai ser empurrado, em seguida, armazena o valor nesse endereço. A instrução pop faz o contrário, primeiro obter o valor, em seguida, incrementa o ponteiro de pilha. (Note-se que a pilha cresce para baixo, então o endereço ponteiro da pilha fica mais baixa quando a pilha cresce.)

ini:
  pushl %ebp             // save ebp on the stack
  rrmovl %esp, %ebp      // ebp = esp (create stack frame)
  pushl %ebx             // save ebx on the stack
  pushl %eax             // push eax on the stack (only to decrement stack pointer)
  irmovl $0, %eax        // eax = 0
  rmmovl %eax, -8(%ebp)  // store eax at ebp-8 (clear return value)

O código segue um padrão standard, por isso parece um pouco estranho quando não há variáveis ??locais, e há um valor de retorno não utilizado. Se houver variáveis ??locais uma subtração seria usado para diminuir o ponteiro de pilha em vez de empurrar eax.

O seguinte é a sequência de saída de uma sub-rotina. Ele restaura a pilha para a posição antes do quadro de pilha foi criado, em seguida, retorna ao código que chamou a subrotina.

ini_finish:
   irmovl $4, %ebx   // ebx = 4
   addl %ebx, %esp   // esp += ebx (remove stack frame)
   popl %ebx         // restore ebx from stack
   popl %ebp         // restore ebp from stack
   ret               // get return address from stack and jump there

Em resposta a seus comentários:

O registo EBX é empurrado e bateu para preservar seu valor. O compilador aparentemente sempre coloca este código lá, provavelmente porque o registo é muito comumente usado, não apenas neste código. Da mesma forma um quadro de pilha é sempre criada copiando esp para ebp mesmo que não é realmente necessário.

A instrução que empurra eax está lá apenas para diminuir o ponteiro de pilha. É feito dessa maneira para pequenos decréscimos como é mais curto e mais rápido do que subtrair o ponteiro da pilha. O espaço que se reserva é para o valor de retorno, mais uma vez o compilador aparentemente sempre faz isso, mesmo que o valor de retorno não é usado.

Em seu diagrama quatro esp registo é constantemente apontando bytes muito alto na memória. Lembre-se que o ponteiro da pilha é reduzido depois de empurrar um valor, por isso vai apontar para o valor empurrado, não para o próximo valor. (Os endereços de memória estão longe também, é algo como 0x600 em vez de 0x20, já que é onde o rótulo Stack é declarado.)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top