Instruction
Instruction Cycle
- Fetch / Decode / Execute 형태
- Fetch: Program / Object로부터 명령어를 가지고 오는 단계
- Decode: 해당 명령어가 무슨 명령어인지 알아오는 단계
- Execute: OPCode에 해당하는 연산을 실행
- 연산 방식과 Branch 계산법은 주소 비트 수[주소 계산 방식]에 따라 달라짐
Register with Instruction
- 항상 프로그램이 돌아가기 위해서는 변수/상수 선언은 필수적이고[없을 수가 없음]
- 메모리에 이를 저장해서 계산을 진행한다.
- 그러면 명령어 그 자체는?
- '레지스터' 를 이용해서 연산을 진행함.
- 레지스터 = [어떻게 보면] 시스템[Low-Level operation]을 위한 메모리라고 할 수 있음!
X86 Instruction
General Purpose Register
- 말 그대로 여러 목적으로 막 쓸 수 있는 레지스터
- 여러 GPR 중에
rax
는 Return-Address를 의미합니다.
- 다만, 함수의 리턴 값이 들어갈 '수' 있다는 것
- 종류
rax
rcx
rdx
r8
r9
r10
r11
rbx
rsi
rdi
rbp
r12
r13
r14
r15
rsp
Calling Convention
Windows Calling Convention[Int 기준]
- RCX/RDX/R8/R9 순으로 레지스터 지원, 그 후로는 Stack 활용
Windows Calling Convention[Double 기준]
- XMM0/XMM1/XMM2/MXX3 순으로 레지스터 지원, 그 후로는 Stack 활용
System V[Linux/macOS] Calling Convention[Int 기준]
- RDI/RSI/RDX/RCX/R8/R9 순으로 레지스터를 지원하고, 그 후로는 Stack 활용
###3 System V[Linux/macOS] Calling Convention[Double 기준]
- XMM0 ~ XMM7까지 모두 사용, 그 후로는 Stack 활용
RSP -> Stack Pointer
- RSP는 함수 파라미터 혹은 변수를 넣고 뺄때 사용 하는 것으로 알고 있다
RIP -> Instruction Pointer
- RIP는 현재 명령어 그 다음 명령어 주소를 저장하고 있다.
- 꼭 '현재 그 다음' 이 아니라, Decode/Execute 단계에서 Branch 계산이 완료된 경우, RIP를 업데이트 한다.
Flag Register
CF
- Carry Flag
- 2진수 덧셈/뺼샘 시 Carry가 발생한 경우 1, 아니면 0
ZF
SF
- Sign Flag
- 어떠한 계산 중에, 양수이면 0, 음수이면 1
OF
- Overflow Flag
- 어떠한 계산에서 Overflow가 발생 했을때 1, 아니면 0
Data Size
Word, Dword, Qword
- Byte: 8비트
- Word: 16비트
- DWord: 32비트
- QWord: 64비트
하위 레지스터 접근[R**기준]
- R -> E = 하위 32비트
- R -> x[없음] = 하위 16비트
하위 레지스터 접근[R8~R15 기준]
- *d -> 하위 32비트
- *w -> 하위 16비트
- *b -> 하위 8비트
OPCode
- 무엇을 건지[어떠한 Operation인지] 알려주는 코드
- 주로 2진/16진수로 이루어져 있음
Assembly Code
- OP코드는 결국에 하나의 명령어 파이프라인 디코딩 단계 중 한 곳에서 쓰임
- 즉, OP코드 = 명령어 전체 가 아님
- OP코드는 결국 어떠한 명령어인지 '식별'만 하는 존재
- 어찌 되었던 이러한 OP코드를 가지고 어떤 연산인지 확인할 수 있는데 그 연산을 문자로 나타낸 것이
Operand
- OP코드에 따라 정의가 달라짐
- 즉, 64비트 명령어 구조에서 항상 피연산자가 고정된 위치에 담겨있는 것이 아님
- 레지스터가 될 수도, 주소가 될 수도, 어떠한 상수가 될 수도 있음
- 명령어의 인자에 속하는 부분
상수값
mov rcx, 0x0 // rcx = 0x0
mov r8, 0x1 // r8 = 0x1
레지스터
mov rcx, rbx // rcx = rbx
sub rcx, rax // rcx = rcx - rax // -= rax
Addressing Mode
- [
reg
]
reg
에다가 값을 쓰는게 아니라, reg
이 가리키는 주소에다가 값을 쓰기!
*reg = val
-> mov의 경우
- [
reg + d
]
- 배열 포인터 접근 방식과 유사
- 레지스터의 값에 d를 더해서 그 주소에 접근
- [
reg + reg
]
- [
reg + reg * 4 + 3
]
- 계산 순서 = 일반 사칙연산과 동일
- 약간 2차원 배열 혹은 구조체와 비슷..?
Instructions
MOV
mov destination, source
equals to
destination = source
LEA[Load Effective Address]
lea destination, addr
equals to
destination = addr
- mov랑 다른 점
- mov는 데이터 이동이고, lea는 주소를 저장하는 방식
INC/DEC
- i++ / i--와 동일
inc dst
, dec dst
Neg
- i = -i와 동일[부호 반전]
neg dst
NOT
- 비트 반전[
dst = ~dst
]
not dst
Add
- i = i + n과 동일
add dst, src
SUB
- i = i - n과 동일
sub dst, src
imul
- i = i * n과 동일
imul dst, src
and
- [Bitwise] i = i & n과 동일
and dst, src
or
- [Bitwise] i = i | n 과 동일
or dst, src
xor
- [Bitwise] i = i ^ n과 동일
xor dst, src
shl/shr[Logical]
- Shift Left, Shift Right
shl dst, k
== dst << k
shr dst, k
== dst >> k
sal, sar[Arith]
- 수적인 내용을 다루므로 MSB유지
- 내용은 SHL/SHR과 동일
Test
- and 연산을 하지만, 피연산자들에게 영향을 끼치지 않음
- 대신 연산 결과가 음수이면 SF = 1을, 연산 결과가 영이면 ZF를. 1로
CMP
- sub과 거의 동일, 그러나 이 역시 TEST처럼 피연산자 들에게 영향을 미치치 않고
- DST/SRC 관계에 따라 ZF/CF가 바뀜
Jump Family
JMP | Meaning | Condition |
---|
jmp | Jump straight to location | N/A |
je | Jump when ZF is 1 | ZF = 1 |
jne | Jump when ZF is 0 | ZF != 1[ZF = 0] |
jg | jump if greater | one's cmp results greater |
jge | jump if greater or equal | one's cmp results greater or equal |
jl | jump if less | one's cmp results less |
jle | jump if less or equal | one's cmp results less or equal |
Function Prologue/Epilogue
Pro/Epilogue
The RSP
Push/Pop
CMD | Meaning | RSP |
---|
push | Push data to stack | Decrease[Sub] |
Pop | Get latest stack data | Increase[Add |
Procedure Call Instructions
Call
Jump and link
?
- 현재 위치[돌아올 주소]를 스택에 푸쉬 하고
- 특정 장소에 푸쉬
Ret
- 모든 과정을 마친 후, 이 전 함수로 돌아갈때 쓰는 명령어
- 이 때는 함수 에필로그까지 마친 상태이므로 rsp는 Return Address를 가지고 있음