Komfortabler Dateimanager mit vielen Funktionen

Abenteuer x64

By Sven on 11.04.2005 - 22:32 in Entwicklung

In den letzten Tagen habe ich mich damit beschäftigt, die Packer-Bibliotheken auf 64 Bit anzupassen. Das Kompilieren unter x64 geschah recht problemlos, allerdings stürzten einige der Bibliotheken (insbesondere die LZ77-Abkömmlinge) beim Komprimieren bzw. beim Entpacken ab.

Die Hauptursache war hier der Umgang mit Zeigern. Das Zeigermanagement kann sich unter x64 zu einer kniffligen Sache entwickeln, wenn man Zeigerarithmetik mit vorzeichenlosen Zahlen betreibt. Unter x32 gibt es hiermit keine Probleme, da es bei einem eventuellen Überlauf automatisch zu einer negativen Zahl kommt. Bei einer Erweiterung der Zeiger auf 64 Bit gibt es aber bei Operationen mit 32 Bit-Datentypen keinen Überlauf mehr, stattdessen wird das niedrigste Bit in den oberen 32 Bit gesetzt.

An einem Beispiel lässt sich das sehr gut demonstrieren. Der Ausgangspunkt ist ein kleines Stück C++-Code, der ein dynamisches Array erstellt und innerhalb dieses Arrays mit Zeigern arbeitet.

BYTE* pMem1 = NULL; BYTE* pMem2 = NULL;
pMem1 = new BYTE[10]; UINT nTest = (UINT) -1; // pMem zeigt auf 0x01814a28

for (int iTemp = 0; iTemp < 10; iTemp++)
{
    pMem1[iTemp] = (BYTE) iTemp;
}

pMem2 = pMem1 + 5; 		// pMem2 ist danach 0x01814a2d
pMem2 = pMem2 + nTest; 	// pMem2 ist danach 0x01814a2c

In den Kommentaren habe ich die Werte für die Zeiger angegeben, die beim Durchlauf auf meinem System entstanden sind. In Zeile 9 wird pMem2 auf den 6. Index (der Index ist 0-basiert) innerhalb des Arrays gesetzt. In Zeile 10 wird pMem2 um 0xFFFFFFFF verschoben, durch den entstehenden Überlauf ist das mit einer Subtraktion von -1 vergleichbar. pMem2 zeigt nun auf das 5. Element im Array.

Unter x64 lässt sich der Code problemlos kompilieren, auch eine explizite Prüfung auf 64bit-Probleme (Schalter /Wp64) meldet keine Probleme. Spätestens aber beim Versuch, mit pMem2 auf das 5. Element im Array zuzugreifen, funkt Windows mit einer Speicherschutzverletzung dazwischen. Wenn man sich den Inhalt von pMem2 genauer anschaut, dann ist der Grund dafür auch ersichtlich. Anstatt wie erhofft auf 0x00000000:00373d84 zeigt pMem2 plötzlich auf 0x00000001:00373d84, was definitiv nicht zum Speicher des Arrays gehört.

BYTE* pMem1 = NULL; BYTE* pMem2 = NULL;
pMem1 = new BYTE[10]; UINT nTest = (UINT) -1; // pMem zeigt auf 0x00000000:00373d80

for (int iTemp = 0; iTemp < 10; iTemp++)
{
    pMem1[iTemp] = (BYTE) iTemp;
}

pMem2 = pMem1 + 5; 		// pMem2 ist danach 0x00000000:00373d85
pMem2 = pMem2 + nTest; 	// pMem2 ist danach 0x00000001:00373d84

Genau wie unter x32 wird auch unter x64 pMem2 um 0xFFFFFFFF verschoben. Da der Wertebereich der 64bit-Zeiger aber nicht bei 0xFFFFFFFF endet, findet hier kein Überlauf statt und pMem2 zeigt auf eine ungültige Adresse.

Ein kleiner Typecast bei Zeigeroperationen hilft hier weiter. Der einzige Unterschied im folgenden Code zum vorherigen liegt in der letzten Zeile. Wird nTest explizit zu einem vorzeichenbehafteten Typ gecastet, dann zeigt pMem2 nach der Zeigerarithmetik auch auf die gewünschte Stelle.

BYTE* pMem1 = NULL; BYTE* pMem2 = NULL;
pMem1 = new BYTE[10]; UINT nTest = (UINT) -1; // pMem zeigt auf 0x00000000:00373d80

for (int iTemp = 0; iTemp < 10; iTemp++)
{
    pMem1[iTemp] = (BYTE) iTemp;
}

pMem2 = pMem1 + 5; 		// pMem2 ist danach 0x00000000:00373d85
pMem2 = pMem2 + (int) nTest; 	// pMem2 ist danach 0x00000000:00373d84

Alternativ kann man natürlich auch alle vorzeichenlosen Datentypen, die im Zusammenhang mit der Zeigerarithmetik verwendet werden, zu vorzeichenbehafteten umdefinieren. Das ist aber leider auch eine Menge Arbeit und es besteht die Gefahr, dass sich ohne große Not Fehler in bereits getesteten Code einschleichen.

Interessierten Lesern kann ich auch noch ein MSDN-Video von Kang Su Gatlin empfehlen. Hier wird nochmals auf einige Sachen in Bezug auf mögliche Probleme bei der Portierung auf 64bit eingegangen.

Es gibt 2 Kommentare zu diesem Beitrag

Trackback URL | RSS-Feed für Kommentare

  1. Uwe sagt:

    Deswegen nur noch mit .NET programmieren, da ist das ganze wenigstens halbwegs handlebar.

  2. Sven sagt:

    Wenn man auf einige elementare Sachen achtet, dann unterscheidet sich die 64bit-Programmierung nicht allzu sehr von der gewohnten 32bittigen.

Top