Forum: Mikrocontroller und Digitale Elektronik AVR gcc: Funktionsparameter werden überschrieben


von G T M. (gt_m)


Lesenswert?

Hallo in die Runde,

ich habe hier gerade ein Problem mit einem Atmega88, das ich mir nicht 
erklären kann.

Ich übergebe einer Funktion einen Funktionspointer, der eigentlich 
ausgewertet werden soll. Stattdessen wird aber sein Wert innerhalb der 
Funktion verändert.

Der Code (aufs relevante reduziert):
1
...
2
uint8_t te = timerexist(led_on);
3
if (!te) ...
4
...
5
uint8_t timerexist(const void (*f)(void*)) {
6
  uint8_t current = t_first;
7
  while(current != T_LAST) {
8
    if(&(tasks[current].func.f) == &f)
9
      return 1;
10
...

Beim Blick in den Assembler ist denn auch schnell klar, wo es kaputt 
geht:
1
  uint8_t te = timerexist(led_on);
2
00000A36  LDI R24,0x95    Load immediate 
3
00000A37  LDI R25,0x07    Load immediate 
4
00000A38  RCALL PC-0x0122  Relative call subroutine 
5
     if(!te){
6
00000A39  TST R24    Test for Zero or Minus 
7
...
8
    uint8_t timerexist(const void (*f)(void*)) {
9
00000916  PUSH R28       Push register on stack 
10
00000917  PUSH R29       Push register on stack 
11
00000918  RCALL PC+0x0001  Relative call subroutine 
12
00000919  IN R28,0x3D    In from I/O location 
13
0000091A  IN R29,0x3E    In from I/O location 
14
    uint8_t current = t_first;
15
0000091B  LDS R24,0x0165  Load direct from data space 
16
    while(current != T_LAST) {
17
0000091D  CPI R24,0xFF    Compare with immediate 
18
0000091E  BREQ PC+0x2D    Branch if equal 
19
    if(&(tasks[current].func.f) == &f)
20
0000091F  LDI R25,0x00    Load immediate 
21
00000920  MOVW R20,R24    Copy register pair

Die Adresse von led_on ist in R25 R24 0x0795. In timerexist wird dann 
erst R24 mit t_first überschrieben und R25 auf 0x00 gesetzt.

Aber warum? Sollte der Compiler nicht wissen, dass in R24 und R25 noch 
Daten sind, die benötigt werden?
Im Debugger wird f weiterhin auf function (word address) 
*{registers}@R25 R24 abgebildet, und man kann beim Durchsteppen 
mitverfolgen, wie der Wert kaputt geht. (von 0x0795 über 0x07ab zu 
0x00ab)

Kompiliert wurde mit
1
avr-gcc -funsigned-char -funsigned-bitfields -O3 -fpack-struct -fshort-enums -g2 -DDEBUG -Wall -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"  -mmcu=atmega88

Ich bin gerade echt ein bisschen ratlos, wie man das Problem beheben 
könnte. Ideen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das Problem liegt ziemlich sicher in den Pünktchen oder in = &f anstatt 
in =f.  Du willst ziemlich sicher nicht die Adresse der lokalen 
Variablen f zuweisen.

Und beachte Warnungen!

von Rene H. (Gast)


Lesenswert?

@Johann
Wo siehst du eine Zuweisung? Ich sehe bloss einen Vergleich.

@G T M
Hast du schon mal ohne -O3 kompiliert?

Grüsse,
R.

von Wusel D. (stefanfrings_de)


Lesenswert?

Da in deinem Code keine Zuweisung auf f stattfindet, vermute ich den 
Fehler ganz woanders. Möglicherweise hast Du einen Stack überlauf. Stack 
überlauf ist beinahe immer die Ursache für unerwartete (vor allem nicht 
programmierte) Schreibzugriffe auf Variablen.

Dass der Compiler R25 überschreibt, muss kein Fehler sein. Wenn f später 
nochmal gebraucht wird, kann er ja erneut einen LDI R25,0x07 ausführen.

Deaktiviere mal den Optimizer mit -O0, dann erhälst Du einen weniger 
veränderten Code der eher dem C Quelltext entspricht. Ich wette, dass 
dein Problem dann immer noch besteht, und dann wird eventuell 
offensichtlicher, das die Fehlerursache ein Stack Überlauf ist (oder 
auch nicht).

Ich habe mir angewöhnt, Code stets mit der Option -O0 zu debuggen. Es 
sei denn, das Problem tritt nur bei einer bestimmten Optimierungs-Stufe 
auf.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ok, ist ein Vergleich.  Für den gilt aber das gleiche in grün.

von foobar (Gast)


Lesenswert?

Johann L. schrieb:
> Ok, ist ein Vergleich.  Für den gilt aber das gleiche in grün.

Sehe ich auch so. f ist schon ein Pointer. Davon noch einmal die Adresse 
zu nehmen macht an dieser Stelle so keinen Sinn.  Kann aber auch daran 
liegen, dass zu wenig Kontext vorhanden ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Soll wohl das gemeint sein:
1
if (tasks[current].func.f == f)

von Rene H. (Gast)


Lesenswert?

Ziemlich sicher sogar. Stellt sich nur die Frage ob das den Fehler 
behebt.

Ohne mehr Kontext kann man da nicht helfen.

Grüsse,
René

von Andreas B. (andreas_b77)


Lesenswert?

G T M. schrieb:
> Aber warum? Sollte der Compiler nicht wissen, dass in R24 und R25 noch
> Daten sind, die benötigt werden?

Wie schon bemerkt wurde, verwendest du die Daten im Parameter nicht, 
sondern nimmst nur die Adresse (zumindest angenommen, den Rest der 
Funktion sehen wir ja nicht). Der Compiler weiß also, dass die Daten 
tatsächlich nicht benötigt werden.

Außerdem ist es etwas sinnlos, einen void Rückgabewert const zu machen. 
Der Funktionspointer Parameter sollte vermutlich "void (*const 
f)(void*)" heißen.

von G T M. (gt_m)


Lesenswert?

Erstmal Danke für die zahlreichen Antworten!

Johann L. schrieb:
> Und beachte Warnungen!

Keine Warnungen vorhanden.

Johann L. schrieb:
> Soll wohl das gemeint sein:
> if (tasks[current].func.f == f)
Rene H. schrieb:
> Ziemlich sicher sogar. Stellt sich nur die Frage ob das den Fehler
> behebt.

Hmm.. problematisch, weil der Compiler dann die Adresse hardcodiert und 
gleichzeitig wegoptimiert?

Wenn ich das & weglasse, bekommt der Debugger jedenfalls mit, was los 
ist:

Rene H. schrieb:
> @G T M
> Hast du schon mal ohne -O3 kompiliert?

Der sagt dann nämlich bei -O>0 zu f:
"Optimized away"

Andreas B. schrieb:
> Wie schon bemerkt wurde, verwendest du die Daten im Parameter nicht,
> sondern nimmst nur die Adresse (zumindest angenommen, den Rest der
> Funktion sehen wir ja nicht). Der Compiler weiß also, dass die Daten
> tatsächlich nicht benötigt werden.

Ja, Dein Post hat sich gerade mit meinem Tippen und ausprobieren 
überschnitten.

Aber auch wenn ich nicht die Adresse verwende, sondern die Pointer 
direkt vergleiche, wird das Ganze wegoptimiert, obwohl es für den 
Vergleich benötigt wird.

> Außerdem ist es etwas sinnlos, einen void Rückgabewert const zu machen.
> Der Funktionspointer Parameter sollte vermutlich "void (*const
> f)(void*)" heißen.
Stimmt natürlich. Muss bei der Fehlersuche verrutscht sein. Ändert aber 
nichts am Ergebnis.

Die gesamte funktion sieht (jetzt) so aus:
1
uint8_t timerexist(void (*const f)(void*)) {
2
  uint8_t current = t_first;
3
  while(current != T_LAST) {  
4
    if(tasks[current].func.f == f)
5
      return 1;
6
    current = tasks[current].next;
7
  }
8
  return 0;
9
}

und f wird außer bei -O0 wegoptimiert.
Wenn current != T_LAST ist, läuft die Schleife auch. Zum Vergleich (der 
nicht wegoptimiert wird) herangezogen werden die Register, in denen f 
vor dem Sprung in timerexist zu finden war.

Viele Grüße!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hast du einen Testfall, so daß man das nachvollziehen kann?

Mit deinem Code (zuzüglich stint.h) kommt:
1
foo.c: In function 'timerexist':
2
foo.c:7:21: error: 't_first' undeclared (first use in this function)
3
   uint8_t current = t_first;
4
                     ^
5
foo.c:7:21: note: each undeclared identifier is reported only once for each function it appears in
6
foo.c:8:20: error: 'T_LAST' undeclared (first use in this function)
7
   while(current != T_LAST) {  
8
                    ^
9
foo.c:9:8: error: 'tasks' undeclared (first use in this function)
10
     if(tasks[current].func.f == f)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Folgender Testfall wird richtig übersetzt:
1
#include <stdint.h>
2
3
extern struct
4
{
5
    struct { void (*f)(void*); } func;
6
    uint8_t next;
7
} tasks[];
8
9
extern uint8_t t_first;
10
11
#define T_LAST 10
12
13
uint8_t timerexist (void (*f)(void*))
14
{
15
  uint8_t current = t_first;
16
  while (current != T_LAST)
17
  {  
18
    if (tasks[current].func.f == f)
19
      return 1;
20
    current = tasks[current].next;
21
  }
22
  return 0;
23
}
ergibt mit avr-gcc 4.8 und -Os
1
timerexist:
2
  lds r18,t_first   ;  8  movqi_insn/4  [length = 2]
3
.L2:
4
  cpi r18,lo8(10)   ;  34  *cmpqi/3  [length = 1]
5
  breq .L7   ;  35  branch  [length = 1]
6
  ldi r19,0   ;  63  movqi_insn/1  [length = 1]
7
  movw r30,r18   ;  61  *movhi/1  [length = 1]
8
  lsl r30   ;  69  *ashlhi3_const/2  [length = 2]
9
  rol r31
10
  add r30,r18   ;  17  *addhi3/1  [length = 2]
11
  adc r31,r19
12
  subi r30,lo8(-(tasks))   ;  18  *addhi3/2  [length = 2]
13
  sbci r31,hi8(-(tasks))
14
  ld r18,Z   ;  19  *movhi/3  [length = 2]
15
  ldd r19,Z+1
16
  cp r18,r24   ;  20  *cmphi/3  [length = 2]
17
  cpc r19,r25
18
  breq .L5   ;  21  branch  [length = 1]
19
  ldd r18,Z+2   ;  30  movqi_insn/4  [length = 1]
20
  rjmp .L2   ;  73  jump  [length = 1]
21
.L7:
22
  ldi r24,0   ;  5  movqi_insn/1  [length = 1]
23
  ret   ;  66  return  [length = 1]
24
.L5:
25
  ldi r24,lo8(1)   ;  4  movqi_insn/2  [length = 1]
26
  ret   ;  68  return  [length = 1]

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.