티스토리 뷰

메모리, 주소 접근 모드


1. 전역, 정적 변수 선언

우리는 x86 어셈블리에서 전역 변수와 비슷한 static 변수 지역을 특별한 어셈블러 명령을 이용해서 선언할 수 있으며,

이는 반드시 .DATA 세그먼트에 선언되어야 한다. (세그먼트와 관련된 내용은 추후 추가 예정)


생성할 수 있는 메모리의 크기로는 1, 2, 4bytes 이다.

* signed / unsigned : 최상위 비트를 부호 연산으로 사용하는지의 유무

DB - Byte : 1바이트 > -127 ~ 127(signed), 0~254(unsigned), ASCII Character

DW - Word : 2바이트 > -32767 ~ 32767(signed), 0~65534(unsigned)

DD - Double Word : 4바이트 > word*word 정수형 크기, Pointer


사용 예

.DATA Segment

var    DB 64      ; 0x64 값을 가지고 있는 'var' 이름의 바이트를 선언한다.

var2  DB ?         ; 값을 정하지 않은 상태로 'var2' 이름의 바이트를 선언한다.

  DB 10      ; 0x10 값을 가지고 있는 이름없는 바이트를 선언한다. (이름이 없더라도, 해당 변수가 위치한 메모리 주소로 접근할 수 있다.)

X       DW ?       ; 값을 정하지 않은 상태로 'X' 이름의 워드 - 2bytes 를 선언한다.

Y       DD  300  ; 0x300 값을 가지고 있는 'Y' 이름의 더블 워드 - 4bytes 를 선언한다.


배열이 다양한 차원(1, 2, n... 다차원 배열)을 가지고 있고, 인덱스 번호(var[idx])로 접근할 수 있는 고급 언어(C++, Java, Python) 들 과는 다르게, x86 어셈블리에서는 단순히 데이터가 담겨있는 공간이 메모리에 연속적으로 위치해 있다. 배열은 아래의 예 처럼 단지 변수들을 나열해 놓은 것으로 선언될 수 있다. 또 다른 배열을 선언할 때 사용하는 일반적인 방법으로는 "DUP" 지시어를 이용하거나, 문자열을 사용하는 방법이 있다.

"DUP" 지시어는 어셈블러에게 연속적으로 명령어를 지정된 수 만큼 반복하라고 지시한다. 예를 들어, 4 DUP(2)는 2, 2, 2, 2 와 동일하다.


사용 예

.DATA Segment

z        DD 1, 2, 3    ; 4바이트 크기로 0x1, 0x2, 0x3 데이터를 차례대로 넣는다. 4바이트 크기이므로, &z + 8 = 3이 담겨있는 메모리 주소가 된다. 

bytes DB 10 DUP(?)    ; 1바이트 크기로 10개, 총 10바이트의 메모리를 할당받는다. 이 때 데이터 값이 선언되지 않았으므로, 런타임 시에 결정된다. (흔히 0x0)

arr     DB 100 DUP(0) ; 1바이트 크기로 100개, 총 100바이트의 메모리를 할당받는다. 이 때 데이터는 0x0 으로 초기화된다.

str     DB 'hello', 0       ; 1바이트 크기로 총 6바이트의 메모리를 할당받는다. 이 때, 이 데이터를 흔히 포인터로 접근하게 될 때 해당 문자열의 끝을 확인하기 위한                 null 문자열을 포함해 'hello'(5bytes) + nullbyte(1byte) = 6bytes 가 된다.

(사진은 문자열 이후 null 문자가 들어감을 보이기 위함)





2. 메모리 접근

현대 x86 프로세서는 2^8 메모리 주소까지 접근 할 수 있도록 설계되어 있다. (메모리 주소 - 포인터의 경우에도 0xFFFFFFFF / 32비트이다.)

위의 예제들 처럼, 메모리 위치를 나타내는 데 사용한 라벨(변수 이름) 은 실제 어셈블러를 통해 32비트 크기의 메모리 주소를 나타내는(일명 포인터) 데이터로 바뀌어진다. 추가적으로, 라벨(변수 이름)을 이용해 메모리 위치를 나타내는 것을 지원하기 위해, x86시스템은 메모리 주소에 대해 연산하고 참조하는 유동적인 스키마를 제공한다.


실제 데이터 / 메모리 주소 참조 개념 사용 예 

mov    eax, [ebx]   ; ebx 레지스터의 메모리 주소에 담긴 값을 eax 레지스터로 복사한다. 

mov    [var], ebx   ;  ebx 레지스터의 값을 var에 담긴 메모리 주소 값에 저장한다. (var 에 포인터가 들어 있음)

mov    eax, [esi-4] ; esi 레지스터 값-4 의 메모리 주소에 있는 값을 eax 레지스터로 저장한다.

mov    [esi+eax], cl ; ecx 레지스터의 오른쪽 2바이트를 esi+eax 값의 메모리 주소에 저장한다.

mov    edx, [esi+4*ebx] ;  esi 레지스터와 ebx레지스터*4 한 값에 위치한 데이터를 edx 에 저장한다. (ebx*4 -> dword 사이즈 데이터를 옮길 때.)




3. 메모리 접근 - 크기 관리

일반적으로, 주어진 메모리 주소의 크기는 선언되는 어셈블리 코드 명령어에서 추정된다. 예를 들어서, 위 예시에 작성된 모든 명령어에서, 메모리 주소의 크기는 레지스터 이름(eax, ebx, cl) 에서 추정되어진다. 우리가 32비트 레지스터를 로드했을 때(eax, ebx와 같은), 어셈블러는 그 메모리 주소를 우리가 4바이트 크기로 나타내고 있다고 추정할 것이다. 우리가 1바이트 레지스터의 값을 메모리에 저장하려고 했을 때, 어셈블러는 우리가 그 메모리 주소에 1바이트를 저장하려 했다고 추측할 것이다.


그러나, 특정한 경우에서 이런 방법의 메모리 크기 확인 방법은 모호할 수 있다.

예를 들어, mov    [ebx], 2 의 명령어를 예로 들어 보자.

이 명령어가 데이터 2를 EBX의 메모리 주소에 1바이트로 옮길까? 아마도 2를 나타내는 4바이트의 데이터를 (0x00000002) EBX가 나타내고 있는 메모리 주소에 복사할 것이다. 하지만 이러한 경우에 어셈블러에 명시적으로 메모리 크기를 선언해 줄 수 있는데, BYTE PTR, WORD PTR, DWORD PTR 과 같은 선언자가 1, 2, 4바이트의 크기를 나타내는 역할을 한다.


사용 예

mov    BYTE PTR [ebx], 2          ; 데이터 2를 1바이트 크기로 ebx가 가리키는 메모리 주소에 복사한다.

mov    WORD PTR [ebx]. 2        ; 데이터 2를 2바이트 크기로 ebx가 가리키는 메모리 주소에 복사한다.

mov    DWORD PTR [ebx], 2     ; 데이터 2를 4바이트 크기로 ebx가 가리키는 메모리 주소에 복사한다.




본 문서는 http://www.cs.virginia.edu/~evans/cs216/guides/x86.html 문서의 내용을 번역한 일부와, 

http://dakuo.tistory.com/9 문서의 내용을 참고하였습니다.


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
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
글 보관함