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
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
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.
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.
@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".
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.
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.
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.
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.
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.
Ein interessantes Artikel in dem Thema: https://embeddedgurus.com/state-space/2014/02/are-we-shooting-ourselves-in-the-foot-with-stack-overflow/
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.
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
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.
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.
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.
> [stack checking]
Hatten die Cortexe nicht eine Art 'MMU'?
Damit könnte man dann eine Fehlerbehandlungsroutine anspringen..
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.
@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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.