Ganzzahlüberlauf nicht konsistent

8

Verzeihen Sie, wenn diese Frage schon einmal gestellt wurde. Ich suchte nach Antworten auf ähnliche Fragen, aber ich bin immer noch verwirrt über mein Problem. Also werde ich die Frage trotzdem erschießen. Ich verwende eine C-Bibliothek namens libexif für Bilddaten. Ich benutze meine Anwendung (die diese Bibliothek verwendet) sowohl auf meinem Linux-Desktop als auch auf meinem MIPS-Board. Für eine bestimmte Bilddatei, wenn ich versuche, die erstellte Zeit abzurufen, erhalte ich einen Fehler / ungültigen Wert. Beim weiteren Debuggen sah ich, dass ich für diese bestimmte Image-Datei nicht wie erwartet das Tag (EXIF_TAG_DATE_TIME) bekam.

Diese Bibliothek verfügt über mehrere nützliche Funktionen. Die meisten Funktionen sind wie folgt strukturiert:

%Vor%

Wenn die Bibliothek versucht, das Vorhandensein von Tags in Rohdaten zu untersuchen, ruft sie exif_get_short() auf und weist den zurückgegebenen Wert einer Variablen zu, die den Typ enum (int) hat.

Im Fehlerfall gibt exif_get_short() , das einen nicht vorzeichenbehafteten Wert (34687) zurückgeben soll, eine negative Zahl (-30871) zurück, was die gesamte Tag-Extraktion aus den Bilddaten zunichte macht.

34687 liegt außerhalb des Bereichs des maximal darstellbaren int16_t-Werts. Und führt daher zu einem Überlauf. Wenn ich diese kleine Änderung im Code mache, scheint alles gut zu funktionieren

%Vor%

Da es sich aber um eine ziemlich stabile Bibliothek handelt, die seit einiger Zeit in Gebrauch ist, führte das zu der Annahme, dass ich hier etwas vermisse. Außerdem ist dies der allgemeine Weg, den der Code für andere Utility-Funktionen auch strukturiert. Beispiel: exif_get_long() ruft exif_get_slong() auf. Ich müsste dann alle Utility-Funktionen ändern.

Was mich verwirrt, ist, dass ich, wenn ich diesen Code auf meinem Linux-Desktop für die Fehlerdatei ausführe, keine Probleme sehe und die Dinge gut mit dem ursprünglichen Bibliothekscode funktionieren. Was zu meiner Überzeugung führte, dass die Makros UINT16_MAX und INT16_MAX auf meinem Desktop und MIPS-Board unterschiedliche Werte haben. Aber leider ist das nicht der Fall. Beide drucken identische Werte auf der Platine und auf dem Desktop. Wenn dieses Stück Code fehlschlägt, sollte es auch auf meinem Desktop fehlschlagen.

Was fehlt mir hier? Alle Hinweise würden sehr geschätzt werden.

BEARBEITEN: Der Code, der exif_get_short () aufruft, sieht ungefähr so ​​aus:

%Vor%

Der Typ ExifTag ist wie folgt:

%Vor%

Der verwendete Cross-Compiler ist mipsisa32r2el-timesys-linux-gnu-gcc

%Vor%

Ich verwende Libexif innerhalb von Qt - Qt Media Hub (eigentlich Libexif kommt mit Qt Media Hub)

EDIT2: Einige zusätzliche Beobachtungen: Ich beobachte etwas Bizarres. Ich habe Druckanweisungen in exif_get_short () geschrieben. Kurz vor der Rückkehr

%Vor%

Ich sehe folgendes o / p: return_value 34665 34665

Ich habe dann auch print-Anweisungen in den Code eingefügt, der exif_get_short ()

aufruft %Vor%

Ich sehe folgendes o / p: TAG -30871 4294936425

EDIT3: Assembly-Code für exif_get_short () und exif_get_sshort () wird auf der MIPS-Karte

abgelegt %Vor%

Nur zur Vollständigkeit, der ASM-Code von meinem Linux-Rechner genommen

%Vor%

EDIT4: Hoffentlich mein letztes Update :-) ASM-Code mit Compiler-Option auf -O1

gesetzt

exif_get_short:

%Vor%     
Spottsworth 20.08.2012, 03:55
quelle

2 Antworten

4

Eine Sache, die die MIPS-Assembly zeigt (obwohl ich kein Experte in der MIPS-Assembly bin, also gibt es eine gute Chance, dass ich etwas vermisse oder sonst falsch) ist, dass die exif_get_short() -Funktion nur ein Alias ​​für die% co_de ist % Funktion. Alles, was exif_get_sshort() tut, springt zur Adresse der Funktion exif_get_short() .

Das Funktionszeichen exif_get_sshort() erweitert den 16-Bit-Wert, den es zurückgibt, in das volle 32-Bit-Register, das für die Rückgabe verwendet wird. Es ist nichts falsch daran - es ist wahrscheinlich, was die MIPS ABI angibt (ich bin mir nicht sicher).

Da jedoch die Funktion exif_get_sshort() nur zur Funktion exif_get_short() springt, hat sie keine Möglichkeit, die oberen 16 Bits des Registers zu löschen.

Wenn also der 16-Bit-Wert 0x8769 aus dem Puffer zurückgegeben wird (ob von exif_get_sshort() oder exif_get_sshort() ), enthält das exif_get_short() -Register, das zum Zurückgeben des Funktionsergebnisses verwendet wird, , das die folgenden Interpretationen haben kann :

  • als 32-Bit 0xffff8769 : -30871
  • als 32-Bit 'unsigned int: 4294936425

  • als 16-Bit-Signatur signed int : -30871

  • als 16-Bit unsigned int16_t : 34665

Wenn der Compiler sicherstellen soll, dass das uint16_t return-Register einen oberen 16-Bit-Wert für einen -Rückgabetyp auf Null setzt, dann hat er einen Fehler im Code, der für uint16_t - stattdessen - ausgibt Wenn Sie zu exif_get_short() springen, sollten Sie exif_get_sshort() aufrufen und die obere Hälfte von exif_get_sshort() löschen, bevor Sie zurückkehren.

Aus der Beschreibung des Verhaltens, das Sie sehen, sieht es aus, als ob der Code, der aufruft, erwartet, dass der für den Rückgabewert verwendete exif_get_short() -Resister die oberen 16 Bits gelöscht hat, so dass das gesamte 32-Bit-Register kann unverändert für den 16-Bit -Wert verwendet werden.

Ich bin nicht sicher, was der MIPS ABI angibt (aber ich würde vermuten, dass es angibt, dass die oberen 16 Bits des uint16_t -Registers durch gelöscht werden sollten), aber es scheint entweder einen Code zu geben Generationsfehler, der exif_get_short() nicht sicherstellt, dass exif_get_short() vollständig korrekt ist, bevor es zurückkehrt oder ein Fehler, bei dem der Aufrufer von annimmt, dass die vollen 32 Bits von exif_get_short() gültig sind, wenn nur 16 Bits sind.

    
Michael Burr 21.08.2012, 08:33
quelle
0

Das ist auf so vielen Ebenen kaputt, ich weiß nicht wo ich anfangen soll. Schauen Sie sich an, was hier gemacht wird:

  • Die Zeichen unsigned werden aus dem Puffer gelesen.
  • Sie sind einem signierten int16_t in exif_get_sshort .
  • zugeordnet
  • Dies ist einem unsigned uint16_t in exif_get_short zugeordnet.
  • Dies wird schließlich einem enum zugeordnet, das vom Typ signiert int .
  • ist

Ich würde sagen, es ist ein Wunder, dass es überhaupt funktioniert.

Zuerst erfolgt die Zuweisung von den Zeichen zu int16_t mit den Werten, nicht mit der Repräsentation:

%Vor%

Was Sie bereits in die Grube von undefiniertem Verhalten versetzt, wenn das Ergebnis tatsächlich negativ ist. Außerdem funktioniert es nur, wenn die int-Darstellung der Implementierung mit der im Dateiformat übereinstimmt (Zweierkomplement, nehme ich an). Es wird für das eigene Komplement und die Zeichengröße scheitern. Überprüfen Sie, was für die MIPS-Implementierung der Fall ist.

Der saubere Weg wäre umgekehrt: Weisen Sie zwei Zeichen aus dem Puffer einem uint16_t zu, was gut definierte Operationen sind, und verwenden Sie dies, um ein int16_t zurückzugeben. Sie könnten dann für weitere Korrekturen des Werts für verschiedene Darstellungen sorgen, falls erforderlich.

Zusätzlich hier:

%Vor%

0 ist eine sehr schlechte Wahl für einen Rückgabewert, da es sich um eine gültige Enum-Konstante handelt:

%Vor%

Wenn dies als Standard für die Invalidität erwartet wird, sollte die Konstante zurückgegeben werden, nicht die magische Zahl. Obwohl dies eine generische Funktion zu sein scheint, um ein int16_t zurückzugeben, sollte hier ein anderer Fehlermechanismus verwendet werden.

Befolgen Sie für Ihre spezielle Frage den Konvertierungsfluss zwischen signierten und unsignierten Elementen in Ihrer MIPS-Implementierung, einschließlich der durchgeführten Standardaktionen, und untersuchen Sie alle Zwischenwerte, um den Punkt zu finden, an dem sie unterbrochen wird. Ihr MIPS verwendet 32-Bit-Ints, nicht 16-Bit, oder? Überprüfen Sie INT_MAX und UINT_MAX.

    
Secure 20.08.2012 07:30
quelle

Tags und Links