Hallo zusammen, ich hab mal kurz eine Frage zum Verständnis. Angenommen ich habe einen µC ohne Betriebssystem und entwickle mit GCC. Nun kann ich ja problemlos libc-Funktionen wie sprintf() verwenden. Was ist denn aber mit libc-Funktionen, die eigentlich einen SystemCall ausführen würden, also z.B. fopen() oder fread()? Es gibt ja kein Betriebssystem, das den SystemCall entgegennehmen könnte. Wie entscheidet der GCC, was er mit diesen Aufrufen machen soll?
Probiere es doch aus! ;-) Ich würde erwarten, dass der Linker Dir einfach ein "undefined symbol" präsentiert. mfg Torsten
Torsten Robitzki schrieb: > Ich würde erwarten, dass der Linker Dir einfach ein "undefined symbol" > präsentiert. Nein, läßt sich einwandfrei builden. Es passiert nur anscheinend nichts.
Jens Langecker schrieb: > Was ist denn aber mit libc-Funktionen, die eigentlich einen SystemCall > ausführen würden, also z.B. fopen() oder fread()? Nein, fopen() und fread() sind ebenfalls keine Syscalls, sondern Bibliotheksfunktionen, genau wie printf(). Was darunter benutzt wird, hängt dann von der jeweiligen Implementierung der Bibliothek ab. Bei der Newlib (wie sie beim ARM-GCC gern benutzt wird) sind das Posix-typische Syscalls (open(), close(), read(), write()), die du selbst beisteuern musst, oder für die es ggf. auch Dummies in der Lib gibt. Bei der avr-libc gibt es fdevopen() als Pendant zum klassischen open(), oder auch statische Varianten davon, bei denen man einen Stream zur Compilezeit vorkonfiguriert. Die Funktionen zum Lesen und Schreiben eines Zeichens muss man natürlich auch da selbst liefern.
Jens Langecker schrieb: > Nein, läßt sich einwandfrei builden. Es passiert nur anscheinend nichts. Das deutet daraufhin, dass die Bibliothek vorgefertigte Dummies enthält. ARM + Newlib?
Jörg Wunsch schrieb: > Jens Langecker schrieb: >> Nein, läßt sich einwandfrei builden. Es passiert nur anscheinend nichts. > > Das deutet daraufhin, dass die Bibliothek vorgefertigte Dummies > enthält. > > ARM + Newlib? Korrekt. Hab mich inzwischen durch die Doku gewühlt und die Stubs gefunden. Danke!
Jörg Wunsch schrieb: > Bei der Newlib (wie sie beim ARM-GCC gern benutzt wird) sind das > Posix-typische Syscalls (open(), close(), read(), write()), > die du selbst beisteuern musst, oder für die es ggf. auch > Dummies in der Lib gibt. Bei der avr-libc gibt es fdevopen() als > Pendant zum klassischen open(), Vor einiger Zeit wollte ich open etc. auf Syscalls abbilden, quasi als Backend für fopen, fprintf, etc. Ich hätte gedacht dafür gäbe es weak Stubs in der avr-libc, die ich dann auf Syscalls hätte abbilden können. Aber solche Stubs scheint es in der avr-libc nicht zu geben...
Johann L. schrieb: > Aber solche Stubs scheint es in der avr-libc nicht zu geben... Nee, die tickt ein bisschen anders. Als ich das stdio dort geschrieben habe, wollte ich deutlich sparsamer mit den Ressourcen umgehen, als das newlib & Co. machen. Selbst mit fdevopen() wurde ich noch ausreichend kritisiert (und habe dann die statischen Varianten nachgeliefert), da es in diesem Umfeld genügend „malloc() ist absolut böse“-Fetischisten gibt. Das, was man schon zu PDP-11-Zeiten in stdio gepuffert hat (in hässlichsten Makros dazumals), ist eben für einen ATmega8 schon mal deutlich zu viel SRAM-Bedarf. Pro Stream eine Funktion zum Lesen eines Zeichens und eine zum Schreiben ist gerade so OK.
coocox zB fügt eine syscalls.c zum projekt hinzu für einfache malloc oder printf funktionen muss dann zumindest die _sbrk() geschrieben werden.
1 | __attribute__ ((used)) |
2 | caddr_t _sbrk ( int incr ) |
3 | {
|
4 | static unsigned char *heap = NULL; |
5 | unsigned char *prev_heap; |
6 | |
7 | if (heap == NULL) { |
8 | heap = (unsigned char *)&_end; |
9 | }
|
10 | prev_heap = heap; |
11 | |
12 | heap += incr; |
13 | |
14 | return (caddr_t) prev_heap; |
15 | }
|
16 | |
17 | __attribute__ ((used)) |
18 | int link(char *old, char *new) { |
19 | return -1; |
20 | }
|
21 | |
22 | __attribute__ ((used)) |
23 | int _close(int file) |
24 | {
|
25 | return -1; |
26 | }
|
27 | |
28 | __attribute__ ((used)) |
29 | int _fstat(int file, struct stat *st) |
30 | {
|
31 | st->st_mode = S_IFCHR; |
32 | return 0; |
33 | }
|
34 | |
35 | __attribute__ ((used)) |
36 | int _isatty(int file) |
37 | {
|
38 | return 1; |
39 | }
|
40 | |
41 | __attribute__ ((used)) |
42 | int _lseek(int file, int ptr, int dir) |
43 | {
|
44 | return 0; |
45 | }
|
46 | __attribute__ ((used)) |
47 | int _read(int file, char *ptr, int len) |
48 | {
|
49 | return 0; |
50 | }
|
51 | __attribute__ ((used)) |
52 | int _write(int file, char *ptr, int len) |
53 | {
|
54 | return len; |
55 | }
|
56 | |
57 | __attribute__ ((used)) |
58 | void abort(void) |
59 | {
|
60 | /* Abort called */
|
61 | while(1); |
62 | }
|
Jörg Wunsch schrieb: > Pro Stream eine Funktion zum Lesen > eines Zeichens und eine zum Schreiben ist gerade so OK. Aus aktuellem Anlass interessiert mich das Thema sehr. Ich dachte mehr bräucht ein Stream auch nicht? Also eine getc() und eine putc(). Inwiefern wird da in der stdio noch gepuffert, bzw. wo würde das malloc() ins Spiel kommen? Gruß, Ben
Kaj G. schrieb: > Beitrag "ARM - Probleme mit sprintf (Linkerfehler)" Nachdem ich diesen Thread gelesen habe, hab ich aber noch eine Frage: Ich benutze Atollic TrueSUTDIO (GCC 4.8.3) für einen STM32 und habe kein Betriebssystem. Ich mache exzessiven Gebrauch von vsnprintf() und das funktioniert problemlos. Wenn ich es richtig verstehe, verwendet vsnprintf() malloc() bzw. sbrk(). sbrk() habe ich nicht selbst beigesteuert. Wer könnte denn in meinem Setup so freundlich sein, mir sbrk() zur Verfügung zu stellen?
Jens Langecker schrieb: > Wenn ich es richtig verstehe, verwendet vsnprintf() malloc() bzw. > sbrk(). Warum sollte es? die 's' Versionen der printf Familie schreiben in einen Buffer, den der Aufrufer zur Verfügung stellen muss. Da gibt es keinen Bedarf an dynamisch allokierten Speicher bzw. das ist so wenig, dass er mit funktionslokalen Variablen abzudecken ist. Ein long hat eine überschaubare und im Vorfeld bekannte maximale Textrepräsentierung. Dafür den Speicher dynamisch mittels malloc zu allokieren, wäre völliger Overkill.
:
Bearbeitet durch User
Ben schrieb: > Ich dachte mehr bräucht ein Stream auch nicht? Braucht er auch nicht. > Also eine getc() und eine > putc(). Yep, als Methoden, die im Stream-Objekt hinterlegt werden. > Inwiefern wird da in der stdio noch gepuffert, Gar nicht. > bzw. wo würde das > malloc() ins Spiel kommen? Nur beim fdevopen(), um den Platz für das Stream-Objekt selbst zu bekommen. Das ist überhaupt nicht tragisch, wenn an diesen Objekten nie mehr was geändert wird, dann fragmentiert davon auch kein Speicher und nichts, das malloc() beißt auch nicht den Programmierer in den Zeh. :-)) Der einzige wirkliche Nachteil ist, dass man sich natürlich zusätzlichen Code (für malloc() selbst) mit auflädt. Daher habe ich dann als Alternativen FDEV_SETUP_STREAM() bzw. fdev_setup_stream() nachgereicht, bei denen der Nutzer den Speicher selbst bereitstellen muss. Jens Langecker schrieb: > Wenn ich es richtig verstehe, verwendet vsnprintf() malloc() bzw. > sbrk(). In der Newlib? Nur für Gleitkommaoperationen, da wird ein Zwischenspeicher alloziert. Da haben sie noch einen bösen Bug drin, sind wir erst vor paar Tagen drüber gestolpert (binutils bug #18212), da wird das Ergebnis eines malloc() ungeprüft weiterbenutzt. Gab malloc() einen Nullzeiger zurück, so versuchen sie anschließend, auf Adresse 0 zu schreiben (was uns in unserem Falle einen HardFault eingebrockt hat).
Ben schrieb: > Ich dachte mehr bräucht ein Stream auch nicht? Also eine getc() und eine > putc(). Inwiefern wird da in der stdio noch gepuffert, bzw. wo würde das > malloc() ins Spiel kommen? Zb brauchst du für jeden fopen eine FILE Struktur. Will dein Anwendungsprogramm gleichzeitig 20 Files öffnen, müssen die irgendwo herkommen. Du hast die Wahl zwischen statischer Allokierung einer entsprechenden Anzahl x im Vorfeld (mit entsprechendem Speicherplatzverbrauch) oder einer dynamischen Allokierung. In dem einen Fall ist nach x Files damit automatisch Schluss mit lustig bzw. je nach x sind es zu wenige oder du investierst zuviel Speicher. Oder eben im anderen Fall musst du die dynamische Allokierung bemühen.
Jörg Wunsch schrieb: > zu PDP-11-Zeiten Da war ich noch flüssig ;o) > für einen ATmega8 schon mal deutlich zu viel SRAM-Bedarf. > Pro Stream eine Funktion zum Lesen eines Zeichens und eine zum > Schreiben ist gerade so OK. War in einem Simulator-Umgebung, da wäre es ganz nett wenn die Standardfunktionen verfügbar werden, etwa in der avr-gcc Testsuite. Da kann man ja nicht einfach fdevopen verwenden. Vor einiger Zeit hatte ich den AVR-Simulator von magischen SFRs auf Syscalls umgestellt. Im Gegensatz zu magischen SFRs haben Syscalls keinen Overhead für den Simulator, d.h. der Simulator könnte so Scherze bieten wie vom avr-Programm auf das Host-Dateisystem zuzugreifen ohne die Simulation zu verlangsamen :-)
:
Bearbeitet durch User
Weils grad thematisch hier passt: Wenn ich so wie im Tutorial hier beschrieben stdout auf meine UART Funktionen umleite dann kann ich per printf/scanf Daten lesen und schreiben, was oft komfortabler ist als die typischen uart_putc() oder uartputs(). Was aber wenn ich Binärdaten lesen/schreiben will? Also z.B. ein UART Protokoll parsen will dass nicht nuur ASCII enthält? Welche Funktionen würd ich da benutzen? Ich tippe mal auf fread und fwrite, denen ich dann stdin und stdout mit-übergebe, richtig? Was mach ich mit dem __size-Parameter? Einfach immer auf 1 setzen? Grüße, Haro
Haro schrieb: > Ich tippe mal auf fread und fwrite, denen ich dann stdin und stdout > mit-übergebe, richtig? > Was mach ich mit dem __size-Parameter? Einfach immer auf 1 setzen? Auf die Größe des zu lesenden Elements.
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.