Hallo, ich schreibe ein etwas großeres Programm für den AVR32. Der compiliert mit dem GCC für diese Architektur. Ich habe vor kurzem das Projekt geteilt, sodass das Compilieren nicht mehr so ewig dauert. Fertige Module habe ich in statische Libs ausgelagert. Nun habe ich folgendes Problem: In den statischen Libs befinden sich globale Klassen. Auf wundersame wiese werden deren Konstruktoren vor der Main nicht mehr aufgerufen. Woran kann sowas liegen? Ich habe mal eine globale Klasse die mit "extern" im Header deklariert ist, in die Main.cpp reinkopiert sodass nur noch die -extern-Deklaration in der ausgelagerten Headerdatei ist. Die Deklaration ohne extern ist in der Main.cpp. Wird die globale Klasse nun im Hauptprogram "definiert", anstatt in einer Datei aus einer static Lib, dann wird der Konstruktor normal aufgerufen. Was genau haut mir das auslagern kaput? Ich hoffe auf fachkundige Nicht-Spam-Antworten :-) MFG
Hi, hab mal etwas gegrübelt.... Normaler weise wird im Startup-Code des MC die Daten von Klassen ins RAM kopiert. Danach erfolgt der Aufruf der Konstruktoren.
1 | /* Call constructors */
|
2 | ldr r0, =__ctors_start__ <<-- diese Sectionen sind wichtig |
3 | ldr r1, =__ctors_end__ <<-- diese Sectionen sind wichtig |
4 | ctor_loop: |
5 | cmp r0, r1 |
6 | beq ctor_end |
7 | ldr r2, [r0], #+4 |
8 | stmfd sp!, {r0-r1} |
9 | mov lr, pc |
10 | #ifdef __ARM_ARCH_3__
|
11 | mov pc, r2 |
12 | #else
|
13 | bx r2 |
14 | #endif
|
15 | ldmfd sp!, {r0-r1} |
16 | b ctor_loop |
17 | ctor_end: |
Kann es nun bei dir möglich sein, dass die Daten von der Lib in einer anderen Section gelandet sind und so nicht mehr ausgeführt werden??? Ich kann dir leider nicht sagen, wo man da schauen könnte. :-( (Aufbau der Lib (MAP), Linker .....) ein Versuch fällt mir ein: Programm einmal mit Lib und einmal ohne Lib erstellen und dann die Grösse von der Section berechnen, sollte sie gleich sein, sind deine Daten von der Lib woanders gelandet! (_ctors_end__ - __ctors_start_ = size) Vielleicht bringt dich das auf eine Idee oder ein anderer hier im Forum kann noch mehr dazu sagen. Stephan
Zwei Sachen fallen mir da ein: Wird mit -nostdlib oder Linkerscript gelinkt, muss meistens trotzdem die libgcc.a mit -lgcc reingelinkt werden. Die kümmert sich auch um Konstruktoren. Wir wissen ja nicht um welche Klassen es sich handelt, aber wenn es um so etwas wie als globale Variablen deklarierte Objekte geht, die sich durch den Konstruktor selber irgendwo einklinken aber sonst nicht direkt referenziert werden, dann erklärt sich das auch. Aus einer statischen Library werden nur die Objektdateien reingelinkt, die auch tatsächlich Referenzen auflösen.
Danke erstmal für euren Einsatz.
Die Sections sind ok. Soweit hab ich am Linkerfile nichts geändert und
außerdem sollte dann auch das Hauptprogramm keine Konstruktoren
aufgerufen bekommen.
Soweit ich sehe, hat Andreas B. recht. Es ist das leidige Problem, dass
>Aus einer statischen Library werden nur die Objektdateien reingelinkt, die >auch
tatsächlich Referenzen auflösen.
Da Konstruktoren nie wirklich aufgerufen werden (aus dem Code), werden
sie nicht mit in die Lib reingenommen. Das nervt.
Es gibt das Linker flag --whole-archive, aber dann kommen wilde fehler,
da ja jede Static lib für sich selbst diverse Grundfunktionen
implementiert. Irgendwie ist das komisch.
Fehlermeldungen sind alle wie:
In function `__emutls_register_common': multiple definition of
`__emutls_register_common'
n function `__emutls_get_address': multiple definition of
`__emutls_get_address'
blahh usw.
ICh verstehe den Grund der Fehlermeldungen. Aber ich sehe dann keinen
Ausweg mehr, wie ich das Konstruktorproblem lößen soll.
Es muss dafür ja eine schöne Lösung geben.
> Da Konstruktoren nie wirklich aufgerufen werden (aus dem Code), werden > sie nicht mit in die Lib reingenommen. Das nervt. Nur um es noch mal zu präzisieren: Wenn die Objektdatei aus einer statischen Lib irgendeine Referenz auflöst wird sie reingelinkt und wenn sie gelinkt wird, werden auch sonstige Konstruktoren in derselben Datei aufgerufen. Aber insgesamt ist wohl das Problem, dass du statische Libs überhaupt verwendest. Libs sind dazu da, um Code für andere Programme zur Verfügung zu stellen, aus diesem Grund wird auch das ausgelassen was nicht referenziert wird. Die Compile-Zeit bei größeren Projekten zu reduzieren ist die Aufgabe von make, der nur die Dateien übersetzt, bei denen es nötig ist. Wenn das nicht funktioniert, sollte das Makefile korrigiert werden anstatt auf Libs auszuweichen.
Du hast schon recht, die CompileZeit ist bei geringen veränderungen nicht lang. Aber ändere mal die Optimierungsstufe um zu tersten dass alles bei =O0, O1, O2, O3 und Os geauso funktioniert. Da sitzte dann da und sagst dir: Ahhhja. Schade dass ich kein Fernsehr habe ;-) Und dazu kommt auch noch die Linkzeit, die aber JEDES mal fällig ist. und für 120 .o Dateien rattert mein Rechner schon ganz schön. Das geht mit den Libs schon wesentlich schneller. Die einzelnen .o dateien in den Libs.. hmmh. Problem: die Klassen werden praktisch über ihren Konstruktor an das GUI system angemeldet, dass dann nur noch Funktionspointer und Namen (zur Laufzeit) kennt. Wenn eine GUI Komponente einen Handler hat, wird dann in der Liste der registrierten Funktionen gesucht und der Funktionspointer zugewiesen. Nur so werden die Klassenfunktionen zum schluss aufgerufen. Die Anmeldung der Klassenfunktionen passiert relativ automatisiert über Vererbungsmechanismen und einem kleinen Macro. So. Nun wird der anmeldende Konstruktor aber nicht ausgeführt, die Funktionen werden nicht registriert. Dadurch wiederrum ist es sinnlos für den Linker die .o noch einzubinden. Teufelskreis. So ein Dreck. Das is ja schon fast Bug-Haftes Verhalten des Linkers. Sinnlose funktionen zu schreiben, nur um jede Datei mit sicherheit einzubinden ist einfach keine sinnvolle Lösung. Es kann doch nicht sein, dass aufgetrennter Code sich anders verhält als Code aus einer einzelnen Compilierung.
> Und dazu kommt auch noch die Linkzeit, die aber JEDES mal fällig ist. > und für 120 .o Dateien rattert mein Rechner schon ganz schön. Das geht > mit den Libs schon wesentlich schneller. Vielleicht weil das Dateisystem fragmentiert ist? Eine statische Lib mit den GNU binutils ist ja nichts anderes als alle Objekt-Dateien in eine Archiv-Datei gepackt. Rein von der Linkerei (Anzahl der .o) bleibt sichs also gleich. > Sinnlose funktionen zu schreiben, nur um jede Datei mit sicherheit > einzubinden ist einfach keine sinnvolle Lösung. Nö. Um eine Datei mit Sicherheit einzubinden, muss man sie nur beim Linken angeben. > Es kann doch nicht sein, dass aufgetrennter Code sich anders verhält als > Code aus einer einzelnen Compilierung. Tut er auch nicht. Libraries sind aber was anderes als aufgetrennter Code. Aber es gibt noch eine Möglichkeit: Man kann mehrere .o zu einer größeren .o zusammenlinken, vielleicht hilft das. Option -r beim GNU ld.
1 | ld --help | grep -i constr |
2 | -Ur Build global constructor/destructor tables |
3 | --warn-constructors Warn if global constructors/destructors are seen |
Das sind binutils-2.19 Was die Build-Zeit angeht, hilft evtl. ne Übersetzung mit gcc -pipe ... Ebenso make -j<n> Zudem muss nach einem build für O0, O1, ... kein clean gemacht werden, wenn mehrer Projekte angelegt sind die die gleiche Quelle verwenden und in unterschiedliche Verzeichnisse generieren --> build parallelisierbar, overhead durch clean fällt weg, besser diff'bar
Johann L. schrieb: > ld --help | grep -i constr > -Ur Build global constructor/destructor tables > --warn-constructors Warn if global constructors/destructors are seen Vor globalen Konstruktoren mit --warn-constructors zu warnen wird nicht helfen, wenn er globale Konstruktoren mit Absicht einsetzt. Aber trotzdem gut, dass du das geschrieben hast. Um aus anderen Objekten wieder ein linkbares Objekt zu linken, muss man die Option -Ur statt -r für C++ angeben. Zumindest, wenn man nicht in mehr als zwei Stufen linkt:
1 | `-Ur' |
2 | For anything other than C++ programs, this option is equivalent to |
3 | `-r': it generates relocatable output--i.e., an output file that |
4 | can in turn serve as input to `ld'. When linking C++ programs, |
5 | `-Ur' _does_ resolve references to constructors, unlike `-r'. It |
6 | does not work to use `-Ur' on files that were themselves linked |
7 | with `-Ur'; once the constructor table has been built, it cannot |
8 | be added to. Use `-Ur' only for the last partial link, and `-r' |
9 | for the others. |
Blöde Frage aber: wo setze ich diese Linkeroption ein? a) in den LinkerCommand des Hauptprogrammes b) in den LinkerCommand der Libs (AVR32 Studio nennt das AVR32 Archiver - der wird mit -r gefüttert zZ) Im Hauptprogramm hat es zu misteriösen fehlern geführt, deren Wortlaut ich gerade nicht mehr wiedergeben kann. Btw: Linkerspeed ist wirklich vn der Anzahl der Dateien abhängig. Mit Fragmentierung hat das nichts zu tun. Die Dateien müssen alle einzeln geöffnet werden und das ist das was Zeit frist. (Wäre auch auf meinem Mp3-Player so ;-) ) Eine Einzelne datei zu öffnen ist hingegen rasend schnell.
Ich kannte die Option bislang auch nicht, hab nur ein grep auf ld --help gemacht. Auch weiß ich nicht, inwieweit das nur relokatierbaren Code betrifft und ob du relokatierbaren Code brauchst und auch dafür compilierst (zB -fpic, -fPIC, -mpic oder so). In den seltensten Fällen ist es git, ld direkt aufzurufen. Stattdessen rugt man gcc auf und passt dio Opt an ld per gcc -Wl,Ur Wissenswert für dich ist bestimmt auch http://gcc.gnu.org/onlinedocs/gccint/Collect2.html
Um die Option einzusetzen muss man das Makefile anpassen. Es geht ja darum, in zwei Stufen zu linken. Normalerweise sieht der letzte Schritt ja so ähnlich aus:
1 | output.elf: a.o b.o c.o d.o |
2 | $(LD) $(LDFLAGS) -o $@ $^ |
Das könnte man dann beispielsweise so machen:
1 | output.elf: ab.o cd.o |
2 | $(LD) $(LDFLAGS) -o $@ $^ |
3 | |
4 | ab.o: a.o b.o |
5 | $(LD) $(LDFLAGS) -Ur -o $@ $^ |
6 | |
7 | cd.o: c.o d.o |
8 | $(LD) $(LDFLAGS) -Ur -o $@ $^ |
Wenn man in diesem Fall schon einmal komplett übersetzt hat und dann was an b.c ändert, wird es zu b.o übersetzt, zu ab.o gelinkt und dann ab.o und (die schon existierende) cd.o gelinkt. Was jetzt noch keine so große Ersparnis ist, aber bei vielen Objekten und guter Aufteilung sicher was bringt.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.