Hi, ich bin auf den ARM "gekommen" und experimentiere nun schon einige Wochen damit rum. Ich nutze einen STR710 von STM. Als Entwiklcungsumgebung kommt Windows mit Yagarto und Codeblocks zum Einsatz. Jetzt wollte ich mich dran machen die C Funktionen wie printf zu benutzen. Dazu habe ich der libc.a die syscalls entzogen und diese in meinem Code deklariert. Alles lässt sich auch wunderbar kompilieren und linken. Er meldet keine nicht aufgelösten Funktionen, also alles wunderbar. Doch wenn ich nun printf aufrufe hängt sich der Debugger bzw. der IC auf. Dabei ist es unerheblich, wie kurz der Text ist, den printf ausgeben soll. Ich habe dann mal den arm-elf-insight debugger genommen, dieser kann auch durch den ASM-Code der libc steppen und habe herausgefunden das nach vielen Unterfunktionsaufrufen das printf in der funktion exit landet, die ja bekanntlich eine endlosschleife ist. Ich bin nicht der ASM freak, und frage mich nun warum diese Funktion aufgerufen wird? mfg starkeeper
Du könntest den printf() Aufruf bzw. deine Source angeben - dann könnte man nachsehen, ob da eine Fehlbedienung deinerseits zu einer Abbruchbedingun führt. Oder - wenn dein Code hochgeheim ist - könntest du die libc mit Debuginfos erstellen und ein Debugging auf C-Sourcelevel machen. Dabei sollte man sehen, wo und warum printf in exit landet.
Also mit sourcen kann ich gerade nicht dienen, da ich nicht an meinem Heim-Rechner sitze. Aber der Fehler taucht auf jeden Fall auch dann auf, wenn ich nur eine main-Funktion habe und dort dann printf("A"); drin steht. void main(void) { printf("A"); while(1); } Bis zu der while-Schleife gelangt der Controller nicht. Ich benutze ein Linker-Script von STR, aber soweit ich das gesehen habe, werden darin alle notwendigen bereiche angelegt, die man benötigt für stack usw.. Die Library mit Debug-Infos zu kompilieren habe ich auch schon geplant. Dazu muss ich mir erstmal ein Linux laden. Oder kannst du mir eine fertige config-Datei geben, mit der ich unter Windows das ganz kompilieren kann?
> Dazu habe ich der libc.a die syscalls entzogen und diese in > meinem Code deklariert. Klingt das nicht irgendwie suspekt?
Rufus t. Firefly wrote: >> Dazu habe ich der libc.a die syscalls entzogen und diese in >> meinem Code deklariert. > > Klingt das nicht irgendwie suspekt? Also das scheint gängige Praxis zu sein, das hab ich aus diesem Thread hier: http://forum.sparkfun.com/viewtopic.php?p=31660&sid=59221e0599e04e799d88abe680327037 Entweder man strippt die libc oder was wohl auch manchmal geht, dass man dem Linker nen Parameter übergibt, der sagt das die alten syscalls nicht gelinkt werden sollen.
Also ich habe das Problem nun einkreisen können. Es dreht sich um die Funktion sbrk. register char *stack_ptr asm ("sp"); caddr_t _sbrk_r(void *reent, size_t incr) { extern char end asm ("end"); // Defined by the linker static char *heap_end; char *prev_heap_end; if( heap_end == NULL ) heap_end = &end; prev_heap_end = heap_end; if(( heap_end + incr ) > stack_ptr ) { exit(1); return (caddr_t) -1; } heap_end += incr; return (caddr_t) prev_heap_end; } Das Problem ist das kein Stack bzw Heap mehr frei zu sein scheint. Was aber nicht sein kann, da der noch ungenutzt ist zu der zeit. Auch das vergrössern des Stacks und des Heap bringt keinen erfolg, sollte es aber ja. Ich vermute das Problem steckt in meinem linker script, das ich mal angehängt habe. Die Pointer von Heap_end und stack liegen nur 40byte auseinander, sodass kein platz mehr zu sein scheint! Vergrössere ich nun die stack grösse, verscheiben sich zwar die pointer aber die entfernung von beiden zueinander ändert sich nicht. Ich vermute das entweder SP oder END nicht korrekt sind.
> Heap_end und stack liegen nur 40byte auseinander
Kannst du genauer ausführen, welche Symbole du meinst. Die mit obiger
Schreibweise finde ich nicht.
Wenn du _heap_end__ und __stack_start_ meinst, ist das ja nicht
kritisch.
Mir scheint aber, dass deine angegebene sbrk Funktion nicht zum
angegebenen Linkercontrolscript passt. Woher stammen diese?
Das Skript baut einen HEAPSIZE grossen HEAP zwischen Datenbereich und
Stackbereich auf. Die Schlüsselsymbole sind _heap_end_ und
_heap_start_ als Endadresse und Anfangsadresse dieses Bereichs. Ich
hätte erwartet, dass eine passende sbrk Funktion diese Symbole
verwendet.
Die Funktion meint die Endadresse der letzten Varuiable zu kennen (end)
und den aktuellen Stackpointer (sp). Den Platz dazwischen sieht die
Funktion als potentiellen HEAP an. Aus dem Bauch raus, würde ich sagen:
Keine gute Idee.
Stefan B. wrote: >> Heap_end und stack liegen nur 40byte auseinander > > Kannst du genauer ausführen, welche Symbole du meinst. Die mit obiger > Schreibweise finde ich nicht. > > Wenn du _heap_end__ und __stack_start_ meinst, ist das ja nicht > kritisch. > > Mir scheint aber, dass deine angegebene sbrk Funktion nicht zum > angegebenen Linkercontrolscript passt. Woher stammen diese? > > Das Skript baut einen HEAPSIZE grossen HEAP zwischen Datenbereich und > Stackbereich auf. Die Schlüsselsymbole sind _heap_end_ und > _heap_start_ als Endadresse und Anfangsadresse dieses Bereichs. Ich > hätte erwartet, dass eine passende sbrk Funktion diese Symbole > verwendet. > > Die Funktion meint die Endadresse der letzten Varuiable zu kennen (end) > und den aktuellen Stackpointer (sp). Den Platz dazwischen sieht die > Funktion als potentiellen HEAP an. Aus dem Bauch raus, würde ich sagen: > Keine gute Idee. Das Linker script kommt von STM. Die sbrk Funktion kommt aus dem Internet.. Ich hab das auch gerade gesehen, das mein Linkerskript einen heap deklariert. Ich hab nun die symbole verwendet und sbrk umgeschrieben: register char *stack_ptr asm ("sp"); caddr_t _sbrk_r(void *reent, size_t incr) { extern char start asm ("__heap_start__"); // Defined by the linker extern char end asm ("__heap_end__"); // Defined by the linker static char *heap_data_end; char *prev_heap_data_end; char *heap_end = &end; if( heap_data_end == NULL ) heap_data_end = &start; prev_heap_data_end = heap_data_end; if(( heap_data_end + incr ) > heap_end ) { // Some of the libstdc++-v3 tests rely upon detecting // out of memory errors, so do not abort here. exit(1); return (caddr_t) -1; } heap_data_end += incr; return (caddr_t) prev_heap_data_end; } Jetzt funktioniert das, den Heap musste ich aber auf 2048Byte erhöhen, damit überhaupt nur eine Anfrage bearbeitet werden konnte. Das Problem ist jetzt das immer mehr vom Heap genutzt wird. Der eine Printf Aufruf: printf("A"); soll schon mehr als 20048Byte heap verbrauchen!! Ich kenne das so das Heap und stack maximal 2K brauchen. Was läuft denn da nun schief? Die erste Anfrage an den Heap klaut 0x408Bytes die nächst will dann schon 0xc8cBytes haben! Und da hat printf nichtmal ein Zeichen ausgegeben?!
Schwierig, schwierig. Ich sehe in der printf der NEWLIB eigentlich nur an zwei Stellen mallocs. Beide Stellen sind, glaube ich, nur aktiv, wenn ein Formatbezeichner (nach dem % im Formatstring) geparst wird. Bei deinem Beispiel printf("A") sollte überhaupt kein malloc aufgerufen werden. Mir ist das schleierhaft. Je nach Debugmöglichkeiten ggf. einen Haltepunkt auf malloc oder sbrk legen und per Stacktrace anschauen, wer der Aufrufer ist...
Wie wird denn bei dir das printf aufgerufen? Bei mir ist das etwas komisch finde ich. Der Compiler ersetzt printf mit putchar und das wird dann ausgeführt? Es scheint kein echtes printf zu geben..
printf benötigt eine Ausgaberoutine putchar. In putchar wird festgelegt wohin die Ausgabe gehen soll. Auf UART0, UART1, LCD oder sonst wo hin. Dafür benötigt putchar die Adresse einer Ausgaberoutine ! Bei WinARM: rprintf_devopen( uart_sendchar ); /* init rprintf */ uart_sendchar wird dann von putchar benutzt um Zeichen auszugeben. Wenn für putchar keine Ausgaberoutine festgelegt wird könnte das einen Sprung auf NULL, also ins leere bedeuten. Hangman.
holger wrote: > printf benötigt eine Ausgaberoutine putchar. > In putchar wird festgelegt wohin die Ausgabe > gehen soll. Auf UART0, UART1, LCD oder sonst wo hin. > Dafür benötigt putchar die Adresse einer Ausgaberoutine ! > > Bei WinARM: > > rprintf_devopen( uart_sendchar ); /* init rprintf */ > > uart_sendchar wird dann von putchar benutzt um Zeichen > auszugeben. Wenn für putchar keine Ausgaberoutine > festgelegt wird könnte das einen Sprung auf NULL, > also ins leere bedeuten. Hangman. Welche version der newlib wird bei WinARM genutzt? Das sollte doch eigentlich immer gleich funktionieren. Nach meinem wissen von anderen Librarys, wird doch normal printf aufgerufen was dann den string parst und die ersetzungen macht und dann mittels putchar die einzelnen buchstaben ausgegeben, wohin man sie haben will. Das muss man natürlich noch angeben. Das Rätsel um die ersetzung von printf habe ich fast gelöst, also immer wenn ich nur einen Buschtaben ausgebe wird printf ersetzt durch putch. Bei mehreren Zeichen wird printf genommen. Das printf funktioniert nun auch korrekt, nachdem ja nun der heap korrekt angegeben ist in sbrk. Der Speicherverbrauch vom heap ist immer noch enorm. Beim aufruf von printf("ABC") wird einmal 0x418 speicher angefordert und ein zweites mal 0x48.
>Welche version der newlib wird bei WinARM genutzt?
Für rprintf gar keine. Das ist eine simple printf
Routine die man als C Datei einbindet. Kann kein
float, aber das meiste was man sonst so braucht.
Sollte nur ein Tip sein :(
Das printf durch puts ersetzt wird wenn kein
Formatstring vorliegt habe ich ja auch schon gesehen,
aber direkt auf putchar ? Nö, das kann nicht klappen.
Das genannte rprintf hat nichts mit newlib-stdio zu tun. Es wird nur in einigen Beispielen von mir genutzt (die ich auch WinARM beilege). Es handelt sich dabei um eine "selbstgebastelte" Funktion, die ich in AVR-Code (wenn richtig erinnert von Holger Klabunde und Volker Oth) gefunden und an ARM angepasst habe (eigentlich ist nichts ARM-spezifisches an der Routine). Inspiriert durch die in der avr-libc verwendete Methodik habe ich später dann noch rprintf_devopen ergänzt. Für viele Anwendungen reicht ein solche Funktion aus und dynamische Speicherverwaltung wird im Gegensatz zu newlibs printf/iprintf nicht benötigt. Dass printf Anweisungen für kurze Zeichenketten zu putchar werden, kann eine Optimierung des GNU Compilers sein. Neuere Versionen von arm-*-gcc scheinen sich printf "genauer anzuschauen" und möglicherweise dabei diese Optimierungsmöglichkeit erkennen. Habe das in eigenen Projekten allerdings noch nicht beobachtet (aber auch nicht danach gesucht). "soll schon mehr als 20048Byte heap verbrauchen!!" Das bezweifle ich. Vielleicht ist hier .text gemeint. Das kann schon gut sein, denn die newlib stdio-Funktionen brauchen recht viel Programmspeicher. Etwas lindern kann man das, wenn man statt printf iprintf verwendet, welches aber keine Floating-Point-Ausgabe unterstützt (i für integer). Der Bedarf an Speicher auf dem Heap kann auch daher rühren, dass die newlib stdio-Funktionen Buffering unterstützen. Testweise könnte man dies deaktivieren (mit ioctrl(?)). Evtl. wird dann ein malloc "ausgelassen". Martin Thomas
Jo also das mit dem Heapverbrauch ist mir immernoch schleierhaft.. Bei verwendung von iprintf wird es aber deutlich weniger. Da ich vorerst nur printf nutzen wollte, werde ich mir wohl mal dein rprintf ansehen. Die Ersetzung von printf durch putch geschieht nur bei einem Zeichen, sobald im printf zwei zeichen ausgegeben werden sollen wird printf verwendet. Ich dachte aber wenn die Optimierung abgeschaltet ist werden solche Ersetzungen nicht gemacht.
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.