전공/시스템 프로그래밍

[시스템 프로그래밍 7강]Stack and Push Operation in a Assembly

뜨거운 개발자 2023. 4. 28. 10:22

Stack Operations

stack data structure

  • 대표적인 LIFO 구조이다.
  • 그동안 알고있던 건 어플리케이션의 스택이고 여기서 다루는 스택은 runtime 스택이다.

런타임 스택

  • CPU의 하드웨어에서 직접 지원된다.
  • 프로시저를 호출하고 프로시저를 반환하는 매커니즘에서 필수적인 부분이다.
  • 메모리의 한 블럭을 스택으로 사용
  • 메모리의 일정 부분을 스택으로 사용한다. (스택 세그먼트 있다.)
  • 즉 시스템 수준에서 작동해서 서브루틴 호출을 처리한다.

프로시저란

서브루틴을 프로시저라고 부른다.

서브루틴 : 명령어들이 모여있는 메모리 블록 즉 쉽게 생각하면 원시적인 수준의 함수이다.

  • 런타임 스택은 서브루틴 호출을 처리한다.

1. Runtime Stack (32-Bit Mode)

  • CPU에서 ESP(스택포인터)레지스터를 사용해서 직접적으로 관리를 하는 메모리 배열이다.
  • 따라서 ESP를 직접 조작하는 경우는 거의 없다.
    • CALL, RET, PUSH, and POP 같은 간접 명령어로 수행한다.
  • ESP레지스터는 32비트 오프셋을 유지한다.
  • ESP레지스터는 스택의 TOP을 가리키고 있다.
  • 아래 그림에서 스택 포인터 값이 감소하면 스택의 맨위가 아래로 이동한다.

1-1. Push Operation

  1. 스택 포인터 4를 감소시킨다.
  1. 스택 포인터가 가리키는 스택 내 위치로 값을 복사한다.
  1. 런타임 스택은 높은 주소에서 낮은 주소로 메모리에서 아래쪽으로 증가합니다.(이 그림은 top이 아래쪽이다.)

1-2. Pop Operation

  1. 스택에서 값을 제거합니다.
  1. 스택 포인터가 스택에서 다음으로 높은 위치를 가리키도록 증가한다.

1-3 stack aplication

  • 스택은 레지스터를 여러 용도로 사용할 때 편리하게 임시 저장공간으로 활용할 수 있다. (수정 한 후 pop하면 원래 값으로 복원할 수 있기 때문)
  • CALL명령이 실행되면(서브루틴 호출) CPU는 현재 서브루틴의 리턴 주소(복귀 주소)를 스택에 저장한다.
  • 서브루틴을 호출 할 때 함수 인자를 스택에 넣어서 전달한다.
  • 스택은 서브루틴 내부의 로컬 변수를 위한 임시 저장소를 제공한다.

PUSH Instruction(지침)

ESP 감소

– 16비트 피연산자는 ESP를 2만큼 감소시킵니다.

– 32비트 피연산자는 ESP를 4만큼 감소시킵니다.

그 이후 소스 피연산자를 스택에 복사한다.

PUSH reg/mem16
PUSH reg/mem32
PUSH imm32

pop Instruction(지침)

  1. ESP가 가리키는 스택 요소의 내용을 16비트 32비트 대상 피연산자로 복사한다.
  1. 그 후 ESP를 증가시킨다. (16비트면 2 32비트면 4)
POP reg/mem16
POP reg/mem32

PUSHFD and POPFD Instructions(지침)

  • PUSHFD 명령은 스택으로 32비트 EFLAGS 레지스터를 푸시합니다.
  • POPFD 명령은 스택을 팝하는데 EFLAGS로 들어간다.
  • MOV는 플래그를 변수에 복사하는 데 사용할 수 없으므로 플래그를 저장하는 가장 좋은 방법은 PUSHFD입니다.
    • 나중에 이전 값으로 복원할 수 있도록 플래그의 백업 복사본을 만드는 것이 유용한 경우가 있습니다.
  • 종종 PUSHFD 및 POPFD 내에 코드 블록을 묶습니다.
pushfd ;sace the flags
;
;any squejce of statement here
;
popfd ; restore the flag
  • POPFD 명령을 건너뛰는지 아닌지 확인 할 필요가 있다!(PUSH했으면 pop하는게 맞다!! )
  • 플래그 레지스터에 대한 mov연산이 없기 떄문에 활용이 가능하다.

PUSHAD, PUSHA, POPAD, and POPA

D가 붙은게 32비트 버전 D가 안 붙은게 이전버전이다.

  • 스택에 있는 모든 32비트 범용 레지스터를 다음과 같은 순서로 푸쉬한다.
    • EAX, ECX, EDX, EBX, ESP (value before executing PUSHAD), EBP, ESI, and EDI
  • 일반적으로 프뢰시저를 시작할 떄 PUSHAD명령을 수행한다.

16비트 버전

  • PUSHA는 스택의 16비트 범용 레지스터를 다음 순서로 푸시합니다:
    • AX, CX, DX, BX, SP, BP, SI, DI
  • POPA 명령은 동일한 레지스터를 역순으로 팝합니다.
  • 16비트 모드에서 프로그래밍할 때만 PUSHA 및 POPA를 사용해야 합니다.

여러 32비트 레지스터를 수정하는 프로시저를 작성하는 경우 프로시저의 시작 부분에는 PUSHAD를, 끝 부분에는 POPAD를 사용하여 레지스터를 저장 및 복원합니다..!

다만 예외적으로 하나 이상의 레지스터에 결과를 반환하는 프로시저는 PUSHA및 PUSHAD를 사용해서는 안됩니다.

  • 다음 ReadValue프로시저가 EAX 의 정수를 반환한다고 가정할 때..!
  • POPAD호출은 EAX 반환값을 덮어쓴다.

문자열 반전 예시코드


Uploaded by N2T

728x90