일반적인 컴파일러 오류
이 섹션에서는 기존 코드 베이스를 마이그레이션할 때 발생하는 일반적인 컴파일러 오류를 보여 줍니다. 이러한 예제는 개념이 사용자 수준 코드에 직접 적용 가능하지만 시스템 수준 HAL 코드에서 발생합니다.
경고 C4311 예제 1
'type cast': 'void *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;
-
Description
-
PtrToUlong 은 사용량에 따라 인라인 함수 또는 매크로입니다. ULONG에 대한 포인터를 자립니다. 32비트 포인터는 영향을 받지 않지만 64비트 포인터의 상반부가 잘립니다.
CIA_PCI_CONFIG_BASE_QVA PVOID로 선언됩니다. ULONG 캐스트는 32비트 세계에서 작동하지만 64비트 세계에서 오류가 발생합니다. pPciAddr-u.AsULONG>이 너무 많은 코드 변경에 정의되어 있는 공용 구조체의 정의를 변경하기 때문에 ULONG에 대한 64비트 포인터를 가져오는 것이 솔루션입니다.
매크로 PtrToUlong 을 사용하여 64비 트 PVOID 를 필요한 ULONG 으로 변환할 수 있습니다. CIA_PCI_CONFIG_BASE_QVA 특정 값에 대한 지식이 있기 때문입니다. 이 경우 이 포인터는 상위 32비트에서 데이터를 갖지 않습니다.
-
솔루션
-
pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);
경고 C4311 예제 2
'type cast': 'struct _ERROR_FRAME *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );
-
Description
-
문제는 이 함수의 마지막 매개 변수가 데이터 구조에 대한 포인터라는 것입니다. PUncorrectableError는 포인터이므로 프로그래밍 모델에서 크기를 변경합니다. 마지막 매개 변수가 ULONG_PTR 있도록 KeBugCheckEx의 프로토타입이 변경되었습니다. 따라서 함수 포인터를 ULONG_PTR 캐스팅해야 합니다.
PVOID가 마지막 매개 변수로 사용되지 않은 이유를 물어볼 수 있습니다. 호출 컨텍스트에 따라 마지막 매개 변수는 포인터 또는 오류 코드가 아닌 것일 수 있습니다.
-
솔루션
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );
경고 C4244 예제 1
'=': '구조체 _CONFIGURATION_COMPONENT *__ptr64'에서 '구조체 _CONFIGURATION_COMPONENT *'로 변환, 데이터 손실 가능성
-
코드
-
Component = &CurrentEntry->ComponentEntry;
-
Description
-
함수는 변수 구성 요소를 PCONFIGURATION_COMPONENT 선언합니다. 나중에 변수가 올바른 것으로 표시되는 다음 할당에서 사용됩니다.
Component = &CurrentEntry->ComponentEntry;
그러나 PCONFIGURATION_COMPONENT 형식은 다음과 같이 정의됩니다.
typedef struct __CONFIGURATION_COMPONENT { ... ... } CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;
PCONFIGURATION_COMPONENT 대한 형식 정의는 POINTER_32 선언되므로 32비트 및 64비트 모델 모두에서 32비트 포인터를 제공합니다. 이 구조의 원래 디자이너는 BIOS의 32비트 컨텍스트에서 사용될 것이라는 것을 알고 있었고 이를 위해 명시적으로 정의했습니다. 이 코드는 포인터가 32비트이므로 32비트 Windows에서 잘 작동합니다. 64비트 Windows에서는 코드가 64비트 컨텍스트에 있으므로 작동하지 않습니다.
-
솔루션
-
이 문제를 해결하려면 32비트 PCONFIGURATION_COMPONENT 대신 CONFIGURATION_COMPONENT *를 사용합니다. 코드의 용도를 명확하게 이해하는 것이 중요합니다. 이 코드가 32비트 BIOS 또는 시스템 공간을 터치하도록 의도된 경우 이 수정이 작동하지 않습니다.
POINTER_32 Ntdef.h 및 Winnt.h에 정의되어 있습니다.
#ifdef (__AXP64__) #define POINTER_32 _ptr32 #else #define POINTER_32 #endif
경고 C4242 예제 2
'=': '__int64'에서 'unsigned long'으로 변환, 데이터 손실 가능
-
코드
-
ARC_STATUS HalpCopyNVRamBuffer ( IN PCHAR NvDestPtr, IN PCHAR NvSrcPtr, IN ULONG Length ) { ULONG PageSelect1, ByteSelect1; ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK; ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
-
Description
-
이 경고는 계산에서 64비트 값(이 경우 포인터)을 사용하고 결과를 32비 트 ULONG에 배치하기 때문에 생성됩니다.
-
솔루션
-
형식은 다음과 같이 계산 결과를 ULONG 으로 캐스팅합니다.
ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
결과를 형식 캐스팅하면 컴파일러가 결과에 대해 확실히 알 수 있습니다. 즉, 계산을 이해하고 실제로 32비 트 ULONG에 맞는지 확인합니다.
결과가 32비 트 ULONG에 맞지 않을 수 있는 경우 결과를 보유할 변수의 기본 형식을 변경합니다.
경고 C4311 - 예제 1
'type cast': 'void *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
ULONG HalpMapDebugPort( IN ULONG ComPort, OUT PULONG ReadQva, OUT PULONG WriteQva) { ULONG ComPortAddress; ULONG PortQva; // Compute the port address, based on the desired com port. switch( ComPort ){ case 1: ComPortAddress = COM1_ISA_PORT_ADDRESS; break; case 2: default: ComPortAddress = COM2_ISA_PORT_ADDRESS; } PortQva = (ULONG)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress; // Return the QVAs for read and write access. *ReadQva = PortQva; *WriteQva = PortQva; return ComPortAddress; }
-
Description
-
이 전체 함수는 주소를 정수로 처리하므로 해당 정수는 이식 가능한 방식으로 입력해야 합니다. 모든 지역 변수, 계산의 중간 값 및 반환 값은 이식 가능한 형식이어야 합니다.
-
솔루션
-
ULONG_PTR HalpMapDebugPort( IN ULONG ComPort, OUT PULONG_PTR ReadQva, OUT PULONG_PTR WriteQva) { ULONG_PTR ComPortAddress; ULONG_PTR PortQva; // Compute the port address, based on the desired com port. switch( ComPort ){ case 1: ComPortAddress = COM1_ISA_PORT_ADDRESS; break; case 2: default: ComPortAddress = COM2_ISA_PORT_ADDRESS; } PortQva = (ULONG_PTR)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress; // Return the QVAs for read and write access. *ReadQva = PortQva; *WriteQva = PortQva; return ComPortAddress; }
PULONG_PTR 32비트 Windows의 경우 32비트, 64비트 Windows의 경우 64비트인 포인터입니다. 부호 없는 정수인 ULONG_PTR 32비트 Windows의 경우 32비트, 64비트 Windows의 경우 64비트를 가리킵니다.
경고 C4311 - 예제 2
'type cast': 'void *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
BOOLEAN HalpMapIoSpace ( VOID ) { PVOID PciIoSpaceBase; PciIoSpaceBase = HAL_MAKE_QVA( CIA_PCI_SPARSE_IO_PHYSICAL ); //Map base addresses in QVA space. HalpCMOSRamBase = (PVOID)((ULONG)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
-
Description
-
모든 QVA(Quasi Virtual Address) 값은 이 단계에서 실제로 32비트 값이며 ULONG에 적합하지만 가능한 경우 모든 주소를 ULONG_PTR 값으로 처리하는 것이 더 일관됩니다.
포인터 PciIoSpaceBase는 매크로 HAL_MAKE_QVA 만들어진 QVA를 보유합니다. 이 매크로는 상위 32비트가 0으로 설정된 64비트 값을 반환하므로 수학이 작동합니다. 단순히 ULONG으로 포인터를 자르기 위해 코드를 그대로 둘 수 있지만 이 방법은 코드 기본 지속성 및 이식성을 향상시키는 것이 좋습니다. 예를 들어 나중에 QVA의 내용이 변경되어 이 수준에서 상위 비트 중 일부를 사용하여 코드를 깨뜨릴 수 있습니다.
-
솔루션
-
안전하고 모든 주소 및 포인터 수학에 ULONG_PTR 사용합니다.
HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
경고 C4311 예제 3
'type cast': 'void *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
PVOID HalDereferenceQva( PVOID Qva, INTERFACE_TYPE InterfaceType, ULONG BusNumber) if ( ((ULONG) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG)Qva << IO_BIT_SHIFT ) ); } else { return (Qva); }
-
Description
-
컴파일러는 포인터 형식에 적용되는 경우 (&) 및 왼쪽 시프트(<<) 연산자의 주소에 대해 경고합니다. 위의 코드에서 Qva는 PVOID 값입니다. 수학을 수행하려면 정수 형식으로 캐스팅해야 합니다. 코드는 이식 가능해야 하므로 ULONG 대신 ULONG_PTR 사용합니다.
-
솔루션
-
if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );
경고 C4311 예제 4
'type cast': 'void *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
TranslatedAddress->LowPart = (ULONG)HalCreateQva( *TranslatedAddress, va);
-
Description
-
TranslatedAddress는 다음과 같은 공용 구조체입니다.
typedef union Struct { ULONG LowPart; LONG Highpart; } LONGLONG QuadPart; }
-
솔루션
-
Highpart에서 코드의 나머지 부분을 알면 여기에 표시된 솔루션 중 하나를 선택할 수 있습니다.
TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );
PtrToUlong 매크로는 HalCreateQva에서 반환한 포인터를 32비트로 자립니다. HalCreateQva에서 반환된 QVA의 상위 32비트는 0으로 설정되고 바로 다음 코드 줄은 TranslatedAddress-Highpart>를 0으로 설정합니다.
주의해서 다음을 사용할 수 있습니다.
TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);
이 예제 에서는 HalCreateQva 매크로가 64비트를 반환하며 상위 32비트는 0으로 설정됩니다. 이 두 번째 솔루션이 실제로 수행할 수 있는 32비트 환경에서 상위 32비트를 정의되지 않은 상태로 두지 않도록 주의하세요.
경고 C4311 예제 5
'type cast': 'void *__ptr64'에서 'unsigned long'으로 포인터 잘림
-
코드
-
VOID HalpCiaProgramDmaWindow( PWINDOW_CONTROL_REGISTERS WindowRegisters, PVOID MapRegisterBase ) { CIA_WBASE Wbase; Wbase.all = 0; Wbase.Wen = 1; Wbase.SgEn = 1; Wbase.Wbase = (ULONG)(WindowRegisters->WindowBase) >> 20;
-
Description
-
WindowRegisters-WindowBase>는 포인터이며 이제 64비트입니다. 코드는 이 값을 20비트 오른쪽으로 이동하도록 말합니다. 컴파일러에서는 포인터에서 오른쪽 시프트(>>) 연산자를 사용할 수 없으므로 일종의 정수로 캐스팅해야 합니다.
-
솔루션
-
Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));
ULONG_PTR 캐스팅하는 것은 우리가 필요로하는 것입니다. 다음 문제는 Wbase입니다. Wbase는 ULONG 이며 32비트입니다. 이 경우 64비트 포인터 WindowRegisters-WindowBase>는 이동 후에도 하위 32비트에서 유효하다는 것을 알고 있습니다. 이렇게 하면 64비트 포인터가 32비트 ULONG으로 잘리기 때문에 PtrToUlong 매크로를 사용할 수 있습니다. PtrToUlong에는 포인터 인수가 필요하기 때문에 PVOID 캐스트가 필요합니다. 결과 어셈블러 코드를 보면 이 모든 C 코드 캐스팅은 로드 쿼드, 오른쪽으로 이동 및 긴 저장이 됩니다.