Forum: Compiler & IDEs Wie ungenutzten Code nicht linken lassen?


von Rolf F. (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ü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.

von Rolf F. (Gast)


Lesenswert?

@ 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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

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


Lesenswert?

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).

von Rolf F. (Gast)


Lesenswert?

@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?

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

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

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

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
 - ...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

@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.

von Rolf F. (Gast)


Lesenswert?

@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.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

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.

von ●● pit ●. (Gast)


Lesenswert?

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.

von Rolf F. (Gast)


Lesenswert?

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.

von Rolf F. (Gast)


Lesenswert?

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.

von Rolf F. (Gast)


Lesenswert?

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?

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


Lesenswert?

Sorry, aber das klingt jetzt nach Kristallkugel...

Kannst du das in einem Miniprojekt mit 3 Dateien nachvollziehbar
gestalten?

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

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

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


Lesenswert?

Ich dachte eigentlich, das hätte Rolf bereits verstanden.

von Rolf F. (Gast)


Lesenswert?

@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!

von Rolf F. (Gast)


Lesenswert?

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?

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

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

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

@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!

von tenner (Gast)


Lesenswert?

sach mal, warum linkst du das obj-file überhaupt, wenn sowieso keine
funktionen daraus genutzt werden???

von Rolf F. (Gast)


Lesenswert?

@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.

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


Lesenswert?

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.

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


Lesenswert?

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).

von Rolf F. (Gast)


Lesenswert?

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.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

War nicht für GCC 4.0 eine Funktion angekündigt die ungenutzten Code
entfernt?

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


Lesenswert?

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.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Andererseits finde ich es auch irgendwie albern irgend einen
Hardwaretreiber der aus vielen kleinen 3-Zeilern besteht in einzelne
Dateien aufzusplitten.

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


Lesenswert?

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.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

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.

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


Lesenswert?

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. :)

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Hmm. Das mit der Heuristik verstehe ich nicht; sollte es für den
Compiler/Linker nicht ganz trivial sein alle referenzierten Funktionen
zu finden?

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


Lesenswert?

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.

von Rolf F. (Gast)


Lesenswert?

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).

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


Lesenswert?

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.

von Rolf F. (Gast)


Lesenswert?

@Jörg Wunsch:
Der Linker bekommt doch den Einstiegspunkt und über den wird auch dein
Startup-Code referenziert.

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


Lesenswert?

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
Noch kein Account? Hier anmelden.