Häufige Compilerfehler

In diesem Abschnitt werden die typischen Compilerfehler veranschaulicht, die beim Migrieren einer vorhandenen Codebasis auftreten. Diese Beispiele stammen aus HAL-Code auf Systemebene, obwohl die Konzepte direkt auf Code auf Benutzerebene anwendbar sind.

Warnung C4311 Beispiel 1

Typumwandlung: Zeigerabkürzung von "void *__ptr64 " auf "unsigned long"

Code

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

Description

PtrToUlong ist je nach Verwendung eine Inlinefunktion oder ein Makro. Es schneidet einen Zeiger auf eine ULONG ab. Obwohl 32-Bit-Zeiger nicht betroffen sind, wird die obere Hälfte eines 64-Bit-Zeigers abgeschnitten.

CIA_PCI_CONFIG_BASE_QVA wird als PVOID deklariert. Die ULONG-Umwandlung funktioniert in der 32-Bit-Welt, führt jedoch zu einem Fehler in der 64-Bit-Welt. Die Lösung besteht darin, einen 64-Bit-Zeiger auf eine ULONG zu erhalten, da das Ändern der Definition der Union, dass pPciAddr-u.AsULONG> in Änderungen zu viel Code definiert wird.

Die Verwendung des Makros PtrToUlong zum Konvertieren des 64-Bit-PVOID in die erforderliche ULONG ist zulässig, da wir Über den spezifischen Wert von CIA_PCI_CONFIG_BASE_QVA verfügen. In diesem Fall enthält dieser Zeiger nie Daten in den oberen 32 Bits.

Lösung

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

Warnung C4311 Beispiel 2

Typumwandlung: Zeigerabkürzung von "Struktur _ERROR_FRAME *__ptr64 " zu "unsigned long"

Code

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

Description

Das Problem besteht darin, dass der letzte Parameter auf diese Funktion ein Zeiger auf eine Datenstruktur ist. Da PUncorrectableError ein Zeiger ist, ändert sich die Größe mit dem Programmiermodell. Der Prototyp für KeBugCheckEx wurde geändert, sodass der letzte Parameter ein ULONG_PTR ist. Daher muss der Funktionszeiger auf eine ULONG_PTR umgewandelt werden.

Sie können fragen, warum PVOID nicht als letzter Parameter verwendet wurde. Je nach Kontext des Aufrufs kann der letzte Parameter etwas anderes als ein Zeiger oder vielleicht ein Fehlercode sein.

Lösung

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

Warnung C4244 Beispiel 1

'=' : Konvertierung von "Struktur _CONFIGURATION_COMPONENT *__ptr64 " in "Struktur _CONFIGURATION_COMPONENT *", möglicher Datenverlust

Code

Component = &CurrentEntry->ComponentEntry;

Description

Die Funktion deklariert die Variable Component als PCONFIGURATION_COMPONENT. Später wird die Variable in der folgenden Zuweisung verwendet, die richtig erscheint:

Component = &CurrentEntry->ComponentEntry;

Der Typ PCONFIGURATION_COMPONENT wird jedoch wie folgt definiert:

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

Die Typdefinition für PCONFIGURATION_COMPONENT bietet einen 32-Bit-Zeiger in 32-Bit- und 64-Bit-Modellen, da er POINTER_32 deklariert wird. Der ursprüngliche Designer dieser Struktur wusste, dass sie im BIOS in einem 32-Bit-Kontext verwendet werden sollte, und definierte sie ausdrücklich für diese Verwendung. Dieser Code funktioniert in 32-Bit-Windows einwandfrei, da die Zeiger zufällig 32-Bit sind. In 64-Bit-Windows funktioniert dies nicht, da sich der Code im 64-Bit-Kontext befindet.

Lösung

Um dieses Problem zu umgehen, verwenden Sie CONFIGURATION_COMPONENT * anstelle des 32-Bit-PCONFIGURATION_COMPONENT . Es ist wichtig, den Zweck des Codes genau zu verstehen. Wenn dieser Code dazu bestimmt ist, das 32-Bit-BIOS oder den Systemspeicher zu berühren, funktioniert dieser Fix nicht.

POINTER_32 ist in Ntdef.h und Winnt.h definiert.

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

Warnung C4242 Beispiel 2

'=' : Konvertierung von "__int64 " in "unsigned long", möglicher Datenverlust

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

Diese Warnung wird generiert, da die Berechnung 64-Bit-Werte verwendet, in diesem Fall Zeiger, und das Ergebnis in einer 32-Bit-ULONG-Instanz platziert wird.

Lösung

Der Typ wandelt das Ergebnis der Berechnung wie hier gezeigt in ein ULONG um:

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

Beim Typecasting des Ergebnisses weiß der Compiler, dass Sie sich über das Ergebnis sicher sind. Stellen Sie jedoch sicher, dass Sie die Berechnung verstehen und wirklich sicher sind, dass sie in eine 32-Bit-ULONG passt.

Wenn das Ergebnis möglicherweise nicht in eine 32-Bit-ULONG passt, ändern Sie den Basistyp der Variablen, die das Ergebnis enthält.

Warnung C4311 – Beispiel 1

'type cast' : Zeigerabkürzung von 'void *__ptr64 ' zu '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

Diese gesamte Funktion behandelt Adressen als ganze Zahlen, was erfordert, dass diese ganzen Zahlen portabel eingegeben werden müssen. Alle lokalen Variablen, Zwischenwerte in Berechnungen und Rückgabewerte sollten portierbare Typen sein.

Lösung

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 ist ein Zeiger, der selbst 32 Bit für 32-Bit-Windows und 64 Bit für 64-Bit-Windows ist. Es verweist auf eine ganze Zahl ohne Vorzeichen, ULONG_PTR, d. h. 32 Bits für 32-Bit-Windows und 64 Bit für 64-Bit-Windows.

Warnung C4311 – Beispiel 2

'type cast' : Zeigerabkürzung von 'void *__ptr64 ' zu '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

Obwohl alle QVA-Werte (Quasi Virtual Address) in dieser Phase wirklich 32-Bit-Werte sind und in eine ULONG passen, ist es konsistenter, wenn möglich alle Adressen als ULONG_PTR Werte zu behandeln.

Der Zeiger PciIoSpaceBase enthält den QVA, der im Makro HAL_MAKE_QVA erstellt wird. Dieses Makro gibt einen 64-Bit-Wert zurück, wobei die obersten 32 Bits auf 0 festgelegt sind, damit die Mathematik funktioniert. Wir könnten den Code einfach so belassen, dass der Zeiger in eine ULONG abgeschnitten wird, aber von dieser Vorgehensweise wird abgeraten, um die Code-Wartbarkeit und Portabilität zu verbessern. Beispielsweise kann sich der Inhalt eines QVA in Zukunft ändern, um einige der oberen Bits auf dieser Ebene zu verwenden und den Code zu unterbrechen.

Lösung

Seien Sie sicher und verwenden Sie ULONG_PTR für alle Adress- und Zeigerberechnungen.

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

Warnung C4311 Beispiel 3

'type cast' : Zeigerabkürzung von 'void *__ptr64 ' zu '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

Der Compiler warnt vor der Adresse von (&) und linken Umschaltoperatoren (<<), wenn sie auf Zeigertypen angewendet werden. Im obigen Code ist Qva ein PVOID-Wert . Wir müssen dies in einen ganzzahligen Typ umwandeln, um die Mathematik auszuführen. Da der Code portierbar sein muss, verwenden Sie ULONG_PTR anstelle von ULONG.

Lösung

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

Warnung C4311 Beispiel 4

'type cast' : Zeigerabkürzung von 'void *__ptr64 ' zu 'unsigned long '

Code

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

Description

TranslatedAddress ist eine Union, die ungefähr wie folgt aussieht:

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

Lösung

Wenn Wir wissen, was der rest des Codes in Highpart platzieren könnte, können wir eine der hier gezeigten Lösungen auswählen.

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

Das PtrToUlong-Makro schneidet den von HalCreateQva zurückgegebenen Zeiger auf 32 Bit ab. Wir wissen, dass für den von HalCreateQva zurückgegebenen QVA die oberen 32 Bits auf 0 festgelegt sind und die nächste Codezeile TranslatedAddress-Highpart> auf 0 festgelegt ist.

Mit Vorsicht könnten wir Folgendes verwenden:

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

Dies funktioniert in diesem Beispiel: Das HalCreateQva-Makro gibt 64 Bits zurück, wobei die oberen 32 Bits auf 0 festgelegt sind. Achten Sie nur darauf, die oberen 32 Bits nicht in einer 32-Bit-Umgebung undefiniert zu lassen, was diese zweite Lösung tatsächlich tun kann.

Warnung C4311 Beispiel 5

'type cast' : Zeigerabkürzung von 'void *__ptr64 ' zu '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> ist ein Zeiger und ist jetzt 64 Bits. Der Code besagt, dass dieser Wert nach rechts um 20 Bit verschoben werden soll. Der Compiler lässt uns den Rechtsverschiebungsoperator (>>) nicht für einen Zeiger verwenden. Daher müssen wir ihn in eine Art ganze Zahl umwandeln.

Lösung

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

Die Umwandlung in eine ULONG_PTR ist genau das, was wir brauchen. Das nächste Problem ist Wbase. Wbase ist ein ULONG und ist 32 Bits. In diesem Fall wissen wir, dass der 64-Bit-Zeiger WindowRegisters-WindowBase> auch nach dem Verschieben in den unteren 32 Bits gültig ist. Dadurch wird das PtrToUlong-Makro akzeptabel, da es den 64-Bit-Zeiger in einen 32-Bit-ULONG abschneidet. Die PVOID-Umwandlung ist erforderlich, da PtrToUlong ein Zeigerargument erwartet. Wenn Sie den resultierenden Assemblercode betrachten, wird all diese C-Codeumwandlung nur zu einem Lastquadrate, nach rechts verschoben und lange gespeichert.