Erreurs courantes du compilateur

Cette section illustre les erreurs typiques du compilateur qui se produisent lors de la migration d’une base de code existante. Ces exemples proviennent du code HAL au niveau du système, bien que les concepts soient directement applicables au code au niveau de l’utilisateur.

Avertissement C4311 Exemple 1

'type cast' : troncation du pointeur de 'void *__ptr64 ' vers 'unsigned long

Code

pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;

Description

PtrToUlong est une fonction inline ou une macro, selon votre utilisation. Il tronque un pointeur vers un ULONG. Bien que les pointeurs 32 bits ne soient pas affectés, la moitié supérieure d’un pointeur 64 bits est tronquée.

CIA_PCI_CONFIG_BASE_QVA est déclaré en tant que PVOID. Le cast ULONG fonctionne dans le monde 32 bits, mais génère une erreur dans le monde 64 bits. La solution consiste à obtenir un pointeur 64 bits vers un ULONG, car la modification de la définition de l’union que pPciAddr-u.AsULONG> est défini dans change trop de code.

L’utilisation de la macro PtrToUlong pour convertir le PVOID 64 bits en ULONG nécessaire est autorisée, car nous avons connaissance de la valeur spécifique de CIA_PCI_CONFIG_BASE_QVA. Dans ce cas, ce pointeur n’aura jamais de données dans les 32 bits supérieurs.

Solution

pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);

Avertissement C4311 Exemple 2

'type cast' : troncation du pointeur de 'struct _ERROR_FRAME *__ptr64 ' vers 'unsigned long

Code

KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );

Description

Le problème est que le dernier paramètre de cette fonction est un pointeur vers une structure de données. Étant donné que PUncorrectableError est un pointeur, il change de taille avec le modèle de programmation. Le prototype de KeBugCheckEx a été modifié afin que le dernier paramètre soit un ULONG_PTR. Par conséquent, il est nécessaire de caster le pointeur de fonction vers un ULONG_PTR.

Vous pouvez vous demander pourquoi PVOID n’a pas été utilisé comme dernier paramètre. Selon le contexte de l’appel, le dernier paramètre peut être autre chose qu’un pointeur ou peut-être un code d’erreur.

Solution

KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );

Avertissement C4244 Exemple 1

'=' : conversion de 'struct _CONFIGURATION_COMPONENT *__ptr64' en 'struct _CONFIGURATION_COMPONENT *', perte possible de données

Code

Component = &CurrentEntry->ComponentEntry;

Description

La fonction déclare la variable Component en tant que PCONFIGURATION_COMPONENT. Plus tard, la variable est utilisée dans l’affectation suivante qui apparaît correcte :

Component = &CurrentEntry->ComponentEntry;

Toutefois, le type PCONFIGURATION_COMPONENT est défini comme suit :

typedef struct __CONFIGURATION_COMPONENT {
...
...
} CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;

La définition de type pour PCONFIGURATION_COMPONENT fournit un pointeur 32 bits dans les modèles 32 bits et 64 bits, car il est déclaré POINTER_32. Le concepteur d’origine de cette structure savait qu’elle allait être utilisée dans un contexte 32 bits dans le BIOS et l’a expressément définie pour cette utilisation. Ce code fonctionne bien dans Windows 32 bits, car les pointeurs sont 32 bits. Dans Windows 64 bits, cela ne fonctionne pas, car le code est dans un contexte 64 bits.

Solution

Pour contourner ce problème, utilisez CONFIGURATION_COMPONENT * plutôt que le PCONFIGURATION_COMPONENT 32 bits . Il est important de bien comprendre l’objectif du code. Si ce code est destiné à toucher l’espace système ou bios 32 bits, ce correctif ne fonctionnera pas.

POINTER_32 est défini dans Ntdef.h et Winnt.h.

#ifdef (__AXP64__)
#define POINTER_32 _ptr32
#else
#define POINTER_32
#endif

Avertissement C4242 Exemple 2

'=' : conversion de '__int64' en 'unsigned long', perte possible de données

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;

Description

Cet avertissement est généré, car le calcul utilise des valeurs 64 bits, dans ce cas des pointeurs, et place le résultat dans un ULONG 32 bits.

Solution

Type caster le résultat du calcul en ULONG comme illustré ici :

ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;

La réception du résultat permet au compilateur de savoir que vous êtes sûr du résultat. Cela étant dit, assurez-vous que vous comprenez le calcul et êtes vraiment sûr qu’il s’adaptera dans un ULONG 32 bits.

Si le résultat peut ne pas tenir dans un ULONG 32 bits, modifiez le type de base de la variable qui contiendra le résultat.

Avertissement C4311 - Exemple 1

'type cast' : troncation du pointeur de 'void *__ptr64' vers '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;
}

Description

Cette fonction entière traite les adresses sous forme d’entiers, ce qui nécessite la nécessité de taper ces entiers de manière portable. Toutes les variables locales, les valeurs intermédiaires dans les calculs et les valeurs de retour doivent être des types portables.

Solution

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 est un pointeur qui est lui-même 32 bits pour Windows 32 bits et 64 bits pour Windows 64 bits. Il pointe vers un entier non signé, ULONG_PTR, soit 32 bits pour Windows 32 bits et 64 bits pour Windows 64 bits.

Avertissement C4311 - Exemple 2

'type cast' : troncation du pointeur de 'void *__ptr64' vers '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);

Description

Même si toutes les valeurs QVA (Quasi Virtual Address) sont en fait des valeurs 32 bits à ce stade et qu’elles s’intègrent dans un ULONG, il est plus cohérent de traiter toutes les adresses comme des valeurs ULONG_PTR lorsque cela est possible.

Le pointeur PciIoSpaceBase contient le QVA créé dans la macro HAL_MAKE_QVA. Cette macro retourne une valeur 64 bits avec les 32 premiers bits définis sur zéro afin que les mathématiques fonctionnent. Nous pourrions simplement laisser le code pour tronquer le pointeur dans un ULONG, mais cette pratique est déconseillée pour améliorer la maintenance et la portabilité du code. Par exemple, le contenu d’une QVA peut changer à l’avenir pour utiliser certains des bits supérieurs à ce niveau, cassant le code.

Solution

Soyez sûr et utilisez ULONG_PTR pour toutes les mathématiques d’adresse et de pointeur.

HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);

Avertissement C4311 Exemple 3

'type cast' : troncation du pointeur de 'void *__ptr64' vers '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);
}

Description

Le compilateur avertit de l’adresse des opérateurs (&) et de décalage gauche (<<) s’ils sont appliqués aux types de pointeurs. Dans le code ci-dessus, Qva est une valeur PVOID . Nous devons convertir cela en type entier pour effectuer les mathématiques. Étant donné que le code doit être portable, utilisez ULONG_PTR au lieu d’ULONG.

Solution

if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) {
  return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );

Avertissement C4311 Exemple 4

'type cast' : troncation du pointeur de 'void *__ptr64' vers 'unsigned long'

Code

TranslatedAddress->LowPart = (ULONG)HalCreateQva(
*TranslatedAddress, va);

Description

TranslatedAddress est une union qui ressemble à ce qui suit :

typedef union
   Struct {
      ULONG LowPart;
      LONG Highpart;
   }
   LONGLONG QuadPart;
}

Solution

Sachant ce que le reste du code peut placer dans Highpart, nous pouvons sélectionner l’une des solutions présentées ici.

TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );

La macro PtrToUlong tronque le pointeur retourné par HalCreateQva sur 32 bits. Nous savons que le QVA retourné par HalCreateQva a les 32 bits supérieurs définis sur zéro et que la ligne de code suivante définit TranslatedAddress-Highpart> sur zéro.

Avec précaution, nous pouvons utiliser les éléments suivants :

TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);

Cela fonctionne dans cet exemple : la macro HalCreateQva retourne 64 bits, les 32 bits supérieurs étant définis sur zéro. Veillez simplement à ne pas laisser les 32 bits supérieurs non définis dans un environnement 32 bits, ce que cette deuxième solution peut en fait faire.

Avertissement C4311 Exemple 5

'type cast' : troncation du pointeur de 'void *__ptr64' vers '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;

Description

WindowRegisters-WindowBase> est un pointeur qui est maintenant de 64 bits. Le code indique de décaler à droite cette valeur de 20 bits. Le compilateur ne nous permettra pas d’utiliser l’opérateur de décalage droit (>>) sur un pointeur ; par conséquent, nous devons le caster en une sorte d’entier.

Solution

Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));

La conversion à un ULONG_PTR est exactement ce dont nous avons besoin. Le problème suivant est Wbase. Wbase est un ULONG et est de 32 bits. Dans ce cas, nous savons que le pointeur 64 bits WindowRegisters-WindowBase> est valide dans les 32 bits inférieurs, même après avoir été déplacé. L’utilisation de la macro PtrToUlong est donc acceptable, car elle tronque le pointeur 64 bits en ULONG 32 bits. La cast PVOID est nécessaire, car PtrToUlong attend un argument pointeur. Lorsque vous examinez le code d’assembleur résultant, tout ce casting de code C devient simplement un quad de charge, un décalage vers la droite et un stockage long.