이명 : ISA(Instruction Set Architecture)
실제 하드웨어가 어떻게 생겼는지의 설계구조뿐 아니라, CPU 를 돌리기위해 모아놓은 Instruction Set(명령어 집합) 을 포함한 개념이다.
=> 어셈블리 코드를 읽거나 쓰는 데 필요한 프로세서 설계 부분을 의미
ex) 예: 명령어 집합 구분 및 레지스터
cf) Machine Code(기계어) 란?
=> 컴퓨터가 사용하는 언어. 아래와 같이 2진수화된 숫자(2진수 숫자)로 구성되어 있다. (좀 더 정확히말하면, 컴퓨터 CPU가 명령을 처리할 때 사용하는 언어)
0000 1011 1000 1111 0000
0010 0011 1010 0010 0100
Assembly Code(어셈블리어) 란?
=> 기계어도 프로그래밍 언어라서 프로그래밍이 가능하나, 위와 같이 나열된 기계어 코드를 나열하면 일반적인 사람들이 이해하거나 수정하는 작업이 힘들 것이다. 그래서 기계어 숫자를 이해하기 쉬운 단어로 바꿔서 사람들이 쉽게 이해하도록 만든 프로그래밍 언어
메모리는 일단 종류가 DRAM 임
code, data, stack 으로 구성
stack : 우리가 매번 말하던 "힙, 스택" 얘기할때의 그 스택이 맞다!
=> data 는 사용자가 넘겨주는 데이터이고, stack 은 프로그램이 돌아가면서 생기는 데이터를 축적하는 곳이다.
cf) 왜 힙(heap) 은 Memory 에 없지..? => 힙은 logical 개념이라, 물리적으로 무언가를 해줘야한다는 개념이 없다. 스택은 반면 LIFO 라는 물리적 특징을 가지고 있어서 그림 설명에 넣었음!
정리
- CPU 와 Memory 사이에서 data address instruction 을 해주면 데이터가 왔다갔다 한다!
p1.c 와 p2.c 라는 실행파일을 gcc 컴파일러로 컴파일 하는 과정
사용자가 -s 옵션을 붙이지 않았음에도, gcc가 알아서 -s 옵션을 붙인다.
컴파일러의 실행 결과(output) 은 어샘블리어가 된다.
또한 실행파일 확장자가 .c (C program) 였던것들이 모두 .s 로 (asm program) 변한다.
Assembler 라는 컴파일러가 컴파일을 시도하면 .s 실행파일이 확장자가 .o (object program) 로 변한다.
이전까지의 파일은 텍스트 파일이였으나, 여기서 부터는 object 파일을 열어보면 binary 파일로 변한다.
(즉, 컴퓨터가 이해할 수 있는 기계어로 변환됨)
linker 라는 놈이 마지막으로 실행파일을 만든다.
여기까지 왔으면 이미 각 실행 파일들이 컴퓨터가 이해할 수있는 기계어 코드이므로, linker 가 우선순위나 dependency 등을 코드를 읽고 파악해서 처음부터 끝까지 줄을 세워준다.
static library (.a) 를 포함 => 라이브러리는 static 과 dynamic 이라는 2가지 형태가 있다.
static (linking) 라이브러리는 컴파일 시간에 링크를 해주는 애들
dynamic (linking) 라이브러리는 말그대로 "동적으로 링크하여 사용하는 라이브러리이다.
=> 어셈블리어 까지만 내려와도 굉장히 specific 한 프로그래밍 코드여서,
c언어와 같이 (나름) high-level 이며 추상적인 코드를 어셈블리와 같은 low-level 이 변환 및 구현할 수 있는 방법은 굉장히 다양하다!
정수형(integer) 타입 : 1,2,4,8 byte 짜리가 존재
float 형 타입 : 4,8,10 byte 짜리가 존재
유의 : strcture 나 배열이 없다!
포인터와 같이 비슷한 참조 데이터 타입이 어셈블리에 있긴하다.
1) load : 메모리에서 CPU의 register 로 데이터를 이동하는 연산
2) store : register 에서 메모리로 데이터를 이동하는 연산
1) unconditional jump - to - from : c언어의 goto와 유사. 원하는 구문으로 바로 jump 함
2) conditional branch
위 c코드를 어셈블리 코드로 변환하면 아래와 같다.
=> rbx 라는 이름의 register에 가서 rax 의 값을 할당(저장)하라는 명령
c언어 코드와 어셈블리 코드 비교
$gdb sum : GNU 라는 툴로 sum 이라는 실행파일을 열고,
(gdb) disassemble sumstore : disassemble 라는 키워드와 함께 함수 이름(sumstore) 을 넣으면, sumstore 함수의 object 코드가 어셈블리 코드로 변환된다.
(gdb) x/14xb sumstore
cf) gdb 사용법은 현업에 나가서 배우자!
=> 8바이트짜리 register 가 16개 달려있다. 각 register 의 이름은 %rax 와 같이 하드코딩 되어있는 이름이기 떄문에, 어셈블리에서 특정 register 를 쓰고 싶다면 rax, rbx, rcx, ... 와 같이 불러줘야 한다.
cf) 빨간색으로 칠해진 특수한 register 는 시스템에서 사용하는 것이지, 우리가 사용하면 안되는 register 이다!
형태 : movq src, dst
1) Immediate (정수형 상수) : ex) $Ox400
2) register : ex) %rax
=> cf) %rsp 라는 register 는 특수한 register로, 시스템에서 사용하는 레지스터라서 프로그래머가 접근 및 사용 불가능하다!
3) memory : ex) (%rax)
=> 형태가 register 밖에다 괄호 "( )" 를 쳐주면 끝!
source 에 Imm(Immediate) 가 할당된 경우
=> destination 에 register 또는 memory 가 할당될 수 있다.
source 에 Reg(register) 가 할당된 경우
=> destination 에 register 또는 memory 가 할당될 수 있다.
Immediate (상수) 를 어딘가(destination)에 move 해야하는데 상수라는 곳에 move 시킬 수 없으니까, register 또는 memory 에 상수를 옮길 수 있는 것이다.
또한 memory to memory 연산이 없다.
=> 어셈블리 입장에서는 memory 에 있는 데이터를 다른 memory 에 옮길 필요가 없기 때문에 문법적으로 허용x. 어셈블리의 역할은 메모리에 있는 데이터를 CPU 의 register 에 가져다줘서 어떤 명령(instruction) 을 시키는 것이다!