WinAVR bekannter Linker Fehler?

OP #2027808
Lesenswert?

Hi,

wie ich beim debuggen feststellen musste packt der Linker Variablen, die 
in getrennten Modulen liegen (nur Modul intern) zu einer Variablen 
zusammen wenn sie den gleichen Namen haben.

Auszug der Deklarationen:

in Main.c
volatile uint16_t impwidth_limit;

in impuls.c
volatile uint16_t impwidth_limit  _attribute_ ((section (".noinit")));

wenn man bei einer der beiden Variablen den Namen ändert sieht man bei 
der Angabe des belegten RAM's sofort, das unterschiedliche Namen zu 
2byte Mehrbelegung führen.
Ich habe jetzt nicht ausprobiert, ob das nur in Verbindung mit ".noinit" 
passiert.

Gruß Ingo
OP #2027863
Lesenswert?

Hi,

das verstehe ich nicht, denn das würde ja heißen, das man alle Variablen 
in Modulen als static deklarieren müsste, selbst wenn sie nicht 
exportiert werden um bei zufälligen gleichen Variablen Namen in anderen 
Modulen diesen Effekt zu verhindern.
Sollte das in "C" oder gcc wirklich so gewollt sein, da doch die export 
Funktion ausreichend sein sollte.

Gruß Ingo
#2027880
Lesenswert?

Ingo Stahl schrieb:
> Hi,
>
> das verstehe ich nicht, denn das würde ja heißen, das man alle Variablen
> in Modulen als static deklarieren müsste, selbst wenn sie nicht
> exportiert werden um bei zufälligen gleichen Variablen Namen in anderen
> Modulen diesen Effekt zu verhindern.

Ja, so ist es.

> Sollte das in "C" oder gcc wirklich so gewollt sein, da doch die export
> Funktion ausreichend sein sollte.

Das hat nichts mit GCC zu tun. Das ist in der Sprache 'C' so definiert. 
Eine Funktion oder Schlüsselwort 'export' gibt es in 'C' erstmal nicht. 
Da haben manche Leute ein '#define export extern' gebaut. So wird 
'export' häufig verwendet.
Ich persönlich finde es sehr unglücklich da es verwirrt, wie sich hier 
gerade zeigt.
Persönliche Seite #2027891
Lesenswert?

Genaugenommen kommt hier noch eine lästige GCC-Gewohnheit zum Tragen: 
Werden in zwei Modulen nichtstatische Variablen gleichen Namens 
angelegt, so gibt der Linker keine Fehlermeldung aus, sondern fasst 
alle doppelt definierten Symbole zusammen.
Andere Linker machen das nicht und geben entsprechende Fehlermeldungen 
aus. Dem GCC lässt sich dieses Verhalten mit -fno-common auch 
abgewöhnen.
OP #2027894
Lesenswert?

Hi,

danke für die Antworten.

Es ist also ein Feature, das ich mir Merken muss um nicht noch einmal 
darauf reinzufallen.

Mit export meinte ich übrigens das "extern" Schlüsselwort, da ich das 
mit exportieren einer Variablen nach außen verinnerlicht habe. Daher 
habe ich das "static" als "Ausfuhrstop" wohl nicht mehr im Sinn gehabt.

Gruß Ingo
#2028244
Lesenswert?

Ingo Stahl schrieb:

> mit exportieren einer Variablen nach außen verinnerlicht habe. Daher
> habe ich das "static" als "Ausfuhrstop" wohl nicht mehr im Sinn gehabt.

Du sollst sogar static verwenden, wo du nur kannst.
Denn surch static baust du eine Barriere rund um deine Compilation Unit 
auf. Du sagst dem Compiler explizit: Diese Variable oder diese Funktion 
KANN nicht von ausserhalb referenziert werden. Sie ist ausserhalb nicht 
sichtbar.
Das wiederrum ermöglicht dem Compiler so manche Optimierung, bis hin zu: 
Funktionen können geinlined werden, ohne dass ein klassische aufrufbare 
Implementierung der Funktion existieren muss.
#2028273
Lesenswert?

Ist mir auch schon aufgefallen, daß der GCC stillschweigend 
Mehrfachdefinitionen als eine Variable zusammenfast.

Der Keil C51, zeigt mir bei sowas sofort die rote Karte.

Wenn -fno-common diesen Fehler behebt, sollte man das als default in 
jedes Make mit aufnehmen.


Peter
Gast #2028311
Lesenswert?

Zum Nachlesen hier die Info aus der GCC Doku:
-fno-common
In C code, controls the placement of uninitialized global variables. 
Unix C compilers have traditionally permitted multiple definitions of 
such variables in different compilation units by placing the variables 
in a common block. This is the behavior specified by -fcommon, and is 
the default for GCC on most targets. On the other hand, this behavior is 
not required by ISO C, and on some targets may carry a speed or code 
size penalty on variable references. The -fno-common option specifies 
that the compiler should place uninitialized global variables in the 
data section of the object file, rather than generating them as common 
blocks. This has the effect that if the same variable is declared 
(without extern) in two different compilations, you will get a 
multiple-definition error when you link them. In this case, you must 
compile with -fcommon instead. Compiling with -fno-common is useful on 
targets for which it provides better performance, or if you wish to 
verify that the program will work on other systems which always treat 
uninitialized variable declarations this way.
Moderator Persönliche Seite #2030386
Lesenswert?

Peter Dannegger schrieb:
> Ist mir auch schon aufgefallen, daß der GCC stillschweigend
> Mehrfachdefinitionen als eine Variable zusammenfast.

Historisches Unix-Compiler-Verhalten, ist durch den Standard explizit
gedeckt (als eine Möglichkeit).

Meine Vermutung ist, dass das "Ur-C" das Schlüsselwort "extern" noch
nicht kannte.  Durch die Implementierung globaler Variablen als ein
common block (FORTRAN lässt grüßen ;-) konnte man dann den gleichen
Effekt erreichen.

Das "static" nicht das default-Verhalten einer Deklaration/Definition
ist, ist eigentlich ein Designfehler von C.  Kann man aber nicht mehr
beheben, ohne dass die gesamte C-Welt zusammenbrechen würde.  Man kann
sich nur angewöhnen, prinzipiell überall "static" davor zu schreiben.
Persönliche Seite #2030503
Lesenswert?

Jörg Wunsch schrieb:
> Historisches Unix-Compiler-Verhalten, ist durch den Standard explizit
> gedeckt (als eine Möglichkeit).

Mag ja sein. Aber es spricht nichts dagegen, die Möglichkeit nur auf 
Wunsch und nicht per Default zu aktivieren, denn sie verschleiert 
Programmierfehler, wie die Definition von globalen Variablen in 
Headerdateien.

Zumal diejenigen, die gccavr verwenden, das ziemlich sicher nicht tun, 
um irgendwelche alten K&R-C-Fragmente aus alten Unix-Zeiten zu 
übersetzen.
Moderator Persönliche Seite #2030511
Lesenswert?

Rufus Τ. Firefly schrieb:

> Mag ja sein. Aber es spricht nichts dagegen, die Möglichkeit nur auf
> Wunsch und nicht per Default zu aktivieren, denn sie verschleiert
> Programmierfehler, wie die Definition von globalen Variablen in
> Headerdateien.

Vermutlich muss man nur eine Konfigurationsanweisung umschalten
irgendwo in den Untiefen der AVR-Anpassung.  Zumindest suggeriert
die Doku ja, dass es Plattformen gibt, auf denen -fno-common die
Voreinstellung ist.

Da auf dem AVR dem -fcommon als Default sicher keiner hinterher weinen
wird, bliebe es also nur, diese Stelle zu finden und einen Bugreport
mitsamt Patch zu schreiben und den dann auch durchzuboxen.
Persönliche Seite #2031311
Lesenswert?

Jörg Wunsch schrieb:

> Vermutlich muss man nur eine Konfigurationsanweisung umschalten
> irgendwo in den Untiefen der AVR-Anpassung.  Zumindest suggeriert
> die Doku ja, dass es Plattformen gibt, auf denen -fno-common die
> Voreinstellung ist.
>
> Da auf dem AVR dem -fcommon als Default sicher keiner hinterher weinen
> wird, bliebe es also nur, diese Stelle zu finden und einen Bugreport
> mitsamt Patch zu schreiben und den dann auch durchzuboxen.

In ./gcc/config/avr/avr.c
1
/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
2
 static const struct default_options avr_option_optimization_table[] =
3
 {
4
   { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
5
+  { OPT_LEVELS_ALL, OPT_fcommon, NULL, 1 },
6
   { OPT_LEVELS_NONE, 0, NULL, 0 }
7
 };

Falls fcommon anders abgehandelt wird (keine implizited Erzeugen von 
OPT_fcommon), funktioniert auf jeden Fall in Target Hook 
TARGET_OPTIMIZATION_OPTIONS flag_no_common = 1 zu setzen, ebenfalls in 
avr.c.

Bei beiden Möglichkeiten hat dann ein -fcommon auf Kommandozeile höhere 
Priorität und überschreibt den Default.
#2204505
Lesenswert?

Auch wenn ich mit meiner Bemerkung etwas nacheile:

Common Blocks sind durchaus eine recht brauchbare Einrichtung, wenn man 
z.B. mehrere unabhängige Programm-Module hat, die zur Übersetzungszeit 
die Größe eines gemeinsamen Puffers festlegen sollen.

Man definiert einfach in jedem Modul den Puffer mit der notwendigen 
Größe. Der Linker kombiniert dann alle Versionen zu einer Variablen mit 
der maximalen Größe.

Dieser Trick ist vor allem in hierarchisch aufgebauten Libs sehr 
praktisch.

Dem Problem mit unerwünschten Variablen-Kolisionen im Commonblock 
begegnet man am besten dadurch, daß man öffentlichen Variablen einen 
Modulspezifischen Präfix verpaßt - so haben das schon die Altvorderen im 
Assemblerzeitalter auch gemacht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren