Nachdem ich zu einem MSP430-Projekt Code für MMC/SDC kopiert habe, der (noch) nirgends verwendet wird und auch keine ISR enthält, ist das Programm deutlich größer; das BSS-Segment ist sogar dreimal größer und wie ich mittels StackCheck (example aus dem CVS) sehe, ist der Stack deshalb nun kurz vor'm overflow! Ich habe schon Optionen wie -Os, --gc-sections und -ffunction-sections ausprobiert, aber ohne Erfolg. Wie bekommt man denn unbenutzten Code eleminiert? Bisher war ich ja der Meinung, daß der Linker ungenutzten Code nicht linkt, so daß ungenutzter Code automatisch nicht im fertigen Programm ist, aber das stimmt leider nicht.
Übliche Linker linken auf Objektdateieebene; wenn Dein ungenutzer Code zusammen mit genutztem Code in einer Objektdatei steht, wird er auch zum Endprodukt gelinkt. Auch deswegen ist es sinnvoll, Code auf mehrere Objektdateien aufzusplitten. Diese entsprechen einzelnen C-Sourcefiles, die einzeln zu übersetzen sind (per Makefile) und nicht, wie hier leider des öfteren gesehen, per #include-Anweisung eingebunden werden sollten. In letzerem Falle ergibt das ganze nur eine einzelne Objektdatei ... Modernere Linker können (mit Unterstützung durch den Compiler) auch auf Funktionsebene linken, in diesem Falle enspricht eine Objektdatei im Grunde genommen einer Library. Ob aber ein Compiler für so "kleine" Prozessoren wie AVR, MSP430 o.ä derartiges unterstützt, entzieht sich gänzlich meiner Kenntnis.
@ Rufus T. Firefly: Der Code ist doch aufgesplittet; deshalb ist es mir ja aufgefallen. Ich könnte die betreffenden Dateien über defines leeren, aber das sollte der Linker doch besser können.
Du verwendest mehrere Sourcefiles und linkst auch die erzeugten Objektdateien, verwendest also nicht #include "blafusel.c"? Dann muss ich gestehen mit meinem Latein am Ende zu sein.
Pack alles in eine library, nur aus dieser werden die Objektmodule nur bei Bedarf extrahiert. Alles, was auf der Kommandozeile steht, wird immer gelinkt (du wirst dir ja was dabei gedacht haben, es auf die Kommandozeile zu schreiben).
@Rufus T. Firefly: *.c-Dateien braucht man nicht zu includieren, denn man kann dem Compiler ja mehrere *.c-Dateien übergeben, selbst wenn man kein Makefile verwendet. @Jörg Wunsch: Eine Library ist doch nur ein Objektfile, oder gibt's da noch Unterschiede?
Hallo Rolf! >Eine Library ist doch nur ein Objektfile, oder gibt's da noch >Unterschiede? Oh ja! Eine Library ist "nur" ein Archiv aus mehreren Objektdateien. Wenn Du für jede Funktion eine eigene Quelldatei anlegst und aus dieser eine Objektdatei erstellst, kannst Du sie nachher mit dem Archivierungstool [avr-]ar zu einer Library zusammenfügen. Der Linker kann dann, anhand der nicht aufgelösten Referenzen, entscheiden, was er aus diesem Archiv benötigt und was nicht. Ich habe dazu auch mal einen kleinen (möglicherweise nicht ganz vollständigen) Artikel im Wiki geschrieben, siehe -> http://www.mikrocontroller.net/articles/Libraries
Nachtrag: Den Namen des Archivierungstools kannst Du natürlich beliebig ersetzen: - auf dem PC: ar - auf den avr: avr-ar - auf den msp430: msp430-ar - auf den arm: arm-elf-ar - ...
@Jörg: Du hast natürlich recht. 's war ein langer Tag, gestern. (Linken mit Libraries<->Objektdateien). Irgendwie war ich auf den -möglichen- #include-Anfängerfehler fixiert. @Rolf: Ich wollte nicht so deutlich schreiben, daß #include "blafusel.c" in meinen Augen ein Fehler ist, da sich hier des öfteren schon Leute wegen banaleren Dingen auf die Füße getreten fühlten.
@Rufus T. Firefly: Das Includieren von *.c-Dateien ist zwar schlechter Stil, aber es gibt noch einiges schlimmere, beispiespielsweise Asssembler-Makros in *.c oder *.h-Dateien; wenn man zum Prettyprinting den indent drüber laufen läßt, wird der C-Code zwar schön formattiert, der Assembler-Code meist unbrauchbar. Anstatt den Assembler-Code in *.s-Dateien zu packen (und notfalls zu inkludieren) landet er leider insbesondere bei Kernel-Sourcen häufig ganz woanders. Wenn man mal den indent, oder einen anderen Prettyprinter/Beautyfier über *.[ch] in den Linux-Kernel-Sourcen laufen lässt, gibt das wenig erfreuliche Ergebnisse und deckt z. B. viele Treiber auf, die sich nicht einmal kompilieren lassen, weil fundamentales wie das Klammer-Gleichgewicht nicht erfüllt ist, da die Dateien nur angefangen wurden. @Patrick Dohmen: Ok, ich seh' mir das mal an und werde wohl ALLE *.c-Dateien in Libraries umwandeln.
Wenn ich das jetzt richtig verstehe, willst Du für jede Quelldatei eine Library erstellen? So war das nicht gedacht... foo.c {func1, func2} -> foo.o bar.c {func3} -> bar.o foo.o, bar.o -> libfoobar.a foo.o enthält zwei Funktionen, die immer beide im Endprodukt vorhanden sind, sobald eine der Funktionen im Code referenziert wird. bar.o enthält eine Funktion die, unabhängig vom Referenzieren einer Funktion aus foo.c, nur dann im Endprodukt vorhanden ist, wenn sie auch referenziert wird.
benutzte symbole kannst du mit gcc ... -Wl,--retain-symbols-file <file> festlegen, wobei in <file> eine liste mit den zu linkenden symbols stehen muss, alle anderen werden ignoriert. (nb: hab das auf avr aber noch nie probiert) der Linker kann ansonsten nur auf .o file ebenen alles oder nichts linken. (--[no]-whole-archive) das ist aber nur für .a oder .so's interessant.
Also ich habe nun mal bis auf die Datei mit main() alle in Libs gepackt und das Ergebnis ist: Vorher: > msp430-size aout.elf text data bss dec hex filename 19476 36 1596 21108 5274 aout.elf Nachher: > msp430-size aout.elf text data bss dec hex filename 17192 28 564 17784 4578 aout.elf Das sieht schon deutlich besser aus. Zum realistischeren Vergleich habe ich mal nachgesehen, was ohne den ungenutzten MMC-Code eingespart wird: before: text data bss dec hex filename 17572 30 568 18170 46fa aout.elf after: > msp430-size aout.elf text data bss dec hex filename 17192 28 564 17784 4578 aout.elf Neben einer bekannt bisher ungenutzten Variablen wurden auch einige aktuell brachliegende Funktionen und eine weitere Variable rausgeworfen. Nun fehlt mir noch ein Makefile, daß dies automatisch macht. Kann mal jemand ein Beispiel posten? Übrigens haben Optionen wie --retain-symbols-file -gfull --gc-sections -fdata-sections -ffunction-sections -fssa-dce -dead_strip nichts gebracht.
Ok, in "make GE-PACKT", ISBN 3-8266-1442-9, habe ich gefunden wie man es macht. Ich schalte dann bei allen meinen Projekten vor dem Linker den Archivierer und in die einzige, nicht zu einer Lib konvertierte Datei, kommt nur die Main-Funktion.
Nach dem Listing-File funktioniert es immer noch nicht richtig: Selbst wenn ich, bis auf main (das nur einen Funktionsauruf macht), alles an Funktionen und Varablen in Bibliotheken packe, gibt es immer noch dutzende Funktionen und andere Objekte (z. B. konstante Felder), die nie genutzt und auch im Sourcecode nirgendwo aufgerufen/verwendet werden und die trotzdem gelinkt werden! Diese Funktionen und konstanten Felder kann ich auskommentieren, ohne daß es nachher beim Compilieren +Linken auch nur eine Warnung gibt! Im Listing-File sehe ich sogar die MMC-Funktionen, die ungenutzt sind und ohne Probleme auskommentiert werden können! Wie bekomme ich den Linker dazu toten Code nicht zu linken?
Sorry, aber das klingt jetzt nach Kristallkugel... Kannst du das in einem Miniprojekt mit 3 Dateien nachvollziehbar gestalten?
Hi wurde hier nicht schon erwähnt das der Linker immer auf Objekt-Ebene arbeitet. Wenn du jetzt also alle MMC-Funktionen in einen C-Datei packst, diese dann in ein Object (*.o) compilierst und abschließend in ein Archiv (*.a) packst wird immer noch das ganze Object gelinkt wenn du auch nur eine Funktion aus dem Object verwendest. Du mußt jede Funktion in ein eigenes Object packen um wirklich auf Funktionsebene linken zu können. Matthias
Ich dachte eigentlich, das hätte Rolf bereits verstanden.
@Matthias Weißer: Aus der mmc.c wird doch absolut nix verwendet; wenn ich die im Makefile weglasse, wird ohne die gelinkt. Wenn ich die aber als Bibliothek einbinde, landen Funktionen daraus im elf-file. Ich finde da sogar aus einer anderen Bibliothek eine Funktion zur Berechnung des Binominalkoeffizienten, die ich nirgends verwende und einigen anderen toten Code. So wie beschrieben wird nicht gelinkt!
Merkwürdig; nun sind die mmc-Sachen wieder draußen, nachdem ich nochmal gecheckt habe. Aber es bleibt noch das Problem, daß die Libs jeweils komplett gelinkt werden, obwohl der Linker nur einzelne Referenzen auflösen sollte, also nur referenzierten Code linken sollte. Tatsächlich werden die Libs aber komplett gelinkt. Wie kann man das abstellen?
Hi @Jörg Offensichtlich nicht. @Rolf Hast du dir mein Posting überhaupt durchgelesen und verstanden? Der Linker der gcc arbeitet eben auf Object-Ebene und nicht auf der Ebene einzelner Referenzen. Wie du dein gewünschtes Verhalten realiseren kannst hab ich in http://www.mikrocontroller.net/forum/read-2-215461.html#217656 bereits beschrieben (weiteres teilen deiner Module in mehrere *.o Dateien) Matthias
@Rolf: Hast Du Dir meinen Artikel durchgelesen? Die Option 's' für 'ar' ist sehr wichtig, sie sorgt für das Erstellen eines Index der Library. Wenn ich mich richtig erinnere: Erstellt man keinen Index für die Library, dann kann der Linker nur das komplette Archiv linken!
sach mal, warum linkst du das obj-file überhaupt, wenn sowieso keine funktionen daraus genutzt werden???
@Matthias Weißer: Ein Objekt ist in C99 "A region of data storage in the execution environment, the contents of which can represent values.", also beispielsweise eine int-Variable oder eine Funktion. Und das Problem ist, daß der Linker diese nicht gezielt linkt, sondern stattdessen einfach die ganze Lib anhängt und nur nötige Referenzen reinpackt. @Patrick Dohmen (OldBug): Ich verwende -rcs; da ist s bei.
Nein, wir reden hier von Objektmodulen, also gewissermaßen dem Kompilat einer `translation unit', um im C99-Jargon zu bleiben. Das ist die Kategorie, in der der Linker denkt. Der hat nämlich von C keine Ahnung, der kann allen möglichen Krempel linken. Du solltest bitte deine C-Programme funktionsweise (bzw. eben objektweise, wenn du das gern so hättest) zerhacken, einzeln compilieren und in eine Bibliothek stopfen.
Falls dich der Begriff ,Objekt' hier verwirrt: ist seit 20 Jahren gängig dafür. Sollte eigentlich wohl ,Objektcode' heißen, im Gegensatz zu ,Quellcode'. Daher die Endung .o (oder .obj in anderen Systemen).
Hm, dann muß ich aus gut 10 Quellcodedateien (+ebensoviele Header) ca. 50 machen ... Gibt's denn keine Tools um toten Code zu beseitigen, beispielsweise Linker, die nur das linken, was wirklich referenziert wird anstatt jeden Müll mit zu linken? Helfen würde auch schon ein Pre-Linker, der alle nicht referenzierten (C99-)Objekte aus den Bibliotheken/Objektdateien entfernt.
War nicht für GCC 4.0 eine Funktion angekündigt die ungenutzten Code entfernt?
Naja, ich finde das einen ziemlichen Hack, -ffunction-sections plus -gc-sections sollte das wohl tun, aber selbst das will ja bei Rolf nicht funktionieren. Ich verstehe aber nach wie vor nicht, was an ,,eine Funktion pro Datei'' für eine Bibliothek so schlimm ist. Unix-Bibliotheken können seit 30 Jahren ganz gut damit leben. Der Vorteil ist, dass man halt selbst die Kontrolle hat, was gemeinsam reingeht, statt das irgendwelchen ominösen Heuristiken zu überlassen.
Andererseits finde ich es auch irgendwie albern irgend einen Hardwaretreiber der aus vielen kleinen 3-Zeilern besteht in einzelne Dateien aufzusplitten.
Brauchst du bei dem den wirklich jede Funktion unabhängig voneinander? Ansonsten bleibt natürlich immer noch die Variante, wie sie z. B. die libgcc selbst macht: eine große Datei, die mit #ifdef n-mal in n verschiedene Objektmodule übersetzt wird.
Braucht man natürlich nicht immer. Aber der libgcc-Hack zeigt doch auch dass das Ganze im Grunde eine unnötige Belastung ist die der Compiler einem ohne weiteres abnehmen könnte.
Ja, wenn ich es partiell einschalten könnte, wäre ich ganz zufrieden damit. Aber nur global für die ganze Applikation? Da misstraue ich der Heuristik zu sehr. Vielleicht bin ich ja auch konservativ. :)
Hmm. Das mit der Heuristik verstehe ich nicht; sollte es für den Compiler/Linker nicht ganz trivial sein alle referenzierten Funktionen zu finden?
Nun ja, was willst du alles drin haben? Gängiges Beispiel sind all die Initialisierungsdinge. crt1.o muss man also schon mal auf jeden Fall ausnehmen, aber eben auch Teile, die man selbst schreibt und die in die Initialisierung eingehen wie die Aktivierung von externem RAM.
Das ist doch ganz klar was reingehört: Der Starup-Code, der vor main gelinkt wird und ansonsten muß rekursiv nur das rein, was von Main (u. den ISRs) verwendet wird. Da ist ganz klar, ob ein Objekt referenziert wird oder nicht. Schließlich muß das, was verwendet wird (nicht-toter Code) ja irgendwie erreicht werden, also von irgendwo referenziert werden. Und wenn es keine Kette von Referenzen von main oder einer ISR gibt, ist der Code tot; der wird niemals ausgeführt (falls nicht zufälligerweise ein wild gewordener Pointer auf ihn und zudem an die richtige Stelle kommt).
Nein, mein eigenes XRAM-Init wird effektiv Bestandteil des Startup-Codes [__attribute__((section(".init3")))] und ist nirgends referenziert. Die garbage collection würde es rauswerfen. Ich müsste für solche Dinge also explizit auf der Linker-Kommandozeile mit -u erzwingen, dass sie aufgenommen werden. Für den vorgefertigten Startup-Code müsste man ähnliche Hac^H^H^HWorkarounds festlegen um zu erreichen, dass der Linker sie nicht rauskippt (da dem bei der GC-Methode ja völlig wurscht ist, ob der ,,tote'' Code aus einer Bibliothek oder einer direkt angegebenen Objektdatei stammte). Ich finde das alles mindestens genaus eklig anzufassen wie die Eine-Objektdatei-pro-Bibliotheksfunktion-Methode.
@Jörg Wunsch: Der Linker bekommt doch den Einstiegspunkt und über den wird auch dein Startup-Code referenziert.
Nein. Der wird über den Linkerscript und die .initN sections verkettet. Die Symbole darin sind Schall und Rauch, die haben nur erläuternden Charakter und werden von niemandem referenziert.
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.