Forum: Compiler & IDEs AVR: fprintf_P String im Progmem


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich will eine feste Zeichenkette per Funktionsaufruf ans Display 
ausgeben. Also ungefähr so:
1
testprint(PSTR("Fcn:lala"));

Der Aufruf-String soll also im Flash liegen. Jetzt gibt es ein Problem 
mit den Parametern von "fprintf_P": Die Funktion erlaubt nur eine 
Zeichenkette im Flash.

Auf anderen Plattformen als AVR wird die Funktion einfach zu "fprintf". 
Das funktioniert auf einem STM32 oder dem PC. Allerdings gilt der Aufruf
1
printf(string);
heutzutage als schlechter Stil, da für zahlreiche Sicherheitslücken 
verantwortlich. Stattdessen ist
1
printf("%s",string);
empfohlen.

Allerdings läßt sich das nicht 1:1 auf den AVR übertragen:
1
//#define constflash const __flash
2
void testprint(constflash char *e)
3
{
4
    // Mal mit stdout probieren:
5
    fprintf_P(stdout,PSTR("Out1:lalala"));            // Funktioniert
6
    fprintf_P(stdout,PSTR("%s"),"Out2:lalala");       // Funktioniert
7
    fprintf_P(stdout,PSTR("%s"),PSTR("Out3:lalala")); // Funktioniert nicht
8
9
    // Diese Variante von fprintf gilt als unzeitgemaess, weil unsicher:
10
    fprintf_P(stderr,e);                              // Funktioniert
11
12
    // Das ist das Ziel:
13
    fprintf_P(stderr,PSTR("%s"),e);                   // Funktioniert nicht
14
15
    
16
    // Displayinhalt darstellen
17
    fprintf_P(stderr,PSTR(" \a"));
18
}

Also bleiben mir mehrere Möglichkeiten

1. "alte" Variante nutzen, mit den Kompilerwarnungen auf anderen 
Plattformen leben lernen.

2. eine Kompilerweiche einbauen

3. Oder kennt jemand aus dem Forum eine saubere 
Implementierungsvariante?

Viele Grüße
W.T.

von Oliver S. (oliverso)


Lesenswert?

4. ein fprintf_PP selber schreiben, das sowohl den Formatstring als auch 
die Argumentenliste ja dem Flash liest.

Das sollte nicht so kompliziert sein, da du ja nur die Bersion für %s 
mit einem Parameter implementieren musst.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Nimm fputs_P.

von Walter T. (nicolas)


Lesenswert?

Johann L. schrieb:
> Nimm fputs_P.

Danke! Manchmal sieht man den Wald vor lauter Bäumen nicht.

von Axel S. (a-za-z0-9)


Lesenswert?

Walter T. schrieb:
> Allerdings gilt der Aufruf
1
printf(string);
> heutzutage als schlechter Stil, da für zahlreiche Sicherheitslücken
> verantwortlich. Stattdessen ist
1
printf("%s",string);
> empfohlen.

Bitte was? Wo hast du das denn her? Ich halte das für ausgemachten 
Blödsinn. Tatsächlich die ist zweite Form deutlich teurer.

von Dr. Sommer (Gast)


Lesenswert?

Axel S. schrieb:
> Bitte was? Wo hast du das denn her
Errm, das ist doch Allgemeinwissen. Was wenn "string" von außen, übers 
Netzwerk, kommt und ein "%s" enthält, was passiert dann? Dann sieht der 
Aufruf äquivalent so aus:
1
printf ("%s");
 und dann wird irgendwas aus dem Speicher ausgegeben. Im besten Fall 
stürzt das Programm einfach nur ab, im schlimmsten gibt es 
möglicherweise geheime Informationen raus...

von Dr. Sommer (Gast)


Lesenswert?

https://en.wikipedia.org/wiki/Uncontrolled_format_string
Es gibt sogar einen Wikipedia Artikel darüber.

Axel S. schrieb:
> Ich halte das für ausgemachten
> Blödsinn
Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht 
kennt...

von Walter T. (nicolas)


Lesenswert?

Dr. Sommer schrieb:
> Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht
> kennt...

Axel ist genausowenig Programmierer wie ich. Also paßt schon.

von Rolf M. (rmagnus)


Lesenswert?

Walter T. schrieb:
> Dr. Sommer schrieb:
>> Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht
>> kennt...
>
> Axel ist genausowenig Programmierer wie ich. Also paßt schon.

Nein, paßt nicht. Wenn er kein Programmierer ist, sollte er vielleicht 
seine Klappe zu dem Thema nicht so weit aufreißen.

von Sebastian S. (amateur)


Lesenswert?

Wenn Du blind einen String ausgibst, kann viel Blödsinn passieren.

Wenn Du aber mit Deiner Funktion (im FLASH), eine unveränderbare 
Zeichenkette (aus dem FLASH) ausgibst, sollte das sicher sein.

Kommt jemand an den Flash ran, so ist sowieso Hopfen und Schmalz 
verrieben.

von Walter T. (nicolas)


Lesenswert?

Sebastian S. schrieb:
> Wenn Du aber mit Deiner Funktion (im FLASH), eine unveränderbare
> Zeichenkette (aus dem FLASH) ausgibst, sollte das sicher sein.

Das ist wohl wahr. Allerdings weiß ich die Warnungen eines modernen 
Compilers (allen voran Clang/LLVM) sehr zu schätzen. Und diese Compiler 
warnen vor Anti-Pattern wie "uncontrolled format string". Es wäre 
unklug, sich die echten, berechtigten Warnungen durch solche für die 
konkrete Plattform unnötigen Warnungen zu überdecken. Und irgendwie ist 
mein Ziel auch bei einem AVR-Programm immer noch 0 Errors, 0 Warnings, 0 
Badboxes. Oder so ähnlich.

Axel S. schrieb:
> Tatsächlich die ist zweite Form deutlich teurer.

Für teurer halte ich die zweite Form allerdings nicht. Wenn ich die Muße 
habe, in das Assembler-Listing zu schauen, entdecke ich immer wieder, 
daß der GCC die Aufrufe aus der printf-Familie munter so 
durcheinandermischt, daß jeder Aufruf durch den billigsten Kandidaten 
aus der Familie ersetzt wird.

(Disclaimer: Entdeckt beim Debuggen auf einem STM32, wo man die 
putc-Funktionen selbst schreiben muß.)

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Walter T. schrieb:
> Axel S. schrieb:
>> Tatsächlich die ist zweite Form deutlich teurer.
>
> Für teurer halte ich die zweite Form allerdings nicht.

Ich wüßte auch nicht, warum sie das sein sollte. Ich würde dann eher 
noch mit einer Ersparnis rechnen, denn der Format-String muss ja im 
Gegensatz zu dem anderen geparst werden und ist jetzt viel kürzer.

von Axel S. (a-za-z0-9)


Lesenswert?

Dr. Sommer schrieb:
> Axel S. schrieb:
>> Bitte was? Wo hast du das denn her
> Errm, das ist doch Allgemeinwissen. Was wenn "string" von außen, übers
> Netzwerk, kommt und ein "%s" enthält, was passiert dann?

Wo war denn bitte davon die Rede, daß das ein String "von außen" ist?
Im Gegenteil, dem TE ging es ja gerade darum, daß der String im Flash 
steht, also schon zur Compilezeit bekannt ist.

Dr. Sommer schrieb:
> Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht
> kennt...

Du mich auch

Wirklich, so Typen wie du sind das eigentliche Problem. Nicht verstanden 
worum es geht, aber den Obermacker raushängen lassen.

Rolf M. schrieb:
> Nein, paßt nicht. Wenn er kein Programmierer ist, sollte er vielleicht
> seine Klappe zu dem Thema nicht so weit aufreißen.

Herr Nuhr, übernehmen Sie!


Walter T. schrieb:
> Axel S. schrieb:
>> Tatsächlich die ist zweite Form deutlich teurer.
>
> Für teurer halte ich die zweite Form allerdings nicht. Wenn ich die Muße
> habe, in das Assembler-Listing zu schauen, entdecke ich immer wieder,
> daß der GCC die Aufrufe aus der printf-Familie munter so
> durcheinandermischt, daß jeder Aufruf durch den billigsten Kandidaten
> aus der Familie ersetzt wird.

Wenn der Compiler das wegoptimiert, dann nicht. Allerdings grenzt das 
Auswerten des Formatstrings zur Compilezeit schon an schwarze Magie. Ich 
würde mich da keineswegs drauf verlassen.

Daß die variadische Funktion andererseits mit dem Parsen des Format- 
strings und der verschachtelten Ausgabe von Formatstring und Argument 
dann aufwendiger ist, muß ich ja hoffentlich nicht noch erklären.

von Dr. Sommer (Gast)


Lesenswert?

Axel S. schrieb:
> Wo war denn bitte davon die Rede, daß das ein String "von außen" ist?
> Im Gegenteil, dem TE ging es ja gerade darum, daß der String im Flash
> steht, also schon zur Compilezeit bekannt ist.
Na, hoffentlich wird der Programmteil niemals bearbeitet/kopiert, oder 
viel schlimmer, hoffentlich gewöhnt sich keiner den Stil printf(string) 
an. Nur weil ein mieser Code an einer ganz bestimmten Stelle "ok" ist, 
sollte man das noch lange nicht so schreiben. Und die richtige Lösung 
ist natürlich puts.

Axel S. schrieb:
> Daß die variadische Funktion andererseits mit dem Parsen des Format-
> strings und der verschachtelten Ausgabe von Formatstring und Argument
> dann aufwendiger ist, muß ich ja hoffentlich nicht noch erklären.
Achja? In diesem Code:
1
printf ("%d", string);
 muss sie nur ein harmloses %s auswerten. In
1
printf (string);
 muss sie einen möglicherweise langen komplizierten string auswerten und 
nach "%" suchen, auch wenn gar keine drin sind.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> fprintf_P(stdout,PSTR("%s"),PSTR("Out3:lalala")); // Funktioniert
> nicht

RTFM:

http://nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1
1
fprintf_P(stdout,PSTR("%S"),PSTR("Out3:lalala"));

von Walter T. (nicolas)


Lesenswert?

Du meinst also, ich hätte besser schreiben sollen:
1
fprintf_P(stdout,PSTR("%s"),PSTR("Out3:lalala")); // Funktioniert nicht und das ist auch voellig konform mit dem dokumentierten Verhalten, aber ich suche eine Variante, die sinngemaess das macht

Ich merke es mir für's nächste Mal, daß die Freunde des horizontalen 
Scrollbalkens auch etwas davon haben sollen.

Nichts für ungut. Es hat mich vorgestern ein Weilchen Zeit gekostet, 
einen Fehler in neuem Quelltext auf einen Fehler in anderem Quelltext, 
der seit ca. einem Jahr (auf anderen Plattformen) fehlerfrei läuft (da 
PROGMEM dort keine Rolle spielt) auf den genau diese eine Zeile 
herunterzubrechen, und da hatte ich tatsächlich die einfache Variante 
mit fputs_P übersehen. Eben der Wald und die Bäume. Aber den Baum hat ja 
Johann schon im zweiten Post herausgeholt.

Der Rest ist ja eh nur eine "wir haben das aber so gelernt!" - 
"heutzutage macht man das aber so!"-Diskussion. Inhaltlich vermutlich 
nutzlos, aber zwei Leute können ihren Frust dort wegschreiben, wo sie 
keinen stören.

Alles in Butter.

P.S.: Edit: Jetzt bin ich aber enttäuscht, daß überlanger Code keinen 
Scrollbalken mehr macht. Menno. Nichts funktioniert am Montagmorgen.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Du meinst also

Wen meinst du mit „Du“?  Mich?  Nein, ich würde das nicht.  Ich schrieb
dir, dass der Konvertierungsbuchstabe Groß-s genau dafür da ist, was
du da wolltest.  Dass für den konkreten Fall fputs_P() besser ist, ist
eine andere Frage.  Aber es kann ja auch die Situation geben, da man
das Argument für %S zuvor aus irgendeiner Tabelle nehmen möchte und
außer dem reinen String noch weitere Dinge formatieren möchte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Jetzt bin ich aber enttäuscht, daß überlanger Code keinen Scrollbalken
> mehr macht

Falscher Browser?  Hier ist da schon einer dran.

von Walter T. (nicolas)


Lesenswert?

Der Formatbuchstabe "%S" ist allerdings für plattformübergreifenden 
Quelltext eine echte Falle. Auf dem AVR steht er für String im PROGMEM, 
unter MinGW und MSVC für wchar_t.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Der Formatbuchstabe "%S" ist allerdings für plattformübergreifenden
> Quelltext eine echte Falle. Auf dem AVR steht er für String im PROGMEM,
> unter MinGW und MSVC für wchar_t.

Plattformübergreifende Strings im Flash gibt es sowieso nicht. ;-)  Du
musst also in solchen Fällen ohnehin eine Abstraktion dafür schaffen,
und in dieser kannst du auch gleich das %S mit unterbringen.

Ansonsten normiert der Standard für wchar_t die Formatierung %ls,
während Großbuchstaben explizit als “may be used in extensions”
benannt werden.  Damit ist die avr-libc-Implementierung in dieser
Hinsicht sogar standardkonform.

von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Eigentlich ist diese Abstraktion relativ primitiv realisierbar. Im 
Anhang ist mal meine Variante für den begrenzten Funktionumfang von 
avr/stdio, den ich wirklich nutze.

Wozu diese Abstraktion natürlich überhaupt nicht taugt, ist Fehler im 
Bezug auf SRAM/Flash schon am PC zu finden. Sonst wäre dieser Thread nie 
gemacht worden :-).

Für die pgm_read/pgm_write-Befehle gibt es dann noch ein paar kleine 
Dummy-Befehle.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ja, so ähnlich haben wir das auch schon vor Jahren getan, damals
um GCC vs. IAR auf dem AVR zu abstrahieren, später kam dann ARM
hinzu.

Wenn du dort jetzt noch einfügst:
1
#ifdef AVR
2
#  define FMT_CONSTSTRING "S"
3
#else
4
#  define FMT_CONSTSTRING "s"
5
#endif

dann hast du halt auch den format specifier noch abstrahiert.
Benutzt wird das dann wie mit den diversen Abstraktionen in
<inttypes.h>:
1
myprintf("The result for %" FMT_CONSTSTRING " is %d\n",
2
         ptr_to_string_possibly_in_flash, some_integer);

von Walter T. (nicolas)


Lesenswert?

Stimmt. Manchmal ist es so einfach.

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
Noch kein Account? Hier anmelden.