미새문지

크래프톤 정글 week03, day20 - 어셈블리어, 오퍼랜드, 점프 인스트럭션, 프로시저 본문

크래프톤 정글/TIL

크래프톤 정글 week03, day20 - 어셈블리어, 오퍼랜드, 점프 인스트럭션, 프로시저

문미새 2024. 2. 20. 00:54
728x90
  • 컴퓨터는 메모리를 관리하고 데이터 처리하는 등의 하위 동작들을 인코딩한 연속된 바이트인 기계어 코드를 실행한다. 컴파일러는 프로그램 언어의 규칙, 운영체제의 관례 등에 따라 기계어 코드를 생성한다.
  • GCC C 컴파일러는 기계어 코드를 문자로 표시한 어셈블리 코드의 형태로 출력을 만들어 프로그램의 각 인스트럭션을 만들어 낸다. 그리고 어셈블러와 링커를 호출하여 어셈블리 코드로부터 실행 가능한 기계어 코드를 생성한다.
    • 컴파일러는 전체 컴파일 순서에서 C에서 제공하는 추상화된 실행모델로 표현된 프로그램을 프로세서가 실행하는 매우 기초적인 인스트럭션들로 변환하는 대부분의 일을 수행한다.
    • 기계어 코드 파일의 내용을 조사하려면, 역어셈블러라고 하는 프로그램이 매우 중요해진다.
      • 역어셈블러는 기계어 코드 파일의 바이트 순서에만 전적으로 의존해서 어셈블리 코드를 결정한다.
    • 정수 레지스터  
      • 전체 16개 레지스터의 하위바이트 들은 바이트, 워드(16비트), 더블워드(32비트), 쿼드워드(64비트)씩 접근할 수 있다.

 

어셈블리어(Assembly Language)

  • 0과 1만으로 코딩을 하는 건 너무 불편했기 때문에, 인간에게 조금 더 친숙한 형태로 어셈블리어가 고안이 됐다.
  • 어셈블리어는 저수준 프로그래밍 언어로, 사람이 이해 가능한 기호와 단어를 사용하여 컴퓨터의 기계어를 표현한다.
    • 어셈블리어 명령은 특정 기계어 명령에 직접적으로 대응되며, 이는 고수준 언어와는 다르게 어셈블리어가 하드웨어에 가까운 언어라는 것을 의미한다.
    • 어셈블리어는 기본적으로 CPU의 명령 세트를 사용하는데, 이는 CPU의 종류나 모델에 따라 다르다. 따라서 어셈블리어는 특정 하드웨어에 종속적인 특성을 가진다.
  • 어셈블리어의 명령
    1. 데이터 처리 및 저장 : 덧셈, 곱셈 등의 산술 연산이나 비트 연산 등, 데이터를 처리하고 메모리에 저장한다.
    2. 데이터 전송 : 이러한 명령은 데이터를 메모리와 레지스터 사이에서 전송한다.
    3. 제어 전송 : 분기, 루프, 함수 호출 등 프로그램의 흐름을 제어한다.
  • 저수준 언어로 작성된 프로그램을 CPU가 이해해서 실행하려면 변환 과정이 필요한데, 저급 언어로 작성된 코드는 어셈블러(Assembler)라는 프로그램에 의해 CPU의 ISA 체계에 맞게 기계어로 번역이 된다.
  • 어셈블리어는 하드웨어를 세밀하게 제어할 수 있는 장점이 있지만, 코드 작성이 복잡하고 디버깅이 어렵다는 단점이 있다.
    • 그래서 일반적으로는 임베디드 시스템, 고성능 컴퓨팅 등 특정 분야에서만 사용되며, 일반적인 애플리케이션 개발에는 고수준 언어가 주로 사용된다.
  • 하나의 프로그램은 여러 소스 파일로 구성될 수 있는데, 그 경우에 번역 과정도 각 파일마다 독립적으로 진행해서 기계어로 이뤄진 오브젝트 모듈을 여러 개 만들게 된다.
    • 링커(Linker)
      • 여러 소스파일들을 적절히 합쳐서 하나의 실행 가능한 파일로 만드는 프로그램
    • 로더(Loader)
      • 실행 가능한 파일의 데이터와 코드를 메모리에 올리고 CPU의 제어를 해당 프로그램의 시작 주소로 바꿔줌으로써 프로그램을 실행시키는 프로그램
 

오퍼랜드(Operland)

  • 오퍼랜드는 프로그래밍에서 연산자가 연산을 수행하는 대상을 의미한다. 즉, 연산이 이루어질 때 사용되는 값이나 변수를 가리킨다.
  • 대부분의 인스트럭션은 하나 이상의 오퍼랜드를 가지며, 연산을 수행할 소스(Source)값과 그 결과를 저장할 목적지(Destination)의 위치를 명시한다.
  • 오퍼랜드의 유형
    • 상수(immediate)
      • ATT 형식의 어셈블리 코드에서, 상수는 ‘$’ 기호 다음에 C 표준 서식을 사용하는 정수
        • 예시) $-577, $0x1F
    • 레지스터(register)
      • 레지스터의 내용을 나타낸다.
        • 각각 16개의 64비트, 32비트, 16비트, 8비트 레지스터들의 하위 일부분인 8, 4, 2, 1바이트 중 하나의 레지스터를 가리킨다.
    • 메모리 참조
      • 유효주소(effective address)라고 부르는 계산된 주소에 의해 메모리 위치에 접근하게 된다.
 

점프 인스트럭션(Jump Instruction)

  • 점프 인스트럭션은 프로그래밍에서, 특히 저수준 프로그래밍 언어나 어셈블리 언어에서 사용되는 명령어로, 프로그램의 실행 흐름을 변경하는데 사용
    • CPU가 기본적으로 명령어를 순차적으로 실행하는 대신, 점프 인스트럭션은 프로그램 카운터(PC)를 특정 주소로 변경해 실행 흐름을 해당 주소의 명령어로 이동시킨다.
    • 점프의 목적지는 일반적으로 어셈블리 코드에서는 레이블(label)로 표시한다.
  • 종류
    1. 무조건적 점프(Unconditional Jump) : 프로그램을 지정된 주소로 무조건적으로 이동시킨다. 이를 통해 루프, 함수 호출 등을 구현할 수 있다.
    2. 조건부 점프(Conditional Jump) : 조건부 점프는 특정 조건이 만족될 때만 실행 흐름을 이동시킨다. 이는 if-else문이나 루프의 조건 종료 등에 사용된다.
    3. 함수 호출(Call) : 함수 또는 서브루틴을 호출할 때 사용되며, 현재의 프로그램 카운터를 스택에 저장한 후, 실행 흐름을 함수의 시작 주소로 이동시킨다. 함수가 끝나면, 저장된 주소로 돌아와 프로그램을 계속 실행
    4. 리턴(Return) : 함수의 끝에서 호출한 지점으로 돌아갈 때 사용되며, 스택에 저장되어 있던 주소를 꺼내 프로그램 카운터를 그 주소로 설정한다.
  • 점프 인스트럭션은 프로그램의 논리를 구성하고, 반복, 분기, 함수 호출 등 복잡한 실행 흐름을 가능하게 만드는 핵심 요소이다.
    • 어셈블리 코드에서 점프 목적지는 심볼 테이블(Symbol Table)을 사용해 작성하고, 어셈블러와 링커는 점프 목적지를 적절히 인코딩한다.
      • 심볼 테이블 : 컴파일러나 인터프리터가 소스 코드를 컴파일하거나 실행하는 동안 식별자의 정보를 저장하는 데이터 구조
    • 점프를 인코딩하는 방법은 여러가지가 있으나, 가장 일반적인 방법은 PC 상대적(PC relative) 방법이다.
  • 실행 가능한 기계어 파일을 만들기 위해서 어셈블러는 모든 레이블이 붙은 인스트럭션의 주소를 결정하고, 점프 인스트럭션의 일부분인 '점프 목적지'를 인코딩한다
 

스트링 프리미티브 명령어(String Premitive Command)

  • 바이트, 워드, 더블 워드 배열을 처리하기 위한 5개 그룹의 명령어
    • 이 명령어들을 스트링 프리미티브라고 부르지만, 문자열 뿐 아니라 다른 형태의 데이터 구조에도 사용될 수 있다.
      • 일부 프로그래밍 언어에서는 문자열 메소드를 배열이나 리스트에도 사용할 수 있다. 즉, 이러한 메소드들은 문자열에 대한 작업을 수행하도록 설계되었지만, 그 기능이 더 넓은 범위의 데이터 타입에도 활용될 수 있다는 뜻이다.
  • 명령어
명령어
기능
MOVSB, MOVSW, MOVSD
Move String Data : 데이터를 esi가 주소 지정하는 메모리에서 edi가 주소 지정하는 메모리로 복사한다.
CMPSB, CMPSW, CMPSD
Compare String : esi와 edi가 주소 지정하는 두 메모리의 내용을 비교한다.
SCASB, SCASW, SCASD
Scan String : 누산기(al, ax, eax)와 edi가 주소 지정하는 메모리 내용을 비교한다.
STOSB, STOSW, STOSD
Store String Data : 누산기의 내용을 edi가 주소 지정하는 메모리에 저장한다.
LODSB, LODSW, LODSD
Load Accumulator From String : esi가 주소 지정하는 메모리를 누산기로 적재한다.
 
  • 누산기란?
    • CPU에서 사용되는 특수한 레지스터 중 하나로, 산술 및 논리 연산의 결과 저장에 사용된다.
    • 종류
      • AL(Accumulator Low)
        • 8비트 누산기로, 가장 작은 단위의 데이터를 처리한다.
      • AX(Accumulator)
        • 16비트 누산기로, AL 레지스터보다 큰 데이터를 처리한다.
      • EAX(Extended Accumulator)
        • 32비트 누산기로, AX 레지스터보다 더 큰 데이터를 처리한다.
    • 이렇게 다양한 크기의 누산기 레지스터가 존재하는 이유는, 연산의 종류나 필요한 정밀도에 따라 적절한 레지스터를 선택해 연산의 효율성을 높이기 위함이다.
  • Repeat 접두사
    • 어셈블리어에서 특정 명령어를 여러 번 반복하도록 하는 기능
    • 사용 시 스트링 프리미티브 명령어를 한 번만 수행하는 대신 여러 번 반복해서 수행할 수 있다.
    • Repeat 접두사를 사용할 땐 ECX 레지스터를 카운터로 사용하여 반복 횟수를 지정한다.
      • 예시)
        • ECX 레지스터의 값이 10이라면, Repeat 접두사가 붙은 명령어는 10번 반복해서 실행된다.
    • Repeat 접두사는 여러 가지 상황에서 유용하게 사용될 수 있는데, 예를 들어, 문자열이나 배열과 같은 데이터 구조에서 동일한 연산을 여러 번 수행해야 하는 경우에 이 접두사를 사용하여 코드를 간결하게 만들 수 있다.
REP
ECX > 0 인 동안 반복한다.
REPZ, REPE
ZERO 플래그가 1이고 ECX > 0인 동안 반복한다.
REPNZ, REPNE
ZERO 플래그가 0이고 ECX > 0인 동안 반복한다.
 

프로시저(Procedure)

  • 프로시저는 일련의 명령들을 모아 놓은 코드 블록을 의미한다. 즉, 함수라는 용어와 거의 동의어인데 함수와 프로시저는 약간 다르다.
    • 함수(Function)
      • 주어진 입력에 대한 출력을 계산하는 코드 블록이며, 일련의 연산을 수행한 후 결과를 반환한다.
      • 일반적으로 반환 값이 필요한 경우에 사용된다.
    • 프로시저(Procedure)
      • 프로시저는 특정 작업을 수행하도록 설계된 코드 블록이며, 어떤 값을 반환하지 않고 프로그램의 상태를 변경하거나 원하는 작업을 수행하는데 사용된다.
  • 잘 설계된 소프트웨어에선 프로시저 호출에 대한 인터페이스를 명쾌하고 간결하게 정의하여 프로시저가 프로그램에 어떤 효과를 미치는 지 잘 알려주는데, 일부 구현은 감추어져 있어, 프로시저는 추상화 메커니즘으로 작동한다.
    • 예시)
      • 프로시저 P가 프로시저 Q를 호출하고 Q가 실행된 후에 다시 P로 리턴한다고 가정할 때, 이 동작들은 세 가지 메커니즘 중 하나 이상이 연관된다.
        • 제어권 전달
          • 프로시저 P가 프로시저 Q를 호출하면, 프로그램 카운터는 Q의 시작점을 가리키게 되어 Q가 실행된다.
          • Q의 작업이 끝나면 프로그램 카운터는 다시 P의 다음 인스트럭션을 가리키게 되어 P의 실행을 이어간다.
            • 프로그램 카운터는 CPU가 다음에 실행할 인스트럭션의 메모리 주소를 가리키는 레지스터이다.
        • 데이터 전달
          • P는 하나 이상의 매개변수를 Q에 제공할 수 있어야 하며, Q는 다시 P로 하나의 값을 리턴할 수 있어야 한다.
        • 메모리 할당과 반납
          • Q가 호출되어 시작할 때 지역 변수들을 위한 공간을 할당할 수도 있고, 리턴 할 때 이 저장소를 반납할 수 있다.
  • 런타임 스택(Runtime Stack)
    • 프로그램이 실행되는 동안 함수 호출과 관련된 정보를 저장하는 데이터 구조이며, 프로시저 호출과 재귀함수 호출 관리에 필수적인 역할을 한다.
      • 스택이라는 자료구조의 특징을 가지고 있어, 후입선출(LIFO) 메모리 관리 방식을 활용 가능하다.
    • 프로시저가 호출될 때마다 그에 대한 정보(지역 변수, 반환 주소, 포인터 등)가 "스택 프레임"이라는 블록 형태로 스택에 쌓인다.
      • 프로시저가 종료되면 해당 스택 프레임은 스택에서 제거되고, 제어는 해당 프레임에 저장된 반환 주소로 돌아간다.
  • 스택 프레임(Stack Frame)
    • 런타임 스택에서 함수 호출에 관련된 정보를 담는 구조이며, 각 프로시저 호출 마다 새로운 스택 프레임이 생성되고 종료될 때 해당 스택 프레임은 제거된다.
    • 스택 프레임 구조
      • 지역 변수(Local Variables) : 해당 프로시저에서만 사용되는 변수들
      • 반환 주소(Return Address) : 프로시저가 종료된 후 제어가 돌아갈 코드 위치를 가리킨다.
      • 이전 스택 프레임에 대한 포인터 : 이전에 호출된 프로시저의 스택 프레임을 참조하는데, 이는 프로시저 호출이 중첩될 경우에 각각 호출 상황을 올바르게 유지하기 위함
    • 이 정보들은 프로시저가 정상적으로 실행되고, 제어가 올바르게 반환되도록 보장하는데 중요한 역할을 한다.
  • 데이터 전송
    • 프로시저가 작업을 수행하는 동안 필요한 데이터를 전달 받고 결과를 반환하는 과정에서 데이터 전송이 필요하다.
    • 전송 방식
      • 매개변수(Parameters)를 통한 전달
        • 프로시저는 주로 매개변수를 통해 데이터를 전달 받는데, 이 매개변수는 값 또는 참조로 전달될 수 있으며, 이는 프로그래밍 언어와 프로시저의 설계에 따라 달라진다.
      • 반환 값(Return Value)을 통한 전달
        • 프로시저는 작업을 수행 후, 그 결과를 반환 값으로 전달한다.
          • 반환 값은 호출한 코드에서 사용될 수 있다.
      • 전역 변수(Global Variables)를 사용
        • 프로시저가 직접 접근해 값을 읽고 쓸 수 있는 변수지만, 과도한 전역 변수의 사용은 코드의 복잡성을 증가시키고 버그를 유발하기 때문에 주의가 필요하다.

학습시간 : 10 ~ 25시

728x90