티스토리 뷰
3. 프로그램의 기계 수준 표현
3.2 프로그램의 인코딩
gcc 명령은 소스 코드를 실행 코드로 변환하기 위해 일련의 프로그램들을 호출한다.
- C 전처리기가
#include
로 명시된 파일을 코드에 삽입해 주고,#define
으로 선언된 매크로를 확장해 준다. - 두 개의 소스파일의 어셈블리 버전인
p1.s
,p2.s
를 생성한다. - 어셈블러는 어셈블리 코드를 바이너리 목적 코드인
p1.o
와p2.o
로 변환한다. 목적코드는 기계어 코드의 한 유형이다. - 마지막으로 링커는 두 개의 목적코드 파일을 라이브러리 함수들을 구현한 코드와 함께 합쳐서 최종 실행파일인
p
를 생성한다.
이것이 프로세서가 실행할 정확한 코드의 형태이다.
3.2.1 기계수준 코드
컴퓨터 시스템은 보다 간단한추상화 모델을 이용해서 세부 구현내용을 감추면서 추상화의 여러 가지 다른 형태를 사용하고 있다. 이들 중에서 두 가지가 기계수준 프로그래밍에서 특히 중요하다.
- 기계 수준 프로그램의 형식과 동작은 인스트럭션 집합구조(Instruction Set Architecture, ISA)에 의해 정의된다.
- 이 ISA는 프로세서의 상태, 인스트럭션의 형식, 프로세서 상태에 대한 각 인스트럭션들의 영향들을 정의한다.
- 여러 인스트럭션을 동시에 실행하지만, ISA는 하나의 인스트럭션이 다음 인스트럭션의 실행 전에 완료되는 순차적인 실행을 하는 것처럼 프로그램의 동작을 설명한다.
- 기계수준 프로그램이 사용하는 주소는 가상주소이며, 메모리가 매우 큰 바이트 배열인 것처럼 보이게 하는 메모리 모델을 제공한다.
x86-64를 위한 기계어 코드는 본래의 C 코드와는 상당히 다르다.
- 프로그램 카운터는 실행할 다음 인스트럭션의 메모리 주소를 가리킨다.
- 정수 레지스터 파일은 64비트 값을 저장하기 위한 16개의 이름을 붙인 위치를 갖는다.
- 조건코드 레지스터들은 가자 ㅇ최근에 실행한 산술 또는 논리 인스트럭션에 관한 상태 정보를 저장한다.
- 벡터 레지스터들의 집합은 하나 이상의 정수나 부동소수점 값들을 각각 저장할 수 있다.
3.2.2 코드 예제
다음과 같은 프로시저 정의를 포함하고 있는 C 코드 파일 mstore.c를 작성한다고 하자.
long mult2(long, long);
void multstore(long x, long y, long *dest) {
long t = mult2(x, y);
*dest = t;
}
C 컴파일러가 생성한 어셈블리 코드를 보기 위해 명령줄에서 -S
옵션을 사용할 수 있다.gcc -Og -S mstore.c
이것은 GCC로 하여금 컴파일러를 실행하도록 해서 어셈블리 파일 mstore.s
를 만들고 더 이상 진행하지 않는다. 실행 결과의 일부를 발췌하면 다음과 같다.
_multstore: ## @multstore
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
pushq %rbx
pushq %rax
.cfi_offset %rbx, -24
movq %rdx, %rbx
callq _mult2
movq %rax, (%rbx)
addq $8, %rsp
popq %rbx
popq %rbp
retq
.cfi_endproc
## -- End function
.subsections_via_symbols
위 코드의 각 라인은 하나의 기계어 인스트럭션에 대응된다. 예를 들어 pushq 인스트럭션은 레지스터 %rbp
가 프로그램 스택에 저장(push)되어야 한다는 것을 의미한다. 지역변수 이름이나 데이터 타입에 관한 모든 정보는 삭제되었다.
만약 명령어 라인 옵션을 사용한다면 GCC는 코드를 컴파일하고 어셈블할 것이다.gcc -Og -c mstore.c
이것은 바이너리 형식이어서 직접 볼 수 없는 목적코드 파일 mstore.o
를 생성한다. 기계어 코드 파일의 내용을 조사하려면, 역어셈블러라고 하는 프로그램이 매우 중요해진다. 이 프로그램들은 기계어 코드로부터 어셈블리어 코드와 유사한 형태를 생성한다. 리눅스 시스템에서 OBJDUMP("object dump")
에 -d
커맨드 라인을 사용하면 이 역할을 수행할 수 있다.objdump -d mstore.o
- 실행 결과는 다음과 같다.
0000000000000000 <_multstore>: 0: 55 pushq %rbp 1: 48 89 e5 movq %rsp, %rbp 4: 53 pushq %rbx 5: 50 pushq %rax 6: 48 89 d3 movq %rdx, %rbx 9: e8 00 00 00 00 callq 0xe <_multstore+0xe> e: 48 89 03 movq %rax, (%rbx) 11: 48 83 c4 08 addq $8, %rsp 15: 5b popq %rbx 16: 5d popq %rbp 17: c3 retq
실제 실행 가능 코드를 생성하기 위해 링커를 목적코드들에 대해 실행해야 하며, 이 중 한개의 파일은 main함수를 포함해야 한다. main.c
파일에 다음과 같은 함수가 있다고 하자.
#include <stdio.h>
void multstore(long, long, long *);
int main(){
long d;
multstore(2, 3, &d);
printf("2 * 3 --> %ld\n", d);
return 0;
}
long mult2(long a, long b) {
long s = a * b;
return s;
}
실행 가능 프로그램인 prog
를 다음과 같이 생성할 수 있다.gcc -Og -o prog main.c mstore.c
이 파일은 다음과 같이 역어셈블할 수 있다.objdump -d prog
역어셈블러는 다음과 같이 여러 가지 코드를 추출해 낸다.
0000000100003f60 <_multstore>:
100003f60: 55 pushq %rbp
100003f61: 48 89 e5 movq %rsp, %rbp
100003f64: 53 pushq %rbx
100003f65: 50 pushq %rax
100003f66: 48 89 d3 movq %rdx, %rbx
100003f69: e8 e2 ff ff ff callq 0x100003f50 <_mult2>
100003f6e: 48 89 03 movq %rax, (%rbx)
100003f71: 48 83 c4 08 addq $8, %rsp
100003f75: 5b popq %rbx
100003f76: 5d popq %rbp
100003f77: c3 retq
이 코드는 mstore.c
를 역어셈블해서 생성한 것과 거의 동일하다.
- 한가지 중요한 차이는 왼쪽에 주소가 다르다는 것이다.
- 두번째 차이점은 링커가
callq
인스트럭션이 함수mult2
를 호출할 때 사용해야 하는 주소를 채웠다는 점이다. 링커의 한가지 임무는 이들 함수들을 위한 실행 코드의 위치들과 함수 호출을 일치시킨다는 것이다.
3.3 데이터의 형식
인텔 프로세서들이 근본적으로 16비트 구조를 사용하다가 추후에 32비트로 확장했기 때문에 인텔은 워드
라는 단어를 16비트 데이터 타입을 말할 때 사용한다. 32비트는 더블워드
, 64비트는 쿼드워드
라고 부른다.
GCC가 생성한 대부분의 어셈블리 코드 인스트럭션들은 오퍼랜드의 크기를 나타내는 다일문자 접미어를 가지고 있다.
예를 들어 데이터 이동 인스트럭션에는 네 개의 유형이 존재한다.
- movb: 바이트 이동
- movw: 워드 이동
- movl: 더블워드 이동 (
l
은long word
의 뜻) - moveq: 쿼트워드 이동
3.4 정보 접근하기
x86-64 주처리장치 CPU는 64비트 값을 저장할 수 있는 16개의 범용 레지스터를 보유하고 잇다. 이들 레지스터는 정수 데이터와 포인터를 저장하는데 사용한다.
일련의 표준 프로그래밍 관습에 의해 스택을 관리하고, 함수의 인자를 넘겨주고, 함수에서 값을 리턴하고, 로컬 데이터와 임시 데이터를 저장하기 위해 어떻게 레지스터가 사용되는지가 정해진다.
오퍼랜드 식별자 (specifier)
대부분의 인스트럭션은 하나 이상의 오퍼랜드를 가진다. 오퍼랜드는 연산을 수행할 소스(source)값과 그 결과를 저장할 목적지(destination)의 위치를 설명한다. 소스값은 상수로 주어지거나 레지스터나 메모리로부터 읽을 수 있다. 결과 값은 레지스터나 메모리에 저장된다. 그래서 여러 가지 오퍼랜드의 종류는 세 가지 타입으로 구분할 수 있다.
- immediate: 상수 값
- register: 레지스터의 내용
- 메모리 참조: 유효 주소(effective address)라고 부르는 계산된 주소에 의해 메모리 위치에 접근.
3.4.2 데이터 이동 인스트럭션
가장 많이 사용되는 인스트럭션은 데이터를 한 위치에서 다른 위치로 복사하는 명령이다. 오퍼랜드 표시 방법의 일반성 때문에 데이터 이동 인스트럭션이 간단해졌다.
가장 간단한 형태의 데이터 이동 클래스는 MOV 클래스
(movb, movw, movl, movq)이다. 이 인스트럭션들은 소스 위치에서 데이터를 목적지 위치로 어떤 변환도 하지 않고 복사한다. 이들은 모두 유사한 효과를 갖으며 서로 다른 크기의 데이터에 대해 계산한다는 점에서 다르다.
- 소스 오퍼랜드는 상수, 레지스터 저장 값, 메모리 저장 값을 표시한다.
- 목적 오퍼랜드는 레지스터 또는 메모리 주소의 위치를 지정한다.
- 하나의 메모리 위치에서 다른 위치로 어떤 값을 복사하기 위해서는 두 개의 인스트럭션이 필요하다
- 하나는 소스 값을 레지스터에 적재하는 인스트럭션
- 다른 하나는 이 레지스터의 값을 목적지에 쓰기 위한 인스트럭션
- 인스트럭션들의 레지스터 오퍼랜드는 레지스터 16개 중 이름을 붙인 부분이 될 수 있으며, 레지스터의 크기는 인스트럭션의 마지막 문자(b, w, l, q)가 나타내는 크기와 일치해야 한다.
3.5 산술연산과 논리연산
위 그림은 x86-64 정수와 논리연산의 리스트를 보여준다. 예를 들어 인스트럭션 클래스 ADD는 네 개의 덧셈 인스트럭션으로 이루어져 있다.(addb, addw, addl, addq로 각각 바이트, 워드, 더블워드, 쿼드워드 덧셈을 의미)
실제로 각 인스트럭션 클래스는 이들 네 개의 서로 다른 크기의 데이터 연산을 갖는다.
3.5.1 유효주소 적재 (Load Effective Address)
유효주소 적재 인스트럭션 leaq
는 실제로는 movq
인스트럭션의 변형이다. 이것은 메모리에서 레지스터로 읽어들이는 인스트럭션의 형태를 갖지만, 메모리를 전혀 참조하지 않는다. 이 인스트럭션의 첫 번째 오퍼랜드는 일종의 메모리 참조처럼 보이지만, 가리키는 위치에서 읽기를 수행하는 대신에 유효주소를 목적지에 복사한다. 컴파일러는 자주 실제 유효 주소 계산과 무관한 경우에 leaq
를 적절히 사용하곤 한다. 목적 오퍼랜드는 반드시 레지스터만 올 수 있다.
3.5.2 단항 및 이항 연산
그림의 두번째 그룹에서의 연산은 하나의 오퍼랜드가 소스와 목적지로 동시에 사용되는 단항 연산이다. 이 오퍼랜드는 레지스터나 메모리 위치가 될 수 있다.
그림의 세번째 그룹에서은 이항 연산들로 구성되며, 두번째 오퍼랜드는 소스이면서 목적지로 사용된다. 그렇지만 소스가 먼저 오고, 나중에 목적지가 나온다는 점에 유의해야한다. 예를 들어 subq %raw, %rdx
는 레지스터 %rdx
에서 %rax
값만큼 빼준다.
첫 번째 오퍼랜드는 상수나 레지스터, 메모리 위치가 올 수 있다. 두 번째는 레지스터나 메모리가 올 수 있다. MOV
인스트럭션에서처럼 두 개의 오퍼랜드가 모두 메모리 위치가 될 수는 없다. 두 번째 오퍼랜드가 메모리 위치일 때 프로세서가 메모리에서 값을 읽고, 연산을 하고, 그 결과를 다시 메모리에 써야 한다는 점에 유의해야 한다.
3.5.3 쉬프트 연산
마지막 그룹은 쉬프트 연산으로 구성되며, 쉬프트 하는 크기를 먼저 주고, 쉬프트 할 값을 두번째로 준다. 산술과 논리형 우측 쉬프트가 모두 가능하다. 원칙적으로 1바이트 쉬프트 양은 쉬프트 양을 2^8 - 1 = 255까지 가능하도록 한다.
쉬프트 연산의 목적 오퍼랜드는 레지스터나 메모리 위치가 될 수 있다.
'Computer science > CSAPP' 카테고리의 다른 글
Chapter 3. 프로그램의 기계수준 표현 - 3 (0) | 2022.09.27 |
---|---|
Chapter 3. 프로그램의 기계수준 표현 - 2 (0) | 2022.09.26 |
Chapter 2. 정보의 표현과 처리 - 2 (0) | 2022.09.22 |
Chapter 2. 정보의 표현과 처리 - 1 (0) | 2022.09.21 |
[CSAPP] 1. 컴퓨터 시스템으로의 여행 (0) | 2022.09.20 |
- Total
- Today
- Yesterday
- DFS
- elasticsaerch
- CSAPP
- Algorithm
- 프로그래머스
- OS
- Espher
- sqoop
- 네트워크
- logstash
- oozie
- Elasticsearch
- DP
- 파이썬
- CS
- Flutter
- HDFS
- 백준
- BOJ
- heapq
- kubernetes
- 빅데이터
- kafka
- mahout
- cka
- Hadoop
- GROK
- Python
- 이코테
- 빅데이터를지탱하는기술
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |