포스트

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 정수형
        1. 컴파일러에 의해 타입 체크가 이루어짐
        1. 컴파일러에 의해 몇 가지 산술 연산 제한
        1. ++/–, 가리키는 데이터 타입 사이즈 만큼
  • *.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
}

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.