Typowe błędy kompilatora
W tej sekcji przedstawiono typowe błędy kompilatora występujące podczas migrowania istniejącej bazy kodu. Te przykłady dotyczą kodu HAL na poziomie systemu, chociaż koncepcje mają zastosowanie bezpośrednio do kodu na poziomie użytkownika.
Ostrzeżenie C4311 — przykład 1
Rzutowanie typu: obcięcie wskaźnika z "void *__ptr64" na "niepodpisane długie"
-
kod
-
pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;
-
opis
-
ptrToUlong jest funkcją śródliniową lub makrem, w zależności od użycia. Obcina wskaźnik do ULONG. Chociaż wskaźniki 32-bitowe nie mają wpływu, górna połowa wskaźnika 64-bitowego jest obcięta.
CIA_PCI_CONFIG_BASE_QVA jest deklarowany jako PVOID. Obsada ULONG działa w 32-bitowym świecie, ale powoduje błąd w 64-bitowym świecie. Rozwiązaniem jest pobranie 64-bitowego wskaźnika do ULONG, ponieważ zmiana definicji unii, że pPciAddr->u.AsULONG jest definiowana w zmianach za dużo kodu.
Użycie makra PtrToUlong do konwersji 64-bitowej PVOID do wymaganych ULONG jest dozwolone, ponieważ mamy wiedzę na temat konkretnej wartości CIA_PCI_CONFIG_BASE_QVA. W takim przypadku wskaźnik nigdy nie będzie miał danych w górnych 32 bitach.
-
rozwiązanie
-
pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);
Ostrzeżenie C4311 — przykład 2
Rzutowanie typu: obcięcie wskaźnika z "struct _ERROR_FRAME *__ptr64" na "niepodpisane długie"
-
kod
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );
-
opis
-
Problem polega na tym, że ostatni parametr tej funkcji jest wskaźnikiem do struktury danych. Ponieważ PUncorrectableError jest wskaźnikiem, zmienia rozmiar modelu programowania. Prototyp KeBugCheckEx został zmieniony tak, aby ostatni parametr był ULONG_PTR. W rezultacie należy rzutować wskaźnik funkcji na ULONG_PTR.
Możesz zapytać, dlaczego PVOID nie był używany jako ostatni parametr. W zależności od kontekstu wywołania ostatni parametr może być czymś innym niż wskaźnik lub być może kodem błędu.
-
rozwiązanie
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );
Ostrzeżenie C4244 — przykład 1
'=' : konwersja z _CONFIGURATION_COMPONENT struktury *__ptr64 " na "struktura _CONFIGURATION_COMPONENT *", możliwa utrata danych
-
kod
-
Component = &CurrentEntry->ComponentEntry;
-
opis
-
Funkcja deklaruje zmienną Component jako PCONFIGURATION_COMPONENT. Później zmienna jest używana w następującym przypisaniu, które wydaje się poprawne:
Component = &CurrentEntry->ComponentEntry;
Jednak typ PCONFIGURATION_COMPONENT jest zdefiniowany jako:
typedef struct __CONFIGURATION_COMPONENT { ... ... } CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;
Definicja typu dla PCONFIGURATION_COMPONENT zawiera wskaźnik 32-bitowy zarówno w modelach 32-bitowych, jak i 64-bitowych, ponieważ jest zadeklarowany POINTER_32. Oryginalny projektant tej struktury wiedział, że będzie używany w kontekście 32-bitowym w systemie BIOS i wyraźnie zdefiniował go do tego użycia. Ten kod działa poprawnie w 32-bitowym systemie Windows, ponieważ wskaźniki są 32-bitowe. W 64-bitowym systemie Windows nie działa, ponieważ kod znajduje się w kontekście 64-bitowym.
-
rozwiązanie
-
Aby obejść ten problem, użyj CONFIGURATION_COMPONENT * zamiast 32-bitowego PCONFIGURATION_COMPONENT . Ważne jest, aby jasno zrozumieć przeznaczenie kodu. Jeśli ten kod ma dotknąć 32-bitowego systemu BIOS lub miejsca systemowego, ta poprawka nie będzie działać.
POINTER_32 jest definiowana w ntdef.h i Winnt.h.
#ifdef (__AXP64__) #define POINTER_32 _ptr32 #else #define POINTER_32 #endif
Ostrzeżenie C4242 — przykład 2
"=" : konwersja z "__int64" na "niepodpisane długie", możliwa utrata danych
-
kod
-
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;
-
opis
-
To ostrzeżenie jest generowane, ponieważ obliczenie używa wartości 64-bitowych, w tym przypadku wskaźników i umieszcza wynik w 32-bitowej ULONG.
-
rozwiązanie
-
Wpisz rzutowanie wyniku obliczenia na ULONG, jak pokazano poniżej:
ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
Wpisywanie wyniku pozwala kompilatorowi wiedzieć, że masz pewność co do wyniku. To powiedzenie, upewnij się, że rozumiesz obliczenie i naprawdę są pewni, że zmieści się w 32-bitowej ULONG.
Jeśli wynik może nie mieścić się w 32-bitowej ULONG, zmień typ podstawowy zmiennej, która będzie przechowywać wynik.
Ostrzeżenie C4311 — przykład 1
Rzutowanie typu: obcinanie wskaźnika z "void *__ptr64" do "niepodpisane długie"
-
kod
-
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; }
-
opis
-
Cała ta funkcja zajmuje się adresami jako liczbami całkowitymi, co wymaga wpisania tych liczb całkowitych w przenośny sposób. Wszystkie zmienne lokalne, wartości pośrednie w obliczeniach i zwracane wartości powinny być typami przenośnymi.
-
rozwiązanie
-
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 jest wskaźnikiem, który jest sam 32 bity dla 32-bitowych windows i 64 bitów dla 64-bitowego systemu Windows. Wskazuje na niepodpisaną liczbę całkowitą, ULONG_PTR, czyli 32 bity dla 32-bitowego systemu Windows i 64 bitów dla 64-bitowego systemu Windows.
Ostrzeżenie C4311 — przykład 2
Rzutowanie typu: obcinanie wskaźnika z "void *__ptr64" do "niepodpisane długie"
-
kod
-
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);
-
opis
-
Mimo że wszystkie wartości QVA (Quasi Virtual Address) są naprawdę wartościami 32-bitowymi na tym etapie i będą mieścić się w ULONG, bardziej spójne jest traktowanie wszystkich adresów jako wartości ULONG_PTR, jeśli to możliwe.
Wskaźnik PciIoSpaceBase przechowuje wartość QVA utworzoną w HAL_MAKE_QVA makra. To makro zwraca wartość 64-bitową z najlepszymi 32 bitami ustawionymi na zero, więc matematyka będzie działać. Możemy po prostu pozostawić kod, aby obcinać wskaźnik do ULONG, ale ta praktyka jest zniechęcana do zwiększenia możliwości konserwacji i przenośności kodu. Na przykład zawartość QVA może ulec zmianie w przyszłości, aby użyć niektórych górnej części bitów na tym poziomie, powodując niezgodność kodu.
-
rozwiązanie
-
Należy bezpiecznie używać ULONG_PTR dla wszystkich matematycznych adresów i wskaźników.
HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
Ostrzeżenie C4311 — przykład 3
Rzutowanie typu: obcinanie wskaźnika z "void *__ptr64" do "niepodpisane długie"
-
kod
-
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); }
-
opis
-
Kompilator ostrzega o adresie (&) i lewym przesunięciu (<<) operatorów, jeśli są stosowane do typów wskaźników. W powyższym kodzie Qva jest wartością PVOID. Aby wykonać obliczenia matematyczne, musimy rzutować tę wartość na typ liczby całkowitej. Ponieważ kod musi być przenośny, użyj ULONG_PTR zamiast ULONG.
-
rozwiązanie
-
if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );
Ostrzeżenie C4311 — przykład 4
Rzutowanie typu: obcinanie wskaźnika z "void *__ptr64" do "niepodpisane długie"
-
kod
-
TranslatedAddress->LowPart = (ULONG)HalCreateQva( *TranslatedAddress, va);
-
opis
-
TranslatedAddress to związek, który wygląda podobnie do następującego:
typedef union Struct { ULONG LowPart; LONG Highpart; } LONGLONG QuadPart; }
-
rozwiązanie
-
Wiedząc, co reszta kodu może znajdować się w składniku Highpart, możemy wybrać jedną z przedstawionych tutaj rozwiązań.
TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );
Makro PtrToUlong obcina wskaźnik zwrócony przez HalCreateQva do 32 bitów. Wiemy, że funkcja QVA zwrócona przez HalCreateQva ma górne 32 bity ustawione na zero i następny wiersz zestawów kodu TranslatedAddress->Highpart na zero.
Ostrożnie możemy użyć następujących elementów:
TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);
Działa to w tym przykładzie: makro HalCreateQva zwraca 64 bity, a górne 32 bity są ustawione na zero. Po prostu uważaj, aby nie pozostawić górnych 32 bitów niezdefiniowane w środowisku 32-bitowym, które to drugie rozwiązanie może rzeczywiście zrobić.
Ostrzeżenie C4311 — przykład 5
Rzutowanie typu: obcinanie wskaźnika z "void *__ptr64" do "niepodpisane długie"
-
kod
-
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;
-
opis
-
WindowRegisters —>WindowBase jest wskaźnikiem i jest teraz 64-bitowy. Kod mówi, aby przesunąć tę wartość w prawo 20 bitów. Kompilator nie pozwoli nam użyć operatora przesunięcia prawego (>>) na wskaźniku; dlatego musimy rzutować ją na jakąś liczbę całkowitą.
-
rozwiązanie
-
Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));
Rzutowanie do ULONG_PTR jest tylko tym, czego potrzebujemy. Następnym problemem jest Wbase. Wbase to ULONG i ma 32 bity. W tym przypadku wiemy, że 64-bitowy wskaźnik WindowRegisters->WindowBase jest prawidłowy w niższych 32 bitach nawet po przesunięciu. Dzięki temu PtrToUlong makro dopuszczalne, ponieważ obcina wskaźnik 64-bitowy do 32-bitowego ULONG. Rzutowanie PVOID jest konieczne, ponieważ PtrToUlong oczekuje argumentu wskaźnika. Gdy spojrzysz na wynikowy kod asemblera, cały ten rzutowanie kodu języka C staje się tylko czworokąt, przesunięcie w prawo i przechowywanie długie.