ARM Register
R0 ~ R12 : 범용 레지스터 (다목적 레지스터), R11(스택 프레임 포인터) |
R0 : 함수 리턴 값 저장 (EAX 같은 느낌) |
R0 ~ R3 : 함수 호출 인자 전달 |
R13 ~ R15 : 특수 레지스터 |
R13(SP) : 스택 포인터 : 스택의 맨 위를 가리킴 |
R14(LR) : 링크 레지스터 : 서브루틴 후에 돌아갈 리턴 주소 저장 |
R15(PC) : 프로그램 카운터 : 현재 fetch되고 있는 명령어의 주소 - 따라서 현재 실행되는 명령어의 다음다음 주소 |
CPSR : 현재 프로그램 상태 레지스터 |
ARM 어셈블리 명령어
OP Code<cond> Rd, Rn, Rm
- OP Code : push, add, sub, mov 등 어셈블리 명령어
- Rd : Destination Register, 반드시 R0~R15인 Register만 가능하며 연산작업의 결과값을 저장하는 레지스터
- Rn : Operand1 Register, 반드시 R0~R15인 Register만 가능
- Rm : Operand2, 레지스터뿐만 아니라 상수(#), 주소([]), 쉬프트 연산식 등 다양한 값이 사용가능.
ARM 어셈블리 명령어
참고:https://m.blog.naver.com/gangst11/145839687
1) 산술 연산
- ADD(SUB) Rd, Rn, Rm
Rd = Rn +(-) Rm
2) 비트 연산
- AND Rd, Rn, Rm
Rd = Rn & Rm
EX)AND R0, R1, R2, lsl #2
=> R2를 left shift 2 (<< 2)한 값과 R1을 AND 연산해서 그 결과값을 R0에 저장
- ORR Rd, Rn, Rm
Rd = Rn | Rm - EOR Rd, Rn, Rm
Rd = Rn ^ Rm - BIC Rd, Rn, Rm
Rd = Rn & !Rm
3) 대입 연산
- MOV Rd, Rm
Rd = Rm
4) 쉬프트 연산
- LSL Rm
Rm = Rm << 2 - RSL Rm
Rm = Rm >> 2
5) 분기 명령
- B : C언어에서 goto와 같은 역할, return 하지 않음,분기 주소에는 label,메모리 주소,register를 사용할 수 없음
- BL : C언어에서 function call과 유사한 개념, ARM에서는 서브루틴이 끝난 후 복귀할 주소를 R14 레지스터에 저장
6) 비교 연산
- CMP Rn, Rm : Rn과 Rm을 비교, Rn - Rm을 수행하여 Status Register에 상태를 저장한다.
- push {Rn, Rm} : Rm, Rn 순서로 스택에 push 된다.
- pop {Rn, Rm} : Rm, Rn 순서로 스택에서 pop된다.
CMP : if(Rn - Rm == 0). Rn과 Rm의 값이 같은지 비교하는 명령어
Status Register (4개)
- N (Negative) : 연산결과가 음수인 경우 set 1
- Z (Zero) : 연산결과가 0인 경우 set 1
- C (Carry) : 덧셈 연산에서 Carry가 발생할 경우 set 1, 뺄셈 연산에서 Borrow가 발생할 경우 set 0
- V (Overflow) : signed 연산의 덧셈, 뺄셈에서 overflow가 발생할 경우 set 1
조건부 실행 옵션(14개)
- unsigned 형태로 비교할때 사용.
HS (>=), LO(<), HI(>), LS(<=) - signed 형태로 비교할때 사용.
GE (>=), LT(<), GT(>), LE(<=) - 단순비교인 같다 또는 같지 않다에 사용.
EQ(==), NE(!=) - 연산결과가 음수/양수에 사용.
MI(음수), PL(0또는 양수) - 연산결과가 Overflow가 발생유무에 따라 사용.
VS(overflow), VC(No overflow)
EX)
CMP R0,R1
ADDEQ R0,R2,#100
MOVNE R0,#1
=> R0와 R1의 값을 비교해서 만약 값이 같다면 R0=R2+100을 수행하고, 틀리면 R0에 1을 대입.
메모리 연산
- STR R1, [R0] : [R0] = R1, 메모리 주소에 레지스터 값 저장
- LDR R1, [R0] : R1 = [R0], 해당 메모리 주소의 값을 레지스터에 저장
EX 1)
MOV R0,#0x100
MOV R1,#0xFF
STR R1,[R0]
=> 0x100 번지에 0xFF를 저장.
=> 레지스터를 중괄호[] 안에 넣으면 해당 레지스터의 값이 있는 메모리 주소가 됨(C언어에서 포인터 역할)
EX 2)
STR R1, [R0],#4
=> R0 번지에 R1 레지스터값을 저장하고, R0의 주소값을 +4 증가
EX 3)
STR R1, [R0,#3]
EX 4)
STR R1, [R0,#3]!
=> R0 + 3 번지에 R1 레지스터값을 저장.
=> 중괄호안에서 콤마(,)와 숫자상수 #을 이용해서 해당 R0에서 상대적인 offset 위치의 메모리 주소를 access
=>이때 !가 있으면 R0의 주소값을 +3 증가까지 진행
ARM 간단한 코드 분석 (에필로그, 프롤로그)
참고:https://5kyc1ad.tistory.com/187
hello world를 출력하는 코드를 분석
#include <stdio.h>
int main(int argc, char *argv[]){
printf("hello world\n");
return 0;
}
0x000103fc <+0>: push {r11, lr}
0x00010400 <+4>: add r11, sp, #4
0x00010404 <+8>: sub sp, sp, #8
0x00010408 <+12>: str r0, [r11, #-8]
0x0001040c <+16>: str r1, [r11, #-12]
0x00010410 <+20>: ldr r0, [pc, #16] ; 0x10428 <main+44>
0x00010414 <+24>: bl 0x102dc <puts@plt>
0x00010418 <+28>: mov r3, #0
0x0001041c <+32>: mov r0, r3
0x00010420 <+36>: sub sp, r11, #4
0x00010424 <+40>: pop {r11, pc}
0x00010428 <+44>: muleq r1, r12, r4
ARM에서 stack과 heap은 만드는 사람에 따라 쌓이는 방향이 정해지지만 보통 x86과 유사하게 stack은 낮은 주소로 heap은 높은 주소로 쌓인다
<+0> push {r11, lr} (프롤로그)
lr(r14)과 r11을 순서대로 스택에 push 해준다.
이는 <+40>에서 pop {r11, pc} 를 해주는데 여기에 다시 들어갈 값을 저장하는 함수 프롤로그 부분이다.
<+4> add r11, sp, #4
r11에 sp(r13)+4를 해준값을 저장해준다
여기서 sp는 현재 스택 포인터 즉 스택의 맨 위를 가리킴킨다
<+8> sub sp, sp, #8
sp(스택의 값)을 8바이트 만큼 뺀다.
이는 <+12>, <+16>에서 들어갈 인자 공간을 생성해 주는 코드이다.
<+12> str r0, [r11, #-8]
(r11-8) 번지에 r0값을 넣어준다.
앞서 말했듯이 main 함수의 첫번째 인자 값을 sp - 4에 넣는 코드이다.
<+16> str r1, [r11, #-12]
sp - 8에 main 함수의 두번째 인자 값을 넣는 코드이다.
<+20> ldr r0, [pc, #16] ; 0x10428 <main+44>
r0 값에 pc(r15)+16의 주소 값을 넣어주는 코드이다. 세미콜론을 참고하면 main+44의 주소인 0x10428을 넣는 코드이다.
이는 hello world의 문자열의 주소를 의미한다.
<+24> bl 0x102dc puts@plt
bl 명령어로 함수(puts)를 call 해주는 명령어이다.
앞서 말했듯이 첫번째 인자 r0를 puts해주기에 hello world가 출력될 것이다.
<+28> mov r3, #0
r3에 0값을 넣어주기 위해 작동되는 코드
<+32> mov r0, r3
r0에 r3의 값을 넣어준다.
함수 return시 r0는 return 값이 들어가기 때문에 main 함수의 종료로 return 0을 수행해 주기 위해 실행되는 코드인것 같다.
<+36> sub sp, r11, #4
sp에 r11-4의 값을 넣어준다.준다.
stack pointer를 원상 복귀 해주는 코드인 것 같다.
<+4>에서 stack pointer에 + 4 해준 코드를 원상 복귀 시켜주기 위해 -4를 해주는 코드라고 해석된다.
<+40> pop {r11, pc} (에필로그)
<+0>에서 push 명령어로 stack에 박아놓았던 return address를 다시 pop하여 r11과 pc에 넣어 주는 역할을 하는 코드이다.
이 코드를 통해 PC에 return address가 들어갔기에 다음 코드를 정상적으로 수행할 수 있게 된다.
<+44> muleq r1, r12, r4
r12와 r를 곱한값이 r1과 같은지 비교하는 코드이다
코드에서 이부분이 의미하는 역할은 좀더 공부해봐야 할것같다.
+ stack 그림을 잘못 그려넣은것 같아 빠른 시일내로 수정할 계획입니다.
'임베디드 > ARM' 카테고리의 다른 글
intel 아키텍쳐에서 다른 아키텍쳐 바이너리 실행 (0) | 2021.01.12 |
---|