Assembly
https://coding-factory.tistory.com/651
https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&blogId=ndb796&logNo=221054682271
http://yuchi.duckdns.org/xe/Programming_QA/5934
http://yuchi.duckdns.org/xe/Programming_QA/5950
https://sunrinjuntae.tistory.com/24
https://araikuma.tistory.com/600
https://nan491.tistory.com/entry/%EC%8A%A4%ED%83%9DStack-PUSH-POP
https://wikidocs.net/145932
RAM, DRAM
ISA, Instruction Set Architecture, 프로그램 명령 구조
ISA를 얼마나 Abstraction 추상화 했는지에 따라,
→ HLL High-Level Language
→ LLL Low-Level Language (= Machine L/기계어, Assemble L/Assembly)
→ 추상화 수준, Machine L < Assembly < HLL
Assembly 어셈블리
Machine L 명령어와 1:1 대응되는 명령어 집합, Machine L ≈ Assembly
→ 둘 다 CPU에 직접 지시할 명령어의 집합을 만드는 것
→ 제약 사항, Machine L = Assembly
Assembly 주로, 가변인자 함수 직접 구현, HLL/HLL문법으로 할 수 없는 일, MMX/SSE 등 특수 기능
같은 명령어도 CPU/CPU 버전마다 기계어/Assembly가 다름
I.E. Intel x86 계열 CPU 기계어/Assembly ≠ 다른 벤더 CPU
(AMD/Cyrix CPU는 원래 Intel 호환칩, 모토롤라의 x68계열이 대표적인 비 호환칩)
→ 기계어/Assembly는 CPU 종속적, CPU가 달라지면 새 언어를 배워야..
→ High-Level 언어의 등장
Assembler, Assemble, Assembly → Machine L
Compiler, Compile, HLL → Machine L, Assembly
VM, HLL → VM(HW/SW Simulation), VM 설정 후 해당 VM용 Machine L로 Compile
I.E. JAVA, PC에 띄워진 SW인 JVM으로 돌아감
UNIX OS가 C로 쓰인 이후, OS 거의 대부분 C
물론 HW 직접 다루거나, 속도에 민감한 일부는 Assembly로
Windows, Unix, Linux도 대부분의 C + 일부 Assembly로
GUI vs CUI (Console)
Dos (Disk Operating System)
IBM PC가 처음 보급될때 끼워판 OS
Windows 98까지만해도 새까만 화면에 프롬프트 하나 뜨는 도스 부팅이 있었음
Windows 2000/XP 이후 도스 부팅은 불가능
OS 뜨고나면 Command Prompt창을 띄울 수 있는데, 엄연히 NT커널 아래 돌아가는 디자인만 도스창 같은 UI
CUI 프로그램의 대표적인 예는 각종 온라인 게임의 서버 프로그램
- Preprocessor (전처리기)
- 선행자 #, Like #include
- Library, 기능 집합, *.lib (*.dll도 사용하지만 Compile 할 땐 *.lib)
- Header File, Library 가져다 쓸 수 있게 선언해둔 것, *.h
- #include, Header File 쓸 수 있게 하는 지시어
- Variable, 데이터를 담을 수 있는 일정 단위의 Register/Memory
- Struct, 포함된 Variable(Data Type, Struct)들은 메모리 위에 연속적으로 존재
- Array
- Struct 내의 필드(Variable)들의 사이즈는 불규칙
- Array 한 칸의 사이즈는 모두 동일
- 숫자로 정확하게 찾아낼 수 있는 것이다. 인덱스 * 한칸 사이즈
- Pointer
- 메모리의 주소를 다루는데 사용하는 데이터 타입
- VS unsigned 정수형
- 컴파일러에 의해 타입 체크가 이루어짐
- 컴파일러에 의해 몇 가지 산술 연산 제한
- ++/–, 가리키는 데이터 타입 사이즈 만큼
- *.exe (pe File의 한 종류)
- pe Header에 main() Address의 상대번지가 Entry Point로 기록
- *.exe가 로드되고 실행되면 이 주소를 읽어 main() 부터 실행
인자 Argument 전달
일반적으로 스택
32/64-bit 이하 데이터 블록은 스택에 push, 초과는 mov 등으로 카피하고 함수에 진입하여 스택 에서 pop하거나 mov 등으로 카피된 데이타를 억세스
큰 메모리 블럭을 바로 전달하려 하면 다 스택에 카피해야 하므로, 함수 진입시 속도 저하
→ Pointer, push 한번이면 되니까
지역변수, 함수를 벗어날때 소멸 (다른 코드들에 의해 Overwrite 가능)
함수 진입 시, 스택포인터(%SP) - 사이즈 (사용하는 지역변수의 총 사이즈를 계산해서 스택 공간 확보)
함수 리턴 시, SP 원래대로
Debug Interrupt
CPU의 작동 흐름을 간섭하는 일종의 긴급 신호
HW Interrupt, 타이머칩/타이머 Interrupt, 주변장치의 IO/HW Interrupt
SW Interrupt, CPU가 연산도중 발생시키는 Interrupt
/0, 금지된 메모리 영역을 Access → Interrupt 발생
게임 중 ‘잘못된 연산오류~’ 를 본다면 이와 같은 SW Interrupt로 인해 APP Crash
SW Interrupt는 Exception, SW적으로 프로그래머가 발생시킬 수 있음, __asm int 3
버그 유형과 원인, 대처 요령
프로그램이 크래쉬하게 만드는 버그:
- commit 되지 않은 메모리 어드레스 억세스
- 정상 OS하에 돌아가는 모든 SW는, OS로부터 메모리를 할당받아야만 쓸 수 있음
- 스택 영역도 프로세스 초기화시에 할당받은 영역
- 보통 reserve와 commit이라는 과정을 거쳐 메모리를 사용할 수 있게 되는데 commit이 이루어져야 할당받은 어드레스가 실제 물리 메모리에 맵핑된다.
- 만약 commit 되지 않은, 그러니까 물리 메모리에 맵핑 되지 않은 영역을 억세스 하려고 하면 CPU는 Exception 발생
- 또한 0번지는 사용이 불가하도록 OS가 설정해놓았으므로 commit/억세스 불가
- Heap Crash
- HEAP : 메모리를 미리 덩어리로 받아놓고 끊어쓰는 메모리 풀
- I.E. malloc()/free(), new/delete
- 힙의 특성상 할당받은 사이즈를 넘겨서 덮어쓰게 되면 다른 블럭이 깨짐
- Heap Crash가 발생하면 구현하기 나름이지만 눈치채지 못하게 조용하게 돌아갈수도 있고 APP Crash 할 수도, 파일이 열리지 않기도
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
__asm
{
; @@ Register, Bus, Memory (Text, Data, Stack, Heap), CPU (ALU)
; Kernel
; OS가 사용, 플머는 사용 금지/불가
; Stack
; Assembly가 빈번하게, 저장되는 값 저장, Subroutine, Procedure 등
; Stack 자료 구조
; BasePointer 시작 위치를 BP 레지스터, StackPointer 제일 위 위치를 SP 레지스터
; Glow Down, 다른 메모리 영역과 달리 거꾸로 자란다
; DLLs
; Linker가 외부에서 가져온 코드를 합칠 때 사용
; Endianness
; Byte Order, Byte를 배열하는 방법
; Big Endian, 큰 단위가 앞에
; Little Endian, 작은 단위가 앞에
; ----
; 문법 { Intel (주로 쓰임), AT&T }
; 명령어 수행 방식 (ADD EAX EBX, Intel은 EAX += EBX, AT&T는 EBX += EAX)
; 값/숫자 표기 방식 (Intel은 16진수 Prefix h와 2진수 Prefix b, AT&T는 Prefix $)
; 레지스터 표기 방식 (Intel은 그대로, AT&T는 Prefix %)
; 한 줄 주석
COMMENT !
여러 줄 주석
느낌표 대신 @, #, $, % 등도 가능
!
COMMENT !
C 인라인 어셈블리
__asm
{
;
}
!
; 대소문자 상관 없음
; NOP, 0x90
; 아무 작업도 안함, 클럭 소모, 1Byte의 빈 공간 차지
; MOV A B
; MOVE, A에 B 값 넣기, 연산 포함 불가능
; 명령어와 조합하여 크기를 나타내는 접미사
; l Long 4-Byte
; w Word 2-Byte
; b Byte 1-Byte
; I.E. movl, 4-Byte 메모리를 옮긴다
; = 크기가 고정되어 있는 레지스터를, 각 크기별로 쪼개어 쓸 수 있다
; LEA A B
; Load Effective Address, 연산 포함 가능
; LEA EAX, [EAX + 1000]
; INC
; Increase 피 연산자에 1을 더하기
; 연산 결과에 따라 Zero Flag ZF, Overflow Flag OF 세트 가능
; JMP A
; 특정 위치 A로 점프
; JA, JB, JE 등
; ---
; 004013D4 EB 01
; 004013D6 CC
; ---
; 215: jmp label_1
; 216: int 3
; 217: label_1:
; ---
; jmp == 0xEB 0x01
; 어셈블리에서 JMP는 한개지만 실제로 기계어코드로 바뀔때는 옆의 오퍼랜드(위에서는 0x01)에 따라 각각 다른 명령으로 대치된다.
; 이 명령은 1바이트 건너뛰라는 뜻이 되겠다.
; CALL
; 함수를 호출했다가 다시 원래 위치로 돌아올 때
; JMP와 다른 점은 실행한 뒤에 끝나면 RET에 저장하고 다시 원래 상태로 돌아옴
! RET
; 현재 함수가 끝난 뒤에 돌아갈 주소를 지정하기 위해 사용
; PUSH
; 스택에 값 넣기
; POP
; 스택에 있는 값 빼내기
; INT 3, 0xCC
; 디버그 인터럽트
; LEAVE
; 현재까지의 메모리 스택 비우고, EBP를 자신이 호출한 메모리로 채움
; 실행중인 함수를 종료하기 위해 정리하는 작업에 사용
; 문법
; 함수 리턴 값은 RAX, EAX에 저장
; 목적지는 RDI, 출발지는 RSI
; RBP Base Pointer
; RSP Stack Pointer
; RIP 현재 실행할 코드의 주소
; 시스템 콜
; 레지스터 0 초기화
; MOV AX, 0
; (크기 3~4)
; SUB AX, AX
; XOR AX, AX
; (크기 1~2)
add eax, edx;
; eax와 edx의 내용을 더한다.
; 더한값은 eax 레지스터에 저장된다.
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Inline Assembly
int a = 0;
if (a) a = 0;
else a = 1;
//
int a = 0;
__asm
{
mov eax,dword ptr[a]
cmp eax,0
jz lb_set_a1
lb_set_a0:
xor eax,eax
mov dword ptr[a],eax
lb_set_a1:
inc eax
mov dword ptr[a],eax
}
//
int i = 0, s = 0;
lb_loop:
if (i >= 100)
goto lb_exit_loop;
s += i;
I++;
goto lb_loop;
lb_exit_loop:
//
int s = 0;
__asm
{
xor ecx,ecx
xor edx,edx
lb_loop:
inc ecx
add edx,ecx
cmp ecx,100
jnz lb_loop
mov dword ptr[s],edx
}