Forum: Compiler & IDEs [WinAVR] Stackverseuchung durch main() vermeiden


von Thomas M. (langhaarrocker)


Lesenswert?

Gibt es eigentlich probate 'best practice' Wege zu unterbinden, dass der 
main() Aufruf das Ram mit nutzlosem Stackmüll verschwendet?

Bei meinem letzten Projektchen auf einem ATtiny13 behalf ich mir durch:
1
int
2
__attribute__ ((naked)) // verhindert dass Register auf dem Stack "gesichert" werden
3
__attribute__ ((section (".init8")))  // umgeht Aufruf per rcall, spart 2 Bytes Stack Ram 
4
main(void) {
5
  while (1) {
6
    //...
7
  }
8
  return 0; 
9
}

Natürlich darf man so keine Rekursion auf main mehr aufrufen, weil wegen 
'naked' keine Register gerettet werden. Aber wer will das schon.
Auch darf main() niemals zu Ende laufen, denn diese Funktion geht ja 
davon aus per rcall aufgerufen worden zu sein. Durch ".init8" wird der 
Code von main() aber direkt vor dem üblichen rcall auf main() eingefügt. 
Also rennt das Programm schon in main() rein, bevor es zu diesem rcall 
gelangt. Somit wird auch keine Rücksprungaddresse auf den Stack gelegt. 
Eigentlich ist das ja dreckig, aber wegen der Endlosschleife in main() 
auch nicht relevant.

Ist der Weg gut? Gibt es bessere? Wie vermeidest Du Verschwendung von 
Ram?

von Achim K. (aks)


Lesenswert?

1
int __attribute__ ((OS_main)) main(void)

ich kann aber nicht sagen, ob das wirklich gleichwertig ist.

von nicht"Gast" (Gast)


Lesenswert?

Hallo,

Wenn du diese paar Bytes ganz dringend braucht und es gar keine 
Möglichkeit gibt auf einen größeren Controller zu wechseln, dann lohnt 
sich der Aufwand.

Ansonsten ist es ziemlich nutzlos und reine Verschwendung von Zeit. 
Außerdem holst du dir damit mit, mit ein wenig Pech, neue Fehlerquellen 
an Bord.

Schreibst ja selber dass Rekursion so nicht geht. Mal davon abgesehen, 
dass man so was mit den eng begrenzten  Mitteln eines kleinen uC eher 
vermeiden sollte.


Grüße

von Yalu X. (yalu) (Moderator)


Lesenswert?

nicht"Gast" schrieb:
> Schreibst ja selber dass Rekursion so nicht geht.

Rekursion geht schon, nur eben nicht mit main. Das ist aber in der
Praxis keine Einschränkung, nicht einmal auf Rechnern mit gigabyteweise
RAM.

von Thomas M. (langhaarrocker)


Lesenswert?

Achim K. schrieb:
>
1
> int __attribute__ ((OS_main)) main(void)
2
>
>
> ich kann aber nicht sagen, ob das wirklich gleichwertig ist.

Hm. Ich finde keine Doku zu OS_main. Auf den ersten Versuch macht es bei 
mir genau das selbe wie '__attribute__ ((naked))'. Allerdings umgeht es 
alleine noch nicht, dass main() via rcall gestartet wird. Also wird mit 
OS_main immer noch Stack für eine ungenutzte Rücksprungaddresse 
verwschwendet. Aber es liest sich durchaus eleganter als 'naked'.

von Stefan E. (sternst)


Lesenswert?

Thomas M. schrieb:
> Auf den ersten Versuch macht es bei
> mir genau das selbe wie '__attribute__ ((naked))'.

Im Gegensatz zu "naked" verhindert es nicht das Anlegen eines eventuell 
nötigen Stack-Frames. Es ist dem "naked" also auf jeden Fall 
vorzuziehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Thomas M. schrieb:
> Ich finde keine Doku zu OS_main.

In der GCC-Dokumentation zu Function Attributes.

von Thomas M. (langhaarrocker)


Lesenswert?

Ah, danke. In meiner lokal installierten Doku (WinAVR) fehlte das noch. 
Aber in der online Doku ist drin:
http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Function-Attributes.html#index-g_t_0040code_007bOS_005fmain_007d-AVR-function-attribute-2653

von Thomas M. (langhaarrocker)


Lesenswert?

nicht"Gast" schrieb:
> Wenn du diese paar Bytes ganz dringend braucht und es gar keine
> Möglichkeit gibt auf einen größeren Controller zu wechseln, dann lohnt
> sich der Aufwand.
>
> Ansonsten ist es ziemlich nutzlos und reine Verschwendung von Zeit.

Ich hingegen halte es für verplemperte Zeit nach einem 
überdimensionierten Controller zu suchen nur weil ich meine 
Programmiersprache (noch) nicht beherrsche.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wenn zwei(!) "verschwendete" Bytes Stack mit "überdimensioniertem 
Controller" und "nicht Beherrschen der Programmiersprache" in Verbindung 
gebracht werden, läuft irgendwas anderes schief.

von Thomas M. (langhaarrocker)


Lesenswert?

Bei sowas wie einem ATtiny13A mit gerade mal 32 Byte Ram sind zwei Bytes 
ziemlich wertvoll. Auch ging es ja nicht nur um diese zwei Bytes Verlust 
durch rcall, sondern vor allem um all die Register, die nutzlos auf den 
Stack gesichert werden. Da ist schnell alleine durch den main Aufruf 
fast das halbe Ram weg - für nix. Mit zwei Attributen für die main 
Funktion ist das Problem auf nil reduziert. Das zu lernen - da ist 
nichts schief gelaufen.

Schief gelaufen wäre es wenn der Mikrocontroller für die zu bewältigende 
Aufgabe so haarscharf knapp bemessen wäre, dass es an zwei Bytes 
scheitern kann. In dem Fall würde ich Deine Meinung teilen.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Wenn man sich so sicher ist könnte man ja auch einfach den Stackpointer 
zu beginn Main auf den Initialwert zurücksetzen...

von Thomas M. (langhaarrocker)


Lesenswert?

Hihi, das hatte ich auch schon gemacht. Allerdings kostet auch das 2 
Bytes - diesmal aber bloß Flash Rom.

Aber das hat noch andere Effekte: Wenn man tatsächlich absolut gar 
keinen Stack verwendet, kann man ja durchaus auch eine Datenstruktur bis 
ganz zum Ende des Rams legen. Normalerweise werden Daten schon vor dem 
Aufruf von main() initialisiert. Wird jetzt aber nicht der rcall Befehl 
auf main() umgangen, wird diese Initialsierung wieder mit der Adresse 
von main kaputt geschrieben. Aus diesem Grunde ziehe ich die Lösung mit 
_attribute_ ((section(".init8"))) vor.

von Kaj (Gast)


Lesenswert?

Thomas M. schrieb:
> Schief gelaufen wäre es wenn der Mikrocontroller für die zu bewältigende
> Aufgabe so haarscharf knapp bemessen wäre, dass es an zwei Bytes
> scheitern kann.
Wenn ich das hier so lese, ist ja genau das der Fall...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas M. schrieb:
> Allerdings umgeht es alleine noch nicht, dass main() via rcall gestartet
> wird.

Wenn dich das auch noch stört, musst du wohl deinen eigenen
Startup-Code schreiben.  Der aus der Bibliothek macht nun einmal
standardgemäß "exit(main());".

von Simon K. (simon) Benutzerseite


Lesenswert?


von Thomas M. (langhaarrocker)


Lesenswert?

Simon K. schrieb:
> 
http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Attribute_noreturn.2C_OS_main_und_OS_task

Eine sehr nützliche Seite, von der ich schon viel profitiert habe. 
Vielleicht sollte man sie ein büschn ergänzen um zu erklären, was 
os_main / os_task machen.

Das 'noreturn' für main habe ich aufgegeben, weil das die Ausgabe nicht 
ändert, aber Warnungen generiert, weil in C ja spezifiziert ist dass 
main int zurückliefern soll.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.