Forum: Compiler & IDEs gcc linkercommandfile


von Stefan A. (king-crash)


Lesenswert?

Hallo,

Ich verwende für einen CortexM0 momentan das Linkercommandfile des AVR 
Studios. Hier ist die Reihenfolge der Sections "relocate - bss - stack".
Ich hätte gerne relocate und bss am Ende des RAMs und den Stack direkt 
davor. Dann würde ein überlaufender Stack hoffentlich auch einen 
BusFault Interrupt auslösen.
Wie kann ich den Linker anweisen die Sections "von Rechts nach Links" im 
Speicher anzuordnen?

Grüße

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


Lesenswert?

Stefan A. schrieb:
> Wie kann ich den Linker anweisen die Sections "von Rechts nach Links" im
> Speicher anzuordnen?

Geht nicht. In dem von dir gewünschten Szenario musst du zwangsweise dem 
Stack eine feste Größe zuweisen und ihn dann im Linkerscript auch als 
erstes in den RAM einplanen lassen, erst danach dann die Variablen 
ansiedeln.

Die dynamische Variante (aller nicht anderweitig benutzter RAM ist 
automatisch für den Stack verfügbar) geht auf diese Weise nicht zu 
organisieren.

: Bearbeitet durch Moderator
von Nop (Gast)


Lesenswert?

Stefan A. schrieb:

> Wie kann ich den Linker anweisen die Sections "von Rechts nach Links" im
> Speicher anzuordnen?

Du kannst z.B. ein uint32_t-Array geeigneter Größe deklarieren, welches 
Du mit einem Section-Attribut für den Linker versiehst. Diese Section 
kommt dann als erstes in dem RAM-Bereich, wo Du den Stack hinhabe willst 
(SRAM oder CCM).

Der erste Eintrag in der Interrupttabelle ist der initiale Stackpointer, 
und das ist dann &my_stack_array[SIZE_DWORDS]. Du solltest SIZE_DWORDS 
(die Stack-Größe mal 4) gerade machen, dann ist der Stack automatisch 
auf 8 Byte aligned. Dann hast Du keinen Ärger, falls Du später mal die 
FPU nutzen willst.

von Nop (Gast)


Lesenswert?

Nop schrieb:
> Du solltest SIZE_DWORDS
> (die Stack-Größe mal 4) gerade machen

... und natürlich zu Beginn der Linker-Section ein "align 8" haben.

von Stefan A. (king-crash)


Lesenswert?

@Jörg
Besten Dank für die Info. Das ist tatsächlich etwas schwach vom 
Linker...
Ich habe gerade gesehen, dass es "sizeof" auch für den Linker gibt. 
Daher erlaube ich mir noch die Frage, kann man eventuell etwas in der 
Form schreiben?
1
.stack (ORIGIN(ram)+LENGTH(ram)-SIZEOF(.bss)-SIZEOF(.relocate)-4)
2
{ ... }
3
.relocate (ORIGIN(ram)+LENGTH(ram)-SIZEOF(.bss)-SIZEOF(.relocate))
4
{ ... }
5
.bss (ORIGIN(ram)+LENGTH(ram)-SIZEOF(.bss))
6
{ ... }
Die Variante mit konstanter Stack Größe habe ich natürlich in der 
Hinterhand.

@Nop
Es ist nicht notwendig ein Array zu deklarieren. Für den Stack gibt es 
die Section ".stack".

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


Lesenswert?

Stefan A. schrieb:
> Frage, kann man eventuell etwas in der Form schreiben?

Probier's aus, bin ich mir nicht sicher.

Ich habe bislang den Stack einfach immer ganz ans Ende gelegt und bin 
damit gut gefahren, war aber halt auch immer genug Reserve.

von Bauform B. (bauformb)


Lesenswert?

Nop schrieb:
> Du kannst z.B. ein uint32_t-Array geeigneter Größe deklarieren,
> Der erste Eintrag in der Interrupttabelle ist der initiale
> Stackpointer, und das ist dann &my_stack_array[SIZE_DWORDS].

Dann hat der Stack aber immer noch eine feste Größe. Das geht auch ohne 
Array, rein im Linker Script, an der gewünschten Stelle
1
$stack : {
2
stack = .;
3
    . += 512;
4
top_of_stack = .;
5
} > ram

"aller nicht anderweitig benutzter RAM ist automatisch für den Stack 
verfügbar" geht so nicht, aber mit Array könnte es funktionieren:
Im Makefile wird das vorhandene ELF-Target nahezu 1:1 kopiert. Das 
Original hängt von der Kopie ab. Der einzige Unterschied: die Kopie 
verwendet SIZE_DWORDS = 8, das Original verwendet die maximal mögliche 
Größe. Diese wird mittels $(shell magic) berechnet, ungefähr so?
SIZE_DWORDS_FINAL = ((ramsize - (data + bss)) / 8) * 8
arm-none-eabi-size liefert die Größen von data und bss.

von Stefan A. (king-crash)


Lesenswert?

Einfach einen maximal großen Stack zu erzeugen ist trivial. Alle 
Sections außer .stack an den Anfang des RAMs setzen und .stack !nicht! 
anlegen. Stattdessen "_stack_start = ORIGIN(ram)+LENGTH(ram);" 
schreiben. In der Interrupt Vector Tabelle wird _stack_start dann 
übernommen.
Bitte meine Frage genau lesen, ich möchte das RAM von der höchsten 
Adresse aus belegen mit dem Stack an unterster Stelle.

Meine vorherige Idee scheint so nicht zu funktionieren. 
"SIZEOF(.relocate)" und "SIZEOF(.bss)" expandieren so wie es aussieht zu 
0. Vermutlich ist die Größe zu der Zeit noch nicht bekannt.
Das ist kein absoluter Beinbruch ich hätte nur gerne die Sicherheit mit 
dem Busfehler bei Stacküberlauf gehabt. Ich bin natürlich weiterhin für 
Vorschläge offen.

von Nop (Gast)


Lesenswert?

Stefan A. schrieb:
> Ich bin natürlich weiterhin für Vorschläge offen.

Ich halte grundsätzlich nichts von "Stack so groß machen wie möglich". 
Wobei den Stack nach unten zu legen natürlich schonmal besser ist als 
die katastrophale Praxis, ihn nach oben zu legen.

Hauptproblem: es ist nicht klar, wieviel Speicher eigentlich wirklich 
noch frei ist für künftige Erweiterungen. Auch kann das Anlegen neuer 
Variablen zu Stackfehlern an komplett anderer Stelle führen.

Durch Testen kann man zudem nur eine untere Grenze des realen 
Stackbedarfes ermitteln. Schließlich ist nicht garantiert, daß 
Interrupts genau an der Stelle passieren, wo das Hauptprogramm maximale 
Stacktiefe erreicht hat.

Die Lösung dafür: statische Stackanalyse, den Stack entsprechend 
statisch dimensionieren (vielleicht noch so 10, 20% drauflegen) und gut.

von nfet (Gast)


Lesenswert?

Ich höre immer mal wieder von statischer Stackanalyse, aber Tools dazu 
habe ich noch nicht so wirklich gefunden. Hat da wer Empfehlungen?

Zur Frage: ich fürchte fast du musst das 2 stufig machen. Also erst 
einmal linken lassen, dann die interessanten Größen raus ziehen und mit 
angepassten linker Script nochmal machen.

Vielleicht geht's auch anders, aber die Linkersyntax mit dem Punkt 
darfst du dann eher nicht benutzen, sondern musst halt irgendwie alles 
vorher berechnen.

Ich wäre interessiert an Skript, solltest du es nur mittels linker 
schaffen.

von Mate Rigo (Gast)


Lesenswert?


von Nop (Gast)


Lesenswert?

nfet schrieb:
> Ich höre immer mal wieder von statischer Stackanalyse, aber Tools
> dazu habe ich noch nicht so wirklich gefunden. Hat da wer Empfehlungen?

Der Keil-Compiler kann den Stackbedarfs des kompletten Calltree 
automatisch auswerfen, bei GCC geht es leider nur funktionsweise mit 
-fstack-usage. Vielleicht kennt jemand ein Tool, das den Quelltext 
analysiert und dann mit den .su-Dateien zusammen den Stackbedarf inkl. 
Calltree auswerfen kann?

Aufgepaßt: -flto ist Tabu, weil GCC dann nichtmal funktionsweise den 
Stackbedarf analysieren kann. Es werden auch haufenweise Sachen inline 
gemacht, auch Datei-übergreifend, und der Stackbedarf kann dabei 
regelrecht explodieren, ohne daß man das statisch analysieren kann.

Was beide nicht können, sind indirekte Aufrufe per Funktionszeiger, das 
muß man manuell untersuchen.

Deswegen ist es trotz Analyse eine gute Idee, den Stack an den Anfang 
des RAMs zu legen, wo es bei Overflow zumindest auf Cortex-M einen 
Hardfault geben wird. Man wird diese Analyse ja nicht für jeden 
Debug-Build erneut durchführen, aber ein Hardfault trotz 10-20% Reserve 
ist immerhin einfacher festzustellen und zu debuggen als undefiniertes 
Verhalten.

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


Lesenswert?

Nop schrieb:
> Ich halte grundsätzlich nichts von "Stack so groß machen wie möglich".

Das wissen wir schon. :-)

Trotzdem habe ich in 20 Jahren Mikrocontroller-Praxis zwar schon 
kurioseste Fehler debuggt, kann mich aber an nicht einen einzigen Fall 
erinnern, bei dem der Stack eine Variable zerschossen hätte, weil er aus 
dem Ruder gelaufen war.

: Bearbeitet durch Moderator
von PittyJ (Gast)


Lesenswert?

Ich verwende auf meinen M0s immer ein Free-Rtos. Ein/Zwei Tasks habe ich 
da immer.
Und Free-Rtos hat noch ein paar Einstellungen, einen Stackoverflow zu 
erkennen.

z.B.
https://www.freertos.org/Stacks-and-stack-overflow-checking.html

Vielleich könnte so etwas helfen.

von Stefan A. (king-crash)


Lesenswert?

Mate Rigo schrieb:
> Ein interessantes Artikel in dem Thema:
> 
https://embeddedgurus.com/state-space/2014/02/are-we-shooting-ourselves-in-the-foot-with-stack-overflow/

Das sind ziemlich die gleichen Überlegungen, die ich auch gemacht hatte. 
Die Lösung dort ist allerdings leider auch ein Stack mit statischer 
Größe. Ich sehe momentan nur eine zweistufige Übersetzung und gefrickel 
mit dem Makefile als dynamische Lösung.

@PittyJ
FreeRTOS prüft scheinbar auf zwei Arten:
1. Stackpointer laufend überprüfen
2. Dummyvariablen unter den Stack schreiben und diese laufend auf 
Veränderung überprüfen
Bei Verwendung mehrerer Stacks ist das sicher die einzige Möglichkeit 
aber leider nicht wirklich zuverlässig.

von PittyJ (Gast)


Lesenswert?

Stefan A. schrieb:
> Bei Verwendung mehrerer Stacks ist das sicher die einzige Möglichkeit
> aber leider nicht wirklich zuverlässig.

Nö, nicht zuverlässig, aber besser als nichts.

von bss-stack collider (Gast)


Lesenswert?

> [stack checking]

Hatten die Cortexe nicht eine Art 'MMU'?

Damit könnte man dann eine Fehlerbehandlungsroutine anspringen..

von Nop (Gast)


Lesenswert?

Jörg W. schrieb:

> Trotzdem habe ich in 20 Jahren Mikrocontroller-Praxis zwar schon
> kurioseste Fehler debuggt, kann mich aber an nicht einen einzigen Fall
> erinnern, bei dem der Stack eine Variable zerschossen hätte, weil er aus
> dem Ruder gelaufen war.

Dann hast Du nicht bei Toyota gearbeitet und auch nicht den Link von 
"Mate Rigo" gelesen.

von Stefan A. (king-crash)


Lesenswert?

@PittyJ
Das ist ohne Zweifel richtig. Wenn aber nur ein Stack vorhanden ist, ist 
den Stack an den Anfang setzen die 100% Lösung, die vom Prinzip her auch 
einfach ist.

Ich hatte tatsächlich auch schon eine Stackkollision. Das war aber ein 
modifiziertes Beispielprojekt, bei dem der Stack sehr klein angelegt war 
und in der Mitte des RAMs lag.

Bei ARMv8-M gibt es wohl neuerdings definierbare Stackgrenzen.

von Bauform B. (bauformb)


Lesenswert?

Stefan A. schrieb:
> FreeRTOS prüft scheinbar auf zwei Arten:
> 1. Stackpointer laufend überprüfen
> 2. Dummyvariablen unter den Stack schreiben und diese laufend auf
> Veränderung überprüfen
> Bei Verwendung mehrerer Stacks ist das sicher die einzige Möglichkeit
> aber leider nicht wirklich zuverlässig.

Außer, man gönnt sich einen Cortex M0+ mit MPU, die kann beliebig viele 
Stacks überwachen, natürlich auf (fast) beliebigen Adressen. Als Bonus 
gibt's auch NULL pointer exceptions.

Stefan A. schrieb:
> Ich sehe momentan nur eine zweistufige Übersetzung und gefrickel
> mit dem Makefile als dynamische Lösung.

Vielleicht gibt es einen Pin-kompatiblen Chip mit M0+, dann geht's ganz 
ohne Gefrickel auch mit dem alten RAM-Layout.

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.