블로그 이미지
vicjung

프로그래밍이나 컴퓨터 관련 위주 블로그

Rss feed Tistory
Programming 2011. 10. 4. 16:20

64비트 포팅에 주의 사항

주요출처: http://blog.naver.com/kkan22/80125996680

 

*  /Wp64 옵션을 켜고 컴파일하여 발생한 경고 / 오류를 해결한다

    1) 일반적으로 발생하는 경고 / 오류

    - 최신 C++ 표준을 적용해 발생하는 템플릿 사용 또는 기본 인자에 관련된 오류 경고

    - 타입 캐스팅 되면서 발생하는 데이타 손실 경고

    - MFC를 오버라이딩해서 사용할때 함수 파라미터/리턴 타입의 변경된 부분에 대한 오류

    - INVALID_HANDLE_VALUE 대신 OxFFFFFFFF를 사용한 경우

    - 사용하는 외부 라이브러리도 모두 64비트이어야 하는데 32비트 라이브러리를 사용하는 경우

 

* 여러개의 모듈로 제품이 구성되어 있을경우 기본 라이브러리=>DLL=>실행 파일순으로 전체 프로젝트에 포팅해야 한다.

[테스트]

1. 먼저 테스트할 프로그램이 순수하게 64비트로 구성된 프로그램인지 아니면 WOW64를 사용하는 부분도 있는지를 검토해야 한다.

    이러한 것은 테스트 계획 수립단계에서 결정되어야 하며 이에 따라서 테스트 도구가 64비트용만 있으면 되는지 아니면 32비트와 64비트용이

    모두 필요한지가 결정이되고 테스트 환경구축 및 일정산정에도 도움이 되기때문이다.

2. 일반적으로 32비트와 64비트 프로그램 모두 같은 소스를 기반으로 하기때문에 테스트 단계에서의 버그 수정은 다음의 순서를 따른다

    1) 64비트 환경에서 테스트 하고 버그를 수정한다.

    2) 32비트 환경에서 부작용이 없는지 다시 컴파일한후 테스트한다.

    3) 다시 64비트 환경에서 다시 컴파일한후 테스트 한다.

 

[포인터 관련]

64비트 윈도우가 채택한 LLP64 데이타 모델의 경우 기존의 데이타 타입은 호환성을 위해 32비트를 유지하고 포인터를 64비트로 변경한

데이타 모델이다 그래서 64비트 응용 프로그램을 개발하기 위해서는 다른 어떤 데이타보다 포인터를 다룰때 주의 해야 한다.

 

1. 포인터를 int, long, UINT, ULONG, DWORD 로 타입캐스팅 하지 말아야 한다.

비트수준의 작업을 하기 위해 포인터를 타입 캐스팅해야 할때는 반드시 32비트와 64비트 윈도우에서 모두 사용할수 있는 타입인 INT_PTR, UINT_PTR를 사용해야 한다. 64비트 윈도우에서는 포인터의 크기가 64비트 이고 int나 long은 32비트이기 때문에 포인터를 int, long, ULONG, DWORD로 타입 캐스팅을 해서는 안된다.

 

< 포인터를 잘못 타입캐스팅 한예>

PVOID ImageBase;

ImageBase = (PVOID)((ULONG)ImageBase  |  1);

 

<포인터를 정상적으로 타입 캐스팅 한예>

ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);

<HANDLE도 void*로 정의 되어 이기때문에 포인터랑 마찬가지로 취급해야 한다>

 

2. 부득이하게 포인터의 데이타가 손실되더라도 32비트로 타입캐스팅 해야 할경우에는 PtrToLong()이나 PtrToUlong() 함수를 이용해야 한다.

이 함수들은 Basetsd.h에 정의 되어 있다. 이 함수들을 사용하면 데이타 손실 경고를 발생시키지 않는다.

이 함수들은 조심해서 사용해야 하는이유는 타입 캐스팅된이후에는 데이타가 손실된것이므로 이 값들을 포인터로 다시 사용해서는 안된다.

이함수들은 상위 32비트 값들을 얻어낸다.

 

3. 값을 얻어오는 OUT 형태의 인자를 사용할때 주의 해야 한다.

다음과 같이 정의된 함수를 예로 생각해보자

void func(OUT PULONG* Pointer);

ULONG ul;

PULONG lp;

func((PULONG *)&ul);

lp = (PULONG)ul;

잘못된 호출이다. &ul 을 PULONG*으로 타입 캐스팅 했기때문에 컴파일러 오류는 발생하지 않지만 함수에서는 &ul에 64비트 포인터 값을 쓰게된다.

이 코드는 32비트 윈도우에서는 정상적으로 동작하겠지만 64비트 윈도우에서는 포인터의 데이타 손실이 발생하게 된다.

대신에 다음과 같이 호출해야 한다.

PULONG lp;

func(&lp);

4. 다형성 인자를 사용할경우 UINT_PTR이나 PVOID를 사용해야 한다.

32 비트 윈도우에서는 어떤 타입의 데이타를 넘길지 결정할수 없을때는 DWORD등을 함수의 인자로 타입캐스팅해서 사용하기도 했었다.

그러나 64비트 윈도우에서는 반드시 UINT_PTR나 PVOID와 같은 64비트 윈도우와 32비트 윈도우에서 모두 사용할수 있는 데이타 형을 사용해야 한다.

5. 플랫폼에따라 LPARAM, WPARAM, LRESULT 타입의 크기는 변한다

이 타입들은 포인터나 다형성 인자로 사용되는 타입이기때문에 64비트 코드로 컴파일할때 64비트 크기로 컴파일된다.

그러므로 64비트 환경에서는 정상적으로 동작하게 하려면 LPARAM, WPARAM, LRESULT들을 32비트의 크기를 가지는 DWORD, ULONG, UINT, INT, int,

long값들과 같이 사용해서는 안된다.

6. 꼭 필요한 경우가 아니라면 명시적인 구조체 패킹을 하지말고 64비트 윈도우에서는 8바이트를 기본으로 패킹되는것이 좋다.

   64비트 윈도우에서는 32비트 윈도우와 달리 데이타 패킹을 기본적으로 8바이트 단위로 한다.

7.비트연산에 대해서는 신경써야 하는데 기존의 32비트 윈도우에서는 -1을 비교할때나 INVALID_HANDLE_VALUE 대신 0xFFFFFFFF를 사용하는

   소스도 있었지만 64비트 윈도우에서는 사용해서는 안되는 값이다. 또한 32비트 소스에서는 1 << 33 과 같이 시프트 연산을 할 경우 정상 동작 하겠지만

   64비트 소스에서는 사용해서는 안되는 값이다.

8. NOT 오퍼레이션을 사용할때 주의 해야 한다.

   UINT_PTR a; ULONG b;

   a = a &~(b-1);

   ==> 올바른 코딩

  a = a & ~((UINT_PTR)b -1);

9.작은 크기의 데이타를 할당할때 주의해야 한다

  struct S{

         DWORD NumberOfPointers;

         PVOID Pointers[100];

}

다음의 코드는 잘못된 코드인데 컴파일러는 8바이트 정렬을 맞추기 위해서 4바이트를 추가적으로 패딩해서 할당하기 때문이다.

malloc(sizeof(DWORD) + 100*sizeof(PVOID));

이코드는 다음과 같이 수정하면 된다.

malloc(offsetof(struct S, Pointers) + 100*sizeof(PVOID));

,
TOTAL TODAY