예시: hello.c
파일
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
이 프로그램을 컴파일한 후, 어셈블리 코드로 변환하면 (gcc -S hello.c -o hello.s
), hello.s
파일에는 다음과 같은 어셈블리 명령어가 생성된다.
hello.s
파일 예시 (x86-64 아키텍처 기반):
.section .rodata
.LC0:
.string "Hello, World!"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
leaq .LC0(%rip), %rdi
call printf
movl $0, %eax
popq %rbp
ret
어셈블리 명령어의 구조
어셈블리 명령어는 일반적으로 연산 코드 (opcode)와 피연산자 (operands)로 구성된다.
- 연산 코드 (Opcode): CPU가 수행할 작업을 지시하는 명령어의 이름이다.
- 피연산자 (Operands): 연산의 대상이 되는 데이터나 레지스터를 나타낸다.
예시:
movq %rsp, %rbp
에서movq
는 연산 코드(opcode)이고,%rsp
와%rbp
는 피연산자(operands)다. 이 명령어는 레지스터%rsp
의 값을%rbp
에 복사하는 작업을 수행한다.
어셈블리 명령어의 역할 분석 (예시)
1. .section .rodata
- 역할: 읽기 전용 데이터 섹션을 정의한다. 이 섹션에는 변경되지 않는 상수나 문자열 리터럴이 저장된다.
- 이 예에서는
"Hello, World!"
문자열이.rodata
섹션에 저장된다.
2. .string "Hello, World!"
- 역할:
"Hello, World!"
라는 문자열을 메모리에 저장한다. 어셈블리 코드에서 이 문자열을 사용할 때는 이 위치를 참조한다.
3. .text
- 역할: 실행할 코드(명령어)가 저장될 코드 섹션을 정의한다. 이후에 오는 명령어들이 이 섹션에 배치된다.
4. .globl main
- 역할:
main
함수가 글로벌 심볼임을 선언한다. 이 함수는 외부에서 참조할 수 있으며, 링커가 이를 인식하여 실행의 시작점으로 사용한다.
5. pushq %rbp
- 역할: 현재의 베이스 포인터(%rbp) 값을 스택에 저장한다.
- 이 명령어는 함수의 호출 직후 스택 프레임을 설정하는 작업의 일부이다. 함수가 호출되면 현재 스택 프레임을 저장하고 새로운 프레임을 설정해야 한다.
6. movq %rsp, %rbp
- 역할: 현재의 스택 포인터(%rsp) 값을 베이스 포인터(%rbp)에 복사한다.
- 이 명령어는 스택 프레임을 설정하는 작업으로, 새로운 스택 프레임의 기준점을 설정하기 위한 것이다.
7. leaq .LC0(%rip), %rdi
- 역할:
.LC0
에 저장된"Hello, World!"
문자열의 주소를 계산하고, 이를 레지스터%rdi
에 저장한다. leaq
는 메모리 주소를 계산하는 명령어로,printf
와 같은 함수에 문자열의 주소를 전달할 때 사용된다. 여기서%rip
는 현재 명령어 포인터를 기준으로 오프셋을 계산하는 방식이다.
8. call printf
- 역할:
printf
함수를 호출한다. call
명령어는 함수 호출을 수행하며, 함수가 끝난 후에 돌아올 주소를 스택에 저장한 다음, 함수의 첫 번째 명령어로 제어를 넘긴다.
9. movl $0, %eax
- 역할: 리턴 값 0을
%eax
레지스터에 저장한다. main
함수가 종료될 때 반환하는 값(0)을 레지스터에 저장하여 운영체제에 전달한다.
10. popq %rbp
- 역할: 스택에서 이전의 베이스 포인터 값을 복원한다.
- 함수가 종료될 때, 스택에 저장된 이전의 스택 프레임을 복원하여 호출된 함수의 실행을 마무리한다.
11. ret
- 역할: 함수가 호출된 위치로 돌아간다.
ret
명령어는 스택에 저장된 복귀 주소를 읽어와, 호출된 함수가 끝난 후 호출된 위치로 제어를 넘긴다.
요약: 어셈블리 명령어의 구조와 역할
- 구조: 어셈블리 명령어는 연산 코드와 피연산자로 구성된다.
- 연산 코드(opcode): CPU가 수행할 작업을 정의하는 명령어.
- 피연산자(operands): 연산의 대상이 되는 값, 레지스터 또는 메모리 주소.
- 역할:
- 어셈블리 명령어는 고수준 C 코드의 기능을 CPU가 이해할 수 있는 기계어로 변환하는 중간 단계이다.
- 예를 들어,
printf
와 같은 C 함수 호출은 어셈블리 코드에서call printf
로 변환되며, 이 명령어는 CPU가 해당 함수의 시작 주소로 제어를 넘기는 역할을 한다.
- 실행 과정: 어셈블리 명령어는 CPU가 각 명령어를 순차적으로 실행하여 프로그램이 예상한 동작을 수행하도록 한다.
'cs' 카테고리의 다른 글
[크래프톤 정글] 가상 메모리 와 페이징 * (0) | 2024.10.07 |
---|---|
[크래프톤 정글] cpu의 명령어 사이클 (0) | 2024.10.01 |
[크래프톤 정글 ] hello.c의 실행 과정 (0) | 2024.09.30 |
[크래프톤 정글] cpu 레지스터 (1) | 2024.09.30 |
[크래프톤 정글] 컴퓨터 시스템 13p - 메모리 계층 구조 (0) | 2024.09.15 |