어셈블리어의 개요, 기초 문법(+ x86, arm)

728x90

[어셈블리어(Assembly Language)]
어셈블리어란 프로그래밍 언어 중 하나로, 기계어와 함께 저급언어(Low-level Langauge)에 속한다.
0과 1로만 이루어진 기계어는 인간의 관점에서 이해하고 다루기 매우 어렵기 때문에 이를 보안하기 위해 개발되었으며, 기계어와 1대1로 대응한다는 특징이 있다. 고급언어와 마찬가지로 컴퓨터가 이해할 수 있도록 번역하는 과정을 거친다는 점에서 고급 언어와 기계어 사이에 있는 '중간 언어'라 불리기도 한다.
 
어셈블리어가 어렵다고 평가되는 것 중 하나가 CPU가 채택한 명령어셋(ISA)에 따르는 기계어와 1대1 대응을 하기 때문에 어셈블리어 역시 표준 규격화가 되어있지 않다는 것이다. 그 말인 즉슨, 문법 아키텍처와 CPU 아키텍처에 따라, 그리고 어셈블러의 종류에 따라 문법/매크로 등이 제각각이다.


[Intel 사의 마이크로 아키텍처]
x86과 x86_64 마이크로 아키텍처에서는 문법 아키텍처가 크게 인텔 방식과 AT&T 방식으로 나뉜다. 인텔 방식은 가독성이 뛰어나고, AT&T 방식은 가독성은 떨어지나, 인텔 방식보다 좀 더 많은 정보를 포함하고 있다.

보통 윈도우 플랫폼에서 사용하는 'IDE'들은 인텔 문법 기반의 어셈블러(NASM, MASM)을 기본으로 하는 경우가 많고, 리눅스에 기반한 오픈소스 진영에서는 스탠다드 컴파일러셋에 포함된 'GNU 어셈블러(GAS)'가 AT&T 방식을 사용하고 있다. 
 

현재 공부하는 방향으로 보아 윈도우 플랫폼의 'IDE'를 주로 사용할 것 같아서 인텔 문법 위주로 정리해보았다.(수정 가능성 O)

 
1. Intel 어셈블리 기초(Intel 문법)

Intel문법에서의 어셈블리 명령어는 다음과 같은 구조를 하고 있다.

라벨: <명령어><피연산자1><피연산자2> ;주석​

여기서 라벨은 기계어로 직접 번역되지는 않으며, jmp 명령어를 사용하거나 메모리 주소의 참조가 필요할 때 사용된다.
 
- 라벨: 명령어의 집합. 명령어 또는 데이터의 주소를 나타낸다.
- 명령어: mov, jmp 등의 동작을 지시한다.
- 피연산자: 명령어의 피연산자(operand), 레지스터, 숫자, 문자, 메모리 주소 등이 포함된다.
- 주석: 앞에 세미콜론(;)를 붙여 주석처리 한다.

1) 데이터 이동 명령: mov, push, pop, lea, movsx, movzx

2) 산술 및 논리 명령: add, sub, inc, dex, imul, idiv, and, or, xor, not, neh, shl, shr

3) 특수 명령어: rep, stosb, stosw, stosd,

4) 흐름 제어 명령: cmp, je, jne, jz, jnz, ja, jae, jna, jnae, jb 등

더보기
  • Unsigned 계열(부호가 없는 값)
    • je: jump equal - 비교 결과가 같을 때 점프
    • jne: jump not equal - 비교 결과가 다를 때 점프
    • jz: jump zero - 결과가 0일 때 점프, je와 같음(cmp 명령에서 결과가 같으면 0을 출력합니다).
    • jnz: jump not zero - 결과가 0이 아닐 때 점프
    • ja: jump above - cmp a, b에서 a가 클 때 점프
    • jae: jump above or equal - 크거나 같을 때 점프
    • jna: jump not above - 크지 않을 때 점프
    • jnae: jump not above or equal - 크지 않거나 같지 않을 때 점프
    • jb: jump below - cmp a, b에서 a가 작을 때 점프
    • jbe: jump below or equal - 작거나 같을 때 점프
    • jnb: jump not below - 작지 않을 때 점프
    • jnbe: jump not below or equal - 작지 않거나 같지 않을 때 점프
    • jc: jump carry - 캐리 플래그가 1일 때 점프
    • jnc: jump not carry - 캐리 플래그가 0일 때 점프
    • jnp/jpo: jump not parity / parity odd - 패리티 플래그가 0일 때 / 홀수일 때 점프
    • jp/jpe: jump parity / parity even - 패리티 플래그가 1일 때 / 짝수일 때 점프
    • jecxz: jump ecx zero - ecx 레지스터가 0일때 점프
  • Signed 계열(부호가 있는 값)
    • jg: jump greater - cmp a, b에서 a가 클 때 점프
    • jge: jump greater or equal - 크거나 같을 때 점프
    • jng: jump not greater - 크지 않을 때 점프
    • jnge: jump not greater or equal - 크지 않거나 같지 않을 때 점프
    • jl: jump less - cmp a, b에서 a가 작을 때 점프
    • jle: jump less or equal - 작거나 같을 때 점프
    • jnl: jump not less - 작지 않을 때 점프
    • jnle: jump not less or equal - 작지 않거나 같지 않을 때 점프
    • jo/jno: jump overflow / not overflow - 오버플로 플래그가 1일 때 / 0일 때 점프
    • js/jns: jump sign / not sign - 사인(부호) 플래그가 1일 때(음수) / 0일 때(양수) 점프

4. Status Flags(상태 플래그): C, O, S, Z, A, P

 

https://dsearls.org/courses/C391OrgSys/IntelAL/8086_instruction_set.html - 각 명령어의 상세 설명/예시

https://riptutorial.com/x86/example/6976/flags-register -  x86 상태 플래그 정리 사이트

 

[ARM 사의 마이크로 아키텍처]
arm, arm64 마이크로 아키텍처는 내부적으로 32bit의 데이터 버스와 32bit의 어드레스 버스를 제공한다. ARM는 대표적인 RISC 코어이며, 명령어의 종류가 적으면서도 다양하게 적용시킬 수 있다는 특징이 있다. intel CPU의 경우 명령어에 따라 대략 1~7byte까지 있는데, ARM는 모든 명령어를 한 Word(32bit)로 처리한다. (RISC의 특징)
 
Immediate 오퍼랜드를 지정할 경우 좀 번거롭고, 코딩 시에 몇몇 제한을 따르긴 하나, 모든 명령어를 같은 사이즈로 처리해 파이프라인 구현이 용이하며, 명령어 해석기를 설계할 경우 예회 처리부분이 없으므로 쉽고 고속으로 처리할 수 있다는 장점이 있다.

 

1. ARM 어셈블리 기초

 

ARM 어셈블리의 문법

OP Code<cond> Rd, Rn, Rm

OP Code: ADD, SUB, MOV, LDR, STR 같은 어셈블리 명령어.

cond: 조건부로 명령을 실행해야 할 경우 명령문의 추가 옵션.

Rd: Destination Register, Register R0~R15만 올 수 있다. 연산 작업의 결과값을 저장.

Rn: Operand1 Register, 반드시 Register R0~R15만 올 수 있다.

Rm: Operand2, 레지스터 뿐만 아니라 상수, 주소, 쉬프트 연산식 등 다양한 값을 사용한다.

 

다시 말해 Rd, Rn는 무조건 Register(R0~R15)만 올 수 있고 Rm은 상수(#), 주소([]), 쉬프트 연산식 등의 다양한 값이 사용될 수 있다.

 

ARM에서는 Rm이 ALU로 입력되기 전에 Barrel Shifter를 사용하여 시프트 연산을 끝낸 값을 ALU으로 입력할 수 있다.

1) 산술 연산: ADD, ADC, SUB, SBC, RSB, RSC

2) 비트 연산(논리 연산): AND, ORR, EOR, BIC

3) 대입 연산: MOV, MVN

4) 비교 연산: CMP, CMN, TST, TEQ

5) 분기 명령: B, BL

6) 메모리 접근 명령: LDR, STR

7) 상태 플래그: N, Z, C, Z

- N: 연산 결과가 음의 값을 가질 경우

- Z: 연산 결과가 Zero인 경우

- C: 연산 결과가 Carry를 가질 경우

- V: 연산 결과에 Overflow가 발생한 경우

 

참고자료

 

 

728x90