@Testato: da quel che dice @gammon SP è un variabile che ritorna indirizzo dello stack (messo a disposizione non sò da chi, forse il compilatore). Assegna questo valore a variabile stack. Poi con stack[1] va a scrivere oltre al puntatore di una cella. e ci scrive indirizzo della funzione bar. In teoria all'uscita della funzione loop() non dovrebbe tornare al chiamante (la main) ma alla bar() stampando il msg e fermandosi (non siamo più nella main)
Invece di usare array uoi anche scrivere:
*(stack+1) = (unsigned int) bar;
A me funziona su Arduino Uno R3 con IDE 1.0.5.
NON funziona su Mega
Come dice alla prima pagina, è C++. Poi alla reference si "parla" di linguaggio Arduino per semplicità , secondo me.
DarkCoffee:
Mentre qui c'è una serie di funzioni, strutture e variabili: Arduino - Home
Inoltre quando ho voluto usere printf e scanf ho dovuto configurare l'uart e aprire stdout e stdin.
Ma tu non usi l'IDE Arduino ma Atmel Studio. Arduino IDE magicamente mette insieme le librerie e il core senza doverle includere nel progetto. Se usi il IDE Arduino e metti il verbose vedi tutti le cose automatiche che fa di sotto Arduino. Mi sembra che alcune librerie le capisce da quali include tu metti. Credo che l'IDE analizzi anche il sorgente per capire quali librerie includere.
In realtà ho iniziato ad usare Atmel Studio perchè volevo fare il debugging per capire l'indirizzi di memoria. Ho dovuto settarlo aggiuggendoci tutte le librerie di Arduino per linkarle in compilazione. In più ho aggiunto anche il core di arduino.
Ma alla fine, se riesco a capire come sovrascrivere il return address senza Atmel Studio, per me va bene lo stesso.
A me l'esempio funziona. Se togli il while() dalla funzione bar() non capisco perchè ma su Monitor Seriale vedi solo una y con puntini sopra
(non funziona su Mega!!!)
Ho provato a anche a pacioccare un pò ma funziona ancora:
void setup ()
{ delay(2000);
Serial.begin(9600);
} // end of setup
void bar ()
{ Serial.println ("hacked!");
while (true);
}
void mybar()
{ bar();
while (true);
}
void foo ()
{ Serial.println ("in foo.");
// corrupt return address
unsigned int * stack = (unsigned int *) SP;
*(stack+1) = (unsigned int) mybar;
}
void loop ()
{ foo ();
} // end of loop
Ci sono alcune costanti predefinite in alcuni file della toolchain che il compilatore, quando le trova, le sostituisce con determinati indirizzi. Ad esempio SP è lo Stack Pointer. Ci sono valori generici per tutti i chip ed altri specifici per singolo micro. Aprite la cartella della toolchain in /avr/avr/include/avr.
Common.h ad esempio contiene gli indirizzi dello stack SP, poi c'è iom328p.h che contiene le cose specifiche dell'Atmega328P e iom2560.h che contiene quelle dell'ATmega2560.
ma probabilmente e' solo diverso il comando SP, forse e' presente nei file che hai suggerito
x nid, grazie della spiegazione, quindi con l'asterisco stiamo dicendo che vogliamo lavorare con i puntatori, e gli indirizzi delle celle di memoria li si assegnano come normali variabili. mai usati e mai li usero' credo, a prima vista pensavo fosse una moltiplicazione
a conferma che si sta pasticciando, almeno sulla mega, quando apro la seriale si blocca il micro, devo resettare per vederla partire a scrivere (ho la rxtx modificata, quella che non resetta all'apertura del serial monitor, chi usa la normale non si accorge di nulla)
Forse ciò dipende dal fatto che per indirizzare 128 o più kB di Flash si deve usare un terzo byte, se non ricordo male.
Forse manipolare lo stack cambiando solo il valore del PC (Program Counter) lì memorizzato non è sufficiente. Forse (continuo con le ipotesi) il compilatore salva anche questo terzo byte, che andrebbe manipolato. O forse si va a cambiare lui e non il PC. Chissà.
Andrebbe disassemblato un programma per capire cosa viene salvato durante il alto ad una subroutine.
nid69ita:
Poi con stack[1] va a scrivere oltre al puntatore di una cella. e ci scrive indirizzo della funzione bar.
spetta pero' dimmi se ho capito, dopo l'istruzione
unsigned int * stack = (unsigned int *) SP;
il micro va sempre a leggere la cella di memoria successiva ? e' questo il discorso del Return di cui si parla ? ad ogni istruzione
c'e' un return (un enter diciamo) che non si vede da codice ?
perche' il succo della questione e' proprio "Chi" e "Quando" va a leggere ed eseguire l'istruzione che c'e' in stack[1] ?
mettendo il 2 stampa sempre solo in foo. pero' con una differenza, con 1 stampa una volta al secondo, come se ci fosse un delay, mentre con 2 stampa velocissimo
Questo perché quando si va a "spippolare" nella SRAM non si sa mai cosa succede
Ti riporto quello che avevo scritto in un mio post ad inizio di questa discussione:
leo72:
A me è capitato l'altro giorno. Lavoravo ad un progetto molto complesso e leggevo/scrivere diverse decine di dati dalla EEPROM . Mi sono accorto che all'avvio il progetto funzionanva come doveva, mentre al successivo reset perdevo l'uso di una fila di pulsantini di una tastierina 4x4 collegata Alla fine ho capito che al primo avvio scrivevo le info sulla EEPROM e contemporaneamente caricavo i dati in un array, mentre al reset seguente andavo a caricare i dati in un array al di fuori dei suoi limiti, ed il chip si comportava nel modo che ti ho descritto
Come vedi, caricavo dei dati in un array andando fuori limite e mi si disattivava un pin dell'Arduino
leo72:
Una domanda, dato che non ho la Mega.
Cos'è che fa, esattamente, lo sketch di Gammon su questa scheda? Che output da?
Sorry. Sulla mega vedo di continuo:
in foý
Cioè non riesce a completare il messaggio. Penso una questione di tempistica.
Anche lo sketch su Uno mi presenta quella y particolare se tolgo dalla bar() il while infinito.
Inoltre, stranamente, aggiungendo solo un println() in foo() anche sulla Uno non esegue la bar().