Hallo !
Ich habe begriffen, was ein Stack-Overflow ist, aber was passiert dann
mit dem Stack-Pointer? Läuft er wieder von oben ins SRAM? Oder bewegt er
sich durch den I/O-Bereich, das heißt, schreibt er dann bei "call" oder
"push" Daten in diese Register? Oder läuft er durch die "normalen" 32
Register?
Im Datenblatt finde ich nichts dazu. :-(
Wenn die CPU einen Hardware-Stack hat, dürfte ein Reset kommen, wenn der
Stackpointer aus den zulässigen Adressen rauskommt. Genaues sagt das
DaBLa des betreffenden Kerns.
Beim Software-Stack (Stack im RAM) bekommt die CPU von allein nicht mit,
dass der Stack überläuft. Die Adesse wird also beim push einfach
weitergesetzt, und die CPU versucht die Adressen zu beschreiben. Beim
pop analog wird normal ausgelesen und der Stackpointer zurückgezählt.
Solange der Pointer nicht eine Adresse erreicht, die beim Schreiben
einen NMI auslöst, passiert (ausser sehr seltsamen Programmfehlern) nix
weiter...
Besonders eklig wirds, wenn der Stack-Pointer Bereiche erreicht, in
denen die Variablen stehen. Die werden dann nämlich überschrieben...
PS: Welche Adressen/Register beim Überlauf überschrieben werden, siehst
du an der Memory-map im DaBla deines Prozessors, und dem Wissen, wo du
den Stack im RAM anlegst...
Danke euch für die vielen nützlichen Hinweise!
Verwendet habe ich einen ATmega328 mit Assembler.
Abgestürzt ist bis jetzt nichts. Das hat mich verwirrt....
Deswegen war ich mir nicht sicher, ob bei einem Push wirklich in
Register geschrieben wird.
Wasserlaeufer schrieb:> Abgestürzt ist bis jetzt nichts. Das hat mich verwirrt....> Deswegen war ich mir nicht sicher, ob bei einem Push wirklich in> Register geschrieben wird.
Nach weiteren Experimenten:
Weder push noch call oder rcall schreiben in Speicherbereiche, die
außerhalb des SRAM liegen. Ich habe bis jetzt nirgends eine
Dokumentation für dieses Verhalten finden können.
In meinem Fall ist es praktisch, dass beim Stacküberlauf nichts
passieren kann. :-) Andere wird es vielleicht enttäuschen, weil dadurch
keine Register hilfsweise als Stack verwendet werden können.
Weitere Infos oder Doku jederzeit willkommen!
Ich würde Deine Ergebnisse, wohl aber nicht Dein Bemühen, eher mit einer
gewissen Skepsis sehen.
Zumindest bedürfen sie meiner Ansicht nach der weiteren Erläuterung.
Soweit ich weiss sind z.B. die Register, sowohl die Arbeitsregister als
auch die Register der Peripherieeinheiten durchaus auch auf
Speicherbereichen erreichbar; wenn auch je nach Atmel-Typ in
verschiedenem Umfang und an anderen Adressen.
Deine Äusserung:
>Nach weiteren Experimenten:>Weder push noch call oder rcall schreiben in Speicherbereiche, die>außerhalb des SRAM liegen. Ich habe bis jetzt nirgends eine>Dokumentation für dieses Verhalten finden können.
widerspricht dem nicht. Andererseits wäre es wissenswert ob Register
verändert werden.
Auch ist mir an dieser Erklärung nicht klar, ob Du "Experimente"
tatsächlich an der realen CPU durchgeführt hast und auf welche Weise.
Das in der Dokumentation pathologische Fälle eher selten wenn überhaupt
geschildert werden überrascht mich nicht. Auch bei anderen Herstellern
geschieht dies eher dann wenn tatsächlich eine Beschädigung auftreten
könnte. Ein Fehlen der Beschreibung dieses Falles überzeugt mich nicht.
Warum ich so lange rede. Ich bin überzeugt davon das die Zugriffe via
Stackpointer bei den AVRs mit Stack im RAM nicht überprüft werden und
das jeder Zugriff tatsächlich ausgeführt wird egal ob da nun RAM oder
ein memory-mapped Register dahinter liegt oder eben das "Nichts".
Hallo Hmm!
Hmm schrieb:> Soweit ich weiss sind z.B. die Register, sowohl die Arbeitsregister als> auch die Register der Peripherieeinheiten durchaus auch auf> Speicherbereichen erreichbar; wenn auch je nach Atmel-Typ in> verschiedenem Umfang und an anderen Adressen.
Richtig! Deswegen dachte ich ursprünglich mal, ein überlaufender Stack
würde für Chaos sorgen.
> Deine Äusserung: (...)> widerspricht dem nicht. Andererseits wäre es wissenswert ob Register> verändert werden.
Sie wurden nicht verändert.
> Auch ist mir an dieser Erklärung nicht klar, ob Du "Experimente"> tatsächlich an der realen CPU durchgeführt hast und auf welche Weise.
An einem echten ATmega328. Dessen SRAM beginnt bei 0x0100. Der
Stackpointer läuft locker nach unten raus, also 0x00ff, 0x00fe usw.,
aber es wird in Adressen unter 0x100 per push nichts geschrieben. Ich
habe das mit IO-Registern probiert und auch mit GP-Registern (r0 bis
r31).
> Warum ich so lange rede. Ich bin überzeugt davon das die Zugriffe via> Stackpointer bei den AVRs mit Stack im RAM nicht überprüft werden und> das jeder Zugriff tatsächlich ausgeführt wird egal ob da nun RAM oder> ein memory-mapped Register dahinter liegt oder eben das "Nichts".
Ja, diese Vermutung ist naheliegend, aber anscheinend ist es in der
Praxis anders. So wie es aussieht, kann zwar z.B. sts in den gesamten
Adressbereich schreiben, nicht aber push, call, rcall, also anscheinend
alles was mit indirekter Adressierung über SP zu tun hat.
Tut mir leid wenn ich nachbohre. Du glaubst nicht was man hier so an
Textverständnis erlebt:
>> Auch ist mir an dieser Erklärung nicht klar, ob Du "Experimente">> tatsächlich an der realen CPU durchgeführt hast und auf welche Weise.>An einem echten ATmega328.
Was heisst "echt"? Ich habe gefragt "real"! Im Simulator? Oder physisch
vorhanden?
Hmm schrieb:> Tut mir leid wenn ich nachbohre. Du glaubst nicht was man hier so an> Textverständnis erlebt:
Voll ok. :-)
Ich lese hier auch oft mit und erlebe immer wieder, dass Leute
aneinander vorbeireden.
>>An einem echten ATmega328.>> Was heisst "echt"? Ich habe gefragt "real"! Im Simulator? Oder physisch> vorhanden?
Mit "echt" meinte ich "physisch". Versuchsaufbau: ATmega328-PU auf
Breadboard, Angeschlossen über USBasp, der fürs Programmieren da ist und
danach auch für die Stromversorgung sorgt. Als weitere Bauteile hatte
ich nur eine Leuchtdiode und einen Vorwiderstand.
Jetzt kommt wahrscheinlich die berechtigte Frage, woher ich dann weiß,
was nach den jeweiligen Versuchen in den Registern steht: ich habs
einfach über die LED rausgemorst. Das mache ich öfter so, bin nämlich zu
faul, viele Bauteile aufzustecken. :-)
Hmm schrieb:> Interessant, wenn mir auch gerade keine nützliche Anwendung dafür> einfällt. Hast Du das mal bei AVRFreaks gepostet?
Nein, bei denen war ich noch nie.
Wozu man das braucht? Auch eine gute Frage... Wenn man Interrupts nutzt,
aber keinen Rücksprung braucht oder kein SRAM nutzen will, weil man es
für andere Zwecke verwendet. Alles in allem irgendwelche seltenen
Spezialfälle.
Werde ich morgen mal ausprobieren.
Definitiv war es früher so, dass der SP bei gewissenloser Nutzung alles
platt gemacht hat :-)
Einen Unterschied sieht man aber schon im Datenblatt: die neueren Typen
(wie 48/88/168/328) haben SP-init-Werte nach reset ramend. Die früheren
Typen (z.B. Mega32) haben 0 im SP stehen. Offensichtlich hat Atmel die
SP-Logik zumindest angefasst. Vielleicht auch einen (minimalen)
Speicherschutz??
Aber einen wirklichen Nutzen hat es in der Tat nicht, zumindest sehe ich
keinen.
H.joachim Seifert schrieb:> Werde ich morgen mal ausprobieren.
Bin gespannt!
> Definitiv war es früher so, dass der SP bei gewissenloser Nutzung alles> platt gemacht hat :-)
Meinst du mit "alles" das komplette SRAM oder auch alle anderen
Register? Das SRAM wird immer noch überschrieben, daran hat sich nichts
geändert.
Wenn du also ein Programm hast, das das SRAM verwendet, um dort
Variablen abzulegen (wie z.B. bei C üblich), musst du aufpassen.
> Typen (z.B. Mega32) haben 0 im SP stehen. Offensichtlich hat Atmel die> SP-Logik zumindest angefasst. Vielleicht auch einen (minimalen)> Speicherschutz??
Ich hab den Verdacht, die früher fehlende automatische Initialisierung
hat damit nichts zu tun.
>Ich hab den Verdacht, die früher fehlende automatische Initialisierung
hat damit nichts zu tun.
Ich denke das hat er damit auch nicht sagen wollen.
Aber diese Aussage
>Meinst du mit "alles" das komplette SRAM oder auch alle anderen>Register? Das SRAM wird immer noch überschrieben, daran hat sich nichts>geändert.>Wenn du also ein Programm hast, das das SRAM verwendet, um dort>Variablen abzulegen (wie z.B. bei C üblich), musst du aufpassen.
weckt leider doch wieder meine Skepsis. Denn die Register sind (wie oben
geschrieben in je nach Typ unterschiedlicher Weise und Umfang) auch in
den Speicher abgebildet. Du kannst z.B. R1 bis R31 durch schreiben ins
RAM ändern.
Zumindest solte noch ein Zweiter mal Deine Tests nachvollziehen.
Magst Du mal Deinen Testcode posten?
test:
#asm
ldi r16, 0
out SPH, r16
ldi r16, 0xff
out SPL, r16
ldi r16, 0xaa
loop:
push r16
rjmp loop
#endasm
Mal kurz einen Mega16 auf das STK500 gesteckt und mit JTAG MKII und
AVR-Studio laufen lassen (ich hoffe, das JTAG wirklich ein exaktes
Prozessorbild liefert??) - der SP überschreibt weder I/O noch Register.
Zählt aber brav bis 0 runter. Danach springt er übrigens auf 0x0fff...
Jetzt bin ich etwas ratlos.
>(ich hoffe, das JTAG wirklich ein exaktes Prozessorbild liefert??)
Ich gehe davon aus, aber...
>Jetzt bin ich etwas ratlos.
...ich erwäge gerade meinen Glauben an den Weihnachtsmann wieder zu
beleben. :-)
Witzig! Haben die Ings. bei Atmel das tatsächlich abgefangen.
Es wäre auch mal interessant, was herauskommt, wenn man bei einem SP,
der in die Register zeigt, einen Wert poppt. Der Wert des Registers, 0
oder gar ff?
H.joachim Seifert schrieb:> Zählt aber brav bis 0 runter. Danach springt er übrigens auf 0x0fff...
Ah, interessant! Aber irgendwie logisch, denn der SP besitzt
wahrscheinlich einen eigenen Hardware-Inkrementierer/Dekrementierer, da
hat man sich ein paar Transistoren gespart, weil die High-Bits sowieso
nicht verwendet werden. Irgendwo im Datenblatt steht sogar, dass die
unbenutzten Bits des SP "don't care" sind, man sich deswegen auf deren
Inhalt nicht verlassen darf und gut daran tut, sie auszumaskieren.
Zumindest dieses Mysterium ist gelöst. ;-)
Ob der AVR-Simulator das alles auch berücksichtigt? Ich hab leider
keinen.
H.joachim Seifert schrieb:> pop mit SP <0x60 liefert 0x00, zumindest laut JTAG.
Wenn das auch für RET/RETI gilt, landet das Programm sowieso irgendwann
im Reset Vector.