ARM을 공부, 개발하기 위한 개발환경을 앞 장에서 다 갖추었다. 이제 이것들을 이용하여 ARM의 명령어들을 배우고 본격적으로 프로그램 분석을 하기 이전에 ARM 프로그램이 작동하는 System 에서 특히 데이터 구조에 대해 알아볼 것이다. 기본적으로, 모든 기계는 0 과 1 으로 표현하는 2진수(Binary + Digit = Bit)를 기반으로 작동한다.

 


Bits :
비트는 [ 0 1 ], [ true false ]로 이루어져있다. 비트를 이용하게 된 이유는 다음과 같다.

 

-       철학적 기반 [ 진실과 거짓 ]

     George Boole 은 당 시대 수학적인 시스템들(정확히 표현하자면 논리학)을 표현하기에 and, or, not 이 기본적인 3가지 연산자와 일련의 Bits만 있으면 충분하다는 것을 발견하였다. 이렇게 논리학을 체계적으로 표현하기 위해 고안한 시스템을 불 대수(Boolean algebra)라 부르며, 이것이 Bits 개념의 시초이며, 이 불 대수는 현대 Bits 시스템의 철학적인 기반이 된다. 후에 C. Shannon2 가지 값만 갖는 불 대수를 연구하여 스위칭 대수’(Switching algebra) 시스템을 고안하였고, 이것이 현대의 디지털 컴퓨터의 근본 원리가 되고 있다. 철학에 기반하여, 효율적인 표현법으로 Bit 가 된것이다.

 

-       기술적 제한 [ 0.0V ~ 0.5V : 0 낮은 상태와 2.8V ~ 3.3V : 1 높은 상태 ]

트렌지스터의 전압상태는 크게 고(2.8V ~ 3.3V), (0.0V ~ 0.5V) 두 가지 상태로 양분될 수 있다. 이렇게 사이 전압 값을 유지하기가 어렵다는 점 때문에 고(1), (0) 두 가지 상태로 컴퓨터의 data를 표현하는 것이 제일 최선의 방법임을 찾아내었다.

 

< 최대 전압, 최소 전압. 이 두 가지 상태만이 확실하다. >

 

 

Memory Organization : Bits를 이용해 컴퓨터의 데이터들을 표현하고, 저장하기 위해 메모리를 사용한다.

 

-       메모리 Memory

메모리는 CPU와 제일 가까우며, 단일 기계 명령어(single language instruction)를 이용해 Load(호출), Save 등으로 '직접 접근 가능'한 가장 큰 저장장치이다. (디스크 저장장치는 따로 OS intervention을 필요로 한다.)

 

-      메모리 주소 Memory Address

메모리에 담긴 자료(content)를 인식하기 위하여 사용되는 주소로, 주소는 1byte 당 할당된다.

< 0x00...01/02/03 등등 하나의 주소당 1 Byte(=8 bits)가 할당되어있다. >

 

-      가상 메모리 Virtual Memory

가상 메모리는 Multicasting kernels을 위해 개발된 메모리 관리 기술로, 컴퓨터 아키텍처상에 존재하는 다양한 컴퓨터 데이터 저장소들(디스크, random-access memory)을 가상화하여 프로그램마다 할당되어 있는 프로세스(한 프로그램당 한 프로세스 할당)들에게 그들 자신만의 메모리 공간을 만들어준다. 이렇게 하여 각 프로세스들은 자신들만의 메모리 공간을 가지게 된다. 각 프로세스에서 같은 ‘가상 메모리 주소 값(virtual address을 가질지라도, 실제로는 각자 다른 자료들 '물리 메모리 주소 값(physical address)'을 가리키고 있다.

 

-      Memory Addresses of 16/32/64bit Words, 워드 단위에 따른 메모리 주소

워드(word)는 특정한 프로세서 디자인에 의해 사용되는 데이터 단위의 표현이다. 약간 구체적으로 설명하자면 워드는 프로세서의 하드웨어들이나 명령어(Instruction set)들에 의해 다루어지는 '기본 단위'로서, 기본적으로 고정된 크기(16/32/64)의 'bits 그룹'이다. 이 워드가 몇 bits 의 단위인지 하는 것은 (word size, word width, or word length) 특정 프로세서 디자인이나, 컴퓨터 아키텍쳐 (ARM 아키텍쳐도 이에 포함)에 매우 중요한 특징이기도 하다. 이유는 이 워드의 크기가 컴퓨터 구조나 연산에 대한 여러 방면에 많은 영향을 미친다. ; 프로세서 안에 있는 대부분의 레지스터는 그 '워드 크기(word size)'를 따르며, 단일 연산에서 작업중인 메모리에서 레지스터 사이를 오가는 큰 크기의 데이터들도 거의 대부분의 아키텍쳐에서(어느 아키텍쳐는 워드로 사용하지 않는다.) 워드 단위로 사용한다. 그렇기 때문에 우리는 프로그램을 작성할때 각 '단위'에 맞추어 align (정렬) 하는 능력이 필요하다. 만약 특정 아키텍쳐의 워드가 몇 비트인지 단위를 모르고 프로그래밍을 한다면, 자연히 '패딩'에 의해 메모리 사용에 있어 '비효율성 문제'가 발생하게된다.

 

< 기본적인 Address 표현에 따라 Words 단위들을 나눈다. >

 

-      데이터를 나열하는 법 Storing Words

8-bits word 단위의 메모리 주소를 갖는 아키텍쳐에서 0x01234567 이라는 자료를 저장하려고 한다. 아래 그림에서 어느 방법이 좋을까?

 

데이터를 나열하는데 있어서, 여러 종류의 방법이 있지만 무엇이 좋다고 단정지을 수 없다.  방법들에 대한 장단점들이 존재하기 때문이다. 처음부터 순서대로 나열하는 Big-Endian 과 역순으로 나열하는 Little-Endian 이 존재한다. 

 

1.     Big-Endian

Big 이라는 뜻은 '자료'에서 자리수가 가장 큰 자료인 MSB 를 시작으로 순서대로 나열한다는 의미이다. '3천 4백 2십 1'을 누구에게든 한번 작성해 보라고하면 종이에 '3421'을 적을 것이다. 이렇게 자료를 순서대로 배열하는 방법으로 우리들이 일반적으로 생각하고 있는 자연적인 나열법이다. 이 방법은 네트워크와 같은 분야에서 '자료들을 다른 쪽으로 전송할 때 받는 쪽에서 순서대로 전송되어야 하므로, 네트워크 분야에서 많이 사용한다. ( '안녕하세요?' 라는 문자를 보냈는데 '?요세하녕안'이라고 문자를 받아버리면 난감하다. )

네트워크 패킷 전송뿐만 아니라, Java(자바는 초기에 인터넷을 위해 개발된 언어였다.), 디버깅할 때 쓰인다,

 

2.     Little-Endian

Little 이라는 뜻은 '자료'에서 자리수가 가장 작은 자료인 LSB 를 시작으로 순서대로 나열한다는 의미이다.

X86 에서 사용되며 작은 자료값(예를 들어, 0x00000007)을 다룰때 Type-casting (타입 변환, 예를 들어 short(2byte)를 char(1byte)로 변환하거나 그 반대의 작업)시에 값이 변화하지 않고 그대로 값을 유지하기 때문에 매우 유용하다.  

 < Big-Endian 과 Little-Endian >

 

뒤로 갈수록 메모리 주소 값이 커진다고 가정하였을때, Big-Endian 으로 저장했을때는 [ 0x00 / 0x00 / 0x00 / 0x17 ] 이렇게 저장되고, Little-Endian 으로 저장했을 경우에는 뒤에 LSB 수부터 메모리에 저장되므로 [ 0x17 / 0x00 / 0x00 / 0x00 ] 이다. 2Byte 인 short 로 이 자료를 읽으면 [ 0x17 / 0x00 ] 이 읽히고, 1Byte 인 char 로 이 자료를 읽으면 [ 0x17 ] 이 읽힌다. 이렇게 작은 수들은 Type-casting 에서 값 변환 없이 자유롭다는 의미이다. '포인터'를 이용할때 포인터는 자료의 맨 앞부분을 가리키기 때문에 0x17 이라는 값을 바로 읽을 수 있는 Little-Endian 이 방법이 Bi-Endian을 채택하고 있는 ARM 에서 ‘Endian 설정 default 값이다. 그 이유는 ARM 프로세싱은 포인터를 이용하기 때문에 그렇다.

 

3.     Bi-Endian

H/W의 환경설정에 의해 Endian의 종류를 목적에 맞게 변화시킬 수 있다.

( ARM 은 Bi-Endian 을 택하고, default 값은 Little-Endian 이다. ) ARM PowerPC에서 사용된다.

 

 

 

Simplified View of ARM Computer in User-Mode*

 

[ 그림 자료 꼭 추가 ]

 

-       레지스터 Register

레지스터는 오직 논리/수학적인 연산에 ‘직접적’으로 사용되는 저장소이다. ‘어셈블리 프로그래밍 모델’에서 저장소는 오직 '레지스터' '메모리' 뿐이다. 어셈블리 프로그래밍 모델의 외부에 있는 (어셈블리 프로그래밍 모델에서 저장소로 간주하지 않는) 저장소로는 다음이 있다.

- 캐시 메모리는 성능을 위한 ‘투명한’ 중간지점의 메모리 시스템이다.

- Disk 컴퓨터 시스템 외부의 I/O 시스템에 존재한다.

 

 

-       ARM 레지스터

ARM의 레지스터는 ‘일반 범용 레지스터(R0 ~ R12), ‘특수 범용 레지스터(R13 ~ R15), '특수 레지스터(CPSR, SPSR)'들을 합쳐서 총 37개에 하나의 레지스터당 32 bits(4 byte)의 크기를 갖는다.

 

하지만 37개의 레지스터를 가질 때는 많은 모드들을 다 작동시켰을때 (FIQ, Undef, Supervisor 등등)의 개수이며, 일반적으로 User & System 모드에서 사용하는 레지스터는 17개이다. 우리는 위의 그림 Simplified View of ARM Computer in User-Mode 을 보면 알 수 있듯이, 오직 User-Mode 에서의 레지스터만을 다룰 것이다. 모든 모드에서 다루는 ARM 레지스터에 대해서는 아래 그림을 참조하고, 더 많은 내용(다른 Mode 예를들어 Supervisor 모드에서의 레지스터)은 한참 나중에 다룰것이나, 굳이 알고싶다면 아래 블로그들을 참조하여라.

 

 

l  일반 범용 레지스터

-       r0, r1, , r12 : 사용자 마음대로 자료를 Save, Load 할 수 있는 자유로운 레지스터.

-       일반 범용 레지스터특수 범용 레지스터를 합쳐서 총 r0부터 r15까지로 총 16개이므로 레지스터를 인식하는데(확인하는데) 4 bits 이면 충분하다. ( 4bits = 2^4 = 16개의 레지스터를 인식 가능하다.)

 

 

l  특수 범용 레지스터

-       SP (r13 : Stack Pointer) : 각 모드당 할당되어 있는 스택의 최상단을 지시하는 레지스터이다.

 

[ 스택이란 :http://blog.naver.com/doridori_33?Redirect=Log&logNo=50035553723 ]

 

+ 스택(Stack) 구조란?

 

스택 구조란 일반 컴퓨터들의 CPU가 갖고 있는 매우 활용도가 높은 기법으로, 메모리에 맨 나중에 저장된 것들을 가장 먼저 꺼내지도록 하는 저장 장치라 할 수 있다. 말 그대로 Stack, 쌓는다는 뜻이다. 스택을 이해할 때 쌓는다는 Stack 의 의미를 사용하기 때문에, 실제 ‘Stack 저장 장치에서도 위로 쌓고 맨 위에서부터 실행한다고 생각하지만, 실제 하드웨어 상으로는 아래방향으로 쌓는다. 메모리 안에 자료를 넣는 행위를 Push 라고 하며, 그것을 꺼내는 행위를 Pop 이라고 한다. 스택도 저장 장치이기 때문에 Data의 위치를 알려줄 주소가 필요하다. 하지만 앞에서 언급했듯이 PopPush연산만 존재하기 때문에, 주소를 알려줄 필드는 단 하나만 있어도 된다. , 스택이라는 통 속에 맨 위에 있는 것이 무엇이냐만 알면 되는 것이다. 그래서 스택 구조 컴퓨터에서는 0-주소 명령어 형식을 가진다.

 

여기서 주소 필드란 피연산자의 주소를 말한다. 맨 위를 가리키는 포인터도 반드시 필요하다. 이것의 이름은 SP(Stack Pointer)로 스택의 Top 값을 담고 있다. 어디에 위치하고 있는지 알려준다는 말이다. 스택에서는 Data를 한 번 꺼내면 사라져버리기 때문에 굳이 주소를 다룰 필요가 없기 때문이다.

 

 

 

-       LR (r14 : Load Register) : 함수 호출 또는 예외가 발생했을시에 복귀 주소를 저장하는 레지스터이다.

 

-       PC (r15 : Program Counter) : 일반적으로 현재 어디를 수행하고 있는 건지를 나타내는 것이라고 표현한다. 정확히 말하자면, 실제로는 실행하는 위치를 가리키는게 아니라 파이프라인에서 명령어를 Fetch 할 위치(주소)를 가리키는 것이다. , 순차적인 명령어 수행 시에 자동으로 증가되면 수행되어야 할명령어의 주소를 가지고 있는 레지스터이다 

 

 

 

 

ARM 은 위 레지스터 부분에서도 말했지만 Load/Store 아키텍처이다.

-       메모리간(memory-to-memory) 데이터 프로세싱 연산들은 지원하지않는다.

-       데이터 값은 사용되기 전에 이동되어져야한다. (레지스터로 이동시킨후 사용한다)

-       듣기에는 비효율적으로 들리지만, 실제로는 그렇지 않다,

 

 

'메모리, 레지스터'. 이렇게 System Data Structure 에 대해 기초적이면서도 ARM 만의 특성들을 알아 보았다. 다음 장에는 이 '시스템 데이터 구조'를 이용하여 (메모리와 레지스터) 여러 실행(Operation)들을 직접 작성해 보기위해 여러 명령어(Instructor)들에 대하여 알아 볼것이다. ARM은 Load/Store 아키텍처라는 것을 이번 장에서 개념적으로 이해하였다면, 다음 장부터는 여러 Instructor 코드들의 구성과 실제로 작동하는것을 통해 눈으로 직접 깨닳을 수 있을것이다.

 

 

 

Posted by 하늘_