어셈블리를 처음부터 이해하자 -1-
assembly

어셈블리를 처음부터 이해해자.

어셈블리를 대충 알고 코딩할려니 아무것도 모르겠더라. C#은 아무것도 몰라도 대충하면 뭐라도 나오던데, 어셈블리는 안되는걸 보고 고급 언어란게 이런 의미인가 싶더라..

여튼 어셈블리를 수박겉핥기로라도 알아야될 것같아서 써본다.

글로만 표현해서 가독성이 좋지않다. 아무래도 나중에 내가 다시 이 문서를 찾아봤을 떄를 위해서 쓰는 글이기에 나에게 초점을 맞춰서 썼다.



CPU 구성

어셈블리를 알기위해서 cpu를 알면 더 이해하기 쉽게 간단하게 먼저 설명한다.

cpu가 무엇인지는 따로 설명하지 않는다.

CPU의 구성은 설명하는 사람마다 범위가 다르다. 좁은 범위로는 cpu가 연산만을 하기위해 필요한 것들만 모아놓았고, 넓은 범위로는 효율적인 연산을 위해 다른 시스템 구성요소등을 포함한 범위를 모아놓기도 했다.
나는 넓은 범위보다는 좁은 범위로 설명하겠다. 넓은 범위로 설명할거면 cpu로 따로 포스팅하는게 낫기 때문이다.

CPU는 레지스터, 제어장치, 산술논리연산장치으로 나뉘어져있다.

  1. 레지스터는 연산에 필요한 데이터를 임시적으로 저장하는 메모리다.
  2. 제어장치는 연산의 실행 순서를 제어한다.
  3. 산술논리연산장치는 제어장치가 지정한 연산을 실행한다.

제어장치에서 연산 명령을 내리면, 레지스터에서 필요한 데이터를 읽고, 산술논리장치연산에서 연산을 실행한다. 라고 생각하면 편하다.


명령어 구성

어셈블리를 알기위해서 명령어 구조를 알면 더 이해하기 쉽게 간단하게 먼저 설명한다.

마찮가지로 명령어가 무엇인지는 따로 설명하지 않는다.

명령어 구성는 크게 연산자부와 모드, 자료부로 나뉘어진다.

  1. 연산자는 실제 실행할 명령어 종류(연산 및 논리 등등)를 지정한다. 당연하지만, 연산의 종류에 따라서 자료부를 사용하는 방식이 달라진다.
  2. 모드는 주소부의 유요한 주소가 결정되는 방법을 지정한다. 유효한 주소가 결정되는 방법이 직접이면 0, 간적이면 1로 표시한다.
  3. 자료부는 데이터에 대한 정보를 표시한다. 주로 데이터, 레지스터의 번호, 주소값이 담긴다.

이떄, 명령어에서 주소가 지정되는 방식이 여러가지가 있다. 자료부에 데이터, 레지스터, 주소가 담길 수 있기에 접근하는 방식이 달라지는데, 이것은 여기서 다루지 않겠다.


레지스터란

cpu가 요청을 처리하는데 필요한 데이터를 일시적으로 저장하는 다목적 공간(메모리) 프로세서 내부에 있는 작은 메모리.

용도에 따라서 레지스터의 종류를 나눌 수 있다.

범용, 세그먼트, 포인터, 인덱스, 플래그 레지스터…등등이 있다. 그중 초반에 자주 쓰일 것같은 범용과 포인터 레지스터를 먼저 알면 될 것같다.


범용 레지스터

범용 레지스트는 4종류로 ax,bx,cx,dx로 4가지가 있다. 64bit에는 R8~R15 레지스터가 더 있지만, 이것들은 특별한 용도가 없기에 따로 설명할 필요는 없을 것같다.
또한 주로 사용하는 범용 레지스터는 위 4가지기에 이것부터 아는 것이 중요할 것같다.

범용 레지스터들은 각각의 용도에 따라서 메모리를 나눠서 사용한다. 용도가 다를뿐 구분하는 방식은 같기에 먼저 설명하고자 한다.

범용 레지스터들은 절반으로 메모리를 나눈다. 예를들어서 64비트인 rax의 우측 절반은 eax로, 다시 rax의 우측 절반은 ax로, ax의 좌측 절반은 hax, ax의 우측 절반은 lax로 나눠서 부른다.
rbx 레지스터는 ebx, bx, hbx, lbx로 나눌 수 있다.

레지스터의 bit가 증가할떄마다 단순히 증가한 메모리에 대한 이름을 붙인게 전부다. 어렵게 생각할 필요는 없다.

  1. ax 레지스터
    산술, 논리 연산의 수행 및 함수의 반환값이 저장된다.

  2. bx 레지스터
    메모리 주소를 저장하는 레지스터

  3. cx 레지스터
    반복문의 반복 카운터로 사용되는 레지스터 반복할 횟수를 저장

  4. rd 레지스터
    eax와 함께 쓰이는 레지스터. 큰 수의 연산이 이루어질떄에 사용된다. 또한 부호 확장 명령 등에도 쓰인다.

(※부호확장이란, 숫자의 부호를 저장하는 이진수의 bit를 확장시키는 산술연산자)


포인터 레지스터

포인터 레지스터 또한 범용 레지스터안에 포함된다. 그러나, 특정 메모리의 주소값(포인터)을 저장하는 용도의 레지스터기에 따로 분리했다.

또한 범용 레지스터를 제외하고, 다른 레지스터들은 16bit 어셈블러 이후에 만들어졌다. 따라서 8bit 레지스터가 아닌 16bit 레지스터까지만 쪼개진다.

  1. bp 레지스터
    현재 스택에 tail을 가리키는 포인터 새로운 함수가 호출될때마다 ebp 레지스터값은 지금까지 사용했던 스택의 top의 주소를 저장함으로서 새로운 stack이 시작되는 것처럼 보이게 만든다.
    따라서 ebp는 새로운 함수가 호출되거나 현재 실행중인 함수가 종료되어 리턴될때마다 갱신된다.

  2. sp 레지스터
    현재 스택의 top의 위치를 저장하는 포인터.
    esp는 다음 data를 push할 위치가 아니라 다음에 pop했을 때 뽑아낼 데이터의 위치를 가리킴
    push가 한 번 발생할때마다 값이 감소된다. 증가가 아닌 감소인 이유는 스택의 tail이 가장 큰 값을 가지고 한 프레임당 값이 작아지기 떄문이다.

  3. si 레지스터 스트림 명령에서 소스를 가리키는 포인터.

  4. di 레지스터 스트림 명령에서 소스의 끝을 가리키는 포인터


어셈블리어란?

이제 어셈블리어에 대해서 쓰고자한다.

프로그래밍하면 빠질 수 없는 것이 기계어다. 이것을 먼저 알아야하는데, 기계어는 단순히 cpu가 해독하고 실행할 수 있는 0과 1의 비트단위의 컴퓨터 언어다.


하지만 컴퓨터 언어는 컴퓨터 언어기에, 사람이 0과 1로 컴퓨터에게 명령을 내리는 일은 굉장히 어렵다. 그렇기에 사용자(프로그래머)가 컴퓨터에 기계어로 명령을 내리기 쉽게하기위해 만들어진 언어가 어셈블리어다.


스텍프레임이란

여튼 이 기본적인 기능을 만들기위해선 스택 프레임을 이해해야한다.
왜냐하면 어셈블리로 코딩한다는 것은 스택프레임을 만든다는 것과 동일한 말이기 떄문이다.


스택프레임의 개념이 어렵진않다. 스택프레임은 절차 또는 함수의 호출에 따라서 그와 관계되는 데이터를 저장해두는 스택 영역을 말한다.

즉 프로세서가 실행할 명령들을 프로세스안의 절차(코드)에 따라서 저장한 영역을 스택프레임이라고 한다.


다만, 이 스택프레임을 관리하는 것이 처음에 정말 어렵다. 외워야할 것이 많다.



'프로그래밍 > 프로그래밍 관련' 카테고리의 다른 글

AES 암호화  (0) 2019.01.13
UML을 살펴보자  (0) 2019.01.13
std::tuple을 알아보자.  (0) 2018.04.26
Lambda (람다)  (0) 2018.04.25
함수 포인터로 함수명 변경하기 (완결)  (0) 2018.04.24
더보기

댓글,

Lowpoly

게임 서버 프로그래머 지망생