01. 어셈블리어는 네이티브 코드와 1:1로 대응된다!
•
CPU가 해석하고 실행할 수 있는 것은 네이티브 코드(기계어)로 구성된 프로그램밖에 없다. C언어 등으로 작성된 소스 코드는 각 프로그래밍 언어용 컴파일러로 컴파일되어 네이티브 코드로 변환할 수 있다.
•
네이티브보다 약간 고급진 어셈블리어(또는 니모닉)가 존재하는데, 각각의 네이티브 코드에 별명을 붙인 것이다.
◦
이 어셈블리어도 끝에 가서는 네이티브 코드로 변환되어야 하며 이를 수행하는 프로그램을 어셈블러라고 한다.
02. 컴파일러로 어셈블리어 소스 코드 만들기
•
네이티브 코드를 역어셈블하는 방법 외에도 C언어를 어셈블리어의 소스 코드로도 변경할 수 있다.
03. 네이티브 코드로 변환되지 않는 의사 명령어
•
어셈블리어로 이루어진 소스 코드는 네이티브 코드로 변환되는 일반 명령어와 어셈블러 자체에 대한 명령어인 의사 명령어로 구성되어 있다.
•
의사 명령어는 프로그램의 구조나 어셈블의 방법을 어셈블러에게 지시하는 명령어로 어셈블 과정을 거쳐도 네이티브 코드로 변환되지 않는다.
•
의사 명령어로 둘러싸인 부분은 프로그램을 구성하는 명령어나 데이터를 함께 묶은 그룹에 이름을 붙인 것으로 세그먼트라고 부른다.
04. 어셈블리어의 형식 = OP 코드 + 오퍼랜드
•
어셈블리어에서는 CPU에 대한 하나의 명령어를 한 줄로 나타낸다. 어셈블리어의 명령어 형식은 OP code + operand다. operand로만 구성된 명령어도 있다.
◦
OP code는 명령어가 가진 기능을 나타내며 operand는 명령어의 대상이 되는 것을 말한다. 일종의 동사, 목적어로 해당한다.
◦
operand가 둘 이상인 경우 ,를 사용하여 구분한다.
•
사용 가능한 OP code의 종류는 CPU의 종류에 따라 달라지며 앞서 설명한 것처럼 네이티브 코드는 메모리에 적재된 후 실행되는데 메모리에는 네이티브 코드 형식으로 구성된 명령어와 데이터가 저장된다.
•
CPU는 메모리로부터 명령어나 데이터를 읽어 CPU의 내부에 있는 레지스터에 저장한 후 처리한다.
05. 어셈블리어에서 가장 많이 사용하는 mov 명령어
•
명령어 중에서 레지스터나 메모리에 값을 저장할 수 있는 mov 명령어가 가장 많이 사용된다.
06. push와 pop으로 데이터 저장, 읽기를 해결하는 스택
•
프로그램이 실행될 때는 스택이라는 데이터 영역이 메모리에 확보된다.
•
스택은 일시적으로 사용되는 데이터가 저장되는 임시 저장 영역으로 push와 pop으로 데이터를 저장하고 읽어 내는 특징을 가지고 있다.
◦
x86 계열 32비트 CPU에서는 1회의 push 또는 pop으로 32비트 길이의 데이터를 다룰 수 있다.
•
이 명령어들에는 오퍼랜드가 하나만 있는데 이는 무엇을 처리하는 가를 나타낸다.
07. 함수 호출의 원리를 낱낱이 밝혀 보자!
•
함수 호출 작업에서는 스택이 사용된다.
•
어셈블리어 소스 코드를 보면 뒤에 있는 매개변수부터 차례로 스택에 저장되고 함수를 호출한다.
◦
어셈블리어에서 함수명은 함수가 있는 메모리 어드레스를 나타낸다.
08. 쿵탕쿵탕! 함수의 내부에서 이뤄지는 작업들
•
호출 전 ebp 레지스터를 이미 사용했을 가능성이 있으므로 함수 내부에서 사용하는 레지스터는 함수 호출 전의 상태로 되돌릴 수 있도록 스택에 저장해서 보관한다. 그런 다음 처리가 끝나기 직전 pop을 통해 원상복구한다.
•
스택의 주소를 관리하고 있는 esp 레지스터의 값을 ebp 레지스터에 저장하고 주소를 사용해 매개변수들을 참조하여 함수를 처리한다.
•
이후 ret 명령어가 실행되면 스택으로부터 함수가 실행을 끝내고 돌아가야할 곳으로 메모리 주소가 자동으로 팝된다.
09. 글로벌 변수를 위한 영역은 언제나 확보되어 있다!
•
C언어에서는 함수의 외부에 정의된 변수를 글로벌 변수라하고 함수 안에 선언된 변수를 로컬 변수라 한다.
◦
볼랜드 C++에서 초기화된 글로벌 변수는 _DATA라는 세그먼트로 정리되고 초기화되어있지 않은 글로벌 변수는 _BSS 세그먼트로 정리된다.
•
이처럼 글로벌 변수를 위한 영역이 확실히 확보가 되어있으므로 프로그램의 생명주기 동안 글로벌 변수를 참조할 수 있다.
10. 로컬 변수를 위한 영역은 일시적으로 확보된다!
•
로컬 변수를 참조하는 작업은 그 변수가 선언된 함수 안에서만 가능하다. 이 이유는 로컬 변수가 레지스터나 스택을 통해 일시적으로 확보되기 때문이다.