Forum: Compiler & IDEs undefined references bei static initialization order fiasco


von Joe D. (kosmonaut_pirx)


Lesenswert?

hallo,

in meinem code bin ich wohl in das "static initialization order
fiasco"
gestolpert. ganz gemeine sache dies :( ok, ich weiß immer noch nicht
genau, warum, aber sei's erstmal drum. die lösungen dazu in der
c++-faq sehen dazu vor, eine statische variable in einer function zu
halten.

soweit so gut. nun beschwert sich aber der avr-c++ mit undefinierten
referenzen auf "__cxa_guard_acquire" bzw. "__cxa_guard_release". im
moment habe ich diese erst einmal leer definiert. ist das korrekt so?
oder muss dort etwas getan werden, wenn ja, was? falls noch zeit ist:
was bedeuten diese funktionen, im netz wird man da nicht gerade mit
info's überhäuft.

vielen dank, schönen sonntag,
bye kosmo

von A.K. (Gast)


Lesenswert?

Ist zwar sicherlich der falsche Prozessor, aber darauf kommt's nicht
an: http://www.codesourcery.com/cxx-abi/abi.html, Sektion 3.3.2.

(google groups, hit #2)

von Joe D. (kosmonaut_pirx)


Lesenswert?

oha.
den link habe ich nicht gefunden, sorry.
danke vielmals.

von Joe D. (kosmonaut_pirx)


Lesenswert?

hm, das problem scheint wo anders zu liegen.

wird jetzt OT: wie legt man globale objekte in einem programm in der
bootloader-area an?
bisher habe ich immer

static Objecttype __object;

und gut ist. im normalen flash funktioniert das auch, aber seit ich in
der bootloader-area arbeite, funktioniert nichts mehr. verschieben
tue ich den ganzen code mittels --section-start=.text=0xXXXXX

hat jemand einen vorschlag und kann mir helfen? mir gehen nach mehreren
stunden leider langsam die ideen aus :(

thx!

von Joe D. (kosmonaut_pirx)


Lesenswert?

einfaches beispiel:

extern "C" {
  #include <avr/io.h>
}

class A {
 public:
  A(){
  /* Set the baud rate */
  UBRR0H = (unsigned char) (51>>8);
  UBRR0L = (unsigned char) 51;
  /* Enable UART receiver and transmitter */
  UCSR0B = ( ( 1 << RXEN0 ) | ( 1 << TXEN0 ) );
  /* Set frame format: 8N1 */
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
  /* send .. something */
  while ( !(UCSR0A & (1<<UDRE0)) );
  UDR0 = 'D';
  while ( !(UCSR0A & (1<<UDRE0)) );
  UDR0 = '\r';
  }
};



mache ich

static A __a;

und in der main nichts, passiert auch nichts, die kiste hängt.

kein globales, sondern einlokales objekt in der main ala

int
main(int argc, char** args)
{
 A __a;
 return 0;
}

und es funktioniert :(

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


Lesenswert?

Bitte benutze keine Bezeichner, die mit zwei Unterstrichen
beginnen.  Das wird zwar an deinem Problem jetzt nichts ändern,
aber diese Bezeichner sind für Compiler und Bibliothek
reserviert.

> extern "C" {
>   #include <avr/io.h>
> }

Erstens steht auch bei C++ das # immer in Spalte 1, zweitens
sollte das extern "C" nicht nötig sein.  Wenn doch, dann schreibe
bitte einen detaillierten Bugreport.  Alle avr-libc-Headerdateien
sollte mittlerweile C++-sicher sein.

von kosmonaut pirx (Gast)


Lesenswert?

keine zwei unterstriche, ok.
include in erster spalte, ok.

beim "extern" richte ich mich nach der avr-libc- doku. da steht zwar
drin, das wird behoben (in der art jdf.), aber gut, nun weiß ich
bescheid.

bitte den anderen thread dann einfach ignorieren, hat sich zeitlich
leider überschnitten.
bug-report mache ich nach einholen einer weiteren meinung.

danke.

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


Lesenswert?

> beim "extern" richte ich mich nach der avr-libc- doku. da steht
zwar
> drin, das wird behoben (in der art jdf.), aber gut, nun weiß ich
> bescheid.

Hmm, das müsste man wohl mal überarbeiten...

Bezüglich des Präprozessors:

#  include ...

ist OK.  Nur das # muss ganz vorn stehen (so will's der Standard).

von Joe D. (kosmonaut_pirx)


Lesenswert?

hallo,
ich habe nun das obige beispiel noch einmal vereinfacht, indem statt
usart-ausgabe verschiedene pins gestzt werden. im detail so:

#include <avr/io.h>
class A {
 public:
  A(){
    PORTE |= (1 << 7);
  }
};

A test_a;

int
main(int argc, char** args)
{
  PORTE |= (1 << 2);
  while(1);
  return 0;
}

auf Port E kann ich an dem Board nach außen geführte Pins auf High
setzen.
das problem ist unverändert. im bootloader ausgeführt, bleiben beide
Pins (durch 7 bzw. 2 angesprochen) auf null.
meine konfiguration: at90can128, fusebits auf D8, text-section
entsprechend nach 1E000 verschoben. gcc version 4.1.1.

@Jörg: was würde Dir noch für den bug-report fehlen? habe derartiges
noch nicht so oft getan.

von Karsten Brandt (Gast)


Lesenswert?

Hallo Joe,

ich hatte auch schon einmal ein ähnliches Problem.
Leider kann ich mich jetzt nicht mehr an die Details erinnern,
aber soweit ich weiß, war es bei mir ein Fehler im Makefile.

Wenn Du willst, kannst Du Dein Makefile mal hier reinstellen.
Vielleicht erinnere ich mich wieder an Details, wenn ich Dein Makefile
einsehe.

Karsten

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


Lesenswert?

> im bootloader ausgeführt, bleiben beide
> Pins (durch 7 bzw. 2 angesprochen) auf null.

Was erst einmal nicht verwundert, da du DDRE nicht auf Ausgang
setzt. ;-)

Im Ernst: ich würde das gern mal selbst nachvollziehen.  Schreib
mir bitte nochmal die genauen Compiler-Kommandozeilen auf.
Zumindest ein kurzer Blick zeigt mir erst einmal keine
Auffälligkeiten, der statische Konstruktor ist auch im Bootloader-
Fall vorhanden.  Ob er wirklich gerufen wird, ist im Disassembler
schwer zu erkennen.

von Joe D. (kosmonaut_pirx)


Angehängte Dateien:

Lesenswert?

jörg, mit dem DDRE hast du wie immer recht :)

anbei das Makefile, falls notwendig.

die compileraufrufe im einzelnen:

/localapp/cross-gcc/avr/bin/avr-c++ -c -mmcu=at90can128 -I. -D
GCC_MEGA_AVR -I. -I../../Source/include -I../Common/include -g -Os
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall
-Wextra -Wshadow -Wpointer-arith -Wcast-align -Wsign-compare
-Waggregate-return -Wunused -Wa,-adhlns=a_test.lst  -fno-exceptions
a_test.cpp -o a_test.o

/localapp/cross-gcc/avr/bin/avr-c++ -mmcu=at90can128 -I. -D
GCC_MEGA_AVR -I. -I../../Source/include -I../Common/include -g -Os
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall
-Wextra -Wshadow -Wpointer-arith -Wcast-align -Wsign-compare
-Waggregate-return -Wunused -Wa,-adhlns=a_test.o  -fno-exceptions
a_test.o    --output a_test.elf
-Wl,-Map=a_test.map,--cref,--section-start=.text=0x1E000

/localapp/cross-gcc/avr/bin/avr-objcopy -O ihex -R .eeprom a_test.elf
a_test.hex

das wars (das eeprom-geplapper des makefiles spare ich aus, weil nicht
gebraucht, ebenso die warnungen)

nach dem hinweis von karsten werde ich noch ein wenig mit den
compiler-options probieren.

von Joe D. (kosmonaut_pirx)


Lesenswert?

ich kann es etwas weiter eingrenzen.

die meisten compiler-options raus
-c -mmcu=at90can128 -I. -I. -g -O0 -Wall -Wextra -Wshadow
-Wpointer-arith -Wcast-align -Wsign-compare -Waggregate-return -Wunused
 -fno-exceptions  a_test.cpp -o a_test.o

den linker nur noch
-Wl,-Map=a_test.map,--cref,--section-start=.text=0x1F000
tun und ende.

wie schon zu sehen, auch mal eine andere bootloader-size ausprobiert,
man weiß ja nie.

und im Konstruktor der globalen Klasse eine while-true angefügt, um ein
mögliches reset und löschen der Pins zu verhindern.

leider alles ohne veränderung bisher.

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


Lesenswert?

Ich habe das via Simulation und Einzelschritt mal runterbrechen
können.

Das Problem ist im GCC bzw. in dessen libgcc.S.  Damit kannst du
den Bugreport für den GCC schreiben (http://gcc.gnu.org/ , dort
zum Bugzilla weitergehen).  Ich würde dir empfehlen, dich bei
avr-gcc-list at nongnu.org anzumelden und das dort ebenfalls noch
einmal zum Besten zu geben.  Wir haben jetzt ja fast einen C++-
Maintainer ;-), Rolf Magnus (auch hier gelegentlich aktiv) wollte
sich dieser Dinge annehmen.

Folgendes Stück aus libgcc.S wird zum Verhängnis (du darfst das
im Bugreport zitieren):

#ifdef L_ctors
        .section .init6,"ax",@progbits
        .global __do_global_ctors
__do_global_ctors:
        ldi     r17, hi8(__ctors_start)
        ldi     r28, lo8(__ctors_end)
        ldi     r29, hi8(__ctors_end)
        rjmp    .do_global_ctors_start
.do_global_ctors_loop:
        sbiw    r28, 2
        mov_h   r31, r29
        mov_l   r30, r28
        XCALL   _tablejump_
.do_global_ctors_start:
        cpi     r28, lo8(__ctors_start)
        cpc     r29, r17
        brne    .do_global_ctors_loop
#endif /* L_ctors */

Die Initialisierung von r28/r29 funktioniert nur für Daten, die
sich unterhalb 64 KiB befinden, da der 16 bits überschreitende
Teil der Adressen (__ctors_start, __ctors_end) nicht beachtet
wird.  Damit werden r30/r31 nicht mit den eigentlichen Daten
für den Konstruktor geladen sondern mit Daten, die 64 KiB weiter
unten stehen, also letztlich mit 0xffff.  Der Code bei
_tablejump_ springt dann die Adresse an, die von dieser Stelle
geladen wird...  Damit ist das Fiasko komplett.

Fazit: C++ im Bootloader ist derzeit nur für AVRs <= 64 KiB ROM
möglich, wenn statische Konstruktoren oder Destruktoren im Spiel
sind.

von Joe D. (kosmonaut_pirx)


Lesenswert?

ok, das werde ich machen.

Vielen Dank!

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.