전공/시스템 프로그래밍

[시스템 프로그래밍 7-2] 프로시저(Procedures)와 USER OPERATOR

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

프로시저

  • 일반적으로 서브루틴이라고 하는 것을 어셈블리어에서는 프로시저라고 부른다.

1. PROC Directive

  • 프로시저 끝에는 return 이 와야한다. (ret)
  • 프로시저는 PROC및 ENDP 지시어를 사용해서 선언된다.!
  • 시작 프로시저 이외에는 다른 프로시저를 생성하는 경우 ret명령으로 끝낸다.
  • ret은 cpu가 프로시저가 호출된 위치로 돌아가도록 강제해준다.

main 프로시저와 그외 프로시저 예시

프로시저 내부에서 Label

  • 기본적으로 레이블은 레이블이 선언된 프로시저 내에서만 표시된다.
  • 이 규칙은 JMP 와 loop명령에 영향을 미친다.
  • 예외사항 : 만약 전역 label을 만들고 싶다면 이름 뒤에 이중 콜론(::)으로 식별되는 전역 레이블을 선언하여 이 제한을 해결할 수 있습니다:
  • 프로그램 설계 측면에서 프로시저를 벗어나 점프하거나 반복하는 방법은 권장되지 않는다.

예시 : 3개의 숫자를 합하기

이 예시는 32비트 3개의 정수의 합을 계산한다.

프로시저가 호출되기 전에 이미 EAX,EBX,ECX에 정수가 할당되어있다고 가정.

프로시저는 합계를 EAX로 반환한다.

SumOf PROC
		add eax,ebx
		add eax,ecx
		ret
SumOf ENDP

프로시저 문서화

  • 주석을 잘 달아주자!!
  • 읽어만 보기..!

CALL and RET Instructions

  • CALL 명령은 프로새ㅔ서가 새 메모리 위치에서 실행을 시작하도록 지시해서 프로시저를 호출한다. (프로시저 이동)
  • Ret은 프로시저 호출된 곳으로 다시 이동(프로세서를 프로시저가 호출된 프로그램의 지점으로 되돌린다.)

기계적 관점에서 CALL과 RET명령어

  • Call 명령을 만나면 두단계의 일을 수행한다.
    1. 돌아올 위치를(현재 EIP(istruction pointer) 에 저장된 위치를 ) 스택에 저장한다.(스택에 반환주소 저장)
    1. 호출된 프로시저의 주소를 EIP(명령포인터)로 복사해 저장.
  • (프로시저가 리턴할 준비가 되면..!) Ret 명령어는 스택에서 리턴주소를 다시 명령 포인터로 팝업한다.

예시 CALL과 RET명령

  • 메인에서 CALL 문이 오프셋 00000020에 위치한다고 가정합니다.
  • 일반적으로 이 명령에는 5바이트의 머신 코드가 필요하므로 다음 문(이경우 MOV)은 오프셋 00000025에 위치합니다.
  • 다음으로 MySub의 첫 번째 실행 명령어가 오프셋 00000040에 위치한다고 가정합니다.
  • CALL 명령이 실행되면 호출 다음 주소(00000025)가 스택에 푸시되고 MySub의 주소가 EIP에 로드됩니다.
  • MySub의 모든 명령은 RET 명령까지 실행됩니다.
  • RET 명령이 실행되면 ESP가 가리키는 스택의 값이 EIP로 팝됩니다.
  • 그런 다음 스택의 이전 값을 가리키도록 ESP가 증가합니다.

중첩 프로시저 호출 (3. Nested Procedure Calls)

  • 중첩 프로시저 호출은 호출된 프로시저가 첫 번째 프로시저가 반환되기 전에 다른 프로시저를 호출할 때 발생합니다
  • 스택을 사용하기 떄문에 호출의 역순으로 처리가 되서 가능하다.

4. 레지스터 인자를 프로시저에게 전달하기

  • 프로시저에서 덧셈을 하는등 여러가지 인자를 사용하는 경우에, 프로시저에서 특정 변수를 참고하는 것은 권장되지 않는다.
  • 이 경우 배열의 오프셋과 배열의 요소 갯수를 전달하는 것이 더 일반적이다.
  • 권장되지 않는 예시 : 값 자체를 사용하는 예제
  • 권장 예시 : 호출 프로그램에서 두개의 매개변수를 수신한다.
    • 32비트 정수 배열에 대한 포인터와 배열의 값의 개수 카운터를 매개변수로 사용..!
  • 권장되는 예시 코드 테스트

5. 예시코드 특징

  • 프로시저 대칭구조
  • 레지스터의 값들을 저장해준다.
  • 프로시저 내부에서 어떤 레지스터를 사용하면 그것을 꼭 push pop을 해준다.

중요한 특징

ArraySum 예제에서 ECX와 ESI는 절차 시작 시 스택에 푸시되었다가 마지막에 팝되었습니다. - 이 작업은 레지스터를 수정하는 대부분의 절차에서 일반적입니다.

프로시저에 의해 수정된 레지스터는 항상 저장하고 복원하여 호출 프로그램이 자신의 레지스터 값을 덮어쓰지 않도록 합니다. 다만 예외적으로 반환값으로 사용되는 (일반적으로 EAX)는 허용한다.

USES OPERATOR

이걸 사용하면 수동으로 push pop을 하는 것을 자동으로 해주는 기능을 한다.

문법이 중요하지않고, 우리는 push와 pop을 할 때 값이 가능하게.

USES 연산자를 PROC 지시어와 함께 사용하면 프로시저 내에서 수정된 모든 레지스터의 이름을 나열할 수 있습니다.

USES는 어셈블러에게 두 가지 작업을 수행하도록 지시합니다. – 먼저, 절차 시작 시 스택에 레지스터를 저장하는 PUSH 명령어를 생성합니다. – 둘째, 프로시저가 끝날 때 레지스터 값을 복원하는 POP 명령어를 생성합니다.

  • USES 연산자는 PROC 바로 뒤에 오는데, 그 뒤에 쉼표가 아닌 공백이나 탭으로 구분된 레지스터 목록이 같은 줄에 나옵니다.

시험문제 :Uses operator을 사용하면 어떻게 되는가.

Exception

  • EAX레지스터에는 결과값 저장할 것이니까 Push pop하지마세요!


Uploaded by N2T

728x90