Běžné chyby kompilátoru
Tato část ukazuje typické chyby kompilátoru, ke kterým dochází při migraci existujícího základu kódu. K těmto příkladům dochází z kódu HAL na úrovni systému, i když se koncepty vztahují přímo na kód na úrovni uživatele.
Upozornění C4311 – příklad 1
'type cast' : zkrácení ukazatele z 'void *__ptr64' na 'unsigned long
-
Code
-
pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;
-
Popis
-
PtrToUlong je vložená funkce nebo makro v závislosti na vašem využití. Zkrátí ukazatel na ULONG. I když nejsou ovlivněny 32bitové ukazatele, horní polovina 64bitového ukazatele je zkrácena.
CIA_PCI_CONFIG_BASE_QVA je deklarován jako PVOID . Přetypování ULONG funguje v 32bitovém světě, ale výsledkem je chyba v 64bitovém světě. Řešením je získat 64bitový ukazatel na ULONG, protože změna definice sjednocení, která pPciAddr->u.AsULONG je definována v příliš mnoho změn kódu.
Použití makra PtrToUlong k převodu 64bitové PVOID potřebné ULONG je povoleno, protože máme znalosti o konkrétní hodnotě CIA_PCI_CONFIG_BASE_QVA. V tomto případě tento ukazatel nikdy nebude obsahovat data v horních 32 bitech.
-
řešení
-
pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);
Upozornění C4311 – příklad 2
'type cast' : zkrácení ukazatele ze struktury _ERROR_FRAME *__ptr64 ' na 'unsigned long
-
Code
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );
-
Popis
-
Problémem je, že poslední parametr této funkce je ukazatel na datovou strukturu. Protože PUncorrectableError je ukazatel, změní velikost pomocí programovacího modelu. Prototyp KeBugCheckEx byl změněn tak, aby poslední parametr byl ULONG_PTR. V důsledku toho je nutné přetypovat ukazatel funkce na ULONG_PTR.
Můžete se zeptat, proč PVOID nebyl použit jako poslední parametr. V závislosti na kontextu volání může být poslední parametr jiný než ukazatel nebo kód chyby.
-
řešení
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );
Upozornění C4244 – příklad 1
'=' : převod ze struktury _CONFIGURATION_COMPONENT *__ptr64 ' na 'struct _CONFIGURATION_COMPONENT *', možné ztráty dat
-
Code
-
Component = &CurrentEntry->ComponentEntry;
-
Popis
-
Funkce deklaruje proměnnou Component jako PCONFIGURATION_COMPONENT. Později se proměnná použije v následujícím přiřazení, které se zobrazí správně:
Component = &CurrentEntry->ComponentEntry;
Typ PCONFIGURATION_COMPONENT je však definován takto:
typedef struct __CONFIGURATION_COMPONENT { ... ... } CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;
Definice typu pro PCONFIGURATION_COMPONENT poskytuje 32bitový ukazatel v 32bitových i 64bitových modelech, protože je deklarován POINTER_32. Původní návrhář této struktury věděl, že se bude používat v 32bitovém kontextu v systému BIOS a výslovně ho definoval pro toto použití. Tento kód funguje v 32bitovém systému Windows správně, protože ukazatele jsou 32bitové. V 64bitovém systému Windows nefunguje, protože kód je v 64bitovém kontextu.
-
řešení
-
Chcete-li tento problém vyřešit, použijte místo 32bitové PCONFIGURATION_COMPONENT CONFIGURATION_COMPONENT * . Je důležité jasně pochopit účel kódu. Pokud je tento kód určen k dotykovému 32bitovému systému BIOS nebo systémovému prostoru, tato oprava nebude fungovat.
POINTER_32 je definován v ntdef.h a Winnt.h.
#ifdef (__AXP64__) #define POINTER_32 _ptr32 #else #define POINTER_32 #endif
Upozornění C4242 – příklad 2
'=' : převod z '__int64' na 'unsigned long ', možné ztráty dat
-
Code
-
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;
-
Popis
-
Toto upozornění se vygeneruje, protože výpočet používá 64bitové hodnoty, v tomto případě ukazatele a umístí výsledek do 32bitového ULONG.
-
řešení
-
Zadejte přetypování výsledku výpočtu na ULONG, jak je znázorněno zde:
ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
Typecasting výsledek dává kompilátoru vědět, že jste si jisti výsledek. To je řečeno, ujistěte se, že rozumíte výpočtu a opravdu jste si jisti, že se vejde do 32bitové ULONG.
Pokud se výsledek nemusí vejít do 32bitové ULONG, změňte základní typ proměnné, která bude obsahovat výsledek.
Upozornění C4311 – příklad 1
'type cast' : zkrácení ukazatele z 'void *__ptr64' na 'unsigned long'
-
Code
-
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; }
-
Popis
-
Tato celá funkce se zabývá adresami jako celá čísla, což vyžaduje, aby tato celá čísla byla zadávat přenositelným způsobem. Všechny místní proměnné, přechodné hodnoty ve výpočtech a návratové hodnoty by měly být přenosné typy.
-
řešení
-
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 je ukazatel, který je sám o sobě 32 bitů pro 32bitovou verzi Windows a 64 bitů pro 64bitovou verzi Windows. Odkazuje na celé číslo bez znaménka ULONG_PTR, který je 32 bitů pro 32bitovou verzi Windows a 64 bitů pro 64bitovou verzi Windows.
Upozornění C4311 – příklad 2
'type cast' : zkrácení ukazatele z 'void *__ptr64' na 'unsigned long'
-
Code
-
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);
-
Popis
-
I když jsou všechny hodnoty QVA (Quasi Virtual Address) v této fázi opravdu 32bitové hodnoty a budou se vejít do ULONG, je konzistentnější považovat všechny adresy za ULONG_PTR hodnoty, pokud je to možné.
Ukazatel PciIoSpaceBase obsahuje QVA, která je vytvořena v makr HAL_MAKE_QVA. Toto makro vrátí 64bitovou hodnotu s nejvyšší 32bitovou hodnotou nastavenou na nulu, aby matematika fungovala. Můžeme jednoduše nechat kód zkrátit ukazatel na ULONG, ale tento postup se nedoporučuje zlepšit udržovatelnost a přenositelnost kódu. Obsah QVA se například může v budoucnu změnit tak, aby používal některé horní části na této úrovni, což by kód rozbilo.
-
řešení
-
Buďte v bezpečí a používejte ULONG_PTR pro všechny matematiky adres a ukazatelů.
HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
Upozornění C4311 – příklad 3
'type cast' : zkrácení ukazatele z 'void *__ptr64' na 'unsigned long'
-
Code
-
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); }
-
Popis
-
Kompilátor upozorní na adresu operátorů (&) a levý posun (<<), pokud jsou použity u typů ukazatelů. Ve výše uvedeném kódu je Qva hodnotou PVOID. Abychom mohli provést matematiku, musíme ji přetypovat na celočíselnou hodnotu. Protože kód musí být přenosný, použijte ULONG_PTR místo ULONG.
-
řešení
-
if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );
Upozornění C4311 – příklad 4
'type cast' : zkrácení ukazatele z 'void *__ptr64' na 'unsigned long'
-
Code
-
TranslatedAddress->LowPart = (ULONG)HalCreateQva( *TranslatedAddress, va);
-
Popis
-
Přeloženáaddress je sjednocení, které vypadá nějak takto:
typedef union Struct { ULONG LowPart; LONG Highpart; } LONGLONG QuadPart; }
-
řešení
-
Když víme, co může zbytek kódu umístit do části Highpart, můžeme vybrat některou z zde zobrazených řešení.
TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );
Makro PtrToUlong zkrátí ukazatel vrácený HalCreateQva na 32 bitů. Víme, že QVA vrácená HalCreateQva má horních 32 bitů nastavenou na nulu a úplně další řádek sad kódu TranslatedAddress->Highpart na nulu.
S opatrností bychom mohli použít následující:
TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);
Tento postup funguje v tomto příkladu: makro HalCreateQva vrací 64 bitů, přičemž horních 32 bitů je nastaveno na nulu. Dávejte pozor, abyste horních 32 bitů nedefinovali v 32bitovém prostředí, které by toto druhé řešení mohlo skutečně provést.
Upozornění C4311 – příklad 5
'type cast' : zkrácení ukazatele z 'void *__ptr64' na 'unsigned long'
-
Code
-
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;
-
Popis
-
WindowRegisters->WindowBase je ukazatel a nyní je 64 bitů. Kód říká, že se má tato hodnota posunout doprava o 20 bitů. Kompilátor nám nedovolí použít operátor pravého posunu (>>) na ukazateli; proto ho musíme přetypovat na nějaké celé číslo.
-
řešení
-
Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));
Přetypování na ULONG_PTR je jen to, co potřebujeme. Dalším problémem je Wbase. Wbase je ULONG a je 32 bitů. V tomto případě víme, že 64bitové ukazatele WindowRegisters ->WindowBase je platné v nižších 32 bitech i po přesunutí. To používá PtrToUlong makro přijatelné, protože zkrátí 64bitový ukazatel na 32bitovou ULONG. Přetypování PVOID je nezbytné, protože PtrToUlong očekává argument ukazatele. Když se podíváte na výsledný kód assembleru, celý tento přetypování kódu jazyka C se stane jen zátěžovým čtyřúhelníkem, posunem doprava a uložením dlouhé.