티스토리 뷰

System/Linux

32bit와 64bit의 차이

Tribal 2015. 12. 29. 16:25

32bit와 64bit의 차이

  • CPU 레지스터(Registers)
  • Calling Conventions(함수 호출 규약)


1. CPU Registers


   CPU의 레지스터란 처리의 지연 시간을 줄이기 위해 사용하고 있는 CPU 내부의 임시 기억장치이다. 컴퓨터가 데이터를 처리할 때 중앙처리장치인 CPU를 통해 처리를 하는데 매 번 메모리에서 처리를 할 때 마다 데이터를 가져오게 되면 시간 낭비가 생기게 되어 지연이 생기게 된다. 그래서 CPU 내부에 임시 기억장치인 레지스터를 두어 메모리에서 데이터를 가져와 저장하고 필요할 때 마다 불러와서 처리를 한다.


  임시 기억장치이기 때문에 이 레지스터 또한 용량을 가지고 있는데 이 용량을 기준으로 CPU를 n bit 레지스터로 나누게 된다. 따라서 32bit 레지스터와 64bit 레지스터로 32bit와 64bit로 나뉘게 된다. 그리고 물론 레지스터의 용량이 클 수록 메모리에서 더 많은 데이터를 가져와 저장할 수 있기 때문에 처리 속도도 더 빨라진다.


  ● 8 bit Register : AH, AL    => H(High), Low(Low) 접미사가 붙는다.
  ● 16bit Register : AX        => AH와 AL이 합쳐졌다.
  ● 32bit Register : EAX      => AX가 확장되어 E(Extend) 접두사가 붙었다.
  ● 64bit Register : RAX      => 접두사 E가 R로 변경되었다.(R이 Register를 뜻한다는 말이 있지만 정확하지 않다.) 


  64bit는 레지스터의 크기가 4byte(32bit)에서 8byte(64bit)로 변경되면서 메모리 주소를 저장하는 레지스터에 맞춰 메모리 주소 또한 4byte에서 8byte로 커지게 된다. 그리고 여기에 맞춰 매핑되는 메모리의 영역의 주소도 변경되었다.  

32bit 매핑

tribal@tribal-virtual-machine:~$ cat /proc/self/maps

08048000-08053000 r-xp 00000000 08:01 131098         /bin/cat

08053000-08054000 r--p 0000a000 08:01 131098         /bin/cat

08054000-08055000 rw-p 0000b000 08:01 131098        /bin/cat

08055000-08076000 rw-p 00000000 00:00 0              [heap]

b7ae3000-b7c15000 r--p 00858000 08:01 141654        /usr/lib/locale/locale-archive

b7c15000-b7e15000 r--p 00000000 08:01 141654        /usr/lib/locale/locale-archive

b7e15000-b7e16000 rw-p 00000000 00:00 0

b7e16000-b7fbe000 r-xp 00000000 08:01 1131            /lib/i386-linux-gnu/libc-2.19.so

b7fbe000-b7fc0000 r--p 001a8000 08:01 1131             /lib/i386-linux-gnu/libc-2.19.so

b7fc0000-b7fc1000 rw-p 001aa000 08:01 1131              /lib/i386-linux-gnu/libc-2.19.so

b7fc1000-b7fc4000 rw-p 00000000 00:00 0

b7fd8000-b7fda000 rw-p 00000000 00:00 0

b7fda000-b7fdc000 r--p 00000000 00:00 0                   [vvar]

b7fdc000-b7fde000 r-xp 00000000 00:00 0                   [vdso]

b7fde000-b7ffe000 r-xp 00000000 08:01 1107               /lib/i386-linux-gnu/ld-2.19.so

b7ffe000-b7fff000 r--p 0001f000 08:01 1107                 /lib/i386-linux-gnu/ld-2.19.so

b7fff000-b8000000 rw-p 00020000 08:01 1107            /lib/i386-linux-gnu/ld-2.19.so

bffdf000-c0000000 rw-p 00000000 00:00 0                  [stack]

 

64bit 매핑

tribal@tribal-virtual-machine:~/debug/test$ cat /proc/self/maps

00400000-0040b000 r-xp 00000000 08:06 259101        /bin/cat

0060a000-0060b000 r--p 0000a000 08:06 259101        /bin/cat

0060b000-0060c000 rw-p 0000b000 08:06 259101        /bin/cat

0060c000-0062d000 rw-p 00000000 00:00 0              [heap]

7ffff7201000-7ffff7a14000 r--p 00000000 08:06 140279  /usr/lib/locale/locale-archive

7ffff7a14000-7ffff7bcf000 r-xp 00000000 08:06 264702   /lib/x86_64-linux-gnu/libc-2.19.so

7ffff7bcf000-7ffff7dcf000 ---p 001bb000 08:06 264702   /lib/x86_64-linux-gnu/libc-2.19.so

7ffff7dcf000-7ffff7dd3000 r--p 001bb000 08:06 264702   /lib/x86_64-linux-gnu/libc-2.19.so

7ffff7dd3000-7ffff7dd5000 rw-p 001bf000 08:06 264702  /lib/x86_64-linux-gnu/libc-2.19.so

7ffff7dd5000-7ffff7dda000 rw-p 00000000 00:00 0

7ffff7dda000-7ffff7dfd000 r-xp 00000000 08:06 264678  /lib/x86_64-linux-gnu/ld-2.19.so

7ffff7fde000-7ffff7fe1000 rw-p 00000000 00:00 0

7ffff7ff6000-7ffff7ff8000 rw-p 00000000 00:00 0

7ffff7ff8000-7ffff7ffa000 r-xp 00000000 00:00 0          [vdso]

7ffff7ffa000-7ffff7ffc000 r--p 00000000 00:00 0          [vvar]

7ffff7ffc000-7ffff7ffd000 r--p 00022000 08:06 264678    /lib/x86_64-linux-gnu/ld-2.19.so

7ffff7ffd000-7ffff7ffe000 rw-p 00023000 08:06 264678   /lib/x86_64-linux-gnu/ld-2.19.so

7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0

7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0            [stack]

ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0     [vsyscall]

  64bit 환경의 주소는 위의 매핑 정보에서 보이는 것과 같이 하드웨어적 문제로 인해 64bit의 주소를 전부 사용하지 않고 48bit의 주소를 사용하고 있다. 이 때, 사용자가 48bit의 주소를 전부 사용하는 것이 아니고 주소를 반으로 나누어 0x00007fffffffffff(47bit) 까지만 사용하고 나머지 이 이상의 주소는 커널이 사용하도록 하고 있다.


  추가로, 1byte(=8bit)가 표현할 수 있는 수의 범위는 2^8인 것 처럼 32bit는 2^32 만큼 64bit는 2^64 만큼 표현이 가능하여 이것에 맞춰 RAM의 크기 또한 달라지게 된다. 32bit는 2^32만큼으로 최대 RAM의 크기 4GB, 64bit는 2^64이므로 최대 RAM의 크기가 8GB가 된다. 그래서 32bit는 RAM을 4GB에서 더 추가하더라도 의미가 없는 것이다.



2. Calling Conventions


   32bit와 64bit의 중요한 차이점 2번째는 함수 호출 규약이다.

32bit Calling Conventions

Calling Conventions

인자 전달 방법

인자 정리 시점

__cdecl

스택

함수 반환 후 인자 정리

__sedcall

스택

함수 반환 전 인자 정리

__fastcall

레지스터 EDX, ECX
이 이후는 스택

함수 반환 전 인자 정리


  32bit는 위와 같이 3가지 함수 호출 규약 방식을 사용하고 있다. 하지만 64bit는 32bit와 달리 fastcall 함수 호출 규약을 한층 더 강화하여 오로지 하나의 함수 호출 규약 방식을 사용하고 있다.

64bit Calling Conventions

Calling Conventions

인자 전달 방법

인자 정리 시점

__fastcall 변형
(Linux ELF)

레지스터로 전달

정수 : EDI, ESI, EDX, ECX, R8, R9
실수 : XMM0 ~ XMM7

       이 이후의 인자는 스택

함수 반환 전 인자 정리

__fastcall 변형
(Windows PE)

레지스터로 전달

정수 : ECX, EDX, R8, R9
실수 : XMM0 ~ XMM4

       이 이후의 인자는 스택

함수 반환 전 인자 정리

  64bit ELF 프로그램 내부의 함수를 예로 들어서 설명하자면 add(int a, int b, float c, int d, float e)라는 함수가 있다. 이 경우 먼저 인자 a는 정수형 int이기 때문에 EDI 레지스터로 들어간다. 다음 b 역시 정수형 int이므로 ESI 레지스터로 들어가고 인자 c는 실수형 float이므로 XMM0 레지스터로 들어가게 된다. 다음은 순서대로 d가 EDX 레지스터, e가 XMM1 레지스터로 향하게 된다.


정리하자면 아래와 같이 된다. 32bit와 동일하게 인자가 뒤에서 부터 들어간다.

add(int a, int b, float c, int d, float e)

float e    -> XMM1

int d       -> EDX

float c     -> XMM0

int b       -> ESI

int a       -> EDI

'System > Linux' 카테고리의 다른 글

SROP 정리  (0) 2016.01.11
ropgadget find  (0) 2016.01.06
포장 함수 gets, fgets의 임시 버퍼  (0) 2015.12.28
쉘 스크립트  (0) 2015.11.30
gcc 컴파일러  (0) 2015.11.28
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   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