Software > Coding

gcc, GEMDOS Super und Stackzerstörung

(1/13) > >>

czietz:
Hallo,

ich habe aus Neugier mal meinen RAM-Tester YAART [1] von Pure C auf gcc und libcmini portiert, was mit sehr wenigen Änderungen möglich war. Ein hinterhältiger Bug hat mich aber doch Zeit gekostet. Daher beschreibe ich ihn hier kurz, in der Hoffnung anderen damit zu helfen.

Ich benutze die GEMDOS-Funktion Super, um innerhalb des Codes in den Supervisor-Modus zu schalten und später wieder zurück:


--- Code: --- /* switch to supervisor mode so we can manipulate memory */
suret = Super(0);

/* Dinge, die nur im Supervisor-Modus möglich sind */
/* [...] */

/* back to user mode */
Super((void *)suret);

--- Ende Code ---

Nun hat der gcc-Optimierer die -- an sich nette -- Eigenschaft, den Stack nicht nach jedem Funktionsaufruf zu korrigieren, sondern die Korrekturen zu sammeln und an späterer Stelle en bloc durchzuführen. Dummerweise stellt aber bereits Super((void *)suret); den Stack zurück. gcc hat nun in meinem Fall die Stackkorrekturen für die Funktionsaufrufe, die im Supervisor-Modus ausgeführt wurden, erst danach gemacht. Dadurch wird der Stack beschädigt und spätestens beim nächsten return fliegt einem das ganze mit Bömbchen um die Ohren.

Glücklicherweise gibt es eine Lösung, ohne dass man diese sinnvolle Optimierung ganz abschalten muss. Man deklariert die Funktion, in der die Super-Aufrufe sind, als


--- Code: ---void foobar(void) __attribute__ ((optimize ("no-defer-pop")));
--- Ende Code ---

und aktiviert somit nur lokal für diese Funktion die sofortige Stackkorrektur nach jedem Funktionsaufruf.

Noch besser wäre es natürlich, könnte man gcc mitteilen, dass Super eine Funktion ist, die den Stack modifiziert und deshalb alle Stackkorrekturen nur bis zum nächsten Aufruf von Super gesammelt werden können. Eine Art Barriere also. Weiß jemand, ob das geht?

[1] http://forum.atari-home.de/index.php?topic=13002.msg208583#msg208583

czietz:
OK, nach einem Hinweis, gefunden mit Google, dass gcc (sinnigerweise) vor einem goto alle noch ausstehenden Stack-Korrekturen durchführt, habe ich mir folgende Konstruktion basteln können:


--- Code: ---#ifdef __GNUC__
/* BARRIER2 is here to expand __LINE__. */
#define BARRIER3(x) goto _barrier##x; _barrier##x:
#define BARRIER2(x) BARRIER3(x)
#define BARRIER BARRIER2(__LINE__)
#else
#define BARRIER
#endif

--- Ende Code ---

Ein BARRIER jeweils von den Super-Aufrufen sorgt nun dafür, dass der Stack heile bleibt. Das unnütze goto wird später vom Optimierer wieder entfernt. Schön ist das aber auch nicht...

simonsunnyboy:
Also nocheinfacher ohne gcc Eigenschaften, packe deinen Code in eine eigene Funktion und rufe diese über XBIOS Supexec() auf. Da kümmert sich das TOS selbst um Stack und alles, das ist Compilerunabhängig.  Geht mit jeder void ...(void) Funktion

mfro:
Um Stack-Fehler zu vermeiden, gibt's in den Bindings (osbind.h) die SuperToUser()-Funktion, die den Stackpointer explizit setzt. Ich hab' mir jetzt deinen Fall nicht genau angesehen, nehme aber einfach mal an, daß der auch abgedeckt wird.

Supexec() ist aber tatsächlich in den meisten Fällen die bessere Wahl.

czietz:
SuperToUser() dürfte tatsächlich eine Lösung sein. Supexec() ist hier nicht so toll, weil ich -- anders im Beispiel mit foobar() -- schon Parameter an die Funktion übergeben will, am liebsten ohne globale Variablen dafür zu verwenden.

Aber das Experiment ist ohnehin mehr oder weniger beendet, weil gcc (selbst mit -O2) unglaublich ineffizienten Code generiert. Die "Moving Inversions" in YAART brauchen ca. 50%(!) länger als in der mit Pure C compilierten Version. Darin werden keine Bibliotheksfunktionen aufgerufen, es ist also wirklich der generierte Code.

Navigation

[0] Themen-Index

[#] Nächste Seite

Zur normalen Ansicht wechseln