문제

내가 잘못 복사하지 않으면 위의 코드는 교사의 도움/수정을 가진 학생이 수업의 칠판에 작성되었습니다.

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

보시다시피, 모든 지시 사항에 많은 의견이 있으며, 대부분의 사람들은 전화, Pushl/Popl 및 RET 지침입니다. 나는 그것들을 잘 이해하지 못하고 또한 스택에 무슨 일이 일어나고 있는지, 그리고 모든 레코드가 어디에서 지적하는지 이해하지 못합니다. 기본적으로, 댓글이있는 줄 (//)은 아무것도 쓰지 않은 줄입니다.

이 모든 것이 어떻게 작동하는지 이해하는 것이 정말 중요합니다. 여러분 중 일부는이 모든 혼란에 약간의 빛을 비출 수 있습니다.

내 의견에 대한 몇 가지 메모 :

  • 0xsum : 이것은 주소가 "합"이라는 것을 의미하지는 않습니다. 불가능합니다. 그것은 정확한 메모리 주소를 사용하지 않고 내가 말하는 것을 이해하는 수단 일뿐입니다.
  • sum = 0] : 이는 C 코드 에서이 시점에서 변수 합이 0으로 설정됨을 의미합니다.
  • I + 1 => I : 이것은 우리가 'i'의 값을 하나씩 증가시키고 있으며 다음 줄에서 'I'는 실제로 그 증분 값을 나타낼 것임을 의미합니다.
도움이 되었습니까?

해결책

코드 중 일부를 살펴 보겠습니다.

main:
  call ini

이렇게하면 명령 포인터의 값이 스택으로 향합니다 (나중에 코드 에서이 위치로 돌아갈 수 있음)가 INI 레이블의 주소로 이동합니다. 'ret'명령어는 스택에 저장된 값을 사용하여 서브 루틴에서 반환합니다.

다음은 서브 루틴의 초기화 시퀀스입니다. 스택의 일부 레지스터 값을 저장하고 스택 포인터 (ESP)를베이스 포인터 레지스터 (EBP)에 복사하여 스택 프레임을 설정합니다. 서브 루틴에 로컬 변수가있는 경우 스택 포인터가 스택의 변수를 공간으로 만들기 위해 줄어들고베이스 포인터는 스택 프레임의 로컬 변수에 액세스하는 데 사용됩니다. 예에서 유일한 로컬 변수는 (미사용) 반환 값입니다.

푸시 명령은 푸시 할 내용의 데이터 크기로 스택 포인터 (ESP)를 줄인 다음 해당 주소에 값을 저장합니다. POP 명령어는 반대쪽으로, 먼저 값을 얻은 다음 스택 포인터를 증가시킵니다. (스택이 아래쪽으로 커지므로 스택이 커지면 스택 포인터 주소가 낮아집니다.)

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)

코드는 표준 패턴을 따르므로 로컬 변수가 없을 때 약간 어색해 보이고 사용되지 않은 반환 값이 있습니다. 로컬 변수가있는 경우 Subtraction은 EAX를 푸시하는 대신 스택 포인터를 줄이는 데 사용됩니다.

다음은 서브 루틴의 출구 시퀀스입니다. 스택 프레임이 생성되기 전에 스택을 위치로 복원 한 다음 서브 루틴이라는 코드로 돌아갑니다.

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

귀하의 의견에 대한 응답으로 :

EBX 레지스터가 푸시되어 값을 보존하기 위해 튀어 나옵니다. 컴파일러는 아마도이 코드가 아닌 레지스터가 매우 일반적으로 사용되기 때문에 항상이 코드를 넣습니다. 마찬가지로 스택 프레임은 실제로 필요하지 않은 경우에도 ESP를 EBP에 복사하여 항상 생성됩니다.

EAX를 밀어 붙이는 지침은 스택 포인터를 줄이기 위해서만 있습니다. 스택 포인터를 빼는 것보다 짧고 빠르기 때문에 작은 감소를 위해 그렇게되었습니다. 보유하는 공간은 리턴 값을위한 것이며, 다시 컴파일러는 반환 값을 사용하지 않더라도 항상이 작업을 수행합니다.

다이어그램에서 ESP 레지스터는 메모리가 너무 높은 4 바이트를 지속적으로 지적합니다. 스택 포인터는 값을 누른 후 감소되므로 다음 값이 아닌 푸시 된 값을 가리 킵니다. (메모리 주소도 꺼져 있습니다. 스택 레이블이 선언되는 곳이기 때문에 0x20이 아닌 0x600과 비슷합니다.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top