Forum: Compiler & IDEs [avr-gcc] "undefined Reference" bei inline Funktionen


von Higg G. (Gast)


Lesenswert?

Hallo zusammen,

ich versuche gerade mich vom Atmel Studio zu lösen und unter Linux via 
make meine Programme zu übersetzen. Dabei stosse ich auf ein Problem.

Ich habe ein paar Funktionen als inline definiert. Bei dem Aufruf einer 
solchen Funktion schmeisst mir avr-gcc nen Fehler. Beispiel:
1
#include <avr/io.h>
2
3
inline void foo()
4
{
5
   //Irgendwelcher Code
6
}
7
8
int main()
9
{
10
   foo();
11
   return 0;
12
}

Das ganze kompiliere ich mit:
1
avr-gcc main.c -mmcu=atmega32

Daraufhin meldet mir der Compiler:
1
/tmp/cc2pO2mP.o: In function `main':
2
main.c:(.text+0x8): undefined reference to `foo'

Warum?

verwenden tu ich den avr-gcc in Version 5.3.0

Grüße

Higg

von Hans (Gast)


Lesenswert?

Probier es mal mit "-std=c99" oder "-std=gnu99". Aktiviere außerdem mit 
"-Wall" die Warnungen. Vielleicht gibt das einen Hinweis auf das 
Problem.

von Higg G. (Gast)


Lesenswert?

Die Fehlermeldung bleibt leider immer exakt die gleiche.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

foo ist extern. Für den Fall, dass foo nicht geinlint wird(*) musst du 
eine Implementierung für foo zur Verfügung stellen.

Gründe, eine Funktion nicht zu inlinen, können u.a. sein:

• Inlining ist zu teuer

• Optimierung oder Inlining ist deaktiviert

• Es wird die Adresse der Funktion genommen

• Die Funktion nimmt die Adresse lokaler Labels

"inline" gibt es erst seit C99.  GCC akzeptiert "inline" auch bei 
älteren C-Versionen falls nicht im strikt ANSI o.ö. Mode übersetzt wird. 
Dieses "inline" hat aber eine andere Semantik als das "inline" von C99. 
Falls du dieses alte "inline" meinst: Das gibt's in den neuen Versionen 
als gnu_inline.

Falls die Funktion immer geinlint werden soll, dann nimm
1
static inline __attribute__((__always_inline__))

Falls du die alte Semantik von inline willst, dann
1
[static] inline __attribute__((__gnu_inline__))

Und was nie schadet ist Doku lesen:

• http://gcc.gnu.org/onlinedocs/gcc/Inline.html
• http://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bgnu_005finline_007d-function-attribute-3184

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

...und falls du Cross-Module Inlining machen willst, dann geht dass 
natürlich nur mit globalen Optimierungen wie LTO.

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


Lesenswert?

Um die C99-inline-„Falle“ zu umgehen, sollte man sich immer „static
inline“ angewöhnen.

von Higg G. (Gast)


Lesenswert?

Vielen dank soweit. Die zusätzliche static-Angabe scheint zu 
funktionieren. Wenn ich ehrlich bin, habe ich den Grund jedoch nicht 
verstanden. Werde mich mit der Doku die nächsten Tage mal beschäftigen. 
Vielleicht kommt dann die Erleuchtung.

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


Lesenswert?

Higg G. schrieb:
> Wenn ich ehrlich bin, habe ich den Grund jedoch nicht verstanden.

Ohne „static“ ist es implizit „extern“, und dann sieht die C99-Semantik
vor, dass die externen (nicht-inline) Variante zusätzlich zur
inline-Variante bereitzustellen ist.  Die müssen dann auch nicht
unbedingt das gleiche tun. :-/

Allerdings ist die Info des Linkers natürlich ein Hinweis drauf, dass
der Compiler das nicht geinlinet hat.

von Hans (Gast)


Lesenswert?

Das ist ja eine merkwürdige Semantik in C. Das heißt, je nachdem wie der 
Compiler optimiert, kompiliert das Programm (bzw. ist der Linker 
zufrieden) oder nicht? :o

Da finde ich die Bedeutung in C++ viel praktischer: Man darf non-static 
Inline-Funktionen in mehreren Translation-Units definieren, es muss aber 
immer die exakt gleiche Definition sein. Im Klartext, man darf sie in 
ein Header-File schreiben und es überall inkludieren. Falls der Compiler 
die Funktion nicht inlinen kann, kümmert er sich selber drum, dass eine 
Standalone-Version erzeugt wird. Die hat dann sogar im gesamten Programm 
die gleiche Adresse, sie wird also nicht in jeder Translation-Unit 
dupliziert.

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Higg G. schrieb:
>> Wenn ich ehrlich bin, habe ich den Grund jedoch nicht verstanden.
>
> Ohne „static“ ist es implizit „extern“, und dann sieht die C99-Semantik
> vor, dass die externen (nicht-inline) Variante zusätzlich zur
> inline-Variante bereitzustellen ist.

Aber genau das passiert hier nicht. Es ist fast so, als ob der Compiler 
die Definition von foo() sofort komplett verwirft, nur weil es nicht 
inline aufgelöst wird. Eine extern-Version wird gar nicht erstellt, ist 
auch im generierten Assembler-Code nicht zu finden. Schreibt man (bei 
-O0) static davor, generiert er die Funktion, aber natürlich nicht als 
extern.

>  Die müssen dann auch nicht unbedingt das gleiche tun. :-/
>
> Allerdings ist die Info des Linkers natürlich ein Hinweis drauf, dass
> der Compiler das nicht geinlinet hat.

Warum generiert er denn nicht die nicht-inline-Version?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

1
§6.7.4 Function specifiers
2
3
5 A function declared with an inline function specifier is an inline
4
function. The function specifier may appear more than once; the behavior
5
is the same as if it appeared only once. Making a function an inline
6
function suggests that calls to the function be as fast as possible.
7
The extent to which such suggestions are effective is implementation-defined.
8
9
6 Any function with internal linkage can be an inline function. For a
10
function with external linkage, the following restrictions apply: If a
11
function is declared with an inline function specifier, then it shall also
12
be defined in the same translation unit. If all of the file scope
13
declarations for a function in a translation unit include the inline
14
function specifier without extern, then the definition in that translation
15
unit is an inline definition. An inline definition does not provide an
16
external definition for the function, and does not forbid an external
17
definition in another translation unit. An inline definition provides an
18
alternative to an external definition, which a translator may use to
19
implement any call to the function in the same translation unit.
20
It is unspecified whether a call to the function uses the inline definition
21
or the external definition.

1) Mit extern oder static ist die Semantik mit und ohne inline gleich.

2) Ob für einen Aufruf eine inline "Kopie" eingefügt wird hängt vom
   Compiler ab, z.B. von den Kosten oder dem Optimierungsgrad.
   "inline" ist eine höfliche Bitte and den Compiler.

3) Wenn weder extern noch static angegeben ist, hat die Funktion
   (bzw. der entsprechende Identifier) "internal" Linkage, und
   "An inline definition does not provide an external definition
    for the function."

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
> 1) Mit extern oder static ist die Semantik mit und ohne inline gleich.
>    "inline" ist eine höfliche Bitte and den Compiler.

Dachte ich auch immer.

> 2) Ob für einen Aufruf eine inline "Kopie" eingefügt wird hängt vom
>    Compiler ab, z.B. von den Kosten oder dem Optimierungsgrad.

Das ist klar. Es ging aber darum, ob der Compiler eine 
nicht-Inline-Version generiert. Das tut er offenbar auch dann nicht, 
wenn er sie eigentlich braucht.

> 3) Wenn weder extern noch static angegeben ist, hat die Funktion
>    (bzw. der entsprechende Identifier) "internal" Linkage, und
>    "An inline definition does not provide an external definition
>     for the function."

Es ging eigentlich nicht um externe Definitionen, sondern um folgenden 
Code:

Higg G. schrieb:
1
#include <avr/io.h>
2
3
inline void foo()
4
{
5
   //Irgendwelcher Code
6
}
7
8
int main()
9
{
10
   foo();
11
   return 0;
12
}

Und das steht alles in einer Datei. Wenn Optimierung ausgeschaltet 
ist, bringt gcc im C99-Modus beim Aufruf von foo() die Fehlermeldung, 
dass diese Funktion nicht definiert sei. Wie schon gesagt wird keine 
non-Inline-Version generiert.
Compiliert mit
1
avr-gcc higg.c -mmcu=atmega32 -std=c99 -O0 -c -S -o-
wird dafür bei mir folgender Code erzeugt:
1
        .file   "higg.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
        .text
8
.global main
9
        .type   main, @function
10
main:
11
        push r28
12
        push r29
13
        in r28,__SP_L__
14
        in r29,__SP_H__
15
/* prologue: function */
16
/* frame size = 0 */
17
/* stack size = 2 */
18
.L__stack_usage = 2
19
        call foo
20
        ldi r24,0
21
        ldi r25,0
22
/* epilogue start */
23
        pop r29
24
        pop r28
25
        ret
26
        .size   main, .-main
27
        .ident  "GCC: (GNU) 4.8.1"
Wie man sieht, wird foo() zwar aufgerufen, aber es wird keinerlei Code 
für die Implementation generiert.
Nur mit eingeschalteten Optimierungen, also wenn die Funktion 
tatsächlich inline aufgleöst wird, läßt sich die Datei compilieren. 
Alternativ funktioniert es auch, wenn man die Funktion "static inline" 
definiert. Dann wird nämlich lustigerweise eine non-Inline-Version 
generiert, wenn nötig. Folgender Code kommt dabei raus (übersetzt mit 
der gleichen gcc-Kommandozeile wie oben):
1
        .file   "higg.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
        .text
8
        .type   foo, @function
9
foo:
10
        push r28
11
        push r29
12
        in r28,__SP_L__
13
        in r29,__SP_H__
14
/* prologue: function */
15
/* frame size = 0 */
16
/* stack size = 2 */
17
.L__stack_usage = 2
18
/* epilogue start */
19
        pop r29
20
        pop r28
21
        ret
22
        .size   foo, .-foo
23
.global main
24
        .type   main, @function
25
main:
26
        push r28
27
        push r29
28
        in r28,__SP_L__
29
        in r29,__SP_H__
30
/* prologue: function */
31
/* frame size = 0 */
32
/* stack size = 2 */
33
.L__stack_usage = 2
34
        call foo
35
        ldi r24,0
36
        ldi r25,0
37
/* epilogue start */
38
        pop r29
39
        pop r28
40
        ret
41
        .size   main, .-main
42
        .ident  "GCC: (GNU) 4.8.1"

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Es ging eigentlich nicht um externe Definitionen, sondern um folgenden
> Code:

Den Code kenn ich wohl. Und weil es KEINE extern ist und auch KEINE 
static, wird eben KEINE Implementierung erzeugt.  Der Aufruf

>    foo();
>         call foo

Hat deshalb external Linkage, und weil offenbar kein solches Symbol 
definiert wurde, kann der Linker es nicht auflösen.

Oder wo siehst du eine extern oder static?

> Wie man sieht, wird foo() zwar aufgerufen, aber es wird keinerlei Code
> für die Implementation generiert.

Siehe oben:

> 3) Wenn weder extern noch static angegeben ist, hat die Funktion
>    (bzw. der entsprechende Identifier) "internal" Linkage, und
>    "An inline definition does not provide an external definition
>     for the function."

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
>> Wie man sieht, wird foo() zwar aufgerufen, aber es wird keinerlei Code
>> für die Implementation generiert.
>
> Siehe oben:
>
>> 3) Wenn weder extern noch static angegeben ist, hat die Funktion
>>    (bzw. der entsprechende Identifier) "internal" Linkage, und
>>    "An inline definition does not provide an external definition
>>     for the function."

Die Funktion hat in diesem Fall aber gar keine Linkage, da vom Compiler 
überhaupt kein Code dafür erzeugt wurde, weder intern noch extern. Nur 
wenn ich static angebe, wird die Funktion tatsächlich mit interner 
Linkage erzeugt. Dass inline dazu führt, dass die Funktion keine externe 
Definition mehr hat, ist schon klar, aber intern sollte sie doch 
trotzdem noch da sein.
Und als Schlussfolgerung ergibt dann das für mich auch keinen Sinn:

Johann L. schrieb:
> Der Aufruf
>
>>    foo();
>>         call foo
>
> Hat deshalb external Linkage, und weil offenbar kein solches Symbol
> definiert wurde, kann der Linker es nicht auflösen.

Weil die Funktion inline definiert ist, sucht der Compiler beim Aufruf 
nicht intern nach ihr, sondern extern? Und wenn ich Optimierungen 
einschalte, findet er sie dann doch auf einmal intern?

von Hans (Gast)


Lesenswert?

Rolf M. schrieb:
> Das ist klar. Es ging aber darum, ob der Compiler eine
> nicht-Inline-Version generiert. Das tut er offenbar auch dann nicht,
> wenn er sie eigentlich braucht.

Der Punkt ist wohl, dass der Compiler beim Übersetzen dieser C-Datei 
davon ausgeht, dass es in einer anderen Übersetzungseinheit eine 
Non-Inline-Version gibt, die er aufrufen kann. Damit spart er sich das 
Duplizieren der Non-Inline-Version in jeder Übersetzungseinheit.

Diese Non-Inline-Version muss man als Programmierer selber in genau 
einer Übersetzungseinheit definieren, wie bei jeder anderen globalen 
Funktion oder Variable auch. Bei C++ können Compiler und Linker das 
dagegen selber hinbekommen, dass im fertigen Programm nur genau eine 
Non-Inline-Version bleibt.

Nur wenn man static davor schreibt, sagt man dem Compiler, dass die 
Duplizierung der Non-Inline-Funktion in jeder Übersetzungseinheit 
gewünscht ist, sofern nötig.

von Rolf M. (rmagnus)


Lesenswert?

Mir ist auch grad klar geworden, dass das hier wohl der wesentliche 
Punkt ist:

Johann L. schrieb:
> It is unspecified whether a call to the function uses the inline
> definition or the external definition.

Der Compiler darf sich also dazu entscheiden, statt der schon in der 
selben Übersetzungseinheit definierten Funktion alternativ nach einer 
externen Definition zu suchen. Das ist nicht unbedingt intuitiv: Ich 
definiere eine Funktion, rufe sie direkt danach auf, und der Compiler 
sagt, sie sei nicht definiert, weil er ganz wo anders danach sucht...

Hans schrieb:
> Der Punkt ist wohl, dass der Compiler beim Übersetzen dieser C-Datei
> davon ausgeht, dass es in einer anderen Übersetzungseinheit eine
> Non-Inline-Version gibt, die er aufrufen kann. Damit spart er sich das
> Duplizieren der Non-Inline-Version in jeder Übersetzungseinheit.

Das könnte sein. So würde es am ehesten Sinn ergeben.

> Diese Non-Inline-Version muss man als Programmierer selber in genau
> einer Übersetzungseinheit definieren, wie bei jeder anderen globalen
> Funktion oder Variable auch.

Das heißt aber, daß man die Funktion in diesem Fall wirklich zweimal 
hinschreiben müßte, einmal für den Fall, dass sie inline aufgelöst wird 
und einmal als extern-Funktion für den anderen Fall, damit der Compiler 
sich dann raussuchen kann, welche von den beiden er tatsächlich nimmt.

> Bei C++ können Compiler und Linker das dagegen selber hinbekommen, dass
> im fertigen Programm nur genau eine Non-Inline-Version bleibt.

Ja. Ich dachte bislang, das sei in C auch so, aber dieser Automatismus 
fehlt hier wohl.

> Nur wenn man static davor schreibt, sagt man dem Compiler, dass die
> Duplizierung der Non-Inline-Funktion in jeder Übersetzungseinheit
> gewünscht ist, sofern nötig.

Im Zitat aus der Norm oben steht aber nichts davon, dass static da 
irgendwas ändert. Die Funktion hat ja ohne static schon internal 
linkage. Also sollte das durch das static doch an sich gleich bleiben. 
Dürfte der Compiler also theoretisch immer noch nach einer externen 
Definition suchen?

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Wenn ich es richtig verstehe, hat eine Funktion mit static grundsätzlich 
internal Linkage und eine Funktion ohne static grundsätzlich external 
Linkage.

Für internal Linkage (d.h. mit static) sind die Regeln einfach:
1
Any function with internal linkage can be an inline function.

Nur für external Linkage (d.h. ohne static) ist es komplizierter:
1
For a function with external linkage, the following restrictions apply:

Da gilt: Eine "inline definition" (d.h. mit inline aber ohne extern) ist 
keine "external definition". Es darf aber eine externe Definition in 
einer anderen Übersetzungseinheit geben.
1
If all of the file scope
2
declarations for a function in a translation unit include the inline
3
function specifier without extern, then the definition in that translation
4
unit is an inline definition. An inline definition does not provide an
5
external definition for the function, and does not forbid an external
6
definition in another translation unit.

Das heißt, es wird hier keine Funktion mit external Linkage definiert, 
sondern nur deklariert. Erst mit "extern" wird es eine externe 
Definition statt einer Deklaration.

von Rolf M. (rmagnus)


Lesenswert?

Hans schrieb:
> Das heißt, es wird hier keine Funktion mit external Linkage definiert,
> sondern nur deklariert. Erst mit "extern" wird es eine externe
> Definition statt einer Deklaration.

Hmm, ok. Die Definition hat dann wohl tatsächlich keine Linkage, wenn 
weder static, noch extern angegeben ist. Das wird ja auch bestärkt durch 
das:

> An inline definition provides an alternative to an external definition,
> which a translator may use to implement any call to the function in the
> same translation unit.

Die inline-definierte Funktion (ohne static oder extern) hat also gar 
keine normalen Linkage-Regeln, sondern ist nur ein optionaler Ersatz, 
für den sich der Compiler statt der "eigentlichen" extern-definierten 
Funktion entscheiden kann. Damit ist also nicht - wie ich bisher dachte 
- nur das Schlüsselwort inline selbst ein optionaler Hinweis an den 
Compiler, sondern die ganze Funktion, die damit definiert ist.
Das passt dann auch dazu, dass sich das Verhalten mit static ändert, 
denn dann bekommt die Funktion tatsächlich internal linkage und kann 
somit immer vom Compiler gefunden werden.

Lustigerweise lässt der Compiler zwar zu, die extern-Funktion in einer 
anderen Übersetzungseinheit zu definieren, aber nicht in der selben. Das 
führt dazu, dass zwar obiger Code mit dem Fehler abbricht, dass die 
Funktion nicht defniert ist, aber der folgende dafür mit dem Fehler, 
dass sie zweimal definiert ist (was ja an sich auch stimmt):
1
#include <stdio.h>
2
3
void foo(void)
4
{
5
    printf("Keine inline-Funktion\n");
6
}
7
8
inline void foo(void)
9
{
10
    printf("inline-Funktion\n");
11
}
12
13
int main()
14
{
15
   foo();
16
   return 0;
17
}

Ich gebe dir also voll und ganz recht: Die Art, wie es in C++ 
spezifiziert ist, ist wesentlich sinnvoller und einleuchtender.

: Bearbeitet durch User
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.