On Thu, 2003-09-11 at 01:06, Philipp Thomas wrote:
Ralf Corsepius
[Wed, 10 Sep 2003 15:03:19 +0200]: warning: dereferencing type-punned pointer will break strict-aliasing rules
Was will mir diese Warnung sagen?
Genau was sie sagt :) Michael hat ja schon auf die Info-Doku hingewiesen. Wenn man weiss wo man suchen muss, findet man sie auch ;)
Ich hatte -fstrict-aliasing noch nie bewusst verwendet.
In Kürze und vereinfacht: Der Standard sagt, dass auf ein Objekt nur mit einem Zeiger des passenden Typs verwiesen kann. Ausnahme sind einzig 'char *' und 'void *', diese können als Alias für beliebige Typen dienen. Also
int a; void **v;
int *pa = &a; /* OK */ void *pv = (Void *)&a; /* OK */ char *pc = (char *)&a; /* OK */
short *ps = (short *)&a; /* type-punning Warnung */ v = (void **)&pa; /* " " */
Wie bedeutsam ist sie?
Sehr.
Wenn gcc schon warnt, wie sähen die Folgen aus, falls etwas "zerbricht"?
Diese Alias-Regeln macht sich der Compiler zunutze, um den Code (ab -O2) entsprechend zu optimieren.
... und dabei eine Menge alter Code zerbrochen ...
Wird jetzt doch gegen die Regeln verstossen, in dem dieser umgecastete Zeiger dereferenziert wird, können sehr merkwürdige Dinge passieren, eben weil der Compiler nicht damit rechnet.
Wäre es dann nicht besser anstatt einer Warnung einen Error zu erzeugen, damit Entwickler, Integratoren und ähnliche Personen entweder bewusst -fno-strict-aliasing verwenden oder aber ihren Code an (!) GCC-3.3 anpassen? So aber rauscht bei während eines stundenlangen Compilerlaufs irgendwo eine "punned pointer" Warnung durch und wird im allgemeinen Warnungsrauschen übersehen.
Man hat jetzt mehrere Möglichkeiten:
1) -fno-strict-aliasing verwenden (wie es im Kernel gemacht wird weil die Kernelhacker behaupten, dass sie den Code nicht ändern könnten). Damit wird aber auch die entsprechende Optimierung deaktiviert. Dieser Vorschlag hat was für sich ;-)
Eine Alternative wäre gcc zu patchen und den Default zu ändern (Ist in diesem Fall machbar).
2) Code so umschreiben, dass kein Umcasten nötig ist. Das ist nicht immer oder nur unter viel Aufwand möglich. Genau das ist mein Problem.
Nahezu alle Stellen, an denen ich auf diese Warnung stosse, sind "opaque"-Code-Teile, die man auf "nicht-opaque" umschreiben und vervielfältigen müsste. Doch alle betroffenen Stellen, die ich bisher gefunden habe, waren Argumente an "static inline" Funktionen. Ich könnte nun, statt "static inline" Funktionen, #define's verwenden, was ich als absurd empfinde und mich in die C-Steinzeit zurückwirft.
Ein leicht lösbarer Fall sind z.B. Wrapper um free(), malloc() etc. , die einen 'void **' übergeben bekommen (hatte ich z.B. bei e2fsprogs, xntp und mutt). Hier ändert man die Wrapperfunktionen dahingehend, dass sie nun einen 'void *' erwarten, den man *intern* in 'void **' umcastet.
Das würde eine API-Änderung bedeuten (Wir verwenden teilweise "static inline" Funktionen in exportierten Headern) und ist deshalb auch nicht applikabel.
Dann kann man bei den jeweiligen Aufrufen der Funktionen einfach jeglichen Cast entfernen ('void *' akzeptiert jede Art von Zeiger).
3) GCC spezifische Lösungen verwenden. Da wäre zum Einen das neue Attribut may_alias (siehe Info-Doku) zum Anderen die Verwendung von Unions. Beispiel für letzteres:
void do_something(double *);
long l; union{ long *lp; double *dp; }cv = { &l };
do_something(dp);
4) Wie gesagt, alle betroffenen Stellen, die ich bisher gefunden habe, waren Argumente an "static inline" Funktionen. Konvertiere ich diese Funktionen nach "extern", tritt die Warnung nicht mehr auf, da dadurch nach meinem Verständnis die Optimierung im GCC nicht mehr aktiv wird, die die Warnung auslöst. Nur führt *DAS* wiederum "static inline" ad absurdum.
Laut Standard ist das Verhalten undefiniert, also waren die GCC-Entwickler frei, das Verhalten für den gcc zu definieren.
Nun ja, die GCC-Entwickler haben schon viele streitbare Entscheidungen getroffen, darunter einige, die ich nicht für unbedingt richtig halte :( Ich würde hier davon sprechen, dass die Entwickler eine Lücke in den Standards ausnutzen, um einen weiteren GCC-ismus zu implementieren ;-)
Aber beide Lösungen *sind unportabel*, sprich bei einem anderen Compiler ist nicht sicher, dass es auch funktioniert.
Kennst Du einen Nicht-GCC C-Compiler, der ähnliche Tricks verwendet? Ich nicht. Ich kann den Eindruck nicht verwehren, dass GCC und/oder C im Allgemeinen langsam daran ist, einem die alten casting-Tricks zu verleiten, die C zwar unsicher aber auch schnell gemacht haben. Unter Linux und für High-Level-Anwendungen mag das ja tolerabel und wünschenswert sein, es erschwert die klassische Low-Level Programmierung in C aber nicht unerheblich (Nicht unähnlich der streitbaren volatile Implementierung in GCC). Ralf