Forum: Compiler & IDEs ARM - Probleme mit sprintf (Linkerfehler)


von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hey,

wieder mal ein Problem mit sprintf.

Platform:
Atmel Studio 6.2
Atmel Sam3X8E (Cortex-M3)

folgendes Problem:
1
#include <stdio.h>
2
3
int main(void)
4
{
5
  char test[30];
6
  char x[] = "Hallo Welt";
7
8
  sprintf(test, "%s", x);
9
}
gibt folgende Fehler:
1
- ld returned 1 exit status
2
- undefined reference to `_sbrk`

schreibe ich das so:
1
  sprintf(test, "Hallo Welt");
ist alles ok.

Was muss ich da jetzt wie genau dazu linken? :-/

Vielen Dank fuer eure Hilfe.

von temp (Gast)


Lesenswert?

Wie das mit _sbrk beim Atmel Studio 6.2 aussieht kann ich dir nicht 
sagen. Sprintf will hier dyn. Speicher anfordern. Das landet am Ende in 
_sbrk. Wie aber im konkreten Fall die IDE mit den Projekteinstellungen 
für den Heap u.s.w umgeht und das dann verwurstet, ist bei jeder IDE 
anders. Ich habe mir angewöhnt eine eigene kleine printf/sprintf Routine 
zu verwenden, die nicht mit der großen Keule umgeht.

Warum das Problem bei sprintf(test, "Hallo Welt"); nicht auftritt, liegt 
daran, dass der gcc hier schon mit der Optimierung zuschlägt. Der parst 
das "Hallo Welt" schon und stellt fest: ups, hier ist ja gar kein 
sprintf nötig, ein strcpy hätte auch gereicht. Und genau das baut er 
dann ein. Kannst du im map-File nachsehen. Bei printf läuft das auf puts 
hinaus, aber nur dann wenn am Ende ein '\n' steht.

von Jim M. (turboj)


Lesenswert?

Klingt nach newlib. Versuche mal "siprintf" statt "sprintf" zu benutzen.
Da geht dann keine float Formatierung, aber es wird IIRC auch kein 
malloc() benutzt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

_sbrk ist ein Syscall, der vom Betriebssystem mehr dynamischen Speicher 
anfordert, d.h. quasi das Backend von malloc et. al.

Welches OS verwendest du denn?

von Jim M. (turboj)


Lesenswert?

Johann L. schrieb:
> Welches OS verwendest du denn?

Auf einem Cortex M3? Der OP dürfte "Bare Metal" fahren.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Danke fuer die Antworten.

Jim Meba schrieb:
> Versuche mal "siprintf" statt "sprintf" zu benutzen.
Selber Fehler :(

Jim Meba schrieb:
> Der OP dürfte "Bare Metal" fahren.
So sieht's aus. :)

Insgesamt komische Angelegenheit.
Jetzt (keine Ahnung warum das gestern nicht Funktioniert hat :-/ ) ist 
es so, das sprintf funktioniert, wenn ich es in main() aufrufe, aber 
nicht, wenn ich es in einer anderen Funktion aufrufe. Dann kommt wieder
1
Error  1  undefined reference to `_sbrk'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/sbrkr.c  58
2
Error  2  ld returned 1 exit status  collect2.exe

Hab jetzt mal aus spass (einfach nur um zu sehen was passiert) folgendes 
getan:
1
#include <stdio.h>
2
3
int main(void)
4
{
5
  char x[] = "Hallo Welt";
6
7
  printf("%s", x);
8
}
Das gibt folgende Fehlerliste:
1
Error  1  undefined reference to `_sbrk'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/sbrkr.c  58
2
Error  2  undefined reference to `_write'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/writer.c  58
3
Error  3  undefined reference to `_close'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/closer.c  53
4
Error  4  undefined reference to `_fstat'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/fstatr.c  62
5
Error  5  undefined reference to `_isatty'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/isattyr.c  58
6
Error  6  undefined reference to `_lseek'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/lseekr.c  58
7
Error  7  undefined reference to `_read'  /home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/readr.c  58
8
Error  8  ld returned 1 exit status  collect2.exe
9
10
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
11
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/sbrkr.c(58,1): error: undefined reference to `_sbrk'
12
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-writer.o): In function `_write_r':
13
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/writer.c(58,1): error: undefined reference to `_write'
14
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-closer.o): In function `_close_r':
15
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/closer.c(53,1): error: undefined reference to `_close'
16
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-fstatr.o): In function `_fstat_r':
17
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/fstatr.c(62,1): error: undefined reference to `_fstat'
18
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-isattyr.o): In function `_isatty_r':
19
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/isattyr.c(58,1): error: undefined reference to `_isatty'
20
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-lseekr.o): In function `_lseek_r':
21
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/lseekr.c(58,1): error: undefined reference to `_lseek'
22
    c:/program files (x86)/atmel/atmel toolchain/arm gcc/native/4.8.1429/arm-gnu-toolchain/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libc.a(lib_a-readr.o): In function `_read_r':
23
/home/tools/hudson/workspace/arm-gnu-toolchain/.build/src/newlib-custom/newlib/libc/reent/readr.c(58,1): error: undefined reference to `_read'
24
collect2.exe(0,0): error: ld returned 1 exit status
Ich arbeite unter Windows (soweit offensichtlich, da ich Atmel Studio 
benutzte), und frage mich jetzt:
Koennen diese komischen Pfade (/home/tools/hudson/...) was mit diesen 
fehlern zu tun haben?
Wenn ja: Wie behebe ich das?
Und viel interessanter fuer mich waere noch:
Wo kommen diese Pfade her?

Hab jetzt mal noch zu Library search path (-L)
1
C:\Program Files (x86)\Atmel\Atmel Toolchain\ARM GCC\Native\4.8.1429\arm-gnu-toolchain\arm-none-eabi\lib\armv7-m
hinzugefuegt und dem Linker (-l) die libc mitgegeben, libm wird 
standardmaessig mitgegeben. Fehler bleibt.

Irgendwie ziemlich uncool das ganze :(

: Bearbeitet durch User
von temp (Gast)


Lesenswert?

Wie oben schon geschrieben sind das die normalen syscalls aus der 
newlib. Versuch mal, ob du bei deiner Atmel-IDE im Projekt einstellen 
kannst ob du printf und Konsorten mit float (double) verwenden willst 
oder nicht. Wenn nicht musst du die Funktionen selbst implementieren 
oder dir Alternativen suchen die ohne dynamischen Speicher auskommen. 
Mit den Pfaden oben hat das aber nichts zu tun.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

temp schrieb:
> Versuch mal, ob du bei deiner Atmel-IDE im Projekt einstellen
> kannst ob du printf und Konsorten mit float (double) verwenden willst
> oder nicht.
Also fuer die AVR gibt es extra 'n Haken um das zu aktivieren, fuer ARM 
nicht :(

Das haendische Eintragen wie hier:
http://www.mikrocontroller.net/articles/FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_bei_WinAVR_bzw_AVR-Studio
und hier:
http://blog.ib-rohde.de/printf_float_atmelstudio6/
beschrieben funktioniert fuer ARM nicht.
1
cannot find -lprintf_flt

temp schrieb:
> Wenn nicht musst du die Funktionen selbst implementieren
Ja, das werd ich mmachen. Schenit einfacher zu sein :-/

Vielen Dank fuer eure Hilfe.

P.S. Koennte ich wegen sowas den Atmel-Support anschreiben?

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


Lesenswert?

Kaj G. schrieb:
> P.S. Koennte ich wegen sowas den Atmel-Support anschreiben?

Meinst du, die schreiben dir einen Speicher-Allokator?

_sbrk() ist eine Funktion, die du liefern musst, eben weil du
kein OS hast, welches so eine Funktion bereitstellt.

Wenn du die Suchmaschine deiner Wahl mal bemühst, solltest du
Beispielimplementierungen für sowas finden können.

Hier eine Implementierung, die ich mal irgendwo gefunden habe:
1
void *_sbrk(int incr) {
2
3
    extern char _ebss; // Defined by the linker
4
    static char *heap_end;
5
    char *prev_heap_end;
6
7
    if (heap_end == 0) {
8
        heap_end = &_ebss;
9
    }
10
    prev_heap_end = heap_end;
11
12
    char * stack = (char*) __get_MSP();
13
    assert(heap_end + incr <  stack);
14
15
    heap_end += incr;
16
    return prev_heap_end;
17
}

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Jörg Wunsch schrieb:
> _sbrk() ist eine Funktion, die du liefern musst, eben weil du
> kein OS hast, welches so eine Funktion bereitstellt.
Vielen danke fuer die Information, das wusste ich nicht.

Jörg Wunsch schrieb:
> Kaj G. schrieb:
>> P.S. Koennte ich wegen sowas den Atmel-Support anschreiben?
>
> Meinst du, die schreiben dir einen Speicher-Allokator?
Noe, aber vielleicht haetten die mir auch erzehlt, das ich diese 
funktion eben selber liefern muss.

Danke fuer die Hilfe, wieder was gelernt. :)

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


Lesenswert?

Kaj G. schrieb:
> Vielen danke fuer die Information, das wusste ich nicht.

Für mein erstes Testprogramm seinerzeit musste ich noch ein paar
Backend-Funktionen mehr selbst liefern:
1
_ssize_t _write(int fd, const void *buf, size_t count)
2
{
3
    if (fd == 1 || fd == 2)
4
    {
5
        char *cp = (char *)buf;
6
        size_t rv = count;
7
8
        while (count-- != 0)
9
        {
10
            uart_write_char(*cp++);
11
        }
12
        return (_ssize_t)rv;
13
    }
14
    errno = EBADF;
15
    return -1;
16
}
17
18
int _close(int fd)
19
{
20
    if (fd >= 0 && fd <= 2)
21
    {
22
        return 0;
23
    }
24
    errno = EBADF;
25
    return -1;
26
}
27
28
int _fstat(int fd, struct stat *sb)
29
{
30
    if (fd >= 0 && fd <= 2)
31
    {
32
        memset(sb, 0, sizeof(struct stat));
33
        sb->st_mode = S_IFCHR;
34
        return 0;
35
    }
36
    errno = EBADF;
37
    return -1;
38
}
39
40
int _isatty(int fd)
41
{
42
    if (fd >= 0 && fd <= 2)
43
    {
44
        return 1;
45
    }
46
    errno = EBADF;
47
    return 0;
48
}
49
50
off_t _lseek(int fd, off_t offset, int whence)
51
{
52
    if (fd >= 0 && fd <= 2)
53
    {
54
        return 0;
55
    }
56
    errno = EBADF;
57
    return (off_t)-1;
58
}
59
60
_ssize_t _read(int fd, void *buf, size_t count)
61
{
62
    if (fd == 0)
63
    {
64
        return 0;
65
    }
66
    errno = EBADF;
67
    return -1;
68
}
69
70
void _exit(int status)
71
{
72
    for (;;)
73
    {
74
    }
75
}
76
77
int _kill(int pid, int signal)
78
{
79
    if (pid == 1)
80
    {
81
        for (;;)
82
        {
83
        }
84
    }
85
    errno = EINVAL;
86
    return -1;
87
}
88
89
int _getpid(void)
90
{
91
    return 1;
92
}

Teilweise sind es natürlich nur stubs oder dummies.

Nimmst du ASF?  Könnte sein, dass sie dir da zum Teil diese Arbeit
abnehmen.  Damit kenn' ich mich nicht aus.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Jörg Wunsch schrieb:
> Nimmst du ASF?
Nein, mache alles von hand, ganz ohne ASF :)

Hab's jetzt aber hinbekommen.
Diese beiden Seiten:
https://balau82.wordpress.com/2010/12/16/using-newlib-in-arm-bare-metal-programs/
https://avrstudio5.wordpress.com/2013/03/05/using-with-arm-c-projects-with-atmel-studio/
haben mir gut geholfen.

Hab mir ein ASF-Beispiel-Projekt aufgemacht, und da die Datei 
"syscalls.c" rausgezogen und meinem Projekt hinzugefuegt.
Musste dann noch im Linker-Script folgende Zeile einfuegen (auch aus dem 
Linker-Script des Beispiel-Projektes kopiert)
1
__ram_end__ = ORIGIN(ram) + LENGTH(ram) - 4;
da sbrk auf diesen Wert zugreift.
in der syscalls.c sind folgende funktionen implemmentiert:
1
link
2
_sbrk
3
_close
4
_fstat
5
_isatty
6
_lseek
7
_exit
8
_kill
9
_getpid

Jörg Wunsch schrieb:
> Könnte sein, dass sie dir da zum Teil diese Arbeit
> abnehmen.
In gewisser weise hat mir das ASF wirklich die Arbeit abgenommen :D

Jörg Wunsch schrieb:
> Für mein erstes Testprogramm seinerzeit musste ich noch ein paar
> Backend-Funktionen mehr selbst liefern:
Kann man das irgendwo nachlesen, welche Funktionen man selber Liefern 
muss? Musste ich hier ja in gewisser weise jetzt auch machen.

Vielen vielen Dank fuer eure Hilfe. Ihr habt mich in die richtige 
Richtung geschubst. :)

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


Lesenswert?

Kaj G. schrieb:
> Kann man das irgendwo nachlesen, welche Funktionen man selber Liefern
> muss?

In der Linker-Fehlermeldung. :-)

Die newlib (die bei ARM-GCC meist, bei AVR-GCC eher selten benutzt
wird) bietet ein C-Standard-API, das seinerseits, soweit erforderlich,
auf dem typischen UNIX-API aufsetzt.  Wenn du also dort ein putchar()
machst, dann wird in der C-Bibliothek das gesamte stdio-Handling
abgewickelt, aber am Ende müsste man natürlich auf die Dienste eines
Betriebssystems zurückgreifen, welches hier nicht existiert.  Daher
ist es am Nutzer, die entsprechenden Backends zu implementieren, die
sonst normalerweise das darunter liegende unixoide System liefern
würde.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Jörg Wunsch schrieb:
> In der Linker-Fehlermeldung. :-)
Ich meinte eigentlich vorher, bevor die Fehlermeldungen auftreten :-)
Aber ok, ist das reicht mir.

Jörg Wunsch schrieb:
> Daher
> ist es am Nutzer, die entsprechenden Backends zu implementieren
Also muss ich das z.B. auch fuer STM32F4 Controller machen? Oder gibt es 
da Hersteller die eine Lib mitliefern wo das schon fertig implementiert 
ist? Ich meine, der Hersteller kennt doch seinen Controller und sollte 
doch wissen was da wie zu implementieren ist.

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


Lesenswert?

Kaj G. schrieb:
> Oder gibt es da Hersteller die eine Lib mitliefern wo das schon fertig
> implementiert ist?

Inwiefern Hersteler da was Vorgefertigtes mitliefern (wie Atmel ja
offenbar beim ASF), hab' ich keine Ahnung.

> Ich meine, der Hersteller kennt doch seinen
> Controller und sollte doch wissen was da wie zu implementieren ist.

Woher sollte aber der Hersteller wissen, wo du deinen Heap gern haben
möchtest, ob dein stdout nun auf eine UART geht oder eher über USB
abgehandelt werden soll etc. pp.?

Sicher, manche Dinge (wie kill, exit und dergleichen) haben in einer
betriebssystemlosen Umgebung wie hier wenig Sinn.  Ich glaube mich zu
erinnern, dass der Krempel nur reinkommt, weil da irgendwo assert()
drin war, was dann wiederum ein abort() machen will.  Die kann man
natürlich auch auf dieser Ebene bereits durch Dummies abfangen, wenn
man sie gar nicht braucht.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Jörg Wunsch schrieb:
> Woher sollte aber der Hersteller wissen, wo du deinen Heap gern haben
> möchtest, ob dein stdout nun auf eine UART geht oder eher über USB
> abgehandelt werden soll etc. pp.?
Ok, guter Einwand :-)

Naja, Problem gelöst und wieder was gelernt. Zeit für Wochenende :)

von isidor (Gast)


Lesenswert?

Lieber allwissender Moderator

wo ist denn nun der Unterschied ob ich eine AVR Code mit diesem
Problem compiliere oder einen ARM?

Oder anders herum gefragt: warum bekomme ich im (Betriebssystem-
losen) AVR das sprintf "automatsch" aufgelöst und beim ARM nicht?

Die "Grundeinstellung" ist doch bei beiden "kein OS" ....

von Programmierer (Gast)


Lesenswert?

Der AVR-GCC liefert schlicht und ergreifend eine andere C-Library mit 
als der ARM-GCC, die kein malloc () und damit kein sbrk braucht. Solche 
libraries gibt es natürlich auch für ARM, die muss man "nur" dem GCC 
beibringen...

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


Lesenswert?

isidor schrieb:
> Oder anders herum gefragt: warum bekomme ich im (Betriebssystem-
> losen) AVR das sprintf "automatsch" aufgelöst und beim ARM nicht?

Wenn du den AVR mit der newlib benutzt, dürfte die Sache sehr ähnlich
zu dem sein (wenn nicht gar gleich), wie man es hier beim ARM sieht.

newlib für AVR ist durchaus verfügbar, nur dass die meisten jedoch
stattdessen die avr-libc benutzen.  Die geht an das Kapitel stdio
komplett anders heran als der vergleichsweise generische Ansatz der
newlib.

von Tobias P. (hubertus)


Lesenswert?

Hallo,

ich versuche gerade auf meinem STM32F4 Discovery ein snprintf() zu 
machen. Mit float.

Mein Aufruf sieht so aus
1
char buffer[100];
2
sprintf(buffer, sizeof(buffer), "%f", 123.456f);
aber es wird nicht der String "123.456" erzeugt, sondern einfach 
"0.000000".
Und das, obwohl ich sbrk() mit implementiert habe, wie oben angegeben. 
Wo könnte der Fehler noch liegen?


Gruss
Tobias

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

an irgendwelchen specs die dem linker mitgegeben werden und eine 
"kleine" Version von sprintf linkt die kein float unterstützt.

Wie sieht dein Linkeraufruf aus?

Matthias

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


Lesenswert?

Tobias Plüss schrieb:
> sprintf(buffer, sizeof(buffer), "%f", 123.456f);

Normalerweise sollte float nach double promotet werden, aber für
alle Fälle, probier mal:
1
sprintf(buffer, sizeof(buffer), "%f", 123.456);

von Tobias P. (hubertus)


Angehängte Dateien:

Lesenswert?

Hallo

im Anhang mal mein Makefile und Linkerskript. Beides habe ich von hier

http://emb4fun.de/arm/examples/index.html

mir kopiert und ein wenig angepasst.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Vielleicht liegts auch am Tippfehler:

Tobias Plüss schrieb:
> ich versuche gerade [..] ein snprintf() zu machen.

Dann aber:

Tobias Plüss schrieb:
1
sprintf(buffer, sizeof(buffer), "%f", 123.456f);

von Markus F. (mfro)


Lesenswert?

A propos Tippfehler: snprintf() hat als zweiten Paramter die 
Buffer-Länge. sprintf() den Formatstring.

Ihr solltet euch schon mal entscheiden, was von beidem ihr wollt. Auf 
das "n" kommt's an!

von Tobias P. (hubertus)


Lesenswert?

Hmm also auch mit
1
sprintf(buffer, "%f", 123.0)
geht es nicht. Diesmal ist das Resultat aber nicht 0.000000, sondern 
3.35544e+07. Woher nur? :O

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

setz in deinem Makefile mal USE_HARD_FPU auf 0. Wenns dann korrekt 
funktioniert kannst du mal versuchen die Option -mfloat-abi auf softfp 
zu setzen und USE_HARD_FPU wieder zurück auf 1. Könnte am floating point 
der Bibliothek liegen.

Matthias

von Tobias P. (hubertus)


Lesenswert?

Hallo,

in der Tat: es scheint am floating Point der Blbliothek zu liegen.
Ich habe USE_HARD_FPU auf 0 gesetzt, dann geht es. Setze ich 
USE_HARD_FPU auf 1 und benutze softfp, kommt das selbe fehlerhafte 
Resultat. Meiner Meinung nach kann das aber nicht sein - da hat man eine 
FPU und darf sie nicht benutzen? :-) wie könnte man das reparieren?

von Dr. Sommer (Gast)


Lesenswert?


von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Evtl. zieht er auch die falsche libc (ich denke du verwendest einen 
multilib-gcc) an. Schau mal ins Makefile woher xyprintf kommt (ich 
vermute armv7-m, richtig wäre armv7e-m). Im Zweifel mal -march=armv7e-m 
direkt übergeben und im Linkerfile nochmal checken.

Matthias

von Tobias P. (hubertus)


Lesenswert?

Die Lösung von Dr. Sommer war richtig.
Interessant interessant! daruaf muss man erst mal kommen.

Dankeschön!

übrigens ist es ja so, dass printf() intern ein malloc() verwendet. 
snprintf() sollte das eigentlich nicht, weil man einen Buffer ja schon 
zur Verfügung stellt. Gibt es inzwischen vielleicht eine Möglichkeit, 
das abzustellen?

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


Lesenswert?

Tobias Plüss schrieb:

> snprintf() sollte das eigentlich nicht, weil man einen Buffer ja schon
> zur Verfügung stellt.

Die Gleitkommaversion benutzt einen per malloc() allozierten Puffer
als Zwischenspeicher.

Im Gegensatz zu anderslautenden Gerüchten auch und insbesondere in
diesem Forum beißt malloc() jedoch nicht und benimmt sich auch sonst
völlig friedlich. ;-)  Wenn du das malloc() nur für sprintf() brauchst,
wird natürlich immer nur der gleiche Puffer angefordert, sodass du
auch eine Variante (von _sbrk()) implementieren könntest, die speziell
darauf getrimmt ist, aber denkst du wirklich, dass sich derartige
Verrenkungen irgendwie ernsthaft amortisieren?

Übrigens sollte man meiner Meinung nach den Stack weder auf 0x2001FFFF
noch auf 0x2001FFF8 legen, sondern auf 0x20020000, denn beim ARM wird
der Stackpointer vor dem Einstapeln eines Wertes dekrementiert.  Ich
war in anderem Zusammenhang schon mal über eine ähnlich blödsinnige
Stackinitialisierung gestolpert, die auf der damaligen Plattform dann
einen Hardfault beschert hat beim ersten printf().

von W.S. (Gast)


Lesenswert?

Kaj G. schrieb:
> wieder mal ein Problem mit sprintf.

Kaj G. schrieb:
> char test[30];
>   char x[] = "Hallo Welt";
>
>   sprintf(test, "%s", x);

Ja, ebenfalls "Hey, du..!"

ich frag mich und hier ebenfalls dich, warum du denn überhaupt sowas 
wie printf und Konsorten auf einem µC verwendest. Wie du siehst, macht 
sowas mit schönster Regelmäßigkeit ein Problem nach dem anderen. Warum 
bloß kommen immer wieder Leute auf die Idee, sich damit herumzuplagen, 
anstatt die Sache aus eigener Kraft zu erledigen...

Mein Rat: Schreib dir einen Satz von nützlichen Ausgabe-Routinen selber, 
wo du weißt, was du selber in die Büchse der Pandora getan hast und 
verkneife dir sowas wie printf und Konsorten. Und wenn du es dir nicht 
selber schreiben willst, dann nimm den Unit conv.c aus der Lernbetty 
(hier immer noch im Forum) und du hast keine Probleme mehr mit deinen 
Testausgaben.

W.S.

von Tobias P. (hubertus)


Lesenswert?

@Jörg
naja, malloc() auf einem Mikrocontroller? Ich erinnere mich grade an die 
"GOTO" Hasspredigt, die irgend ein Programmierer mal verfasst hatte: 
"Goto Statement considered harmful". Gemäss dem was ich in diesem Forum 
gelernt habe, gilt das auch auf MCs ;-) nicht?

Ich bin mir eben auch nicht so sicher, wie zuverlässig das denn auf dem 
STM32 funktioniert. Gibt es bestimmt keine Memory Leaks und so weiter. 
Ist meine sbrk-Implementierung wirklich richtig und alles. Leider ist ja 
dazu nichts dokumentiert und im Netz findet man tausenderlei Varianten 
... aber vielleicht kannst du mir ja Licht ins Dunkel bringen, wie (und 
ob?) ich sbrk implementieren soll, damit ich sicher sein kann, dass es 
zuverlässig funktioniert und der Heap auch wirklich da liegt wo er soll.

Und: wie kommst du darauf, dass mein Stack bei 0x2001FFFF liegt? habe 
ich das irgendwo geschrieben? muss jetzt gleich nochmal meinen Code 
anschauen. 0x2001FFFF wäre wirklich falsch.

@W.S.:
ich gebe dir recht, es sind wirklich nicht so tolle funktionen. Das 
Problem ist halt, dass man halt manchmal einen int oder float in einen 
String umformen möchte, und zwar so, dass er in einem Textfeld z.B. 
rechtsbündig ausgerichtet ist, mit führenden Nullen und solche Spässe. 
Selbergebaute Konvertierer, die man von Projekt zu Projekt wieder neu 
bastelt, sind immer maximal unflexibel und irgendwie immer ein bisschen 
unbefriedigend :-( und wie man float konvertiert, wüsste ich nicht mal.

Gruss

von W.S. (Gast)


Lesenswert?

Tobias Plüss schrieb:
> Das
> Problem ist halt, dass man halt manchmal einen int oder float in einen
> String umformen möchte, und zwar so, dass er in einem Textfeld z.B.
> rechtsbündig ausgerichtet ist, mit führenden Nullen und solche Spässe.

Dann schau dir die Lernbetty und dort speziell die conv.c in der 
BettyBase an. Ich benutze diese Routinen nun schon auf einer ganzen 
Breitseite von Plattformen - von 8 bis 32 Bit (bei ersteren aber ohne 
int64, das wäre m.E. übertrieben). Da sind Spezereien wie bei Bedarf 
führende Nullen usw. mit dabei. Sowas reicht für eigentlich alle Fälle 
in der µC Szene aus.

W.S.

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


Lesenswert?

Tobias Plüss schrieb:

> naja, malloc() auf einem Mikrocontroller?>

Wie ich schon schrieb: es beißt nicht.

> Ich erinnere mich grade an die
> "GOTO" Hasspredigt, die irgend ein Programmierer mal verfasst hatte:
> "Goto Statement considered harmful".

Edsger Dijkstra.  Donald Knuth's Antwort dafür war: "Structured
Programming with GO TO".

Es ist nie sinnvoll, irgendeine Sache einfach nur ihres Namens wegen
zu verteufeln oder deshalb, weil sie missbraucht werden kann.

> Gemäss dem was ich in diesem Forum
> gelernt habe, gilt das auch auf MCs ;-) nicht?

Ja.  Nicht.

> Ich bin mir eben auch nicht so sicher, wie zuverlässig das denn auf dem
> STM32 funktioniert. Gibt es bestimmt keine Memory Leaks und so weiter.

Wo zum Geier™ soll denn ein memory leak herkommen, wenn ein vfprintf
immer wieder einen Speicherblock einer festen Länge anfordert, darauf
was macht, und wieder freigibt?

> Ist meine sbrk-Implementierung wirklich richtig und alles. Leider ist ja
> dazu nichts dokumentiert

Na klar ist das dokumentiert.

Die Newlib ist eine mehr oder minder lieblos zusammengewürfelte
Sammlung aller möglicher C-Bibliotheks-Schnipsel unter der Prämisse,
eben mal keinen GPL-Code reinzuschleppen (sonst hätte man die glibc
nehmen können), da viele Anwender an einer solch zentralen Stelle im
Embedded-Bereich die virulente Eigenschaft der GPL oft nicht haben
möchten.  Die zusammegewürfelten Schnipsel stammen in großen Teilen
vor allem aus älteren BSD-Quellen und setzen daher auf dem
Programmiermodell von Unix auf, d. h. alle zugrundeliegenden Dienste,
die dort durch syscalls erledigt werden, muss man für die Newlib in
der Applikation selbst liefern.

Du kannst dir also ein beliebiges Unix angucken und dort die Doku
von sbrk(2) lesen um zu wissen, wie sich dessen Implementierung
verhalten muss.

> Und: wie kommst du darauf, dass mein Stack bei 0x2001FFFF liegt? habe
> ich das irgendwo geschrieben? muss jetzt gleich nochmal meinen Code
> anschauen. 0x2001FFFF wäre wirklich falsch.

Das war in dem anderen Thread so geschrieben, den du als Lösung des
Problems angedeutet hast.

> @W.S.:
> ich gebe dir recht, es sind wirklich nicht so tolle funktionen.

Lass ihn reden.  Du hast hier keinen ATtiny10, und an irgendeiner
Stelle sollte man sich lieber um seine eigene Anwendung kümmern,
statt den Urschleim selbst zum …zigsten Male zu reimplementieren.
Auf einem Controller dieser Größe ist es müßig, über den Nutzen von
printf() noch zu diskutieren.

: Bearbeitet durch Moderator
von Klaus H. (klummel69)


Lesenswert?

Jörg Wunsch schrieb:
>Die Gleitkommaversion benutzt einen per malloc() allozierten Puffer als
> Zwischenspeicher…

>Wo zum Geier™ soll denn ein memory leak herkommen, wenn ein vfprintf immer
>wieder einen Speicherblock einer festen Länge anfordert, darauf was macht,
>und wieder freigibt?

Kritisch sehe ich bei solchen Implementierungen eher, dass jemand einen 
Scheduler / ein RTOS drum baut und in mehreren Tasks sprintf nutzt (oder 
für ganz Verückte: im Interrupt… alles schon gesehen…) und die selbst 
implementierte sbrk() nicht reentrant ist.

Deine _sbrk(int incr) könnte in diesem Fall den heap_end Wert 
verfälschen, oder?
1
heap_end += incr;

von Tobias P. (hubertus)


Lesenswert?

jup, das würde mich auch interessieten. Was muss noch vorgekehrt werden, 
wenn ein RTOS verwendet wird?

von Konrad S. (maybee)


Lesenswert?

Ein RTOS sollte ein sbrk() mitbringen.

von Tobias P. (hubertus)


Lesenswert?

nein, muss es nicht...

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


Lesenswert?

Konrad S. schrieb:
> Ein RTOS sollte ein sbrk() mitbringen.

Und sich dann wiederum drum kümmern, einen exclusive lock zu bekommen,
sodass immer nur eine Task gleichzeitig einen Syscall bearbeitet.

Das ist ja dann nicht nur sbrk(), auch die anderen Syscall-Primitiven
(write() etc.) haben das gleiche Problem.

von Le X. (lex_91)


Lesenswert?

W.S. schrieb:
> warum du denn überhaupt sowas
> wie printf und Konsorten auf einem µC verwendest.

Ach das übliche Gesülze unsres Lieblings-Trolles.

Ich benutz hier auf den ATmegas den ganzen Sack an Standard-Funktionen, 
printf (sowieso), svnprintf, strtok, strcmp, sscanf und alle möglichen 
Abwandlungen davon, fopen, fwrite...
Schreibe und Lese damit aufs Lcd (jaja, write-only, für die 
Klugscheißer), in Dateien, auf die UART.

Meingott, wieso muss man denn immer wieder Probleme lösen die in den 
70ern schon gelöst wurden? Urschleim triffts ganz gut, danke Jörg Wunsch 
:-)

Spätestens auf nem ARM stellt sich die Frage gar nicht mehr.


@ W.S. deine LernBetty ist toll, kriegst nen Keks.

von Konrad S. (maybee)


Lesenswert?

Jörg Wunsch schrieb:
> Konrad S. schrieb:
>> Ein RTOS sollte ein sbrk() mitbringen.
>
> Und sich dann wiederum drum kümmern, einen exclusive lock zu bekommen,
> sodass immer nur eine Task gleichzeitig einen Syscall bearbeitet.
>
> Das ist ja dann nicht nur sbrk(), auch die anderen Syscall-Primitiven
> (write() etc.) haben das gleiche Problem.

Eben das OS in RTOS. Hab' ich mir so gedacht ...

von Klaus H. (klummel69)


Lesenswert?

Jörg Wunsch schrieb
> Das ist ja dann nicht nur sbrk(), auch die anderen Syscall-Primitiven
>(write() etc.) haben das gleiche Problem.

Korrekt, mir ging es darum hinzuweisen, dass der Einsatz dieser 
Funktionen Effekte haben kann, an die man im embedded Bereich nicht 
gleich denkt.
Klar sollte ein RTOS thread-safe Syscalls haben.

Aber es gibt halt auch nur reine Scheduler Implementierungen, die es 
nicht haben.

Auch der geschilderte Fall eines snprintf Calls im Interrupt gibt es. 
Warum auch nicht, wenn der Prozessor für den Anwendungsfall genug 
Performance hat? Gut wenn man dann daran denkt, dass da ggf. ein 
malloc() Call versteckt sein könnte…

Autor: le x. (lex_91) schrieb
>Spätestens auf nem ARM stellt sich die Frage gar nicht mehr.

Auch ich nutze Standard-Funktionen auf ARM und ATMEGAs, funktioniert 
tadellos, aber ein Fehler durch non-reentrant Funktionen kann halt auch 
jahrelang schlummern bis er zuschlägt.

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.