32Bit(x86)/64Bit(x64) 차이점 정리
[32Bit / 64Bit 차이점]
32Bit |
64Bit |
|
주소공간 | 2^32 만큼 사용가능 |
2^64만큼 사용가능 (실제로는 2^48) |
레지스터 |
EAX,EBX,ECX,EDX,EBP,ESP,EDI,ESI (총 8개) |
RAX,RBX,RCX,RDX,RBP,RSP,RDI,RSI R8,R9,R10,R11,R12,R13,R14,R15 (총 16개) |
함수 호출 규약 | Cdecl / Stdcall | Fastcall |
#64Bit에서 실제로는 48Bit만 사용 가능한 이유?
64Bit라면 64Bit만큼 주소 공간을 사용할 수 있어야 한다.
0xFFFF FFFF FFFF FFFF
총 16개Byte로 주소공간을 표현한다.
하지만, 64Bit를 전부 사용하게 되면 배보다 배꼽이 더 큰 상황이 생긴다.
2^64 만큼을 관리할 메모리 크기를 생각해보자
데이터의 크기가 20Byte라고 한다면 20Byte*2^64 만큼의 메모리가 필요하다.
(정확한 계산법은 아니겠지만 주객전도되는 상황이라는 점만 이해하도록 하자)
또한, 방대한 주소를 사용함으로써 그 주소들을 관리하기위해서
하드웨어, 소프트웨어적인 측면의 문제들도 생기게 된다.
그래서 현IT상황을 고려해서 타협한게 48Bit만 사용하자는 대안이 나오게 된 것이다.
따라서, 주소지정은 48Bit만 하고 그 상위Bit들은 47Bit를 그대로 복사되도록 짜여져있다.
BitTable로 이해를 돕자면
[주소 0번]
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
[주소 1번]
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
[주소 2번]
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010
.
.
.
[주소 0x0000 7FFF FFFF FFFF]
0000 0000 0000 0000 0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
이렇게 46Bit까지 1로 채워진 시점에서 47Bit가 1이되면
[주소 0xFFFF 8000 0000 0000]
1111 1111 1111 1111 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
그 순간 상위16Bit들은 1로 채워지면서 47Bit도 1로 바뀌게 된다.
이러한 주소표기를
Canonical address space 라고 한다.
중요한 개념이니 64Bit환경을 공부한다면 반드시 알아두도록 한다.
또한, 리눅스 등의 운영체제들은 이러한 주소표기를 이용해서
0 ~ [0x0000 7FFF FFFF FFFF] => 사용자 영역
[0xFFFF 8000 0000 0000] => [0xFFFF FFFF FFFF FFFF] => 커널 영역
으로 사용하고 있다.
#함수 호출 규약 [Cdecl / Fastcall]
32Bit에서는 Cdecl이나 Stdcall과 같은 규약을 사용했다.
그래서 x86에서 함수를 호출할때 Argument(인자값)가 스택을 통해 전달됐다.
실습을 통해 이해해보자
[assemble_Test.c]
실습을 위해 간단한 코드를 짰다.
이 코드를
gcc를 이용해 32Bit로 컴파일 해주면
아키텍처가 i386-32-little인 실행파일이 생긴다.
gdb로 열어보자
[32_assem main]
[32_assem Stack]
보는 것처럼 Stack을 이용해서 1~7을 저장해서 Argument를 전달함을 알 수 있다.
[32_assem t_func]
t_func도 특별한 동작없이 Stack을 통해 인자값을 사용함을 확인할 수 있다.
이번에는 Fastcall 규약을 사용하는 64Bit를 알아보자
Fastcall은 전달하는 6개 이후의 인자값만 Stack을 통해 전달한다.
6개까지는 레지스터를 통해 전달한다.
RDI,RSI,RDX,RCX,R8,R9 를 사용한다.
이번에도 실습을 통해 알아보도록 한다.
마찬가지로 gcc로 아키텍처가 amd64-64-little인 파일을 생성한다.
여기서 amd64는 x86-64를 뜻하는데, 이는 x86 명령어 집합 아키텍처의 64비트 모임이다.
인텔의 IA-64와 완전 다른것이니 착오 없길 바란다.
(자세한 내용 https://ko.wikipedia.org/wiki/X86-64)
이것도 gdb로 열어보도록 하자
[64_assem main]
보면 a7로 넘어가는 인자값 7은 Stack으로 전달되고
1~6 까지의 인자값은 레지스터로 전달됨을 확인할 수있다.
여기서 EDI,ESI,ECX,EDX가 쓰인 이유는 공간절약을 위해 쓰인 것으로 보인다.
[64_assem 레지스터/스택]
레지스터와 스택을 확인해보면 저장이 잘된 모습을 볼 수 있다.
[64_assem t_func]
t_func함수에서도 레지스터를 이용해 스택에 인자값을 스택에 저장하는 모습을 볼 수 있다.
여기서 64Bit환경의 특징을 하나 더 볼 수있는데
스택에 push를 하는게 아니라 미리 sub로 0x20(32)만큼 공간을 확보한 후
mov로 값들은 복사하는 방식으로 인자값을 스택으로 저장한다.
[t_func 스택]
t_func함수에서도 인자값 전달이 잘 된 모습을 볼 수 있다.