Hallo zusammen,
beim ARM-GCC gibt es ja keine Streams für stdout und stderr, sondern man
hängt seine eigene "putchar"-Funktion in stdio.c, die dann von printf &
Co benutzt wird.
Die Frage ist: Wie bindet man einen Ersatz für stderr ein? Wie kann ich
eine Ausgabe für assert() und error() festlegen?
Viele Grüße
W.T.
Aus
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gab599ddf60819df4cc993c724a83cb1a4
(Doku zu fdevopen()):
...
The first stream opened with read intent is assigned to stdin, and the
first one opened with write intent is assigned to both, stdout and
stderr.
...
------------------------
Zu assert siehe
http://www.nongnu.org/avr-libc/user-manual/group__avr__assert.html :
As there is no standard error output stream available for many
applications using this library, the generation of a printable error
message is not enabled by default. These messages will only be generated
if the application defines the macro
__ASSERT_USE_STDERR
before including the <assert.h> header file. By default, only abort()
will be called to halt the application.
Oliver schrieb:> Welche standardlib die von dir verwendete toolchain verwendet, weiß ja> hier keiner. ARM-gcc ist da als Angabe nicht ausreichend.
Ich ahnte nicht, daß es innerhalb des ARM-GCCs so große Unterschiede
bezüglich stderr-ähnlichem Vorgehen gibt. Aus der stdio.c konnte ich das
nicht herauslesen.
Ich verwendet den ARM-GCC 4.7 2013q3 (arm-none-eabi) mit den
Headerdateien von ST für stm32f10x.
Du schreibst immer von der 'stdio.c'
Hast du die in Source Form vorliegen und ist die Bestandteil der glibc?
Hast du auch den restlichen Code der glibc vorliegen`?
Denn wenn ja: das gibts doch gar nicht, dass man mit den vorliegenden
Sourcen nicht rausfindet, wie das gedacht ist. So ein Hexenwerk ist das
I/O Subsystem ja auch wieder nicht.
Walter Tarpan schrieb:> Ich ahnte nicht, daß es innerhalb des ARM-GCCs so große Unterschiede> bezüglich stderr-ähnlichem Vorgehen gibt.
Gibt es auch nicht. ARM-gcc ist der Compiler, der hat mit stderr nichts
am Hut. Egal, mit welcher toolchain.
stdio.c ist dagegen ein Bestandteil der lib, und welche das ist, sollte
irgendwo dabei stehen.
Oliver
Karl Heinz schrieb:> Du schreibst immer von der 'stdio.c'>> Hast du die in Source Form vorliegen und ist die Bestandteil der glibc?> Hast du auch den restlichen Code der glibc vorliegen`?> Denn wenn ja: das gibts doch gar nicht, dass man mit den vorliegenden> Sourcen nicht rausfindet, wie das gedacht ist. So ein Hexenwerk ist das> I/O Subsystem ja auch wieder nicht.
Hallo Karl-Heinz,
ja, die stdio.c habe ich in Source-Form vorliegen (siehe Anhang) - sie
wurde von der IDE ins Projektverzeichnis kopiert. Die stdio.h nicht-
deshalb nehme ich mal an, daß sie unverändert ist.
Den restlichen Quelltext der glibc habe ich noch nicht entdeckt - was
nicht heißen muß, daß er nicht bei der Installation der Toolchain dabei
war. Ich werde mal heute Abend Ausschau danach halten. Leider konnte ich
bislang keine assert.c oder error.c finden, wo sich das Verhalten von
"stderr" ablesen läßt.
Es mag kein Hexenwerk sein. Nur momentan habe ich noch keine Übersicht,
nach was ich Ausschau halten sollte. Im Gegensatz zum AVR-GCC, der sich
ja an den Streams von Standard-C orientiert, scheint beim ARM-GCC ein
eigenes Süppchen gekocht zu werden. Und mit der wirklich wunderbaren
Doku beim AVR-GCC kann sich das leider nicht messen. (Und selbst da
mußte ich damals ein, zwei Fragen im Forum stellen.... ob ich die Doku
jetzt besser als damals lesen kann, weil ich weiß, was gemeint ist, oder
ob Jörg die inzwischen noch weiter verbessert hat, weiß ich natürlich
nicht.)
Viele Grüße
W.T.
Walter Tarpan schrieb:> ja, die stdio.c habe ich in Source-Form vorliegen (siehe Anhang) - sie> wurde von der IDE ins Projektverzeichnis kopiert.
Hng. Ein Widerspruch direkt im ersten Satz. Meine natürlich: "stdio.c
habe ich nicht, nur den Ausschnitt, der von der IDE ins
Projektverzeichnis kopiert wird."
Walter Tarpan schrieb:> Ich ahnte nicht, daß es innerhalb des ARM-GCCs so große Unterschiede> bezüglich stderr-ähnlichem Vorgehen gibt.
Die Standardblibliothek gehört nicht zum GCC. Deswegen nützt es hier
auch nichts, immer nur die GCC-Version aufzuführen. Du solltest Du
lieber die Standardblibliothek nennen. Dies kann z.B. die glibc, uClibc,
newlibc sein.
Walter Tarpan schrieb:> Es mag kein Hexenwerk sein. Nur momentan habe ich noch keine Übersicht,> nach was ich Ausschau halten sollte.
Ich würde mal hier ansetzen
1
signedintfputc(signedintc,FILE*pStream)
2
{
3
if((pStream==stdout)||(pStream==stderr)){
4
5
PrintChar(c);
6
7
returnc;
8
}
9
else{
10
11
returnEOF;
12
}
13
}
Laut Kommentaren ist fputc der 'untere' Anlaufpunkt für printf und
Konsorten.
Da steht recht deutlich drinnen, dass die Streams 'stdout' und 'stderr'
über PrintChar abgehandelt werden.
Hast du denn schon probiert, was passiert, wenn du ein ...
1
#include .... was immer du brauchst ....
2
3
intmain()
4
{
5
fprintf(stdout,"hallo ");
6
fprintf(stderr,"welt");
7
8
fprintf(stdout,"\n");
9
fprintf(stderr,"\n");
10
}
... machst?
Kriegst du Ausgaben?
Notfalls musst du eben entweder in fputc oder in PrintChar eingreifen,
um die Ausgaben auf ein Gerät deiner Wahl umzuleiten, damit du auch was
siehst.
Walter Tarpan schrieb:> Walter Tarpan schrieb:>> ja, die stdio.c habe ich in Source-Form vorliegen (siehe Anhang) - sie>> wurde von der IDE ins Projektverzeichnis kopiert.>> Hng. Ein Widerspruch direkt im ersten Satz. Meine natürlich: "stdio.c> habe ich nicht, nur den Ausschnitt, der von der IDE ins> Projektverzeichnis kopiert wird."
Genau.
Drum heißt die Datei, die du gepostet hast, ja auch printf.c
Oh Mann. Wenn ich für jedes mal raten hier im µC-Forum in den letzten 10
Jahren 1 Euro bekommen hätte, wäre ich heute ein reicher Mann.
Karl Heinz schrieb:> Ich würde mal hier ansetzen> signed int fputc(signed int c, FILE *pStream)> {> if ((pStream == stdout) || (pStream == stderr)) {>> PrintChar(c);>> return c;> }> else {>> return EOF;> }> }>> Laut Kommentaren ist fputc der 'untere' Anlaufpunkt für printf und> Konsorten.> Da steht recht deutlich drinnen, dass die Streams 'stdout' und 'stderr'> über PrintChar abgehandelt werden.
Hallo Karl-Heinz,
Du hast Recht. Das sieht extrem vielversprechend aus. Werde ich heute
abend direkt ausprobieren.
Karl Heinz schrieb:> Oh Mann. Wenn ich für jedes mal raten hier im µC-Forum in den letzten 10> Jahren 1 Euro bekommen hätte, wäre ich heute ein reicher Mann.
Dito. Und wenn ich für jedes Mal richtig raten einen Euro bekommen
hätte, würde es zumindest für eine Pizza reichen :-)
Danke, daß Du Dir die Zeit genommen hast, das durchzusehen! Ich war
schon so darauf versteift, daß es hier keine Streams und damit "stderr"
gibt, daß ich nie danach gesucht hätte.
Viele Grüße
W.T.
In printf.c findet sich auch die angemängelten Symbole mit einer
lapidaren Begründung:
1
/** Required for proper compilation. */
2
struct_reentr={0,(FILE*)0,(FILE*)1,(FILE*)0};
3
struct_reent*_impure_ptr=&r;
Werden sie auskommentiert, funktioniert der Build. Bei
1
printf("hallo\a");
2
fprintf(stdout,"hallo2\a");
3
printf("hallo3\a");
ist auf dem LCD allerdings nur "hallohallo3" zu sehen (\a ist der alert,
der dafür sorgt, daß der Inhalt neu gezeichnet wird).
In printf.c läuft allerdings ab "vfprintf" bei "printf" und "fprintf"
alles gleich ab. Sehr merkwürdig.
Was mich etwas mißtrauisch macht, ist daß beim Verwenden von "fprintf"
plötzlich etwas anderes gelinkt wird. Die Link-Optionen habe ich
allerdings mich noch nie anzufassen getraut...
[EDIT:]
Sieh an, es gibt schon eine Application Note zu dem Thema. Jetzt müßte
man nur noch chinesisch können:
http://www.coocox.org/forum/topic.php?id=3740
Aber ich denke es ist einfacher, die printf.c zu reparieren.
[EDIT2:]
Es wird immer mysteriöser. Lasse ich die Stream-Auswahl heraus, also
ändert sich nichts am Verhalten. Es wird aber definitiv diese Funktion
aufgerufen, denn wird PrintChar doppelt aufgerufen, ist auch alles
doppelt auf dem Display.
Also irgendetwas muß in der Kette
fprintf -> vfprintf -> fputs -> fputc -> PrintChar kaputt sein, was in
printf -> vprintf -> vfprintf -> fputs -> fputc -> PrintChar
funktioniert.
[EDIT 3]: WTF??? Der Debugger sagt mir, daß in fprintf gar nicht
eingesprungen wird.
[EDIT 4]: Eine Endlosschleife in fprintf bestätigt diese Vermutung.
Tja. Die Stunde ist abgelaufen. Ich kann nicht mehr editieren. Und
bleibe verwirrt. Was mag den Linker dazu bewegen, einen Funktionsaufruf
zu entfernen? Oder existiert irgendwo eine Funktion mit demselben Namen
und stärkerer Bindung?
mar IO schrieb:> Das könnte dich interessieren>> http://embdev.net/topic/133927#1218465
Das ist interessant... aber ich nehme an, daß die Stubs schon in
syscalls.c richtig implementiert sind. Ich bekomme ja schon Output.
Ich habe printf dahingehend modifiziert, daß es fprintf benutzt (siehe
Anhang). In main mache ich:
1
printf("hallo1 \a");
2
fprintf(stdout,"hallo2 \a");
3
printf("hallo3 \a");
4
5
while(1);
und die Ausgabe ist "hallo1 hallo3".
Es sieht aus, als sähe main ein anderes fprintf als printf.c. Wie geht
soetwas?
So langsam befürchte ich, daß das hier meine Fähigkeiten übersteigt.
Ich muß heute abend mal ein lauffähiges Minimalbeispiel erzeugen.
Walter Tarpan schrieb:> Was mag den Linker dazu bewegen, einen Funktionsaufruf> zu entfernen? Oder existiert irgendwo eine Funktion mit demselben Namen> und stärkerer Bindung?
Zumindest avr-gcc kann bei printf sehr aggresiv optimieren.
Ein
1
printf("Hallo");
führt bspw. dazu, dass kein dickes printf dazugelinkt wird sondern ein
puts(), weil dieses auch einen konstanten String ausgeben kann.
Wird wirklich ein printf benötigt stehen dem Compiler soweit ich weiß
mehrere Versionen zur Verfügung, abhängig vom nötigen Funktionsumfang.
Möglicherweise solltest du die Optimierung abschalten, oder dem Compiler
die Möglichkeit zur Optimierung nehmen, z.B. indem du einen printf
Aufruf in eine globale Funktion kapselst, die von extern aufgerufen
werden kann.
Haro schrieb:> Zumindest avr-gcc kann bei printf sehr aggresiv optimieren.
Hallo Haro,
ich weiß nicht, ob das gerade verwechselt wurde, aber ich schreibe es
lieber noch einmal groß auf:
Kein AVR! ARM *ARM* ARM *ARM* ARM *ARM* ARM
;-)
Das war es mir schon allein wegen der interessanten Formatierung wert.
In printf kann mit dem Debugger gesprungen werden - also ist es auch
vorhanden. fprintf fehlt.
Haro schrieb:> Zumindest avr-gcc kann bei printf sehr aggresiv optimieren.
Das gibt's nicht nur auf dem AVR. Kann also gut sein, daß der
fprintf()-Aufruf durch einen direkten Aufruf von fputs() oder so ersetzt
wurde. Am besten mal in den generierten Assembler-Code schauen, was
tatsächlich passiert.
Walter Tarpan schrieb:> Haro schrieb:>> Zumindest avr-gcc> Kein AVR! ARMARMARMARMARMARMARM
Du weißt aber schon, was das Wörtchen "zumindest" hier ausdrücken soll?
Rolf Magnus schrieb:> Du weißt aber schon, was das Wörtchen "zumindest" hier ausdrücken soll?
Hmm....die Zitat-Formatierung ist noch interessanter. Ja, ich weiß, was
damit gemeint ist. Allerdings will ich verhüten, daß die Diskussion
Richtung AVR abdriftet.
Walter Tarpan schrieb:> Ja, ich weiß, was> damit gemeint ist. Allerdings will ich verhüten, daß die Diskussion> Richtung AVR abdriftet.
Genau dafür war mein "zumindest" gedacht. Denn der Folgegedanke meiner
Aussage ist ja: möglicherweise arbeitet der gcc-arm-none-eabi oder was
immer Coocox benutzt ähnlich.
Aber Diskussionen driften hier schnell ab, da hast du leider recht.
Wie auch immer, wenn du Debuggen kannst ist die Funktion drinnen. Im
Zweifel das Map-File konsultieren, dem traue ich da immer mehr.
Aber wie gesagt, schalt doch mal die Optimierung ab.
Auch wenn ich mich jetzt wieder auf dünnes Eis begebe: du weißt ja
anscheinend, wie man in der avr-libc printf auf eine eigene
Ausgabemethode umleitet (Stichwort stdout umleiten, gibts hier ja
irgendwo ein Tut).
Wenn es bei deiner Toolchain kein stderr gibt könntest du dir diesen
Stream ja selber anlegen, mit einem Funktionszeiger auf deine gewünschte
putc-Funktion. Ein eigenes error() zu schreiben ist dann trivial: dein
error() kapselt nur einen fprintf-Aufruf auf dein zuvor erstelltes
stderr.
Nachteil: du arbeitest an der Toolchain vorbei und kochst dein eigenes
Süppchen
Allerdings kann man den Mechanismus ja in die plattform-spezifischen
Header legen, dann kompiliert der gleiche Code unter ARM und AVR, bei
letzterem läuft die Ausgabe über die avr-libc.
M. K. schrieb:> Schau doch mal ob du bei den Link-Optionen die "newlib" oder ähnliches> auswählen kannst.Haro schrieb:> Möglicherweise solltest du die Optimierung abschalten,
Ich habe jetzt mal folgende Optionen ausprobiert:
- Not use C Library
- Use nano C Library
- Use base C Library
- Semihosting
- Retarget.
Außerdem die Optimierung auf O0 geschaltet.
Das Ergebnis ist immer dasselbe. (Selbstverständlich habe ich bei jedem
Build einen String geändert, um sicherzustellen, daß keine alten
Darstellungen übrigbleiben).
Es bleibt mysteriös.
[EDIT:] Ich werde (oder bin bereits) bekloppt. So wird alles
dargestellt:
1
printf("hallo1 \a");
2
fprintf(stdout,"hallo2 %i \a",s);
3
printf("hallo8 \a");
4
fprintf(stdout,"hallo4 %i\a",4);
(s ist ein uint_fast8_t und Rückgabewert einer Funktion). Dabei war mein
Minimalbespiel gerade fast fertig...
Das heißt bei fprintf mit konstantem String wird in fwrite und nicht in
fprintf gesprungen. Und fwrite ist in printf.c gar nicht definiert.
fwrite liegt in libg.a.
Ob mir dieses Wissen irgendwie hilft?
Vor zweieinhalb Stunden dachte ich, ich hätte es verstanden. Aber in
Wirklichkeit ist es mir rätselhaft:
Wie schafft es der Build-Prozeß, aus einem Funktionsaufruf (fprintf)
einen Funktionsaufruf einer komplett anderen Funktion (fwrite) zu
machen, die nichts miteinander zu tun haben? fwrite kommt in der
Aufruf-Kette von fprintf nirgendwo vor, und die Signatur ist ja auch
komplett anders. Und es kommt ja auch etwas Anderes heraus (fprintf ruft
letztendlich PrintChar auf, fwrite nicht)?
[EDIT:} OK, hier:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9967
gibt es einen Bug-Report, daß dieser Verhalten manchmal unerwünscht ist.
Aber wie kommt es überhaupt dazu?
Walter Tarpan schrieb:> Aber wie kommt es überhaupt dazu?
Naja, normalerweise gehen fprintf und fwrite ja ans selbe Ziel, nämlich
den angegebenen Stream. Wie schon erwähnt, optimiert der Compiler den
fprintf-Aufruf zu was einfacherem, wenn kein Formatstring geparst werden
muß.
Walter Tarpan schrieb:> Und es kommt ja auch etwas Anderes heraus (fprintf ruft letztendlich> PrintChar auf, fwrite nicht)?
Das ist aber spezifisch für die von dir verwendete Libc. Der Compiler
weiß von dieser Sonderbehandlung halt nix.
Rolf Magnus schrieb:> Wie schon erwähnt, optimiert der Compiler den> fprintf-Aufruf zu was einfacherem, wenn kein Formatstring geparst werden> muß.
Also ist davon auszugehen, daß irgendwo im Compiler eine Tabelle ist, wo
hart verdrahtet ist:
fprintf(<file>,<konstanter String>) ->
fwrite(<konstanter String>,<strlen(<konstanter String>-1,1,<file>) ?
Walter Tarpan schrieb:> Rolf Magnus schrieb:>> Wie schon erwähnt, optimiert der Compiler den>> fprintf-Aufruf zu was einfacherem, wenn kein Formatstring geparst werden>> muß.>> Also ist davon auszugehen, daß irgendwo im Compiler eine Tabelle ist, wo> hart verdrahtet ist:> fprintf(<file>,<konstanter String>) ->> fwrite(<konstanter String>,<strlen(<konstanter String>-1,1,<file>) ?
Da steckt schon noch etwas mehr dahinter als eine simple Tabelle.
fprintf ist eineim Compiler eingebaute Funktion, die versucht, so viel
wie möglich von der Rechenarbeit auf die Compilezeit zu verlagern und
das fprintf der Laufzeitbibliothek nur dann aufzurufen, wenn's kein
Optimierungspotenzial gibt. Abschalten kannst du dieses Verhalten mit
-fno-builtin-fprintf. Das gilt letztendlich für einen sehr großen Teil
der Standardfunktionen. memcpy() kann z.B. auch auf ganz
unterschiedliche Arten umgsetzt werden, je nachdem, welche Argumente man
übergibt. Da wird nicht immer eine Funktion aufgerufen, die in einer
Schleife Byte für Byte kopiert. Siehe die Liste der built-in-Funktionen
hier:
http://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Other-Builtins.html#Other-Builtins