메뉴 건너뛰기


Developer > Application


출처 : http://minzkn.wowdns.com:2744/phpBB2/viewtopic.php?t=819


이 내용은 제가 기존에 만들었던 여러 소스(mzapi) 들을 64bit 에서 동작하도록 포팅하면서 발생했던 고려사항들을 정리합니다. (최신 내용은 http://minzkn.wowdns.com:2744/phpBB2/viewtopic.php?t=819 에서 확인할수 있습니다. 


1. 변수형의 크기는 어떻게 달라지는가? 32bit 환경에서 64bit 환경으로 옮겨가면서 달라진 변수형은 다음과 같습니다. 

int 형은 32bit 를 유지합니다. 
short 형은 16bit 를 유지합니다. 
long 형은 32bit에서 64bit로 확장됩니다. 
long long 형은 64bit 를 유지합니다. 
pointer 형은 32bit 에서 64bit로 확장딥니다. 
long double 형이 12bytes 에서 16bytes로 확장됩니다. 
size_t과 ssize_t 형이 32bit 에서 64bit 로 확장됩니다. 
이와 관련된 typedef 문 역시 확장됩니다. 


2. 64bit linux platforms 

코드:
 Architecture    | uname -m | Size | Endian | Libpath | Miscellaneous 
 ----------------+----------+------+--------+---------+-------------------------------- 
 Alpha           | alpha    | LP64 | little | lib     | 
 AMD64           | x86_64   | LP64 | little | lib64   | executes x86 code natively 
 IPF             | ia64     | LP64 | little | lib     | executes x86 code via emulation 
 MIPS64          | mips64   | LP64 | both   | lib64   | executes MIPS code natively 
 PowerPC64       | ppc64    | LP64 | big    | lib64   | executes PowerPC code natively 
 Sparc64         | sparc64  | LP64 | big    | lib64   | executes Sparc code natively 
 PA-RISC64       | parisc64 | LP64 | big    | -       | only kernel support, no 64-bit 
                 |          |      |        |         | executes 32-bit PA-RISC code 
 zSeries (s390x) | s390x    | LP64 | big    | lib64   | executes s390 code natively



3. 문제가 되는 코드들 

(1) 크기가 다른 변수형에 대한 포인터를 사용한 경우 (스택붕괴가 우려되는 경우) 
getsockopt의 size인자가 socklen_t 로 정의되는 경우 아래의 코드는 s_socklen 변수를 int로 정의한것에서 문제가 발생할수 있습니다. 

코드:
int s_socklen; 
s_result = getsockopt(s_socket, s_level, s_optname, &s_optval, &s_socklen); 


이것은 다음과 같이 변경되어야 합니다. 이것은 int와 socklen_t 는 크기가 다르기 때문입니다. 

코드:
socklen_t s_socklen; 
s_result = getsockopt(s_socket, s_level, s_optname, &s_optval, &s_socklen); 



(2) 조건식을 int 로 받는 함수에 포인터를 넘겨줄경우 (컴파일러 경고) 
일반적으로 다음과 같은 함수가 있을때 

코드:
void my_assert(int s___EXPRESSION__) 

 if(!s___EXPRESSION__)return; 
 (void)fprintf(stderr, "assert....\n"); 
}


위와 같은 함수에 조건문을 사용할때 

코드:
void *s_ptr = NULL; 
my_assert(s_ptr);


다음과 같이 변경하여야 합니다. 

코드:
void *s_ptr = NULL; 
my_assert(s_ptr != NULL);


이것은 포인터가 int 형과 다른 크기이므로 포인터는 int로 casting 되면서 잘리게 되고 NULL포인터가 아님에도 하위 32bit가 0인경우 my_assert 는 의도하지 않은 동작을 할수 있기 때문입니다. 

(3) dword 정의 (만약 이렇게 써왔다면..) 
다음과 같이 my_DWORD를 정의하여 사용했다면 

코드:
#define my_DWORD unsigned long int


다음과 같이 변경하여야 합니다. 

코드:
#define my_DWORD unsigned int



(4) x86계열 어셈블리 호환성 (안바꿔도 동작한다.) 
예를 들어서 atomic exchange 를 다음과 같이 C함수로 구현했다면 

코드:

int my_atomic_exchange(int * volatile s_to, int s_value) 

 __asm volatile ("xchgl (%2), %0\n\t" : "=r"(s_value) : "0"(s_value), "r"(s_to) : "memory"); 
 return(s_value); 


전혀 수정할 필요없이 그대로 컴파일 가능합니다. 하지만 int형이 아닌 long 형을 사용했다면 register 는 eax, ebx, .. 가 아닌 rax, rbx, ... 로 변경을 고려해야 합니다. 

(5) implementation 함수의 잘못된 고려 (size_t 등의 의미를 전혀 활용하지 못한경우 포팅이 쉽지 않을수 있음.) 
예를 들어서 memcpy 를 한번 감싸서 다음과 같이 자신만의 함수를 만들었다면 

코드:
void *my_memcpy(void *s_to, const void *s_from, int s_size) 

 return(memcpy(s_to, s_from, s_size)); 
}


당연히 s_size 변수는 64bit를 담을수 없는 그릇으로 전달되기 때문에 원하지 않은 결과를 발생할수 있습니다. 때문에 다음과 같이 변경되어야 합니다. 

코드:
void *my_memcpy(void *s_to, const void *s_from, size_t s_size) 

 return(memcpy(s_to, s_from, s_size)); 
}


size_t, ssize_t 의 용도를 전혀 중요하게 생각지 않은 개발자라면 이 문제로 64bit 포팅에 좌절을 겪을수도 있습니다. 고려하지 않은 개발자분들은 위의 예제처럼 memcpy 에서보다는 memory offset 연산에서 후회할수 있습니다. 

(6) 정렬 
기존에는 구조체의 sizeof() 에 의한 정렬된 값이 보통 4byte align로 사용되었으나 이것은 이제 기본값으로 믿을수 없습니다. 이제는 보통 8byte align 이 기본값으로 사용됩니다. 때문에 반드시 의도적으로 align을 해야 되는 경우라면 다음과 같이 작성되어야 합니다. (이것은 뻔한 내용이지만 그것을 이야기 하려고 한것이 아니고 신중한 library 개발자라면 모든 구조체는 명시적으로 align 을 지정해줘야 될수 있다는 것을 말합니다.) 

코드:
#pragma pack(push,8) 
struct ts_my_struct 

 unsigned char b; 
 unsigned short w; 
 unsigned int d; 
 unsigned long d; 
 unsigned long long q; 
 long double paragraph; 

#pragma pack(pop)



(7) predefine 으로 64bit 를 검출 
각 architecture 별 predefine의 종류가 많아서 간단하게 검출할수 있는 내용은 아니지만 제가 지금까지 포팅한 architecture는 다음과 같이 하여 검출하였습니다. 사실 이것이 모두 완벽하게 검출한다고 할수는 없습니다. 들어보지도 못한 architecture도 많으니까.. 

코드:
#if defined(__x86_64__) || defined(__ia64__) || defined(_M_AMD64) || defined(_M_IA64) || defined(_WIN64) || defined(__alpha__) || defined(__s390__) 
  
#endif